MCP Client
Feature Definition
Section titled “Feature Definition”The Model Context Protocol (MCP) allows AI coding agents to connect to external tool servers — code search engines, database interfaces, documentation providers, deployment platforms. The agent acts as an MCP client, connecting to one or more MCP servers that expose tools, resources, and prompts through a standardized JSON-RPC protocol.
The implementation challenges are substantial:
- Transport diversity. MCP supports three transport mechanisms: stdio (spawn a child process, communicate via stdin/stdout), SSE (Server-Sent Events over HTTP), and Streamable HTTP (bidirectional HTTP streaming). Each has different lifecycle management, error recovery, and authentication needs.
- Authentication. Remote MCP servers often require OAuth 2.0 with PKCE for public clients. This means implementing a full OAuth flow: dynamic client registration, authorization code exchange, token refresh, and secure credential storage. Some servers use simpler bearer tokens.
- Tool discovery and naming. Each MCP server exposes tools with arbitrary names. When multiple servers are connected, tool names can collide. The agent must namespace tools (e.g.,
mcp__server__tool) and sanitize names to match LLM API constraints (alphanumeric, underscores, hyphens, length limits). - Schema validation. MCP tools declare JSON Schema for their inputs and outputs. The agent must convert these schemas into the format its LLM provider expects (OpenAI function calling, Anthropic tool use, etc.).
- Lifecycle management. Stdio servers are child processes that must be spawned, initialized (MCP handshake), monitored, and cleaned up. Remote servers need connection pooling, timeout handling, and reconnection logic.
- Approval workflows. Some tools are dangerous (file deletion, network requests). The agent must integrate MCP tool calls into its existing permission system. For runtime management APIs and status/event orchestration around these connections, see MCP Integration. For running the coding agent itself as an MCP server, see MCP Server.
Aider Implementation
Section titled “Aider Implementation”Pin: b9050e1d
Aider does not implement MCP client functionality. The requirements.in file contains no MCP-related dependencies (no mcp, modelcontextprotocol, or similar packages). Aider’s tool system is entirely built-in — file editing, shell commands, and the repo map. External tool integration is not supported.
This is a deliberate design choice: Aider focuses on the edit loop (user → LLM → edit → commit) and delegates everything else to the user’s terminal. There’s no plugin or extension mechanism beyond custom system prompts.
Codex Implementation
Section titled “Codex Implementation”Pin: 4ab44e2c5
Codex has the most comprehensive MCP client implementation, with a dedicated codex-rmcp-client crate wrapping the official rmcp Rust SDK, plus a 1600-line connection manager that handles transport selection, authentication, tool discovery, and lifecycle management.
Architecture Overview
Section titled “Architecture Overview”The MCP subsystem spans four crates:
| Crate/Module | Purpose |
|---|---|
codex-rs/rmcp-client/ | Low-level MCP client wrapping the rmcp SDK |
codex-rs/core/src/mcp_connection_manager.rs | Connection lifecycle, tool discovery, namespacing |
codex-rs/core/src/mcp_tool_call.rs | Tool call execution, approval, result handling |
codex-rs/protocol/src/mcp.rs | MCP type definitions (Tool, Resource, CallToolResult) |
Transport Layer
Section titled “Transport Layer”Two transport types are supported (mcp_connection_manager.rs:65–77):
enum PendingTransport { ChildProcess { transport: TokioChildProcess, process_group_guard: ProcessGroupGuard, }, StreamableHttp { transport: StreamableHttpClientTransport<reqwest::Client>, }, StreamableHttpWithOAuth { transport: StreamableHttpClientTransport<AuthClient>, oauth_persistor: OAuthPersistor, },}Stdio (ChildProcess): Spawns a child process using TokioChildProcess from the rmcp crate. Communication is via stdin/stdout JSON-RPC. The ProcessGroupGuard ensures the child process is killed when the connection is dropped — it uses process groups with SIGTERM (2-second grace period) followed by SIGKILL.
Streamable HTTP: Uses reqwest for bidirectional HTTP streaming. Supports both plain bearer token authentication and full OAuth flows.
SSE is notably absent — Codex only implements Streamable HTTP for remote servers.
Configuration
Section titled “Configuration”MCP servers are configured in TOML (codex-rs/core/src/config/types.rs:234–262):
Stdio servers:
[mcp_servers.my-server]command = "npx"args = ["-y", "@my/mcp-server"]env = { API_KEY = "..." }cwd = "/path/to/project"HTTP servers:
[mcp_servers.remote-server]url = "https://api.example.com/mcp/"bearer_token_env_var = "REMOTE_TOKEN"http_headers = { "X-Custom" = "value" }Additional configuration per server:
startup_timeout_sec— time to wait for MCP handshake (default: 10 seconds)tool_timeout_sec— per-tool call timeout (default: 60 seconds)enabled_tools/disabled_tools— allowlist/denylist for specific toolsscopes— OAuth scopes for remote servers
OAuth and Authentication
Section titled “OAuth and Authentication”codex-rs/rmcp-client/src/oauth.rs implements the full OAuth 2.0 + PKCE flow:
- Credential storage: Uses the OS keyring (via
keyringcrate) as the primary store, with a fallback to~/.codex/.credentials.json. The keyring entry is keyed by server URL. - Token lifecycle: Tracks
access_token,refresh_token,expires_at. Before each request, checks if the token is expired and attempts a refresh. - Authorization flow: When no valid token exists, initiates the OAuth authorization code flow:
- Generates PKCE
code_verifierandcode_challenge - Opens the authorization URL in the user’s browser
- Starts a local HTTP callback server to receive the authorization code
- Exchanges the code for tokens
- Generates PKCE
- Dynamic client registration: For servers that support it, registers the client dynamically to obtain a
client_idwithout manual configuration.
The CLI command codex mcp login <server> triggers the OAuth flow interactively.
Tool Discovery and Namespacing
Section titled “Tool Discovery and Namespacing”mcp_connection_manager.rs:1119–1162:
- After the MCP handshake, the client calls
listTools()to enumerate available tools. - Tool names are sanitized to match the OpenAI Responses API constraint:
^[a-zA-Z0-9_-]+$. - Names are qualified as
mcp__<server>__<tool>with collision detection. If the qualified name exceeds 64 characters, a hash-based truncation is applied. - Per-server
enabled_tools/disabled_toolslists filter the tool set. - Each tool retains its
connector_idmetadata for analytics and routing.
MCP Protocol Types
Section titled “MCP Protocol Types”codex-rs/protocol/src/mcp.rs defines the wire types:
pub struct Tool { pub name: String, pub title: Option<String>, pub description: Option<String>, pub input_schema: Value, pub output_schema: Option<Value>, pub annotations: Option<Value>,}
pub struct CallToolResult { pub content: Vec<Value>, pub structured_content: Option<Value>, pub is_error: Option<bool>,}Resources and resource templates are also supported:
pub struct Resource { pub name: String, pub uri: String, pub description: Option<String>, pub mime_type: Option<String>, pub size: Option<u64>,}Tool Call Execution
Section titled “Tool Call Execution”codex-rs/core/src/mcp_tool_call.rs:
- Approval workflow: Even in “trusted” mode, certain MCP tools (Codex Apps connectors) require per-call user approval. The approval request includes the tool name, arguments, and server identity.
- Event streaming: Each call emits
McpToolCallBeginEventbefore execution andMcpToolCallEndEventafter. This allows the TUI to show real-time progress. - Result sanitization: If the model doesn’t support image inputs, image content in tool results is stripped.
- Metrics: Each call increments a
codex.mcp.callcounter with status tags (success/failure/timeout).
Sandbox State Propagation
Section titled “Sandbox State Propagation”A custom MCP capability codex/sandbox-state allows Codex to push sandbox policy updates to connected MCP servers. When the user changes the sandbox policy mid-session, all connected servers receive the update. This lets MCP servers respect the agent’s file access restrictions.
Error Handling
Section titled “Error Handling”- Startup timeout: Default 10 seconds, configurable per server. If the handshake doesn’t complete, the server is marked as failed with a user-friendly error message.
- Authentication errors: Detected during handshake and tool calls. User is prompted to run
codex mcp login <server>. - GitHub-specific: GitHub’s MCP server requires a Personal Access Token (PAT), not OAuth — Codex detects this and provides a specific error message.
- Process cleanup: On connection close, the child process receives SIGTERM. After a 2-second grace period, SIGKILL is sent if the process hasn’t exited.
OpenCode Implementation
Section titled “OpenCode Implementation”Pin: 7ed449974
OpenCode uses the official TypeScript MCP SDK (@modelcontextprotocol/sdk) with a transport fallback strategy and a full OAuth implementation including dynamic client registration.
Architecture
Section titled “Architecture”The MCP subsystem lives in packages/opencode/src/mcp/:
| File | Purpose |
|---|---|
index.ts | Connection manager, tool conversion, status state machine |
auth.ts | Credential storage and retrieval |
oauth-provider.ts | OAuth 2.0 client implementation |
oauth-callback.ts | Local HTTP callback server for OAuth redirects |
HTTP routes for the web client are in src/server/routes/mcp.ts.
Transport Fallback
Section titled “Transport Fallback”index.ts:328–343 implements a two-tier transport strategy for remote servers:
transports: [ { name: "StreamableHTTP", transport: new StreamableHTTPClientTransport(...) }, { name: "SSE", transport: new SSEClientTransport(...) },]The client tries Streamable HTTP first. If the connection fails, it falls back to SSE. This handles the common case where older MCP servers only support SSE (the original MCP transport) while newer servers support the more efficient Streamable HTTP.
Stdio transport is used for local servers (configured with command instead of url).
OAuth Flow
Section titled “OAuth Flow”The OAuth implementation supports the full PKCE flow with dynamic client registration:
McpOAuthProvider(oauth-provider.ts) implements the MCP SDK’sOAuthClientProviderinterface.- Checks configuration for a pre-registered
clientId. - If none, falls back to stored client info from a previous dynamic registration.
- Validates tokens against the server URL — if the URL changes, tokens are invalidated.
- Uses PKCE for all flows (public client, no
client_secret).
Callback handling (oauth-callback.ts):
- Starts a local HTTP server on port
19876at path/mcp/oauth/callback. - Each auth attempt gets a 5-minute timeout.
- CSRF protection: validates the
stateparameter against the pending request. - On success, auto-closes the browser window and resolves the pending promise.
Credential storage (auth.ts):
interface Entry { tokens: { accessToken: string refreshToken?: string expiresAt?: number scope?: string } clientInfo: { clientId: string clientSecret?: string } codeVerifier?: string serverUrl?: string // Track URL to invalidate on change}Stored at Global.Path.data/mcp-auth.json (typically ~/.local/share/opencode/mcp-auth.json). Unlike Codex (which uses the OS keyring), OpenCode uses a plain JSON file — less secure but more portable.
Tool Conversion
Section titled “Tool Conversion”index.ts:120–148 converts MCP tool schemas to Vercel AI SDK dynamicTool objects:
- Each MCP tool’s
inputSchemabecomes the AI SDK tool’s parameters. additionalProperties: falseis enforced on all schemas.- Tool names are sanitized:
[^a-zA-Z0-9_-]→_. - Names are qualified as
<sanitized_client>_<sanitized_tool>(underscore separator). - Timeout per tool uses the server’s configured timeout with
resetTimeoutOnProgress: true.
Status State Machine
Section titled “Status State Machine”Each MCP connection has a status (index.ts:66–109):
connected → (normal operation)disabled → (user disabled in config)failed → (startup/connection error)needs_auth → (OAuth token expired or missing)needs_client_registration → (dynamic registration required)Status changes trigger toast notifications in the TUI.
Server Notifications
Section titled “Server Notifications”The client listens for ToolListChanged notifications from the server and publishes a ToolsChanged bus event. The current implementation does not automatically re-fetch tool definitions on that event inside packages/opencode/src, so updates depend on subsequent explicit tool-list fetches.
HTTP API for Web Client
Section titled “HTTP API for Web Client”src/server/routes/mcp.ts exposes MCP management via the HTTP server:
| Route | Method | Purpose |
|---|---|---|
/ | GET | Status of all servers |
/ | POST | Add server dynamically |
/:name/auth | POST | Start OAuth flow |
/:name/auth/callback | POST | Complete OAuth with code |
/:name/auth/authenticate | POST | Start + wait (opens browser) |
/:name/auth | DELETE | Remove credentials |
/:name/connect | POST | Connect server |
/:name/disconnect | POST | Disconnect server |
This allows the web TUI to manage MCP connections without direct process access.
Resource and Prompt Support
Section titled “Resource and Prompt Support”Beyond tools, OpenCode also discovers MCP resources (listResources()) and prompts (listPrompts()). Resources provide read-only data (file contents, database records), while prompts provide pre-built message templates.
Claude Code Implementation
Section titled “Claude Code Implementation”Reference: Claude Code MCP docs (fetched 2026-02-21)
Claude Code has the most feature-rich MCP client, with three transports, three configuration scopes, full OAuth 2.0 support, MCP Tool Search for context efficiency, resource @ mention integration, managed MCP for enterprise control, and the ability to serve as an MCP server itself.
Three Transports
Section titled “Three Transports”| Transport | Use Case | Status |
|---|---|---|
| Stdio | Local process servers (spawn child) | Primary for local |
| Streamable HTTP | Remote servers | Recommended for remote |
| SSE (Server-Sent Events) | Legacy remote servers | Deprecated, still supported |
Configuration via CLI:
# Stdio (local)claude mcp add --transport stdio my-server -- npx -y @my/mcp-server
# HTTP (remote)claude mcp add --transport http my-api https://api.example.com/mcp
# SSE (legacy remote)claude mcp add --transport sse legacy-api https://api.example.com/sseThree Configuration Scopes
Section titled “Three Configuration Scopes”| Scope | Storage | Default | Shareable |
|---|---|---|---|
| Local | ~/.claude.json under project path | Yes | No |
| Project | .mcp.json in project root | No | Yes (VCS) |
| User | ~/.claude.json global | No | No |
Precedence: local > project > user. Servers with the same name at multiple scopes — the local scope wins.
Environment variable expansion is supported in .mcp.json files: ${VAR} and ${VAR:-default} syntax in command, args, env, url, and headers fields. This allows teams to share configurations while keeping sensitive values in environment variables.
OAuth and Authentication
Section titled “OAuth and Authentication”Full OAuth 2.0 with PKCE for public clients:
- Dynamic client registration: For servers that support it, no manual
client_idneeded. - Pre-configured credentials:
--client-id,--client-secret,--callback-portfor servers that don’t support dynamic registration. - Token storage: Secure storage in system keychain (macOS) or credentials file.
- Interactive management:
/mcpcommand for browser-based OAuth flows, viewing server status, and clearing authentication.
# Pre-configured OAuthclaude mcp add --transport http \ --client-id your-client-id --client-secret --callback-port 8080 \ my-server https://mcp.example.com/mcpMCP Tool Search
Section titled “MCP Tool Search”When many MCP servers are configured, tool definitions can consume a significant portion of the context window. Tool Search solves this by dynamically loading tools on-demand:
- Auto mode (default): Activates when MCP tool descriptions exceed 10% of context window.
- How it works: MCP tools are deferred rather than loaded upfront. Claude uses a search tool to discover relevant MCP tools when needed. Only tools actually used are loaded into context.
- Model requirement: Requires
tool_referenceblock support (Sonnet 4+, Opus 4+). Haiku does not support. - Configuration:
ENABLE_TOOL_SEARCHenv var —auto(default),auto:<N>(custom threshold %),true(always),false(disabled).
This is a capability unique to Claude Code — neither Codex nor OpenCode implement on-demand tool loading.
MCP Resources and Prompts
Section titled “MCP Resources and Prompts”Resources are referenced via @server:protocol://resource/path mentions in prompts. Resources are automatically fetched and included as attachments. Multiple resources can be referenced in a single prompt.
Prompts from MCP servers are exposed as slash commands: /mcp__servername__promptname. Arguments are passed space-separated after the command.
Dynamic Tool Updates
Section titled “Dynamic Tool Updates”Claude Code supports MCP list_changed notifications. When a server sends this notification, Claude Code automatically refreshes available capabilities without requiring disconnect/reconnect.
Output Limits
Section titled “Output Limits”- Warning threshold: 10,000 tokens (display warning only)
- Default maximum: 25,000 tokens
- Configurable:
MAX_MCP_OUTPUT_TOKENSenv var
Plugin-Provided MCP Servers
Section titled “Plugin-Provided MCP Servers”Plugins can bundle MCP servers in .mcp.json at the plugin root or inline in plugin.json. Plugin servers auto-start when the plugin is enabled and use ${CLAUDE_PLUGIN_ROOT} for relative paths. Restart required when enabling/disabling plugins with MCP servers.
Managed MCP (Enterprise)
Section titled “Managed MCP (Enterprise)”Two approaches for organizational control:
1. Exclusive control (managed-mcp.json):
Deploy a fixed set of servers at system paths — users cannot add/modify servers:
- macOS:
/Library/Application Support/ClaudeCode/managed-mcp.json - Linux:
/etc/claude-code/managed-mcp.json
2. Policy-based (allowedMcpServers/deniedMcpServers):
Allow users to configure servers within restrictions. Entries can restrict by:
serverName: exact server name matchserverCommand: exact command + args match (for stdio)serverUrl: URL pattern with wildcard support (for remote)
Deny list takes absolute precedence — a server matching any deny entry is blocked even if it matches an allow entry.
Claude Code as MCP Server
Section titled “Claude Code as MCP Server”claude mcp serve starts Claude Code as a stdio MCP server, exposing its tools (View, Edit, LS, etc.) to other applications. Useful for integration with Claude Desktop or other MCP clients.
Key Design Differences from Codex and OpenCode
Section titled “Key Design Differences from Codex and OpenCode”| Feature | Codex | OpenCode | Claude Code |
|---|---|---|---|
| Transports | Stdio + Streamable HTTP | Stdio + Streamable HTTP + SSE fallback | Stdio + Streamable HTTP + SSE |
| OAuth | Full PKCE + keyring | Full PKCE + JSON file | Full PKCE + keyring + pre-configured creds |
| Tool naming | mcp__server__tool (hash truncation) | client_tool (underscore) | mcp__server__tool |
| Tool search | No | No | Auto-enabled at 10% context threshold |
| Resources | Supported | Supported | Supported + @ mention integration |
| Prompts | N/A | N/A | Exposed as / commands |
| Managed config | No | No | managed-mcp.json + allowlists/denylists |
| Output limits | N/A | N/A | Warning at 10K, max 25K (configurable) |
| Sandbox integration | codex/sandbox-state capability | N/A | Via proxy-based network architecture |
| Plugin MCP | No | No | Yes, bundled in plugins |
| Serve as MCP | No | No | claude mcp serve |
Pitfalls & Hard Lessons
Section titled “Pitfalls & Hard Lessons”Transport Fragmentation
Section titled “Transport Fragmentation”The MCP spec has evolved through three transport mechanisms: stdio, SSE, and Streamable HTTP. Many servers in the wild only support SSE (the original transport). OpenCode handles this with a fallback strategy; Codex only supports Streamable HTTP for remote servers, which means it can’t connect to older SSE-only servers. Any production MCP client should implement all three transports.
OAuth Complexity
Section titled “OAuth Complexity”The OAuth flow for MCP is significantly more complex than simple API key authentication:
- Dynamic client registration adds a round-trip before the auth flow even starts.
- PKCE code verifier/challenge pairs must be persisted across the redirect (the browser callback happens in a different execution context).
- Token refresh must happen transparently — if a refresh fails, the user must be prompted to re-authenticate, not shown a cryptic error.
- Server URL changes invalidate tokens (OpenCode tracks this explicitly).
Codex’s approach of using the OS keyring for credential storage is more secure than OpenCode’s plain JSON file, but less portable (keyring may not be available in CI environments or containers).
Tool Name Collision
Section titled “Tool Name Collision”When multiple MCP servers expose tools with the same name (e.g., two servers both expose a search tool), the qualified name must be unique. Codex uses mcp__<server>__<tool> with hash-based truncation when the name exceeds 64 characters. OpenCode uses <client>_<tool>. Both sanitize names to match provider constraints.
Startup Reliability
Section titled “Startup Reliability”Stdio MCP servers are child processes that can fail to start (missing binary, wrong arguments, port conflicts). The startup handshake (MCP initialize request/response) must have a timeout, and failure should be reported clearly. Codex’s 10-second default with per-server override is reasonable. OpenCode exposes timeout in both local and remote MCP config entries and applies it to connection attempts.
Tool Schema Drift
Section titled “Tool Schema Drift”MCP servers can change their tool schemas between restarts. If the agent caches tool schemas and the server updates, the agent may send invalid arguments. OpenCode emits a ToolsChanged bus event on ToolListChanged, but does not auto-refresh tool definitions in-process. Codex caches Codex Apps tools with a 1-hour TTL and currently performs single-call MCP tool listing without pagination handling. Neither implementation fully solves schema drift without explicit refresh.
Process Lifecycle
Section titled “Process Lifecycle”Stdio servers are child processes that must be properly cleaned up. If the agent crashes without sending SIGTERM, orphaned processes linger. Codex uses ProcessGroupGuard for RAII-style cleanup and a two-stage shutdown (SIGTERM → wait → SIGKILL). On Linux, setting the parent death signal (PR_SET_PDEATHSIG) ensures the child dies if the parent crashes. OpenCode, being a Node.js process, relies on the runtime’s process cleanup which is less deterministic.
OpenOxide Blueprint
Section titled “OpenOxide Blueprint”Crate: openoxide-mcp
Section titled “Crate: openoxide-mcp”A multi-transport MCP client with OAuth support, tool namespacing, and integration with the OpenOxide permission system.
Transport Abstraction
Section titled “Transport Abstraction”pub enum Transport { Stdio(StdioTransport), StreamableHttp(HttpTransport), Sse(SseTransport),}
pub struct StdioTransport { child: tokio::process::Child, stdin: FramedWrite<ChildStdin, JsonRpcCodec>, stdout: FramedRead<ChildStdout, JsonRpcCodec>, process_group: ProcessGroupGuard,}
pub struct HttpTransport { client: reqwest::Client, url: Url, auth: AuthState,}
pub struct SseTransport { client: reqwest::Client, url: Url, event_source: EventSource, auth: AuthState,}All three transport types should be supported from day one. Use the rmcp crate as a starting point (it’s what Codex uses), but add SSE support.
Connection Manager
Section titled “Connection Manager”pub struct McpConnectionManager { connections: HashMap<String, McpConnection>, tool_registry: ToolRegistry,}
pub struct McpConnection { name: String, transport: Transport, status: ConnectionStatus, tools: Vec<McpTool>, config: ServerConfig,}
pub enum ConnectionStatus { Connected, Disconnected, Failed(String), NeedsAuth,}Tool Registry and Namespacing
Section titled “Tool Registry and Namespacing”pub struct ToolRegistry { /// Map from qualified name to (server_name, original_tool_name). tools: HashMap<String, (String, String)>,}
impl ToolRegistry { /// Register a tool with collision-safe namespacing. /// Format: mcp__{server}__{tool}, truncated with hash if >64 chars. pub fn register(&mut self, server: &str, tool: McpTool) -> String;
/// Look up the server and original name for a qualified tool name. pub fn resolve(&self, qualified_name: &str) -> Option<(&str, &str)>;}Follow Codex’s mcp__<server>__<tool> convention — it’s compatible with all LLM providers’ naming constraints.
OAuth Module
Section titled “OAuth Module”pub struct OAuthManager { /// Credential store (keyring with file fallback). store: CredentialStore, /// Active authorization flows (state → pending). pending: HashMap<String, PendingAuth>,}
pub enum CredentialStore { Keyring(KeyringStore), File(FileStore), // Fallback for environments without keyring}
pub struct PendingAuth { code_verifier: String, redirect_uri: Url, server_url: Url, expires_at: Instant,}Use the keyring crate as primary storage (like Codex) with a JSON file fallback (like OpenCode). Implement PKCE for all public client flows. The callback server should listen on a configurable port with a timeout.
Process Lifecycle
Section titled “Process Lifecycle”For stdio transports, use Codex’s two-stage shutdown pattern:
impl Drop for ProcessGroupGuard { fn drop(&mut self) { // Send SIGTERM to process group nix::sys::signal::killpg(self.pgid, Signal::SIGTERM).ok(); // Spawn a task to SIGKILL after 2 seconds if still alive let pgid = self.pgid; tokio::spawn(async move { tokio::time::sleep(Duration::from_secs(2)).await; nix::sys::signal::killpg(pgid, Signal::SIGKILL).ok(); }); }}On Linux, set PR_SET_PDEATHSIG on the child process so it dies if the parent crashes unexpectedly.
MCP Tool Search (from Claude Code)
Section titled “MCP Tool Search (from Claude Code)”When many MCP servers are configured, tool definitions consume context. Implement on-demand tool loading:
pub struct ToolSearch { /// All registered MCP tools (deferred, not loaded into context). deferred_tools: Vec<DeferredTool>, /// Threshold as percentage of context window. threshold_pct: f32, // default: 0.10 (10%) /// Whether tool search is active. active: bool,}
pub struct DeferredTool { pub qualified_name: String, pub server_name: String, pub description: String, pub schema: Value, /// Whether this tool has been loaded into the current context. pub loaded: bool,}
impl ToolSearch { /// Check if tool definitions exceed the context threshold. /// If so, defer tools and expose a search tool instead. pub fn should_activate(&self, tool_token_count: usize, context_limit: usize) -> bool;
/// Search deferred tools by description/name similarity. pub fn search(&self, query: &str, max_results: usize) -> Vec<&DeferredTool>;}Configuration via ENABLE_TOOL_SEARCH env var (matching Claude Code):
auto(default): activate at thresholdauto:<N>: custom threshold percentagetrue: always activefalse: disabled
MCP Resources and Prompts (from Claude Code)
Section titled “MCP Resources and Prompts (from Claude Code)”Support MCP resources via @server:protocol://resource/path mentions:
pub struct McpResourceRef { pub server_name: String, pub uri: String,}
impl McpConnectionManager { /// Fetch a resource from an MCP server. pub async fn read_resource(&self, server: &str, uri: &str) -> Result<ResourceContent>;
/// List available prompts from all connected servers. pub fn list_prompts(&self) -> Vec<McpPrompt>;
/// Execute a prompt from an MCP server. pub async fn execute_prompt( &self, server: &str, prompt: &str, args: Vec<String>, ) -> Result<Vec<PromptMessage>>;}MCP prompts should be exposed as /mcp__server__prompt slash commands, matching Claude Code’s convention.
Output Limits (from Claude Code)
Section titled “Output Limits (from Claude Code)”pub struct McpOutputLimits { /// Display warning when output exceeds this many tokens. pub warning_threshold: usize, // default: 10_000 /// Hard maximum tokens per MCP tool output. pub max_tokens: usize, // default: 25_000}Configurable via MAX_MCP_OUTPUT_TOKENS env var.
Managed MCP (from Claude Code)
Section titled “Managed MCP (from Claude Code)”For enterprise deployments, support two control modes:
pub enum ManagedMcpMode { /// Exclusive control: only servers in managed config file are allowed. Exclusive { config_path: PathBuf, // /etc/openoxide/managed-mcp.toml }, /// Policy-based: users can add servers within restrictions. Policy { allowed: Vec<McpServerMatcher>, denied: Vec<McpServerMatcher>, },}
pub enum McpServerMatcher { ByName(String), ByCommand(Vec<String>), // exact command + args match ByUrl(String), // URL pattern with * wildcard}Deny list takes absolute precedence over allow list.
Serve as MCP Server (from Claude Code)
Section titled “Serve as MCP Server (from Claude Code)”Support openoxide mcp serve to expose OpenOxide’s tools as an MCP server:
pub async fn serve_mcp(config: &Config) -> Result<()>;This enables integration with Claude Desktop, other MCP clients, or orchestration tools.
Environment Variable Expansion (from Claude Code)
Section titled “Environment Variable Expansion (from Claude Code)”Support ${VAR} and ${VAR:-default} syntax in MCP configuration files for command, args, env, url, and headers fields. This allows teams to share .mcp.toml files while keeping secrets in environment variables.
Crates
Section titled “Crates”- rmcp — Official Rust MCP SDK (transport, protocol, client).
- reqwest — HTTP client for Streamable HTTP and SSE transports.
- eventsource-client or reqwest-eventsource — SSE client implementation.
- keyring — OS-native credential storage.
- tokio — Async runtime for all I/O.
- serde/serde_json — JSON-RPC serialization.
- url — URL parsing and manipulation for OAuth flows.
- sha2 — PKCE code challenge generation (S256 method).