Files
llmx/codex-rs/docs/codex_mcp_interface.md
Michael Bolin d9dbf48828 fix: separate codex mcp into codex mcp-server and codex app-server (#4471)
This is a very large PR with some non-backwards-compatible changes.

Historically, `codex mcp` (or `codex mcp serve`) started a JSON-RPC-ish
server that had two overlapping responsibilities:

- Running an MCP server, providing some basic tool calls.
- Running the app server used to power experiences such as the VS Code
extension.

This PR aims to separate these into distinct concepts:

- `codex mcp-server` for the MCP server
- `codex app-server` for the "application server"

Note `codex mcp` still exists because it already has its own subcommands
for MCP management (`list`, `add`, etc.)

The MCP logic continues to live in `codex-rs/mcp-server` whereas the
refactored app server logic is in the new `codex-rs/app-server` folder.
Note that most of the existing integration tests in
`codex-rs/mcp-server/tests/suite` were actually for the app server, so
all the tests have been moved with the exception of
`codex-rs/mcp-server/tests/suite/mod.rs`.

Because this is already a large diff, I tried not to change more than I
had to, so `codex-rs/app-server/tests/common/mcp_process.rs` still uses
the name `McpProcess` for now, but I will do some mechanical renamings
to things like `AppServer` in subsequent PRs.

While `mcp-server` and `app-server` share some overlapping functionality
(like reading streams of JSONL and dispatching based on message types)
and some differences (completely different message types), I ended up
doing a bit of copypasta between the two crates, as both have somewhat
similar `message_processor.rs` and `outgoing_message.rs` files for now,
though I expect them to diverge more in the near future.

One material change is that of the initialize handshake for `codex
app-server`, as we no longer use the MCP types for that handshake.
Instead, we update `codex-rs/protocol/src/mcp_protocol.rs` to add an
`Initialize` variant to `ClientRequest`, which takes the `ClientInfo`
object we need to update the `USER_AGENT_SUFFIX` in
`codex-rs/app-server/src/message_processor.rs`.

One other material change is in
`codex-rs/app-server/src/codex_message_processor.rs` where I eliminated
a use of the `send_event_as_notification()` method I am generally trying
to deprecate (because it blindly maps an `EventMsg` into a
`JSONNotification`) in favor of `send_server_notification()`, which
takes a `ServerNotification`, as that is intended to be a custom enum of
all notification types supported by the app server. So to make this
update, I had to introduce a new variant of `ServerNotification`,
`SessionConfigured`, which is a non-backwards compatible change with the
old `codex mcp`, and clients will have to be updated after the next
release that contains this PR. Note that
`codex-rs/app-server/tests/suite/list_resume.rs` also had to be update
to reflect this change.

I introduced `codex-rs/utils/json-to-toml/src/lib.rs` as a small utility
crate to avoid some of the copying between `mcp-server` and
`app-server`.
2025-09-30 07:06:18 +00:00

4.8 KiB
Raw Blame History

Codex MCP Interface [experimental]

This document describes Codexs experimental MCP interface: a JSONRPC API that runs over the Model Context Protocol (MCP) transport to control a local Codex engine.

  • Status: experimental and subject to change without notice
  • Server binary: codex mcp-server (or codex-mcp-server)
  • Transport: standard MCP over stdio (JSONRPC 2.0, linedelimited)

Overview

Codex exposes a small set of MCPcompatible methods to create and manage conversations, send user input, receive live events, and handle approval prompts. The types are defined in protocol/src/mcp_protocol.rs and reused by the MCP server implementation in mcp-server/.

At a glance:

  • Conversations
    • newConversation → start a Codex session
    • sendUserMessage / sendUserTurn → send user input into a conversation
    • interruptConversation → stop the current turn
    • listConversations, resumeConversation, archiveConversation
  • Configuration and info
    • getUserSavedConfig, setDefaultModel, getUserAgent, userInfo
  • Auth
    • loginApiKey, loginChatGpt, cancelLoginChatGpt, logoutChatGpt, getAuthStatus
  • Utilities
    • gitDiffToRemote, execOneOffCommand
  • Approvals (server → client requests)
    • applyPatchApproval, execCommandApproval
  • Notifications (server → client)
    • loginChatGptComplete, authStatusChange
    • codex/event stream with agent events

See code for full type definitions and exact shapes: protocol/src/mcp_protocol.rs.

Starting the server

Run Codex as an MCP server and connect an MCP client:

codex mcp-server | your_mcp_client

For a simple inspection UI, you can also try:

npx @modelcontextprotocol/inspector codex mcp-server

Use the separate codex mcp subcommand to manage configured MCP server launchers in config.toml.

Conversations

Start a new session with optional overrides:

Request newConversation params (subset):

  • model: string model id (e.g. "o3", "gpt-5", "gpt-5-codex")
  • profile: optional named profile
  • cwd: optional working directory
  • approvalPolicy: untrusted | on-request | on-failure | never
  • sandbox: read-only | workspace-write | danger-full-access
  • config: map of additional config overrides
  • baseInstructions: optional instruction override
  • includePlanTool / includeApplyPatchTool: booleans

Response: { conversationId, model, reasoningEffort?, rolloutPath }

Send input to the active turn:

  • sendUserMessage → enqueue items to the conversation
  • sendUserTurn → structured turn with explicit cwd, approvalPolicy, sandboxPolicy, model, optional effort, and summary

Interrupt a running turn: interruptConversation.

List/resume/archive: listConversations, resumeConversation, archiveConversation.

Event stream

While a conversation runs, the server sends notifications:

  • codex/event with the serialized Codex event payload. The shape matches core/src/protocol.rss Event and EventMsg types. Some notifications include a _meta.requestId to correlate with the originating request.
  • Auth notifications via method names loginChatGptComplete and authStatusChange.

Clients should render events and, when present, surface approval requests (see next section).

Approvals (server → client)

When Codex needs approval to apply changes or run commands, the server issues JSONRPC requests to the client:

  • applyPatchApproval { conversationId, callId, fileChanges, reason?, grantRoot? }
  • execCommandApproval { conversationId, callId, command, cwd, reason? }

The client must reply with { decision: "allow" | "deny" } for each request.

Auth helpers

For ChatGPT or APIkey based auth flows, the server exposes helpers:

  • loginApiKey { apiKey }
  • loginChatGpt → returns { loginId, authUrl }; browser completes flow; then loginChatGptComplete notification follows
  • cancelLoginChatGpt { loginId }, logoutChatGpt, getAuthStatus { includeToken?, refreshToken? }

Example: start and send a message

{ "jsonrpc": "2.0", "id": 1, "method": "newConversation", "params": { "model": "gpt-5", "approvalPolicy": "on-request" } }

Server responds:

{ "jsonrpc": "2.0", "id": 1, "result": { "conversationId": "c7b0…", "model": "gpt-5", "rolloutPath": "/path/to/rollout.jsonl" } }

Then send input:

{ "jsonrpc": "2.0", "id": 2, "method": "sendUserMessage", "params": { "conversationId": "c7b0…", "items": [{ "type": "text", "text": "Hello Codex" }] } }

While processing, the server emits codex/event notifications containing agent output, approvals, and status updates.

Compatibility and stability

This interface is experimental. Method names, fields, and event shapes may evolve. For the authoritative schema, consult protocol/src/mcp_protocol.rs and the corresponding server wiring in mcp-server/.