Files
dev/docs/advanced.md
T
Michael BolinandGitHub 56e664c6d6 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

3.4 KiB

Advanced

Non-interactive / CI mode

Run Codex head-less in pipelines. Example GitHub Action step:

- name: Update changelog via Codex
  run: |
    npm install -g @openai/codex
    export OPENAI_API_KEY="${{ secrets.OPENAI_KEY }}"
    codex exec --full-auto "update CHANGELOG for next release"

Resuming non-interactive sessions

You can resume a previous headless run to continue the same conversation context and append to the same rollout file.

Interactive TUI equivalent:

codex resume             # picker
codex resume --last      # most recent
codex resume <SESSION_ID>

Compatibility:

  • Latest source builds include codex exec resume (examples below).
  • Current released CLI may not include this yet. If codex exec --help shows no resume, use the workaround in the next subsection.
# Resume the most recent recorded session and run with a new prompt (source builds)
codex exec "ship a release draft changelog" resume --last

# Alternatively, pass the prompt via stdin (source builds)
# Note: omit the trailing '-' to avoid it being parsed as a SESSION_ID
echo "ship a release draft changelog" | codex exec resume --last

# Or resume a specific session by id (UUID) (source builds)
codex exec resume 7f9f9a2e-1b3c-4c7a-9b0e-123456789abc "continue the task"

Notes:

  • When using --last, Codex picks the newest recorded session; if none exist, it behaves like starting fresh.
  • Resuming appends new events to the existing session file and maintains the same conversation id.

Tracing / verbose logging

Because Codex is written in Rust, it honors the RUST_LOG environment variable to configure its logging behavior.

The TUI defaults to RUST_LOG=codex_core=info,codex_tui=info and log messages are written to ~/.code/log/codex-tui.log (Code still reads the legacy ~/.codex/log/ path), so you can leave the following running in a separate terminal to monitor log messages as they are written:

tail -F ~/.code/log/codex-tui.log

By comparison, the non-interactive mode (codex exec) defaults to RUST_LOG=error, but messages are printed inline, so there is no need to monitor a separate file.

See the Rust documentation on RUST_LOG for more information on the configuration options.

Model Context Protocol (MCP)

The Codex CLI can be configured to leverage MCP servers by defining an mcp_servers section in ~/.code/config.toml (Code will also read a legacy ~/.codex/config.toml). It is intended to mirror how tools such as Claude and Cursor define mcpServers in their respective JSON config files, though the Codex format is slightly different since it uses TOML rather than JSON, e.g.:

# IMPORTANT: the top-level key is `mcp_servers` rather than `mcpServers`.
[mcp_servers.server-name]
command = "npx"
args = ["-y", "mcp-server"]
env = { "API_KEY" = "value" }

Using Codex as an MCP Server

Tip

It is somewhat experimental, but the Codex CLI can also be run as an MCP server via codex mcp. If you launch it with an MCP client such as npx @modelcontextprotocol/inspector codex mcp and send it a tools/list request, you will see that there is only one tool, codex, that accepts a grab-bag of inputs, including a catch-all config map for anything you might want to override. Feel free to play around with it and provide feedback via GitHub issues.