> ## Documentation Index
> Fetch the complete documentation index at: https://docs.open4rena.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP servers

> Connect external tools to o4 with the Model Context Protocol.

An MCP server is a separate program or web service that gives the model extra
tools, such as access to an issue tracker, a database, or a docs site. o4
speaks the Model Context Protocol (MCP), so you can connect any MCP server
over stdio, Streamable HTTP, or the older SSE transport.

Add an MCP server when you want the model to use a system that o4's built-in
tools can't reach.

## Add a server

The quickest way to add a server is the MCP browser. In a session, run:

```text theme={null}
/mcp
```

Choose **Add server** and follow the steps: pick a transport (`stdio` or
`sse`), give the server a name, enter the command and arguments or the URL,
add any environment variables or headers (one `KEY=value` per line; press
`Shift+Enter` for a new line), choose the scope, and confirm. o4 splits the
arguments the way a shell does, so quote an argument that contains spaces.
o4 starts the server right away and the model can use its tools in the same
session. If the server fails to connect, o4 shows the error and doesn't save
the entry.

In the steps, `Enter` or `Tab` moves on, `Backspace` in an empty field goes
back a step, and `Esc` cancels. To add a Streamable HTTP server, edit the
config file.

You can also edit the config file yourself. For a server that runs as a local
command, create `.o4/.mcp.json` in your project:

```json theme={null}
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
    }
  }
}
```

o4 reads the config files when a session starts. If you edit a file while a
session is running, start a new session to pick up the change, or change the
server from `/mcp` with **Edit config**, which reloads the file when you close
your editor.

<Note>
  Servers in a project's `.o4/.mcp.json` only load after you trust the
  workspace. See [Project servers need workspace trust](#project-servers-need-workspace-trust).
</Note>

## Where servers are configured

o4 reads MCP servers from two files. Both use the same format.

| Scope | File | Loaded |
| - | - | - |
| User (global) | `~/.o4/.mcp.json` | In every workspace |
| Project | `.o4/.mcp.json` in the workspace | Only when the workspace is trusted |

If both files define a server with the same name, the project entry wins.

When you add a server through **Add server** in `/mcp`, the scope step starts
on global. `o4 mcp add` and `/mcp add <name>` write to the project file unless
you pass `--global` to the CLI command.

o4 writes these files with owner-only permissions, and refuses to read a
config file that is a symlink or larger than 4 MiB.

Plugins can also provide MCP servers. Those servers are never written to
`.mcp.json`, and a server with the same name in your own config takes
precedence over the plugin's. See [Plugins and marketplaces](/extend/plugins).

## Config format

Each entry under `mcpServers` is keyed by the server name. The name also
prefixes the server's tools: a tool called `search` on a server named `docs`
reaches the model as `docs__search`.

### Local servers (stdio)

o4 starts the command as a child process and talks to it over standard input
and output. `transport` defaults to `stdio`, so you can leave it out.

```json theme={null}
{
  "mcpServers": {
    "tracker": {
      "command": "tracker-mcp",
      "args": ["--read-only"],
      "env": { "TRACKER_URL": "https://tracker.example.com" },
      "pass_env": ["TRACKER_TOKEN"]
    }
  }
}
```

The child process does not inherit your full environment. o4 passes only a
small safe set of variables (`PATH`, `HOME`, `USER`, `LOGNAME`, `SHELL`,
`TERM`, `TMPDIR`, `TZ`, `LANG`, `PWD`, and the `LC_*` locale variables), so
API keys in your shell don't leak to MCP servers. To give a server more, set
values in `env` or name variables to copy from your environment in
`pass_env`. A value in `env` wins over one from `pass_env`. `LD_PRELOAD` and
`DYLD_*` variables are never passed through `pass_env`.

### Remote servers (HTTP and SSE)

For a server reached over the network, set `transport` and `url`:

```json theme={null}
{
  "mcpServers": {
    "docs": {
      "transport": "http",
      "url": "https://mcp.example.com/mcp",
      "bearer_token_env_var": "DOCS_MCP_TOKEN"
    }
  }
}
```

| `transport` value | Protocol |
| - | - |
| `http`, `streamable_http`, or `streamable-http` | Streamable HTTP |
| `sse` | The older HTTP with Server-Sent Events transport |

The URL must start with `http://` or `https://`. o4 rejects URLs that point
at `localhost`, loopback, private-network, or link-local addresses, so you
can't use a remote transport for a server running on your own machine. Run
local servers with `stdio` instead.

o4 includes a test switch for this: start o4 with `O4_ALLOW_LOCALHOST=1` and
it accepts loopback and private-network addresses too. Link-local
addresses, such as the cloud metadata address `169.254.169.254`, stay
blocked.

### All server fields

<ParamField path="transport" type="string" default="stdio">
  `stdio`, `sse`, `http`, `streamable_http`, or `streamable-http`. You can
  write `type` instead of `transport`.
</ParamField>

<ParamField path="command" type="string">
  The program to run. Required for `stdio`.
</ParamField>

<ParamField path="args" type="string[]">
  Arguments for `command`.
</ParamField>

<ParamField path="env" type="object">
  Environment variables to set for a `stdio` server.
</ParamField>

<ParamField path="pass_env" type="string[]">
  Names of variables to copy from o4's environment into a `stdio` server,
  when they are set.
</ParamField>

<ParamField path="url" type="string">
  The server endpoint. Required for `sse` and HTTP transports.
</ParamField>

<ParamField path="headers" type="object">
  HTTP headers to send, written into the config as plain text. You can write
  `httpHeaders` instead of `headers`.
</ParamField>

<ParamField path="bearer_token_env_var" type="string">
  The name of an environment variable that holds a token. o4 reads it when it
  connects and sends `Authorization: Bearer` with the token. HTTP and SSE
  only. You can't combine it with an `Authorization` entry in `headers`.
</ParamField>

<ParamField path="env_http_headers" type="object">
  Headers whose values come from environment variables, as
  `"Header-Name": "VARIABLE_NAME"`. HTTP and SSE only. A header can't appear
  in both `headers` and `env_http_headers`.
</ParamField>

<ParamField path="enabled" type="boolean" default="true">
  Set to `false` to keep the entry but not start the server.
</ParamField>

<ParamField path="startup_timeout_sec" type="integer">
  Seconds to wait for the server to finish its startup handshake and list its
  tools. The default is 10 seconds, or 30 seconds for `/mcp restart <name>`
  and for servers a subagent starts. `/mcp restart` without a name gives each
  server at most 10 seconds, even when you set this higher.
</ParamField>

<ParamField path="tool_timeout_sec" type="integer" default="30">
  Seconds to wait for a single tool call. Must be more than 0.
</ParamField>

<ParamField path="enabled_tools" type="string[]">
  Only these tools are shown to the model. Use the tool names without the
  server prefix.
</ParamField>

<ParamField path="disabled_tools" type="string[]">
  Tools hidden from the model. A tool listed in both `enabled_tools` and
  `disabled_tools` is hidden.
</ParamField>

<ParamField path="required" type="boolean" default="false">
  In print mode, a required server that fails to start stops the run with an
  error. In an interactive session, the failure is listed on the **Errors**
  tab of `/mcp` as "Required MCP server failed to start" and the session
  continues.
</ParamField>

o4 checks each entry when it loads the file. An invalid entry, such as a
`stdio` server without `command` or a blocked URL, is skipped and a warning is
written to the log. The other servers still load.

## Authentication

o4 does not run an OAuth sign-in flow for MCP servers. For a server that needs
a token, keep the token in an environment variable and point the config at it
with `bearer_token_env_var` or `env_http_headers`:

```json theme={null}
{
  "mcpServers": {
    "tracker": {
      "transport": "http",
      "url": "https://tracker.example.com/mcp",
      "env_http_headers": { "X-Api-Key": "TRACKER_API_KEY" }
    }
  }
}
```

o4 reads the variables when it connects to the server and doesn't write their
values to disk or to the log. If a variable is missing or empty, that server
fails to start with an error that names the variable. Start o4 from a shell
where the variable is set:

```bash theme={null}
export TRACKER_API_KEY="your-key"
o4
```

For `stdio` servers, pass credentials with `env` or `pass_env` instead.

## Manage servers in a session

`/mcp` opens the MCP browser. It has three tabs:

* **Configured**: your servers with their status, scope, transport, and tool
  count. Select a server to restart it, enable or disable it, remove it, open
  its config file in your editor (`$EDITOR`, then `$VISUAL`, then `vi`), or
  view its tools.
* **Registry**: search the MCP registry and add a server from the results.
* **Errors**: servers that failed to start, and failed MCP actions such as a
  registry search or a restart, with the error message.

Disabling a server sets `enabled` to `false` in its config file and removes
its tools from the session. **Edit config** opens the file that defines the
server; when you close the editor, o4 reloads the config and restarts that
server.

While the model is working, you can still open the browser and search the
registry, but adding, removing, enabling, disabling, restarting or editing a
server is refused: o4 closes the browser and shows
`Wait for the current turn to finish before making this change.` The typed
commands that change servers, such as `/mcp add <name>` and
`/mcp remove <name>`, wait for the turn to finish instead.

In the browser, `Up` and `Down` move between rows, and `Tab` and `Shift+Tab`
(or `Right` and `Left`) switch tabs. Type to filter the **Configured** list;
`Backspace` deletes a character and `Ctrl+U` clears the filter. On the
**Registry** tab, type a query and press `Enter` on **Search registry**.
`Enter` opens a server's details or runs the selected action, `Backspace`
goes back from the details, and `Esc` closes the browser. See
[Keyboard shortcuts](/reference/keyboard-shortcuts#browsers).

You can also go straight to an action:

| Command | What it does |
| - | - |
| `/mcp` | Open the MCP browser |
| `/mcp add` | Open the **Add server** steps |
| `/mcp add <name>` | Add a server from the registry to the project config and start it |
| `/mcp remove <name>` | Remove a server and stop it. o4 removes it from the project config if it's there, and otherwise from the global config. |
| `/mcp list` | Open the browser on your configured servers |
| `/mcp search` | Open the browser on the **Registry** tab |
| `/mcp search <query>` | Search the registry and show the results on the **Registry** tab |
| `/mcp restart [name]` | Restart one server, or all of them when you leave out the name |

The **MCP** tab in `/config` (run `/config mcp` to go straight to it) has one
row, **Open MCP browser**. It opens the same MCP browser as `/mcp`.

## Manage servers from the command line

The `o4 mcp` commands work outside a session, which is useful in scripts.

| Command | What it does |
| - | - |
| `o4 mcp list` | List the servers in your config files. `o4 mcp` on its own does the same. |
| `o4 mcp search <query>` | Search the MCP registry |
| `o4 mcp add <name>` | Add a server from the registry to the project config |
| `o4 mcp add <name> --global` | Add a server from the registry to `~/.o4/.mcp.json` |
| `o4 mcp remove <name>` | Remove a server |
| `o4 mcp restart [name]` | Not available outside a session (see below) |

`o4 mcp list` only reads the config files, and skips the project file in an
untrusted workspace. It doesn't start the servers, so the `Status` column
always shows `stopped` and `Tools` shows `?`:

```text theme={null}
Name                 Transport  Status     Tools
--------------------------------------------------
filesystem           stdio      stopped    ?
```

`o4 mcp remove` removes the server from the project config if it's there, and
otherwise from the global config.

`o4 mcp restart` exists, but servers only run inside a session, so it exits
with "Server restart is only available within an interactive session". Use
`/mcp restart` in a session instead.

<Warning>
  In o4 0.2.74, registry lookups fail. o4 queries
  `https://registry.modelcontextprotocol.io/servers`, which the registry no
  longer serves, so `o4 mcp search` exits with "Registry search failed with
  status 404 Not Found", and `o4 mcp add <name>` reports that the server was
  not found. The **Registry** tab and `/mcp add <name>` use the same lookup.
  Until this is fixed, add servers with **Add server** in `/mcp` or by editing
  `.mcp.json`.
</Warning>

## Project servers need workspace trust

An MCP server entry can run any program on your machine, so o4 ignores a
project's `.o4/.mcp.json` until you trust that workspace. When you open an
untrusted workspace that has project servers, o4 offers to trust the
workspace and load them. To trust it from the command line, review the
checkout and run this from the workspace root:

```bash theme={null}
o4 trust
```

Trust also covers the folders inside the trusted one. `o4 untrust` revokes
it. Setting `O4_TRUST_WORKSPACE=1` trusts every workspace for that o4
process, so use it only for automation that already vets the repository.

`o4 mcp add` without `--global` refuses to write to an untrusted workspace.
See [Workspace trust](/safety/workspace-trust) for everything trust controls.

## How the model uses MCP tools

Each MCP tool reaches the model as `<server>__<tool>`, with the description
and input schema the server reports. If your built-in and MCP tools together
come to more than 64, o4 doesn't send the MCP tools up front. The model finds
them with the `tool_search` tool and loads the ones it needs.

MCP tool calls go through the same checks as built-in tools:

* In the default `ask` permission mode, the first call to a server's tools
  asks for approval. After you approve, other calls to that server's tools
  don't ask again for the rest of the session.
* Permission rules that match the tool name, such as `docs__search`, can
  allow, deny, or always ask for a tool. See
  [Permissions](/safety/permissions).
* `PreToolUse` hooks run before each call and can block it. See
  [Hooks](/extend/hooks).
* Plan mode blocks MCP tools. See [Plan mode](/guides/plan-mode).

### Resources

Some MCP servers also expose resources, such as files or records the model
can read. The model has two built-in tools for them:

* `list_mcp_resources` lists a server's resources. It doesn't ask for
  approval.
* `read_mcp_resource` reads one resource by its URI. It asks for approval
  each time.

## MCP in print mode and evals

Print mode (`o4 -p`) loads the same MCP servers as an interactive session,
including project servers when the workspace is trusted. If a server fails to
start, o4 prints an `[mcp error]` line to standard error and the run
continues, unless the server is marked `"required": true`, in which case the
run stops with an error. See [Print mode and scripting](/guides/print-mode).

`o4 eval` runs don't load MCP servers unless you pass `--enable-mcp`. See
[Evaluations](/extend/evals).
