Codex MCP Server Removed: How to Migrate to the App Server

TL;DR
codex mcp-server is gone as of Codex CLI 0.154.0.
OpenAI removed codex mcp-server and the standalone codex-mcp-server binary in Codex CLI 0.154.0 (PR #42993, merged September 5, 2026), after deprecating the command on August 24. If anything in your stack launched Codex as an MCP server, it breaks on upgrade, and the supported replacement is the Codex app server: a separate JSON-RPC protocol that OpenAI labels experimental and "isn't supported for production workloads." Our verdict: pin below 0.154.0 only as a short stopgap, move CI and scripted jobs to codex exec or the Codex SDK, and reach for the app server only when you are building a rich interactive client. codex mcp, which lets Codex use other MCP servers, is unaffected.
Last updated: October 17, 2026
What changed, exactly#
The change is narrow. It removes one direction of the MCP relationship: Codex acting as a server that another agent or app calls as a tool. Codex acting as an MCP client is untouched. If the server-versus-client distinction is fuzzy, What is MCP covers hosts, clients and servers in detail.
| Date | Event | Source |
|---|---|---|
| August 24, 2026 | "Codex MCP server command deprecated" - changelog says to use the app server instead | Codex changelog |
| September 5, 2026 | PR #42993 "Remove the deprecated codex mcp-server command" merged; changelog entry "Codex MCP server removed" | PR #42993, changelog |
| September 9, 2026 | Codex CLI 0.154.0 published, the first release without the entry point (listed under Chores) | rust-v0.154.0 |
| October 15, 2026 | Codex CLI 0.163.0, the current release we tested against | rust-v0.163.0 |
The changelog entry is blunt: "The codex mcp-server command and standalone codex-mcp-server binary have been removed after their deprecation on August 24, 2026. Update integrations that launch either command before upgrading Codex."
Removed: the codex mcp-server subcommand, the standalone codex-mcp-server crate (with its tests, interface documentation, build dependencies and run recipe, per the PR), and the MCP tool reference and Agents SDK examples that used to live on the MCP server docs page. That page now says those examples "are no longer supported."
Still supported: codex mcp add, list, get, remove, login and logout, plus the [mcp_servers.<name>] tables in ~/.codex/config.toml. OpenAI's changelog: "Codex continues to support connecting to external MCP servers through codex mcp."
%% title: Which Codex MCP path broke
flowchart LR
A[Your MCP client] -->|spawns| B["codex mcp-server"]
B -.- E[Removed in 0.154.0]
C[Codex CLI or app server] -->|codex mcp| D[External MCP servers]
D -.- F[Still supported]Am I affected?#
You are affected if both of these are true:
- Something launches
codex mcp-serverorcodex-mcp-server. Typical places: an.mcp.jsonor other MCP client config with"command": "codex"and"args": ["mcp-server"], an editor or desktop app's MCP settings, or code that wraps Codex in an MCP stdio transport (for example the Agents SDKMCPServerStdiopattern the old docs showed). - The Codex binary on that machine is 0.154.0 or newer. Check with
codex --version.
A quick triage pass over a repo and your home config:
codex --version
grep -rnE 'codex-mcp-server|mcp-server' \
--include='*.json' --include='*.toml' --include='*.yaml' \
--include='*.py' --include='*.ts' --include='*.js' \
. ~/.codex 2>/dev/null
Expect false positives from other tools that also use the string mcp-server; you only care about lines where the command is codex.
Gotcha found in testing: on 0.163.0, codex mcp-server does not fail with "unknown command." The CLI parses mcp-server as a positional prompt for the interactive terminal UI. Launched by an MCP client (no TTY), it prints Error: stdin is not a terminal and exits with code 1. If an MCP integration that used to work now reports a terminal error, this is the likely cause.
Pick your migration target#
There is no one-to-one replacement, because the app server is not an MCP server. Pick by what the integration actually did.
What you used codex mcp-server for | Move to | Why |
|---|---|---|
| CI jobs, batch scripts, single-shot "do this task" calls | codex exec or the Codex SDK | The app-server docs say it directly: "If you are automating jobs or running Codex in CI, use the Codex SDK instead." |
| Embedding Codex in your own product with approvals, history and streaming | Codex app server | It is "the interface Codex uses to power rich clients (for example, the Codex VS Code extension)." |
| Another agent needs Codex exposed as MCP tools | No official replacement | You would have to write and maintain your own thin MCP wrapper over the SDK or app server. That is an option, not something OpenAI recommends or supports. |
For the CI path, our Codex exec in CI guide has working recipes, and Codex SDK vs CLI vs GitHub Action covers which surface fits which job.
%% title: Choosing a replacement
flowchart TD
A[Need Codex from code?] --> B{CI or batch job?}
B -->|Yes| C[codex exec or Codex SDK]
B -->|No| D{Interactive UI with approvals?}
D -->|Yes| E[Codex app server]
D -->|No| F{Another agent needs MCP tools?}
F -->|Yes| G[Own wrapper - unsupported]
F -->|No| CHow the app server works#
The app server speaks JSON-RPC 2.0 with the "jsonrpc":"2.0" header omitted on the wire. The protocol docs list these transports:
- stdio (default,
--listen stdio://): newline-delimited JSON, one message per line. - Unix socket (
--listen unix://orunix://PATH): WebSocket over Codex's control socket or a custom path. - WebSocket (
--listen ws://IP:PORT): experimental and unsupported, one message per text frame. - off (
--listen off): no local transport.
Three primitives carry everything:
- Thread: a conversation.
- Turn: one user request and the agent work that follows.
- Item: a unit of input or output, such as an agent message, a command run, a file change or a tool call.
The handshake has strict rules. Each connection sends exactly one initialize request, then an initialized notification. Anything sent before that gets a Not initialized error (we reproduced this: JSON-RPC code -32600), and a second initialize returns Already initialized. The clientInfo.name you send is how OpenAI identifies your client in the Compliance Logs Platform, so set it to something stable.
Some methods and fields sit behind capabilities.experimentalApi: true in initialize. Without it, the server rejects them with <descriptor> requires experimentalApi capability. Leave it off unless you need a gated feature; the stable surface is smaller but less likely to shift under you.
%% title: App server turn lifecycle
sequenceDiagram
participant C as Client
participant S as codex app-server
C->>S: initialize
S-->>C: result (userAgent, codexHome)
C->>S: initialized
C->>S: thread/start
S-->>C: result + thread/started
C->>S: turn/start
S-->>C: turn/started, item/started
S->>C: item/commandExecution/requestApproval
C-->>S: { decision }
S-->>C: serverRequest/resolved
S-->>C: item/agentMessage/delta
S-->>C: item/completed
S-->>C: turn/completedApprovals are the part that differs most from a request-response tool. The server sends its own JSON-RPC requests to your client: item/commandExecution/requestApproval and item/fileChange/requestApproval. Your client answers with a decision. For commands the documented choices are accept, acceptForSession, decline, cancel or acceptWithExecpolicyAmendment; for file changes, accept, acceptForSession, decline or cancel. The server then emits serverRequest/resolved and item/completed.
Step-by-step migration (verified on Codex CLI 0.163.0)#
Everything below was run against @openai/codex@0.163.0 from npm, in a scratch directory with an empty CODEX_HOME and no credentials. That is enough to prove the protocol wiring; it is not enough to run a model turn, and we did not.
Step 1: Upgrade and confirm the version#
npm i -g @openai/codex
codex --version
codex app-server --helpOn 0.163.0, codex --version prints codex-cli 0.163.0, and codex app-server --help labels the command [experimental] with four subcommands: daemon, proxy, generate-ts and generate-json-schema. codex --help no longer lists mcp-server.
Step 2: Generate bindings for your installed version#
codex app-server generate-ts --out ./schemas
# or
codex app-server generate-json-schema --out ./schemas
The output is specific to the Codex version that generated it. Treat it as the source of truth over any JSON you copy from the docs (see Gotchas below for why). Regenerate whenever you upgrade Codex, and commit the result so version drift shows up in code review.
Step 3: Smoke-test the handshake without credentials#
Pipe four JSONL messages into the server. Keep stdin open for a few seconds, or the server exits before it answers:
(printf '%s\n' \
'{"method":"initialize","id":0,"params":{"clientInfo":{"name":"smoke","title":"Smoke","version":"0.0.1"},"capabilities":null}}' \
'{"method":"initialized"}' \
'{"method":"account/read","id":1,"params":{}}' \
'{"method":"thread/start","id":2,"params":{"cwd":"/tmp","sandbox":"workspace-write","approvalPolicy":"on-request"}}'; \
sleep 5) | codex app-server
Trimmed output from our run:
{"id":0,"result":{"userAgent":"smoke/0.163.0 (Ubuntu 24.4.0; x86_64) linux (smoke; 0.0.1)","codexHome":"/path/to/codexhome","platformFamily":"unix","platformOs":"linux"}}
{"id":1,"result":{"account":null,"requiresOpenaiAuth":true,"workspaceRouting":null}}
{"id":2,"result":{"thread":{"id":"01a14bd4-...","model":"gpt-6.1-sol","status":{"type":"idle"},"historyMode":"paginated", ...}}}
{"method":"thread/started","params":{"thread":{"id":"01a14bd4-...", ...}}, ...}
Three useful facts fall out of this. initialize returns your user agent and the resolved codexHome. account/read reports account: null and requiresOpenaiAuth: true, which is your signal to prompt for sign-in. And thread/start succeeds without credentials, so a failure there points at config (for example a required MCP server), not auth. Notifications also carry an emittedAtMs timestamp.
Step 4: Sign in before running turns#
A real turn needs an authenticated account. For local development, codex login in a terminal is the simplest route; a client can also drive login over RPC with account/login/start. Note the auth caveat in Gotchas before you build login into anything you ship.
Step 5: A typed stdio client#
This is a minimal client that starts a thread, sends one turn, answers approval requests and prints the streamed reply. It imports the types generated in Step 2.
import { spawn } from "node:child_process";
import readline from "node:readline";
import type {
ClientNotification,
ClientRequest,
ServerNotification,
ServerRequest,
} from "../schemas";
import type {
CommandExecutionRequestApprovalResponse,
FileChangeRequestApprovalResponse,
ThreadStartResponse,
} from "../schemas/v2";
const proc = spawn("codex", ["app-server"], {
stdio: ["pipe", "pipe", "inherit"],
});
const rl = readline.createInterface({ input: proc.stdout });
const write = (msg: ClientRequest | ClientNotification | { id: string | number; result: unknown }) => {
proc.stdin.write(`${JSON.stringify(msg)}\n`);
};
rl.on("line", (line) => {
const msg = JSON.parse(line);
// 1. Responses to our requests carry our id and a result or error.
if ("result" in msg || "error" in msg) {
if (msg.id === 1) {
const { thread } = msg.result as ThreadStartResponse;
write({
method: "turn/start",
id: 2,
params: {
threadId: thread.id,
input: [{ type: "text", text: "Summarize this repo.", text_elements: [] }],
},
});
}
return;
}
// 2. Server-initiated requests (approvals) carry an id and a method.
if ("id" in msg && "method" in msg) {
const req = msg as ServerRequest;
if (req.method === "item/commandExecution/requestApproval") {
const result: CommandExecutionRequestApprovalResponse = { decision: "decline" };
write({ id: req.id, result });
} else if (req.method === "item/fileChange/requestApproval") {
const result: FileChangeRequestApprovalResponse = { decision: "accept" };
write({ id: req.id, result });
}
return;
}
// 3. Notifications have a method and no id.
const note = msg as ServerNotification;
if (note.method === "item/agentMessage/delta") {
process.stdout.write(note.params.delta);
} else if (note.method === "turn/completed") {
console.log(`\nturn ${note.params.turn.status}`);
proc.kill();
}
});
write({
method: "initialize",
id: 0,
params: {
clientInfo: { name: "my_product", title: "My Product", version: "0.1.0" },
capabilities: null,
},
});
write({ method: "initialized" });
write({
method: "thread/start",
id: 1,
params: { cwd: process.cwd(), approvalPolicy: "on-request", sandbox: "workspace-write" },
});
This file type-checks with tsc --noEmit (strict, moduleResolution: "bundler") against bindings generated by @openai/codex@0.163.0. Two details the generated types forced on us: the text input variant requires text_elements (an empty array is fine), and the initialized notification takes no params. The demo declines every command and accepts every file change to show both reply shapes; a real client should surface these to a user. No model calls were run.
Mapping old MCP habits to app-server calls#
If your integration was built around "call a Codex tool, get text back," this is how the concepts translate. The app server is stateful and streaming, so expect more client code than a single tool call.
| What you did over MCP | App-server equivalent |
|---|---|
| Start a Codex session with a prompt | thread/start, then turn/start with the prompt as input |
| Continue an existing session | thread/resume, then turn/start (or turn/steer to add input to a turn still in flight) |
| Branch a session | thread/fork |
| Approve a command or patch | Answer item/commandExecution/requestApproval or item/fileChange/requestApproval server requests |
| Read the final answer from the tool result | Accumulate item/agentMessage/delta; treat item/completed as authoritative |
| Cancel | turn/interrupt with threadId and turnId; the turn ends with status: "interrupted" |
| Use Codex's own MCP servers | mcpServerStatus/list, mcpServer/tool/call, mcpServer/resource/read, config/mcpServer/reload |
That last row is worth noting: the app server still consumes the MCP servers configured in Codex. Your [mcp_servers] setup carries over; only the "Codex as a server" path is gone. Steering behavior changed recently too; our Codex CLI 0.163 notes cover how mid-turn input now works.
Remote and WebSocket use#
For a remote terminal UI, OpenAI documents this pair:
codex app-server --listen ws://127.0.0.1:4500
codex --remote ws://127.0.0.1:4500
For anything that is not localhost or an SSH port-forward, use TLS (wss://) and pass the bearer token by environment variable name with --remote-auth-token-env, rather than putting it on the command line.
The WebSocket listener also serves GET /readyz (200 once it accepts connections) and GET /healthz. Requests that carry an Origin header are rejected with 403 Forbidden, which blocks browser pages from talking to it directly.
The setting to get right: non-loopback WebSocket listeners currently allow unauthenticated connections by default during rollout. Configure auth before exposing one:
# shared capability token read from a file
codex app-server --listen ws://0.0.0.0:4500 \
--ws-auth capability-token --ws-token-file /absolute/path/to/token
# or signed bearer tokens
codex app-server --listen ws://0.0.0.0:4500 \
--ws-auth signed-bearer-token --ws-shared-secret-file /absolute/path/to/secret
Signed bearer tokens also accept --ws-issuer, --ws-audience and --ws-max-clock-skew-seconds. Auth is enforced during the WebSocket handshake, before initialize. Under load, WebSocket mode returns JSON-RPC error -32001 with "Server overloaded; retry later," and OpenAI asks clients to retry with exponential backoff and jitter.
If you have noticed a background Codex process on your machine, that is the same binary: the managed daemon runs codex app-server --managed-daemon. Our background daemon post covers how to stop it.
%% title: Remote app server setup
flowchart LR
A[Remote TUI or your client] -->|wss + bearer token| B[codex app-server --listen ws://]
B -->|local default| C[Code Mode host]
B -.->|--code-mode-host wss://| D[Remote Code Mode host]
E[Health checks] -->|GET /readyz /healthz| BBy default the app server starts a local Code Mode host; --code-mode-host wss://... points it at a remote one. That flag controls the app server's outbound connection only and does not change --listen.
Gotchas and limits#
- It is experimental. OpenAI's wording on the changelog and docs: the app-server command "is experimental and isn't supported for production workloads." Plan for breaking changes between releases.
- Auth is restricted. The docs state: "App-server authentication has never been permitted for commercial or hosted services." For those, OpenAI points to Sign in with ChatGPT. Local and open-source apps can keep using it, with a recommendation to migrate.
- Doc JSON does not always match the binary. The docs'
thread/startexample uses"sandbox": "workspaceWrite". On 0.163.0 that returns-32600"unknown variantworkspaceWrite, expected one ofread-only,workspace-write,danger-full-access." Theturn/startexample's"approvalPolicy": "unlessTrusted"fails the same way; the accepted values areuntrusted,on-request,neveror agranularobject. Meanwhile the turn-levelsandboxPolicydoes use camelCase type tags (workspaceWrite,readOnly). Generate bindings from your installed version and let the compiler catch this. - Required MCP servers can block threads. If an enabled server in
[mcp_servers]is markedrequiredand fails to initialize,thread/startandthread/resumefail instead of continuing. - Default model access. The docs' examples use
gpt-6.1-sol, which needs access on the signed-in account; our unauthenticatedthread/startalso defaulted to it. Callmodel/listand pick from what comes back. - The removed command fails confusingly. As covered above, old launchers get a TTY error, not a removal notice. Search configs rather than waiting for a clear error.
App server vs Codex SDK#
The Codex SDK is the closest alternative and, for many former mcp-server users, the better fit. They serve different jobs: the app server gives the deepest control over a session, and the SDK gives the shortest path from code to a finished task.
| Codex app server | Codex SDK | |
|---|---|---|
| Interface | JSON-RPC over stdio, Unix socket or WebSocket | Library calls (startThread, run, resumeThread) |
| What it drives | The Codex agent directly | TypeScript @openai/codex-sdk@0.163.0 spawns codex exec --experimental-json (from our inspection of the package); Python openai-codex "controls the local Codex app-server over JSON-RPC" per the docs |
| Approvals | Interactive, per item, via server requests | sandboxMode and approvalPolicy set up front in thread options |
| Streaming | Item-level events and message deltas | run returns the final result; runStreamed yields events |
| History and control | Thread resume, fork, steer, interrupt, filesystem, MCP status | Thread start and resume |
| Maturity | Experimental, not for production | Python SDK is a stable release (Python 3.10+, ships a pinned Codex runtime) |
| Best for | Building an IDE-style client | CI, scripts, backend jobs |
The TypeScript SDK path is short:
import { Codex } from "@openai/codex-sdk";
const codex = new Codex();
const thread = codex.startThread();
const result = await thread.run("Make a plan to diagnose and fix the CI failures");
console.log(result.finalResponse);
const again = codex.resumeThread(thread.id ?? "");
const next = await again.run("Implement the plan");
console.log(next.finalResponse);
Type-checked against @openai/codex-sdk@0.163.0 (strict, moduleResolution: "bundler"); thread.id is typed as nullable, hence the fallback. Install with npm install @openai/codex-sdk; it depends on the matching @openai/codex version. For Python, pip install openai-codex gets you the app-server-backed client without hand-writing JSON-RPC.
FAQ#
Is codex mcp also removed?#
No. The removal only affects hosting Codex as an MCP server. codex mcp add, list, get, remove, login and logout still manage the external MCP servers Codex connects to, configured under [mcp_servers.<name>] in ~/.codex/config.toml.
Which version removed codex mcp-server?#
Codex CLI 0.154.0, published September 9, 2026. The removal came from PR #42993, merged September 5, after the command was deprecated on August 24, 2026. Any version from 0.154.0 onward lacks both codex mcp-server and the codex-mcp-server binary.
Is the Codex app server an MCP server?#
No. OpenAI's migration page says it "uses its own JSON-RPC protocol. It isn't an MCP server or a drop-in replacement for an MCP client." Existing MCP tool calls need to be rewritten against the thread, turn and item methods.
Can I use the app server in production or a hosted product?#
Not with OpenAI's support. The command is experimental and "isn't supported for production workloads," and app-server authentication "has never been permitted for commercial or hosted services." OpenAI recommends Sign in with ChatGPT for hosted use cases.
Should I just pin Codex below 0.154.0?#
Only as a stopgap while you migrate. Pinning means missing later fixes; 0.154.0 itself shipped startup hardening that "avoids running workspace-controlled helpers before trust is established." Set a date to move to codex exec, the SDK or the app server.
Sources#
- Codex changelog (deprecation and removal entries)
- Codex MCP server migration page
- Codex app server documentation
- Codex MCP client configuration
- Codex SDK documentation
- openai/codex PR #42993: Remove the deprecated codex mcp-server command
- Codex CLI release rust-v0.154.0
- Codex CLI release rust-v0.163.0
- App server source: openai/codex/codex-rs/app-server
Continue Reading#
- Codex SDK vs CLI vs GitHub Action - which Codex surface to build on once the MCP server is off the table.
- Codex CLI Background Daemon: How to Turn It Off - the managed daemon is the same app server, running in the background.
- Codex Exec in CI - the headless path for jobs that used
codex mcp-serverfor batch work.
Get the next deep dive like this in your inbox
One email a week on Codex and the rest of the AI dev stack. Free.
Read next on AI coding tools
Codex SDK vs CLI vs GitHub Action: Which Surface Should You Build On?
Codex SDK vs Codex CLI vs the Codex GitHub Action: when to use each surface, and how to avoid building the same agent loop three times.
8 min readCodex CLI Background Daemon: How to Turn It Off
Codex CLI starts a persistent, self-updating background daemon since 0.157.0. What it does, why it hit Hacker News and the reported workaround to turn it off.
7 min readCodex Exec in CI: Practical Guide to Headless OpenAI Agents
codex exec is OpenAI's non-interactive mode to run Codex agents from scripts, CI and GitHub Actions. How to set it up safely with real flags and working YAML.
9 min readNew here? Start with
Technical content at the intersection of AI and development. Building with AI agents, Claude Code, and modern dev tools - then showing you exactly how it works.







