ThinkFacility

Error messages

MCP startup failed

The message

MCP startup failed
Codex CLI 0.157.0 read September 25, 2026CodexOpenAIMCP

What it means

One of the MCP servers in your config didn't finish starting, so Codex went on without its tools. The words after “MCP startup failed:” say which step broke: launching the program, the handshake, a login, or the 30-second startup timer.

What to do

Run the server's command yourself in a terminal to see if it starts. If it's slow, raise startup_timeout_sec under [mcp_servers.NAME]; if it needs a variable from your shell, list it in env_vars.

Codex starts every MCP server in your config when a session opens. An MCP server is a small helper program (or a web endpoint) that gives the model extra tools, speaking the Model Context Protocol. When one of them doesn't come up, the terminal shows a line like this:

⚠ MCP client for `node_repl` failed to start: MCP startup failed: Access is denied. (os error 5)
⚠ MCP startup incomplete (failed: node_repl)

That pair is from a Windows report on September 23, 2026. Codex carries on without that server's tools, so the session still works, just with less in it. 154 issues in the openai/codex repo quote "MCP startup failed" as of September 25, 2026, and we read the 0.157.0 source to see where it comes from.

Why it says "failed to start" twice

The phrase is two wrappers around one error. The inner one, MCP startup failed: {reason}, is how Codex's MCP client labels anything that went wrong while bringing a server up. The terminal then wraps that in MCP client for `NAME` failed to start:. So the only part that tells you anything is whatever comes after the second colon.

Two cases get rewritten into friendlier lines before you see them. A server that needs a login prints The NAME MCP server is not logged in. Run `codex mcp login NAME`., and a timeout prints its own sentence with a config snippet (more on that below). The raw MCP startup failed: form still turns up for those in logs and in tool-call errors, which is why people search both.

The reasons that come up most

We went through about a hundred of the issues and sorted them by the text after the colon.

handshaking with MCP server failed: connection closed: initialize response is the biggest group by far. Codex launched the program and sent it the opening initialize message, and the program exited (or closed its output) before replying. Usually the server crashed on startup, or it printed something other than protocol messages to standard output. The HTTP version of the same failure reads Send message error Transport [...] and carries the HTTP problem at the end, a 404 or an Auth required.

No such file or directory (os error 2) or program not found means the command in your config couldn't be found. In one September report a plugin launched plain node on a Mac whose shell had no Node on its PATH. On Windows, Codex looks commands up with the PATH and PATHEXT it passes to the server, which is how npx finds npx.cmd.

Environment variable X for MCP server 'NAME' is not set surprised us the most, because the reporter had the variable set in the shell they launched Codex from. Here's the reason. Codex doesn't hand a local server its whole environment. On macOS and Linux it passes a short list (HOME, PATH, USER, SHELL, LANG, TERM, TMPDIR and a few more), plus whatever you name in the server's env_vars list or set in its env table. An API token sitting in your shell doesn't reach the server unless you name it there.

The 30-second startup timer

Each server gets 30 seconds to launch, finish the handshake and list its tools. That's DEFAULT_STARTUP_TIMEOUT in the source. Run past it and you see:

MCP client for `NAME` timed out after 30 seconds. Add or adjust `startup_timeout_sec` in your config.toml:
[mcp_servers.NAME]
startup_timeout_sec = XX

Both keys the snippet names are real. startup_timeout_sec takes seconds (decimals are fine), and there's a startup_timeout_ms that's only read when the seconds key is absent. Separately, tool_timeout_sec covers each tool call afterwards and defaults to 300 seconds. A server run through npx that downloads its package on first launch is the usual candidate for a longer timer (that first download counts against the same timer).

Finding the real error

Start by running the exact command and args from your config in a terminal. If it dies there, you'll see why, which is more than Codex's one line gives you.

If it runs fine by hand, look at the server's own error output as Codex saw it. Codex logs every line a local server writes to standard error as MCP server stderr (program): .... The TUI's plaintext log is off by default, so start Codex with codex -c log_dir=./.codex-log and read ./.codex-log/codex-tui.log. Whatever the server complained about is there in its own words.

Two more keys are worth knowing. enabled = false under a server skips it entirely, which is quicker than deleting it while you debug. And required = true makes codex exec stop with an error when that server fails, rather than carrying on without its tools, which you'll want in scripts. codex mcp list prints what's configured.

When the desktop app fails and the CLI doesn't

A cluster of September reports have a config that starts fine in the terminal and fails in the Codex desktop app, or a built-in connector (codex_apps, node_repl, computer-use) failing after an app update. Those point at the app's environment or at OpenAI's side, not your config. One July report of a 30-second codex_apps handshake timeout cleared up on its own within hours, and the reporter closed it. For your own servers in the app, the environment list above is the first thing to check, since the app isn't launched from your shell at all.

If the line says Access is denied. (os error 5) on Windows, the Windows sandbox may be involved; our page on its failures explains the permissions it sets up. Claude Code has its own version of this problem, “✘ Failed to connect”, with a different timeout and different knobs.

Other lines the same feature prints

Match yours against these if the one at the top of the page is not quite it. They come from the same code and mean related things.

  • MCP client for `NAME` failed to start: MCP startup failed: handshaking with MCP server failed: connection closed: initialize response
  • MCP client for `NAME` failed to start: MCP startup failed: No such file or directory (os error 2)
  • MCP startup failed: timed out handshaking with MCP server after 30s
  • MCP client for `NAME` timed out after 30 seconds. Add or adjust `startup_timeout_sec` in your config.toml:
  • MCP startup incomplete (failed: NAME)
  • MCP startup failed: Environment variable NAME for MCP server 'NAME' is not set
  • The NAME MCP server is not logged in. Run `codex mcp login NAME`.