Context
The codex_sdk upstream client plumbs MCP server config end-to-end on the Rust side — McpServerConfig { url, headers } is built from the per-agent objectiveai::mcp::Connection and shipped on every run line via RunParams.mcp_servers. The Python runner currently ignores this field; see the NOTE(MCP) comment in objectiveai-codex-sdk-runner-py/main.py::handle_run.
This issue tracks wiring params.mcp_servers into the actual codex Thread so codex agents can call MCP tools.
Why it's deferred
The codex Python SDK's Codex.start_thread / resume_thread and Thread.run_streamed don't expose an MCP-server knob — the SDK's ThreadOptions / TurnOptions types have no field for it. The codex CLI binary itself supports MCP via ~/.codex/config.toml, but that's process-wide config, not per-thread.
Options
-
Per-call config.toml synthesis — write a temporary ~/.codex/config.toml (or --config <path>) snippet before each Thread.run_streamed, scoped to the request's tempdir. Concurrency-hostile: codex reads its config at process start, so this would force a fresh codex subprocess per turn (defeating part of the multiplex win).
-
Wait for SDK support — file an upstream issue / PR on openai-codex-sdk adding an mcp_servers field to ThreadOptions that propagates to the spawned codex exec invocation. Cleanest path; outside our control.
-
Bypass the SDK — invoke the codex binary directly with our own arguments. Loses the SDK's event-streaming wrapper and forces us to re-implement ThreadEvent parsing.
Recommend #2 (waiting / upstream PR) unless we hit a customer with hard MCP requirements; in that case fall back to #1 with a per-request subprocess.
Touchpoints when we do wire it up
objectiveai-codex-sdk-runner-py/main.py::handle_run — replace the NOTE(MCP) comment with the actual MCP plumbing.
- Optionally relax the
_validate_run_params check if mcp_servers becomes required.
- No Rust-side changes expected —
RunParams.mcp_servers already serializes the right shape.
- Re-test that
codex_sdk::McpServerConfig::from(&Connection) produces the correct headers (Mcp-Session-Id, Authorization, User-Agent, X-Title, Referer/HTTP-Referer) for whatever consumer ends up reading them.
Out of scope
- MCP wiring for
codex_sdk agents only. Claude / OpenRouter MCP routing is unchanged.
- The agent-level
mcp_servers field on objectiveai::agent::codex_sdk::AgentBase is already declared and validated; this issue is about runtime routing only.
Context
The codex_sdk upstream client plumbs MCP server config end-to-end on the Rust side —
McpServerConfig { url, headers }is built from the per-agentobjectiveai::mcp::Connectionand shipped on everyrunline viaRunParams.mcp_servers. The Python runner currently ignores this field; see theNOTE(MCP)comment inobjectiveai-codex-sdk-runner-py/main.py::handle_run.This issue tracks wiring
params.mcp_serversinto the actual codexThreadso codex agents can call MCP tools.Why it's deferred
The codex Python SDK's
Codex.start_thread/resume_threadandThread.run_streameddon't expose an MCP-server knob — the SDK'sThreadOptions/TurnOptionstypes have no field for it. The codex CLI binary itself supports MCP via~/.codex/config.toml, but that's process-wide config, not per-thread.Options
Per-call config.toml synthesis — write a temporary
~/.codex/config.toml(or--config <path>) snippet before eachThread.run_streamed, scoped to the request's tempdir. Concurrency-hostile: codex reads its config at process start, so this would force a fresh codex subprocess per turn (defeating part of the multiplex win).Wait for SDK support — file an upstream issue / PR on
openai-codex-sdkadding anmcp_serversfield toThreadOptionsthat propagates to the spawnedcodex execinvocation. Cleanest path; outside our control.Bypass the SDK — invoke the codex binary directly with our own arguments. Loses the SDK's event-streaming wrapper and forces us to re-implement
ThreadEventparsing.Recommend #2 (waiting / upstream PR) unless we hit a customer with hard MCP requirements; in that case fall back to #1 with a per-request subprocess.
Touchpoints when we do wire it up
objectiveai-codex-sdk-runner-py/main.py::handle_run— replace theNOTE(MCP)comment with the actual MCP plumbing._validate_run_paramscheck ifmcp_serversbecomes required.RunParams.mcp_serversalready serializes the right shape.codex_sdk::McpServerConfig::from(&Connection)produces the correct headers (Mcp-Session-Id,Authorization,User-Agent,X-Title,Referer/HTTP-Referer) for whatever consumer ends up reading them.Out of scope
codex_sdkagents only. Claude / OpenRouter MCP routing is unchanged.mcp_serversfield onobjectiveai::agent::codex_sdk::AgentBaseis already declared and validated; this issue is about runtime routing only.