Skip to content

MCP Client

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:

  1. 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.
  2. 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.
  3. 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).
  4. 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.).
  5. 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.
  6. 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.

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.


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.

The MCP subsystem spans four crates:

Crate/ModulePurpose
codex-rs/rmcp-client/Low-level MCP client wrapping the rmcp SDK
codex-rs/core/src/mcp_connection_manager.rsConnection lifecycle, tool discovery, namespacing
codex-rs/core/src/mcp_tool_call.rsTool call execution, approval, result handling
codex-rs/protocol/src/mcp.rsMCP type definitions (Tool, Resource, CallToolResult)

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.

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 tools
  • scopes — OAuth scopes for remote servers

codex-rs/rmcp-client/src/oauth.rs implements the full OAuth 2.0 + PKCE flow:

  1. Credential storage: Uses the OS keyring (via keyring crate) as the primary store, with a fallback to ~/.codex/.credentials.json. The keyring entry is keyed by server URL.
  2. Token lifecycle: Tracks access_token, refresh_token, expires_at. Before each request, checks if the token is expired and attempts a refresh.
  3. Authorization flow: When no valid token exists, initiates the OAuth authorization code flow:
    • Generates PKCE code_verifier and code_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
  4. Dynamic client registration: For servers that support it, registers the client dynamically to obtain a client_id without manual configuration.

The CLI command codex mcp login <server> triggers the OAuth flow interactively.

mcp_connection_manager.rs:1119–1162:

  1. After the MCP handshake, the client calls listTools() to enumerate available tools.
  2. Tool names are sanitized to match the OpenAI Responses API constraint: ^[a-zA-Z0-9_-]+$.
  3. Names are qualified as mcp__<server>__<tool> with collision detection. If the qualified name exceeds 64 characters, a hash-based truncation is applied.
  4. Per-server enabled_tools / disabled_tools lists filter the tool set.
  5. Each tool retains its connector_id metadata for analytics and routing.

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>,
}

codex-rs/core/src/mcp_tool_call.rs:

  1. 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.
  2. Event streaming: Each call emits McpToolCallBeginEvent before execution and McpToolCallEndEvent after. This allows the TUI to show real-time progress.
  3. Result sanitization: If the model doesn’t support image inputs, image content in tool results is stripped.
  4. Metrics: Each call increments a codex.mcp.call counter with status tags (success/failure/timeout).

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.

  • 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.

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.

The MCP subsystem lives in packages/opencode/src/mcp/:

FilePurpose
index.tsConnection manager, tool conversion, status state machine
auth.tsCredential storage and retrieval
oauth-provider.tsOAuth 2.0 client implementation
oauth-callback.tsLocal HTTP callback server for OAuth redirects

HTTP routes for the web client are in src/server/routes/mcp.ts.

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).

The OAuth implementation supports the full PKCE flow with dynamic client registration:

  1. McpOAuthProvider (oauth-provider.ts) implements the MCP SDK’s OAuthClientProvider interface.
  2. Checks configuration for a pre-registered clientId.
  3. If none, falls back to stored client info from a previous dynamic registration.
  4. Validates tokens against the server URL — if the URL changes, tokens are invalidated.
  5. Uses PKCE for all flows (public client, no client_secret).

Callback handling (oauth-callback.ts):

  • Starts a local HTTP server on port 19876 at path /mcp/oauth/callback.
  • Each auth attempt gets a 5-minute timeout.
  • CSRF protection: validates the state parameter 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.

index.ts:120–148 converts MCP tool schemas to Vercel AI SDK dynamicTool objects:

  1. Each MCP tool’s inputSchema becomes the AI SDK tool’s parameters.
  2. additionalProperties: false is enforced on all schemas.
  3. Tool names are sanitized: [^a-zA-Z0-9_-]_.
  4. Names are qualified as <sanitized_client>_<sanitized_tool> (underscore separator).
  5. Timeout per tool uses the server’s configured timeout with resetTimeoutOnProgress: true.

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.

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.

src/server/routes/mcp.ts exposes MCP management via the HTTP server:

RouteMethodPurpose
/GETStatus of all servers
/POSTAdd server dynamically
/:name/authPOSTStart OAuth flow
/:name/auth/callbackPOSTComplete OAuth with code
/:name/auth/authenticatePOSTStart + wait (opens browser)
/:name/authDELETERemove credentials
/:name/connectPOSTConnect server
/:name/disconnectPOSTDisconnect server

This allows the web TUI to manage MCP connections without direct process access.

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.


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.

TransportUse CaseStatus
StdioLocal process servers (spawn child)Primary for local
Streamable HTTPRemote serversRecommended for remote
SSE (Server-Sent Events)Legacy remote serversDeprecated, still supported

Configuration via CLI:

Terminal window
# 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/sse
ScopeStorageDefaultShareable
Local~/.claude.json under project pathYesNo
Project.mcp.json in project rootNoYes (VCS)
User~/.claude.json globalNoNo

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.

Full OAuth 2.0 with PKCE for public clients:

  1. Dynamic client registration: For servers that support it, no manual client_id needed.
  2. Pre-configured credentials: --client-id, --client-secret, --callback-port for servers that don’t support dynamic registration.
  3. Token storage: Secure storage in system keychain (macOS) or credentials file.
  4. Interactive management: /mcp command for browser-based OAuth flows, viewing server status, and clearing authentication.
Terminal window
# Pre-configured OAuth
claude mcp add --transport http \
--client-id your-client-id --client-secret --callback-port 8080 \
my-server https://mcp.example.com/mcp

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_reference block support (Sonnet 4+, Opus 4+). Haiku does not support.
  • Configuration: ENABLE_TOOL_SEARCH env 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.

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.

Claude Code supports MCP list_changed notifications. When a server sends this notification, Claude Code automatically refreshes available capabilities without requiring disconnect/reconnect.

  • Warning threshold: 10,000 tokens (display warning only)
  • Default maximum: 25,000 tokens
  • Configurable: MAX_MCP_OUTPUT_TOKENS env var

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.

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 match
  • serverCommand: 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 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”
FeatureCodexOpenCodeClaude Code
TransportsStdio + Streamable HTTPStdio + Streamable HTTP + SSE fallbackStdio + Streamable HTTP + SSE
OAuthFull PKCE + keyringFull PKCE + JSON fileFull PKCE + keyring + pre-configured creds
Tool namingmcp__server__tool (hash truncation)client_tool (underscore)mcp__server__tool
Tool searchNoNoAuto-enabled at 10% context threshold
ResourcesSupportedSupportedSupported + @ mention integration
PromptsN/AN/AExposed as / commands
Managed configNoNomanaged-mcp.json + allowlists/denylists
Output limitsN/AN/AWarning at 10K, max 25K (configurable)
Sandbox integrationcodex/sandbox-state capabilityN/AVia proxy-based network architecture
Plugin MCPNoNoYes, bundled in plugins
Serve as MCPNoNoclaude mcp serve

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.

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).

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.

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.

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.

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.


A multi-transport MCP client with OAuth support, tool namespacing, and integration with the OpenOxide permission system.

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.

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,
}
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.

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.

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.

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 threshold
  • auto:<N>: custom threshold percentage
  • true: always active
  • false: 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.

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.

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.

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.

  • 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).