Configuration
~/.config/tapioca/config.toml — created (with comments) on first run and kept up to date by the app: the settings dashboard, model picker, toggles and the system prompt editor all write back to it, carrying your comments across. Manual edits are fine too; /settings opens the file in $EDITOR and hot-reloads it on save.
The config directory honors TAPIOCA_CONFIG_DIR and XDG_CONFIG_HOME; session data lives under ~/.local/share/tapioca/ (TAPIOCA_DATA_DIR, XDG_DATA_HOME).
Top-level settings
| Key | Default | Meaning |
|---|---|---|
default_provider | "ollama" | which [providers.*] entry new agents use |
default_model | "" | empty = first model reported by the provider |
system_prompt | built-in | the agents' system prompt |
max_tokens | 4096 | max output tokens per response |
temperature | 1.0 | sampling temperature |
thinking | false | ask models to emit reasoning (or use /effort) |
thinking_budget | 2048 | thinking token budget (Anthropic) |
verbose | false | full thoughts and tool output in chat |
zen | false | hide keybind hints (/zen) |
editor | "" | prompt editor; falls back to $VISUAL, $EDITOR, nvim, vim |
autosave | true | save the session after every completed turn |
auto_compact | true | summarize old turns when context nears the limit |
title_model | "" | [provider:]model for session titles; empty uses the agent's |
bash_timeout | 180 | seconds a tool call may run; a bash call can ask for up to 30 min |
model_catalog | true | fetch model prices/context sizes from models.dev at startup |
sandbox | false | confine bash with bubblewrap |
sandbox_network | true | false cuts network inside the sandbox |
theme | "taro" | taro | contrast | mono |
glyphs | "unicode" | unicode | ascii | nerd |
wordmark | "auto" | welcome screen mark: auto | compact | text | off |
permission_mode | "manual" | plan | manual | auto | bypass |
bash_allow | [] | bash command words that never prompt ([p] adds here) |
secret_env | [] | extra env vars hidden from tools and subprocesses |
[permissions]
Per-tool rules, checked before the permission mode:
[permissions]
allow = ["bash(go test*)", "edit_file(internal/**)"]
ask = ["bash(git push*)"]
deny = ["read_file(**/.env)", "bash(rm *)", "mcp:*__delete_*"]Semantics, matching rules and caveats are covered in Permissions.
[providers.*]
One table per backend; the table name is the provider name used in /model provider:name.
| Key | Applies to | Meaning |
|---|---|---|
type | all | ollama | llamacpp | anthropic | bedrock | vertex | azure | gemini | openai | vercel | custom |
base_url | most | endpoint override (required for OpenAI-compatible servers) |
api_key_env | keyed APIs | env var holding the key (preferred) |
api_key | keyed APIs | literal key (discouraged) |
context_window | all | tokens, for the context gauge; 0 = per-type default |
api_version | azure | defaults to a known-good one |
region | bedrock, vertex | cloud region |
profile | bedrock | AWS shared-credentials profile |
project | vertex | GCP project |
credentials_file | vertex | service account JSON |
auth_style | custom | bearer | header | query | none — where the credential goes |
auth_header | custom | header name, for auth_style = "header" |
auth_query | custom | query parameter name, for auth_style = "query" |
[providers.*.headers] | custom | extra headers sent on every request |
Per-provider examples are in Providers.
[costs]
Price table for the cost estimate, $ per million tokens, matched by model prefix:
[costs."claude-sonnet"]
in = 3.0
out = 15.0Sensible defaults exist for common models; entries here override them.
[colors]
Override any theme color with hex. "#hex" applies to both backgrounds, "#light/#dark" differs per background. Setting one lifts the mono theme's no-color rule.
[colors]
accent = "#6C4FD8/#A78BFA"
dim = "#8B8B98/#6A6A78"
user = "#1F8A5D/#6EE7A8"
error = "#D03050/#FB7185"
ok = "#1F8A5D/#6EE7A8"
warn = "#B45309/#FBBF24"
border = "#D9D9E3/#33333E"
think = "#8E44AD/#D8B4FE"
tool = "#1D6FB8/#7CC7FF"
code_bg = "#F1F1F6/#232330"
agents = "#A78BFA, #7CC7FF, #5EEAD4"accent covers titles, focus and highlights; dim secondary text; user your messages; think reasoning output; tool tool calls; agents is a comma-separated list of per-agent identity colors, cycled.
[dashboard]
[dashboard]
visible = true
width = 0.33
position = "right"
panels = ["agents", "tokens", "todos", "git", "tools", "settings"]width is a fraction of the screen (0.2 – 0.5), position one of right / left / top / bottom. Available panels: agents, tokens, todos, git, changes, tools, mcp, session, settings. An explicit panels = [] turns them all off.
[[mcp]]
MCP servers — stdio child processes, or remote HTTP endpoints when url is set:
[[mcp]]
name = "filesystem"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
[mcp.env]
EXAMPLE = "value"
[[mcp]]
name = "example"
url = "https://mcp.example.com/mcp"
[mcp.headers]
Authorization = "Bearer ${EXAMPLE_MCP_TOKEN}"${VAR} in headers expands from the environment.
A hosted server that wants an account rather than a token takes auth = "oauth": /mcp <name> logs in through the browser once and it refreshes on its own afterwards. The tokens are kept outside this file.
[[mcp]]
name = "linear"
url = "https://mcp.linear.app/mcp"
auth = "oauth"See MCP & LSP.
[[hooks]]
Commands of yours run around tool calls:
[[hooks]]
event = "post_tool" # pre_tool | post_tool | session_start | session_end
match = "edit_file" # globs the tool name; every tool when omitted
command = 'gofmt -w "$TAPIOCA_TOOL_PATH"'
timeout = 30 # seconds; 0 means 30A pre_tool hook that exits non-zero blocks the call, and so does one that is missing or times out. See Hooks.
[[fallback]]
When a model cannot answer — rate limited, out of quota, provider erroring — try these instead, in order:
[[fallback]]
when = "anthropic:claude-opus-5"
then = ["anthropic:claude-sonnet-5", "ollama:qwen3-coder"]A refusal or a bad request is an answer, not a failure, and is never retried elsewhere.
[[agents.external]]
Other agents to drive over the Agent Client Protocol. /connect lists them; picking one gives it a tab, and its work streams into the transcript like any other agent's:
[[agents.external]]
name = "claude-code"
command = "claude"
args = ["--acp"]
[agents.external.env]
EXAMPLE = "value"Whatever it asks to run goes through your permission rules — a deny still denies, even under bypass. See Editor Integration.
[[lsp]]
Language servers used to check every file the agent writes:
[[lsp]]
name = "gopls"
command = "gopls"
extensions = [".go"][keys]
Rebind any action. Values are Bubble Tea key names; commas separate alternatives; empty string unbinds:
[keys]
save_session = "ctrl+s,f5"The full action list is in Keybindings.