Docs: plugin .mcp.json example uses mcp_servers but Codex expects mcpServers
Affected documentation URL
https://developers.openai.com/codex/plugins/build#bundled-mcp-servers-and-lifecycle-config
Problem
The plugin documentation says that mcpServers in .codex-plugin/plugin.json can point to an .mcp.json file, and then shows this wrapped .mcp.json format:
{
"mcp_servers": {
"docs": {
"command": "docs-mcp",
"args": ["--stdio"]
}
}
}
In practice, this wrapped format is not recognized by Codex for plugin MCP config in my testing. Changing the wrapper key to mcpServers makes the MCP server load correctly:
{
"mcpServers": {
"docs": {
"command": "docs-mcp",
"args": ["--stdio"]
}
}
}
A direct top-level server map also works:
{
"docs": {
"command": "docs-mcp",
"args": ["--stdio"]
}
}
Evidence from current code
The current plugin MCP parser appears to deserialize the wrapped plugin MCP file with camelCase JSON field names:
codex-rs/core-plugins/src/loader.rsPluginMcpServersFilehas#[serde(rename_all = "camelCase")]- the Rust field is named
mcp_servers, which maps to JSONmcpServers, not JSONmcp_servers codex-rs/core-plugins/src/loader_tests.rsplugin_mcp_file_supports_mcp_servers_object_formatparses JSON containing"mcpServers"- the flat map test parses a direct top-level server map
This also matches the rust-v0.123.0 release note for PR #18780, which says plugin MCP loading supports .mcp.json files containing an mcpServers object or top-level server maps:
https://github.com/openai/codex/pull/18780
Local version where reproduced
codex-cli 0.130.0
Suggested docs change
In the "Bundled MCP servers and lifecycle config" section, change the wrapped server map example from mcp_servers to mcpServers, or clarify that plugin .mcp.json accepts mcpServers and direct top-level server maps, while mcp_servers is the TOML/config naming used in ~/.codex/config.toml.
The corrected plugin .mcp.json wrapper example would be:
{
"mcpServers": {
"docs": {
"command": "docs-mcp",
"args": ["--stdio"]
}
}
}
Notes
I tried to locate the source for the developers.openai.com Codex plugin docs in this public repository but could not find the page source via GitHub code search or the repo tree, so I am filing this as a docs issue instead of opening a documentation PR.
4 Comments
I took a small pass at this because the loader already has a narrow parsing point for plugin
.mcp.jsonfiles.Patch branch/commit:
What it does:
#[serde(alias = "mcp_servers")]to the wrapped MCP server config field.mcpServerssupport and the flat top-level server map format unchanged.mcp_serversas a server name.Validation:
cargo test -p codex-core-plugins plugin_mcp_file_supports_snake_case_mcp_servers_object_format -- --nocapturefailed before the alias with"mcp_servers"parsed as the server key.cargo test -p codex-core-plugins— 196 passed.just fmt— completed successfully.I attempted to open this as an upstream PR from the fork, but GitHub returned a create-pull-request permission/404 for
openai/codex, so I’m leaving the verified patch here for maintainers rather than creating noise elsewhere.@slegarraga Are you allowing
mcp_serversas an alias? Why? Isn't this just a documentation bug sayingmcp_serversinstead ofmcpServers? (I'm concerned because.mcp.jsonis meant to be a _standard_ between agents.)Thanks — that’s a fair concern. Yes, the patch allowed
mcp_serversonly as a parser alias on Codex’s wrapped config field, not as a new canonical spelling or emitted format; the intent was to avoid breaking users who copied the current docs while keepingmcpServersas the canonical.mcp.jsonshape.That said, I agree the standardization point matters. If the preferred direction is strict canonical input, I’m happy to drop the alias approach and make this a docs-only fix to use
mcpServersinstead.I re-checked the current published docs page through the OpenAI docs source, and the
Bundled MCP servers and lifecycle hookssection still describes the wrapped.mcp.jsonform asmcp_serversand shows that key in the JSON example.That still appears to conflict with the current loader/tests: the wrapped config struct uses camelCase serde naming, so the Rust field
mcp_serversmaps to JSONmcpServers; the tests cover themcpServerswrapper and the direct top-level server map.Given the concern above about keeping
.mcp.jsoninteroperable, I agree this is best treated as a docs-only correction: usemcpServersin the wrapped JSON example, and keepplugins.<plugin>.mcp_servers.<server>only for the TOML/plugin-scoped policy example where snake_case is expected.