# How MCP Server Support is Implemented in OpenAI Codex

> Explore how MCP server support is implemented in OpenAI Codex. Discover its Rust architecture, JSON-RPC connections, and TUI integration for seamless tool management.

- Repository: [OpenAI/codex](https://github.com/openai/codex)
- Tags: internals
- Published: 2026-03-06

---

**Codex implements MCP server support through a layered Rust architecture that parses user configurations from `~/.codex/config.toml`, manages persistent JSON-RPC connections via `McpConnectionManager`, and integrates external tools into the TUI with sandbox-state negotiation and human-in-the-loop approvals.**

The **Model Context Protocol (MCP)** allows Codex to extend its capabilities by connecting to external "MCP servers"—sandboxed processes that expose tools via a standardized protocol. According to the `openai/codex` source code, this integration is handled through a coordinated stack of configuration parsers, connection managers, protocol clients, and UI components written in Rust.

## MCP Server Architecture Stack

Codex’s MCP implementation is organized into four distinct layers that handle everything from user configuration to UI rendering.

### User Configuration Layer

Users declare MCP servers in **`~/.codex/config.toml`** under the `[mcp_servers]` section. The configuration parser maps these entries into **`McpServerConfig`** and **`McpServerTransportConfig`** structs, which define the command, arguments, and transport type (stdio, HTTP, or UDS) for each server.

When Codex starts, `McpConnectionManager::new` reads these configurations and initiates the connection lifecycle for each defined server.

### Connection Management Layer

The **`McpConnectionManager`** (located in [`codex-rs/core/src/mcp_connection_manager.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/mcp_connection_manager.rs)) serves as the central coordinator. For each configured server, it creates a single **`RmcpClient`** instance, performs the MCP handshake, and negotiates capabilities. The manager also tracks startup progress using **`McpStartupStatus`** and aggregates the complete list of available tools from all connected servers.

### Protocol Transport Layer

The low-level MCP JSON-RPC protocol is implemented in **[`codex-rs/rmcp-client/src/rmcp_client.rs`](https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/rmcp_client.rs)** by the **`RmcpClient`** struct. This client handles:

- OAuth authentication and session initialization
- **Tool discovery** via `list_tools`
- **Tool invocation** via `call_tool`
- **Elicitation requests** (human-in-the-loop approvals) via `send_elicitation`
- Resource listing and management

The client exposes async helpers that the connection manager uses to forward requests from the LLM to the appropriate MCP server.

### UI Integration Layer

The TUI renders MCP-specific components in two key locations:

1. **[`codex-rs/tui/src/history_cell.rs`](https://github.com/openai/codex/blob/main/codex-rs/tui/src/history_cell.rs)** — Displays the "MCP Tools" section and renders tool execution results in the conversation history.
2. **[`codex-rs/tui/src/bottom_pane/mcp_server_elicitation.rs`](https://github.com/openai/codex/blob/main/codex-rs/tui/src/bottom_pane/mcp_server_elicitation.rs)** — Implements **`AskForApproval`**, the UI component that presents elicitation requests when an MCP server requires human approval before executing a sensitive operation.

## Connection Lifecycle and Capability Negotiation

The MCP connection follows a strict lifecycle managed by the `McpConnectionManager`.

### 1. Initialization and Handshake

When Codex launches, the manager iterates through the server configurations and calls `RmcpClient::new` followed by `initialize` for each. This establishes the JSON-RPC connection and negotiates protocol capabilities.

### 2. Sandbox-State Negotiation

After the handshake, the manager advertises the **`codex/sandbox-state`** capability (defined by constants `MCP_SANDBOX_STATE_CAPABILITY` and `MCP_SANDBOX_STATE_METHOD`). It constructs a **`SandboxPolicy`** struct from the user’s configuration and sends it to the server via `send_request(MCP_SANDBOX_STATE_METHOD, ...)`.

The server confirms the policy is active by returning an empty result, ensuring all subsequent tool executions adhere to the defined sandbox restrictions (e.g., writable roots, network access).

### 3. Tool Discovery and Qualification

Once capabilities are negotiated, the manager calls `client.list_tools` to retrieve the server’s tool catalog. These tools are then processed by the **`qualify_tools`** function, which:

1. **Namespaces** tools using the delimiter `__` with the format: `mcp__<server_name>__<tool_name>`
2. **Sanitizes** names for the Responses API
3. **Truncates** names exceeding 64 bytes, appending a SHA-1 suffix to maintain uniqueness

The qualified tool list is cached for the session using the `codex_apps_tools_cache_key` identifier.

### 4. Tool Invocation and Elicitation

When the model requests an MCP tool (e.g., `mcp__bash__shell`), the TUI routes the request to `McpConnectionManager::call_tool`. The manager forwards the call to the appropriate `RmcpClient`.

If the tool requires human approval (based on the server’s `.rules` configuration), the server returns an elicitation request. The UI renders this via `AskForApproval`, and the user’s decision is sent back to the server using `client.send_approval`, allowing the command to proceed or be blocked.

## Configuring an MCP Server in Codex

To add an external tool server, define it in your Codex configuration file:

```toml

# ~/.codex/config.toml

[features]
shell_tool = false                     # disable the built-in shell tool

[mcp_servers.bash]                     # logical name used in tool prefixes

command = "npx"
args = ["-y", "@openai/codex-shell-tool-mcp"]

# optional: transport = {type = "stdio"}   # default, can also be HTTP/UDS

```

With this configuration, the shell tool becomes available as **`mcp__bash__shell`** and appears in the TUI under the **🔌 MCP Tools** section. The experimental `@openai/codex-shell-tool-mcp` package demonstrates a complete implementation that patches Bash to intercept `execve(2)` calls and applies the sandbox policy received from Codex.

## Programmatic MCP Interactions

Beyond configuration, the Rust API allows direct interaction with MCP servers during execution.

### Invoking Tools via the Connection Manager

```rust
// Inside a TUI turn, after the model requests `mcp__bash__shell`
let request = CallToolResult {
    tool_name: "mcp__bash__shell".to_string(),
    arguments: serde_json::json!({ "command": "ls -l /home/user" }),
    // … other fields omitted for brevity
};

let response = codex_app.mcp_connection_manager()
    .call_tool(request)
    .await?;   // async call to the appropriate RmcpClient

println!("MCP output:\n{}", response.result);

```

If the server’s rules specify `action = "prompt"`, the UI automatically renders an approval prompt before executing the command.

### Updating Sandbox Policies Dynamically

```rust
use codex_protocol::protocol::SandboxPolicy;
use codex_rmcp_client::RmcpClient;

// Build a policy that blocks network access and limits write roots
let policy = SandboxPolicy {
    policy_type: "workspace-write".into(),
    writable_roots: vec!["/home/user/projects".into()],
    network_access: false,
    exclude_tmpdir_env_var: false,
    exclude_slash_tmp: false,
};

// Send the update to every configured MCP server
for manager in codex_app.mcp_connection_manager().servers() {
    manager.client().send_request(
        codex_rs::core::mcp_connection_manager::MCP_SANDBOX_STATE_METHOD,
        serde_json::json!({ "sandboxPolicy": policy })
    ).await?;
}

```

This policy update instructs the MCP server to apply the new restrictions to all subsequent process spawns.

## Summary

- **Configuration**: Users define servers in `~/.codex/config.toml` under `[mcp_servers]`, parsed into `McpServerConfig` structs.
- **Connection Management**: `McpConnectionManager` in [`codex-rs/core/src/mcp_connection_manager.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/mcp_connection_manager.rs) creates and monitors `RmcpClient` instances for each server.
- **Protocol Handling**: [`codex-rs/rmcp-client/src/rmcp_client.rs`](https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/rmcp_client.rs) implements the MCP JSON-RPC protocol, including OAuth, tool invocation, and elicitation.
- **Tool Qualification**: The `qualify_tools` function namespaces tools as `mcp__<server>__<tool>`, sanitizes API names, and enforces a 64-byte limit with SHA-1 hashing.
- **Sandbox Negotiation**: The manager advertises `codex/sandbox-state` and transmits `SandboxPolicy` configurations to control server-side execution environments.
- **UI Integration**: [`history_cell.rs`](https://github.com/openai/codex/blob/main/history_cell.rs) displays tools while [`mcp_server_elicitation.rs`](https://github.com/openai/codex/blob/main/mcp_server_elicitation.rs) handles human approval flows via `AskForApproval`.

## Frequently Asked Questions

### How do I configure an MCP server in Codex?

Add an entry to the `[mcp_servers]` section of your `~/.codex/config.toml` file. Specify the command and arguments needed to launch the server (e.g., `npx -y @openai/codex-shell-tool-mcp`). You can optionally set the transport type to `stdio`, `http`, or `uds` depending on how the server communicates.

### What is the `codex/sandbox-state` capability?

The `codex/sandbox-state` capability is a Codex-specific MCP extension defined by `MCP_SANDBOX_STATE_CAPABILITY` and `MCP_SANDBOX_STATE_METHOD`. It allows Codex to transmit a `SandboxPolicy` to the MCP server after the initial handshake, dictating which directories are writable and whether network access is permitted for tools like the sandboxed Bash shell.

### How are MCP tool names formatted in Codex?

MCP tools are qualified using the pattern `mcp__<server_name>__<tool_name>`, with the delimiter `__` separating the namespace components. If the resulting name exceeds 64 bytes, the `qualify_tools` function truncates it and appends a SHA-1 hash suffix to maintain uniqueness while satisfying API constraints.

### Where is the MCP connection lifecycle managed in the codebase?

The connection lifecycle—including client creation, handshake, capability negotiation, and tool aggregation—is managed by the `McpConnectionManager` struct in [`codex-rs/core/src/mcp_connection_manager.rs`](https://github.com/openai/codex/blob/main/codex-rs/core/src/mcp_connection_manager.rs). This file also defines the `codex/sandbox-state` constants and the `qualify_tools` sanitization logic.