Skip to content

MCP servers

An MCP server is a program that offers tools to an agent: search a code host, read a database, drive a browser. Tatu runs the servers you list and gives their tools to the model next to its own.

Put this in ~/.tatu/mcp.json to give the model a GitHub server and a documentation server:

{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" }
},
"docs": {
"type": "http",
"url": "https://mcp.example.com/mcp"
}
}
}

Start Tatu and run /mcp. It lists the servers, whether each one is running and how many tools it has. The model sees the tools as mcp__github__search_code, mcp__docs__lookup and so on, and a call shows in the conversation as github/search_code(...).

Most MCP servers document a command to add them. Tatu takes the same one with tatu in place of the other agent’s name:

Terminal window
tatu mcp add --transport http sentry https://mcp.sentry.dev/mcp
tatu mcp add github -e GITHUB_PERSONAL_ACCESS_TOKEN='${GITHUB_TOKEN}' -- npx -y @modelcontextprotocol/server-github

The first adds a remote server by its address. The second adds a local one: everything after -- is the program and its arguments, and -e sets a variable for it. The flags can come before or after the name. The server is written to ~/.tatu/mcp.json and Tatu uses it from the next session you start, so there is no file to edit and no JSON to get wrong.

Command What it does
tatu mcp add <name> <url> Add a remote server. --transport http is the default for an address, and -H "Name: value" adds a header.
tatu mcp add <name> -- <command> [args...] Add a local server. -e KEY=value sets an environment variable.
tatu mcp add-json <name> '<json>' Add a server from its JSON entry, with any of the keys of the file, such as exposure or timeout.
tatu mcp remove <name> Take a server out of its file, and forget that you turned it on or off and the sign-in to its address.
tatu mcp list The configured servers: kind, scope, whether each is ready, off or waiting for trust, and what it runs.
tatu mcp get <name> One server: where it comes from, what it runs, and the names (not the values) of its variables and headers.

-s project writes the server to .mcp.json in the folder you are in, for the project. The default, -s global, is ~/.tatu/mcp.json. The scope names of other agents work too: user means global, and local also means global, with a note, because Tatu has no scope of its own for one folder. remove looks in both files; if the name is in both, say which with -s. A name that is already taken is refused unless you add --force. A name has up to 40 letters, digits, - or _.

A server is checked before it is written, with the same rules Tatu uses to read the file, so what tatu mcp add refuses (an sse server, an address that is not http or https, a missing command) is what /mcp would have left out. Nothing starts and nothing connects to the network. Only the servers in the command change: the rest of the file is kept, although its keys come out sorted and indented by two spaces.

A server added to a project still waits for your OK. Run /mcp trust in Tatu once, after reading the file.

File Servers
~/.tatu/mcp.json yours, for every project
.mcp.json in the folder you start Tatu in the project’s. Parent folders are not searched.

A project server with the same name as one of yours replaces it. Tatu reads the files when it starts: restart it after you edit them. A server added with tatu mcp add is used by the next session you start. The format is the one other agents use, so a .mcp.json you already have works as it is.

A server is one of two kinds:

Kind Fields
Local command (required), args, env, cwd. It runs as a program on your computer, in the project’s folder unless cwd says otherwise (a relative cwd is relative to the project).
Remote url (required, https:// or http://) and headers. Add "type": "http" if you like; a url is enough.

${VAR} in a value is replaced by the environment variable, and ${VAR:-fallback} uses the fallback when the variable is empty or not set. This works in command, args, env, cwd, url, headers and oauth. A variable that is not set with no fallback becomes empty, and Tatu says so at startup. A bare $VAR is left as it is.

These keys are Tatu’s own and are all optional:

Key What it does
enabled false leaves the server off. /mcp enable can still turn it on for you.
timeout seconds one tool call may take. Default 60.
exposure how the server’s tools reach the model: "auto" (default), "direct", "codemode" or "hidden". See How many tools the model sees.
toolExposure exposure for tools whose name matches a pattern: { "delete_*": "hidden" }. Patterns use *, ? and [...].
oauth how to sign in to a remote server: clientId, clientSecret, scope, callbackPort. See below.

A problem in the file, such as a server with neither command nor url, or "type": "sse", is reported at startup as a note that starts with mcp:. The server with the problem is left out and the others load.

A server does not start when Tatu does. It starts the first time the model calls one of its tools, and stops after 10 minutes without a call. A call that takes longer than the server’s timeout fails, and so does a server that does not start within 30 seconds.

To tell the model which tools exist without starting anything, Tatu keeps the list of each server’s tools in ~/.tatu/mcp-cache/. The first time Tatu sees a server there is no list yet, so it starts that server in the background just to read it. If nothing uses it, it stops again after 30 seconds. A server that is changed (another command, other arguments or environment) is read again.

A server that stops on its own is started again by the next call. If a server cannot start, /mcp shows why, with what the program wrote to its error output.

Every tool in the model’s list costs tokens on every request and gives the model more to choose from. A few servers are fine; a dozen servers with hundreds of tools are not. So a server’s tools reach the model in one of three ways, set with exposure:

exposure The model
"direct" has every tool of the server in its list, as mcp__<server>__<tool>.
"codemode" does not have them in its list. It is told the server exists, and a program written with the codemode tool finds the tools (searchTools, describeTool) and calls them (tools.mcp__github__search_code({...})). Intermediate results stay in the program.
"hidden" has none of them, and neither has a program.
"auto" (default) gets "direct" while the declarations of the servers fit about 3,000 tokens, in the order of your files, and "codemode" for the servers that no longer fit.

auto decides for the whole server, once, the first time Tatu knows its tools, and it does not change for the rest of the session, so the tool list stays as it was. From the second session on, the tools of every server are known from the start and the choice is the same each time. A tool matched by toolExposure keeps what you wrote, and a server you set to "direct" or "codemode" does not use up the budget of the others. /mcp shows what each server got, and /mcp <server> marks the tools that only a program calls.

If you turn codemode off (codemode.enabled in the settings), every tool is direct, whatever exposure says: the model is never left without its tools.

  • The model calls a tool mcp__<server>__<tool>. Characters other than letters, digits, _ and - become _, and a name is cut to 64 characters.
  • The description and parameters are the server’s. Tatu adds the name of the server to the description.
  • A tool the server declares read-only can run together with other calls in the same response. The others run one at a time.
  • An answer longer than 50,000 bytes comes back as its first lines, with the path of a file in the temporary folder (tatu-mcp) that holds all of it. The model can read or search that file.
  • Images in an answer (PNG, JPEG, GIF, WebP) reach the model if it takes images. Audio and other binary content are left out, and the model is told so.
  • Subagents of the general-purpose type have the tools of your servers: the direct ones in their list, the others through their own codemode tool. In your own custom subagents, list them in tools by name or with a pattern, and they get exactly those, in their list whatever the exposure: tools: read, grep, mcp__github__*.
  • Hooks see these tools like any other. To match every tool of a server, use the matcher mcp__github__.*.
Command What it does
/mcp Open the list of servers with their state. Without servers, it shows how to add one.
/mcp <server> One server in detail: what runs, its instructions, its tools and any error.
/mcp trust, /mcp untrust Allow or refuse the servers of this project’s .mcp.json.
/mcp enable <server>, /mcp disable <server> Turn a server on or off for you.
/mcp login <server>, /mcp logout <server> Sign in to a remote server, or forget the sign-in.
/mcp reconnect <server> Stop a server and start it again.

/mcp opens a list in place of the editor, a row for each server with how it is started, where it is defined and its state. What needs you is marked after the name: needs sign-in, failed, off, waits for trust. Press Enter on a server to open its own panel, which offers only what makes sense for it: its details and tools, turn it on or off, sign in or out (remote servers), reconnect, and trust or untrust the project’s servers. Esc goes back to the list. Ctrl-R on a row reconnects that server without opening it.

When the project’s .mcp.json waits for your OK, the last row of the list is Trust this project’s servers. It is last on purpose, so that pressing Enter as soon as the list opens never trusts anything.

What an action says, such as github is off, appears at the bottom of the panel that comes back with the new state, and not in the conversation. A command you type yourself, such as /mcp disable github, prints its answer in the conversation, as before. In -p mode the commands print text.

Turning a server on or off is remembered in ~/.tatu/mcp-projects.json. Turning a server on or off with /mcp does not edit the files. Only tatu mcp add and tatu mcp remove do.

Trust is per project folder and is remembered in ~/.tatu/mcp-projects.json. /mcp untrust takes it back and stops the servers. In a run with -p, a project you have not trusted starts nothing and prints the note; trust the project once in an interactive session. Your own ~/.tatu/mcp.json needs no trust.

Some remote servers answer 401 until you sign in. Tatu shows such a server as needing a sign-in, and its tools are not offered to the model. Run /mcp login <server>: Tatu shows an address to open in your browser. After you approve there, the browser returns to a page on your own computer (127.0.0.1) and Tatu finishes by itself. If your browser is on another computer, paste the code, or the address the browser ended on, into the question Tatu shows.

Tatu registers itself with the server when the server allows it. If the server does not, give a client in the server’s oauth key:

{
"mcpServers": {
"docs": {
"url": "https://mcp.example.com/mcp",
"oauth": { "clientId": "abc123", "scope": "read", "callbackPort": 8765 }
}
}
}

callbackPort is the port of the page the browser returns to, for a server that accepts only one address; by default Tatu uses any free port.

The sign-in is kept in ~/.tatu/mcp-auth.json, which only you can read. Tatu renews it when it expires, and asks you to sign in again if the server no longer accepts it. /mcp logout <server> removes it. If a server has an Authorization header in headers, Tatu sends that and does not sign in.

  • There are no permission prompts for these tools, as for the others: a server’s tools run with your permissions. Install servers you trust.
  • Tatu uses the tools of a server. It does not use its resources or prompts, and it does not answer a server’s requests to use your model.
  • A server of "type": "sse" is not supported. Use the server’s streamable HTTP address.
  • A server’s answer is data for the model, not an instruction from you, but a server you do not trust can still try to steer the model.