Plugin MCP servers need a supported user secret/env configuration path
Summary
I maintain a Codex plugin that bundles an MCP server via .codex-plugin/plugin.json:
{
"mcpServers": "./.mcp.codex.json"
}
The plugin MCP config includes defaults and declares a user-provided secret through env_vars:
{
"mcpServers": {
"poka": {
"command": "node",
"args": ["mcp-server/dist/index.js"],
"cwd": ".",
"env": {
"POKA_BROKER_URL": "wss://...",
"WORKPLUS_API_ENDPOINT": "https://...",
"WORKPLUS_READ_ONLY": "false"
},
"env_vars": [
"WORKPLUS_ACCESS_TOKEN"
]
}
}
}
The unclear part is: what is the intended UX for a plugin user to provide their own WORKPLUS_ACCESS_TOKEN?
Problem
For normal MCP servers, users can write a complete block in ~/.codex/config.toml:
[mcp_servers.my_server]
command = "..."
args = ["..."]
env_vars = ["MY_TOKEN"]
or:
[mcp_servers.my_server.env]
MY_TOKEN = "..."
But for a plugin-provided MCP server, the server definition already comes from the plugin bundle. If the user adds only:
[mcp_servers.poka.env]
WORKPLUS_ACCESS_TOKEN = "..."
Codex appears to treat this as an incomplete user-defined MCP server config instead of a partial override/merge for the plugin-provided server. The MCP config then breaks because command / url is missing.
So there does not appear to be a stable way for users to provide a secret env var for a plugin-provided MCP server without one of the following workarounds:
- Launching Codex with a parent environment variable already set.
- Duplicating the entire plugin MCP server block in
config.toml, including plugin cache paths. - Having the plugin implement its own credential storage outside Codex config.
All three are awkward for end users, especially in Codex Desktop.
Expected Behavior
Codex should provide a documented, supported way for users to configure secret values for plugin-provided MCP servers.
Possible designs:
Option A: Partial plugin MCP env override
Allow this to merge into the plugin-provided server named poka:
[mcp_servers.poka.env]
WORKPLUS_ACCESS_TOKEN = "..."
without requiring users to repeat command, args, or cwd.
Option B: Plugin user config / secrets
Allow plugin manifests to declare configurable secret fields, similar to:
{
"user_config": {
"workplus_access_token": {
"type": "string",
"sensitive": true,
"required": false
}
}
}
and allow .mcp.codex.json to reference them:
{
"env": {
"WORKPLUS_ACCESS_TOKEN": "${user_config.workplus_access_token}"
}
}
Ideally Codex Desktop would expose this in plugin settings and store sensitive values securely.
Option C: Documented env_vars UX for Desktop
If env_vars is the intended solution, please document how a Codex Desktop user is supposed to set those values persistently without relying on shell-specific launch behavior.
Actual Behavior
Plugin MCP config loads and works. However, user-level env customization for that plugin server is unclear.
Adding only:
[mcp_servers.poka.env]
WORKPLUS_ACCESS_TOKEN = "..."
breaks the server config because Codex appears to treat it as a separate incomplete server definition rather than an override for the plugin-provided server.
Why This Matters
Many MCP plugins need per-user credentials: API keys, access tokens, internal service tokens, etc.
As a plugin maintainer, I currently do not know what to tell users. Asking users to copy the full MCP server config is brittle because plugin cache paths and versions can change. Asking Desktop users to set parent process environment variables is not a reasonable product UX.
Environment
- Codex version:
0.133.0-alpha.1 - Platform: macOS
- Plugin MCP server via
.codex-plugin/plugin.jsonmcpServers - MCP transport: stdio
Related Observations
env_varsis documented/used for allowing selected parent environment variables into MCP subprocesses.config.tomldoes not appear to support shell-style env expansion likeTOKEN = "$TOKEN".- Plugin-provided MCP config and user
config.tomlMCP server config do not appear to support partial deep merge for env overrides.
2 Comments
Solution: codex #24401 — Plugin MCP servers need a supported user secret/env configuration path
Issue Summary
Plugin MCP servers bundled via
.codex-plugin/plugin.jsonhave no standard way to accept user-provided secrets throughenv_vars. Adding only[mcp_servers.poka.env]inconfig.tomlis treated as a separate incomplete server definition rather than a partial override for the plugin-provided server.Root Cause Analysis
The MCP server configuration loading has two separate code paths:
config.toml: Full server definition required (command, args, env).codex-plugin/plugin.json: Complete definition from plugin bundleThese paths don't support partial deep merge. When a user adds
[mcp_servers.poka.env]to override just the env section, Codex treats it as a new incomplete server definition (missingcommand/args/cwd) rather than merging it with the plugin-provided definition.The
env_varsfield is documented for allowing parent environment variables into MCP subprocesses, but there's no documented UX for Desktop users to persistently set those values.Proposed Fix
Option A: Partial plugin MCP env override (recommended, minimal change)
Allow user
config.tomlto provide partial overrides that merge with plugin-provided server definitions:Implementation:
Option B: Plugin user config / secrets manifest (long-term, more comprehensive)
Allow plugin manifests to declare configurable secret fields:
User provides values via:
~/.codex/user-secrets.json(encrypted)CODEX_USER_CONFIG_WORKPLUS_ACCESS_TOKEN=xxxOption C: Documented env_vars UX for Desktop
If
env_varsis the intended solution, document and implement a clear path:Or via environment:
Recommended Implementation Order
Test Plan
command/args/env. User adds[mcp_servers.poka.env]with just a token → server should start with merged config${userConfig.key}should resolve correctly in env templatesImpact Assessment
Submission
Post as comment on: https://github.com/openai/codex/issues/24401
Ideally the solution should follow Anthropic's own standard for
userConfigvalues w/sensitive: true. This should support the system's keychain for secret storage.See here for the full spec: https://code.claude.com/docs/en/plugins-reference#user-configuration