Plugin manifest cannot declare user-supplied config, leaving credentials with no input path
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Start by tracing RawPluginManifest, PluginMcpServerConfig, and create_env_for_mcp_server to understand the current manifest, user-config, and environment-variable boundaries. Review the existing authentication ON_INSTALL and ON_USE policies, then define how declared inputs, sensitive-value storage, prompting, and substitution should work. Done requires an agreed end-to-end credential path rather than relying only on the host environment.
Written by the indexing model from the issue text.
Description
A plugin whose MCP server needs a credential — an API key, a token, a client secret — has no way to ask the user for it, and the user has no way to supply it through Codex.
Where it breaks
Two independent gaps close off every path:
The manifest cannot declare a config input. RawPluginManifest accepts name, version, description, keywords, skills, mcp_servers, apps, hooks, and interface. There is no field for user-supplied values, so a plugin cannot state that it needs a secret, let alone have Codex prompt for one at install time.
User config cannot fill the gap either. PluginMcpServerConfig carries only enabled, default_tools_approval_mode, enabled_tools, and the tool deny-list. Its doc comment is explicit about why:
This intentionally excludes transport settings: plugin manifests own how the MCP server is launched, while user config owns enablement and tool policy.
So [plugins."name@marketplace".mcp_servers.server] in config.toml cannot add env or env_vars.
That leaves exactly one channel: env_vars forwarding from the host process environment, resolved in create_env_for_mcp_server via env::var_os. The plugin names the variables; the user must arrange for them to exist in the environment Codex itself was started in.
Why that channel is not sufficient
- It is invisible. Nothing in
codex plugin addor/pluginstells the user which variables to set. They have to read the plugin's README, or the manifest. - It fails silently by default. Install the plugin, restart, and the server dies on startup with whatever error it emits for missing config. Nothing connects that back to "you needed to export three variables."
- Shell config does not cover GUI launches.
~/.zshrconly applies to interactive shells;~/.zshenvcovers non-interactive ones too, but neither is read when Codex is launched from Spotlight or the Dock, because no shell is involved. Getting a variable to a GUI-launched Codex meanslaunchctl setenvor a LaunchAgent — which puts the secret in the environment of every GUI application on the machine. - It pushes secrets into plaintext. The natural place to put an
exportis a dotfile, which people back up and commit. Reading from the system keychain is possible but every plugin author has to document that dance themselves.
What Claude Code does
Its plugin manifest has a userConfig block: each entry declares type, title, description, and optionally sensitive and required. The client prompts at enable time, stores sensitive values in the OS keychain rather than settings, and substitutes them into the MCP server config as ${user_config.KEY}.
A plugin shipping the same MCP server to both clients ends up with two manifests that diverge on exactly this point — Claude Code prompts and stores in the keychain, Codex needs the user to edit a shell file and restart.
Suggestion
Let the manifest declare inputs, and let their values reach the MCP server config:
{
"userConfig": {
"api_token": {
"type": "string",
"title": "API token",
"description": "Found under Settings → Developer",
"sensitive": true,
"required": true
}
},
"mcpServers": {
"example": {
"command": "npx",
"args": ["-y", "example-mcp"],
"env": { "EXAMPLE_TOKEN": "${user_config.api_token}" }
}
}
}
Prompting could reuse the existing authentication: ON_INSTALL / ON_USE policy already present on marketplace entries. Storage for sensitive: true values ideally goes to the OS keychain; even config.toml would be an improvement over "figure out your own shell and launchd setup."
A narrower fix that would also help: allow env and env_vars in PluginMcpServerConfig, so a user can at least supply credentials in config.toml without touching their shell. That contradicts the current doc comment, but the boundary it draws — manifests own launching, user config owns policy — leaves credentials with no owner at all.
Context
Hit this shipping a WeCom MCP server as a plugin to both clients. The Claude Code side is a dialog with three fields. The Codex side is a README section explaining ~/.zshenv, security find-generic-password, and why the variables have to exist before Codex starts.
Versions: codex-cli 0.146.0, source read at main.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- Avg merge
- 1m
- Merged PRs (30d)
- 1k
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from openai/codex
-
enhancement remote
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
bug CLI windows-os
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
macOS sandbox blocks hw.optional.arm64 sysctl, causing Flutter to misdetect Apple Silicon as x64 Openbug CLI sandbox
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
bug CLI TUI
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
CLI config enhancement skills
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
kwakseongjae/auto-hwp#319 ·
-
area:cli bug filter-quality good first issue priority:medium
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
Difficulty 1/5 Under an hour Newbie friendliness 72/100
bevyengine/bevy#25861 ·
-
comp-datalake
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
ClickHouse/ClickHouse#121222 ·
-
A-linter
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
oxc-project/oxc#26863 ·