[BUG] Plugins whose manifest is .codex-plugin/plugin.json lose their MCP servers (typed skill_bundle)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 3.8k
- Forks
- 362
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 132
Description
Describe the bug
apm does not recognise Codex's plugin manifest, .codex-plugin/plugin.json. A marketplace plugin whose only manifest sits there — for example one that declares "mcpServers": "./.mcp.codex.json" — is installed as a skill_bundle: its skills and hooks integrate, but none of the MCP servers its manifest declares is registered with any runtime, and none appears in the lockfile's mcp_servers or mcp_configs.
Where, in the 0.30.0 source
utils/helpers.py,find_plugin_json: the manifest lookup checksplugin.json,.github/plugin/plugin.json,.claude-plugin/plugin.jsonand.cursor-plugin/plugin.json..codex-plugin/plugin.jsonis not among them, and the source contains no reference to.codex-pluginanywhere.models/format_detection.py,ClaudePluginDetectorandNormalizationPlanner.plan: with none of those manifests present, nestedskills/<name>/SKILL.mdresolves the package toSKILL_BUNDLE.deps/plugin_parser.py,_extract_mcp_servers: resolvesmcpServers(and the.mcp.jsonauto-discovery), substitutes${CLAUDE_PLUGIN_ROOT}, and injects the result as_mcp_deps— on theMARKETPLACE_PLUGINpath only.- Observed on a user-scope install: such a plugin is locked with
package_type: skill_bundle, its skills and hooks are deployed, and no MCP server it declares is registered.
To Reproduce
Derived from the source above; not executed as a standalone script. A plugin laid out as:
example-plugin/
├── .codex-plugin/plugin.json {"name": "example-plugin", "skills": "./skills", "hooks": "./hooks/hooks.json", "mcpServers": "./.mcp.codex.json"}
├── .mcp.codex.json {"mcpServers": {"example": {"command": "npx", "args": ["-y", "example-mcp"]}}}
├── hooks/hooks.json
└── skills/example/SKILL.md
published through a marketplace and installed with apm install example-plugin@<marketplace> -g, is locked as package_type: skill_bundle and registers no example MCP server. The same manifest placed at .claude-plugin/plugin.json or at the root as plugin.json makes the package a marketplace_plugin, whose mcpServers are resolved and registered.
Expected behavior
.codex-plugin/plugin.json is recognised as a plugin manifest alongside the root, GitHub, Claude and Cursor locations, so a Codex-format plugin's mcpServers are registered for every target, as any other plugin's are.
Environment
- OS: macOS, Apple Silicon
- APM: Homebrew binary,
Agent Package Manager (APM) CLI version 0.30.0 - Codex CLI 0.154.0 (whose binary carries the
.codex-pluginmanifest location) - Scope: user (
-g)
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.
Research direction
Start in utils/helpers.py at find_plugin_json, then trace models/format_detection.py through ClaudePluginDetector and NormalizationPlanner.plan. Read deps/plugin_parser.py, especially _extract_mcp_servers, and compare the existing manifest paths with the .codex-plugin path. Done means the sample plugin is classified as a marketplace_plugin and its MCP server appears in the lockfile and is registered for each target.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100