You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: .archcore/plugin/bundled-cli-launcher.adr.md
+7-8Lines changed: 7 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,7 +18,7 @@ In practice this produced real friction:
18
18
- The `claude mcp add ...` step is discoverable only by reading the README — `/plugin install` gives no hint that MCP registration is still required.
19
19
- Error messages from `bin/session-start` when MCP was unreachable ("install the CLI and register the MCP server") were ignored or misread as install failures.
20
20
21
-
The Claude Code plugin runtime now supports `${CLAUDE_PLUGIN_ROOT}` substitution in `.mcp.json` shipped at the plugin root, and treats plugin-provided MCP servers as first-class. Duplicate suppression only kicks in when the `command`/`args` exactly match a user- or project-registered server — and if a user has installed `archcore` globally, the PATH resolution inside the launcher picks it up, so the effective command is identical to the user's global registration and deduping is benign. Codex CLI v0.117.0+ (March 2026) gained plugin-shipped MCP via the Codex manifest's `mcpServers` pointer. Codex uses a different resolution mechanism than Claude Code: rather than env-var substitution in `command`/`args`, it rebases a relative `cwd` field against the plugin install root via `codex-rs/core-plugins/src/loader.rs::normalize_plugin_mcp_server_value`. Codex therefore ships a separate plugin-root MCP config at `.codex.mcp.json` with `command: "./bin/archcore"` and `cwd: "."`. The `cwd: "."` is the resolution mechanism — Codex rebases it to the plugin install root so the relative command resolves correctly regardless of the user's project directory; without `cwd`, Codex spawns from the user's project CWD and the MCP fails with ENOENT. See `plugin/codex-plugin-spawn-semantics.adr.md` for the canonical record on Codex's MCP cwd rebase vs hook `${PLUGIN_ROOT}` substitution.
21
+
The Claude Code plugin runtime now supports `${CLAUDE_PLUGIN_ROOT}` substitution in `.mcp.json` shipped at the plugin root, and treats plugin-provided MCP servers as first-class. Duplicate suppression only kicks in when the `command`/`args` exactly match a user- or project-registered server — and if a user has installed `archcore` globally, the PATH resolution inside the launcher picks it up, so the effective command is identical to the user's global registration and deduping is benign. Codex CLI v0.117.0+ (March 2026) gained plugin-shipped MCP via the Codex manifest's `mcpServers` pointer, but its plugin examples use plugin-relative command paths rather than Claude's root-variable substitution. Codex therefore ships a separate plugin-root MCP config at `.codex.mcp.json` that points at the same launcher with `./bin/archcore`.
22
22
23
23
### Drivers
24
24
@@ -56,7 +56,7 @@ The plugin root ships `.mcp.json`:
56
56
}
57
57
```
58
58
59
-
Claude Code reads this and registers `archcore` as a plugin-provided MCP server. The command points at the launcher — the launcher resolves to the right binary at invocation time. Resolution mechanism: env-var substitution at the host level (`${CLAUDE_PLUGIN_ROOT}` is replaced with the plugin install path before spawn).
59
+
Claude Code reads this and registers `archcore` as a plugin-provided MCP server. The command points at the launcher — the launcher resolves to the right binary at invocation time.
60
60
61
61
Codex CLI reads `.codex-plugin/plugin.json` and follows its `mcpServers` pointer to `.codex.mcp.json`. The Codex build-plugins docs reserve `.codex-plugin/` for `plugin.json`, so the Codex-specific MCP file lives at the plugin root:
62
62
@@ -66,21 +66,20 @@ Codex CLI reads `.codex-plugin/plugin.json` and follows its `mcpServers` pointer
66
66
}
67
67
```
68
68
69
-
The pointed-to file uses the same wrapper shape as public Codex plugin examples, with the addition of a `cwd` field that's the resolution mechanism for Codex:
69
+
The pointed-to file uses the same wrapper shape as public Codex plugin examples:
70
70
71
71
```json
72
72
{
73
73
"mcpServers": {
74
74
"archcore": {
75
75
"command": "./bin/archcore",
76
-
"args": ["mcp"],
77
-
"cwd": "."
76
+
"args": ["mcp"]
78
77
}
79
78
}
80
79
}
81
80
```
82
81
83
-
Codex resolves the command via its `cwd` rebase mechanism: `normalize_plugin_mcp_server_value` (`codex-rs/core-plugins/src/loader.rs`) rebases the relative `cwd` (`.`) to the plugin install root; `launch_server` (`codex-rs/rmcp-client/src/stdio_server_launcher.rs`) then sets that as `current_dir` for the spawned process, so `./bin/archcore` resolves against the plugin root rather than the user's project CWD. This avoids relying on an undocumented `${CODEX_PLUGIN_ROOT}` environment variable (Codex does not export one to MCP processes, nor does it substitute env-var placeholders in MCP `command`/`args`) while still using the same launcher and cache behavior. Without `cwd: "."`, Codex would spawn the MCP from the user's project CWD and fail with ENOENT — the original symptom that motivated the spawn-semantics investigation. See `plugin/codex-plugin-spawn-semantics.adr.md`.
82
+
Codex resolves the plugin-relative command path from the installed plugin root. This avoids relying on an undocumented `${CODEX_PLUGIN_ROOT}` environment variable while still using the same launcher and cache behavior.
84
83
85
84
### Pinned CLI version
86
85
@@ -134,12 +133,12 @@ Add `bin/archcore` but leave MCP registration to the user.
134
133
-**Enterprise/offline escape hatches.**`ARCHCORE_BIN` pins an explicit binary. `ARCHCORE_SKIP_DOWNLOAD=1` disables network access at the launcher layer.
135
134
-**Survives plugin updates.** The cache lives under `$CLAUDE_PLUGIN_DATA/archcore/cli` (Claude Code's stable data dir) or `$CODEX_PLUGIN_DATA/archcore/cli` (Codex CLI's stable data dir), so plugin re-installs don't re-download.
136
135
-**Security.** Downloads are checksum-verified before execution. No `curl | sh`.
137
-
-**Codex parity with Claude Code.** Codex CLI uses the same launcher mechanism with a Codex-specific MCP config: `command: "./bin/archcore"` paired with `cwd: "."`, where Codex's `normalize_plugin_mcp_server_value` rebases the relative cwd to the plugin install root. Host-prefixed cache directories keep the binary lifecycle aligned with Claude Code. The resolution mechanism differs from Claude Code (cwd rebase vs env-var substitution), but the user experience is identical: zero-setup install, plugin-shipped MCP, same launcher binary, same cache.
136
+
-**Codex parity with Claude Code.** Codex CLI uses the same launcher mechanism with a Codex-specific MCP config and plugin-relative command paths. Host-prefixed cache directories keep the binary lifecycle aligned with Claude Code without relying on undocumented root-variable substitution.
138
137
139
138
### Negative
140
139
141
140
-**Plugin now owns part of the CLI lifecycle.** Cache invalidation (stale cached binaries after CLI bugfix releases) requires bumping `bin/CLI_VERSION` in the plugin and shipping a plugin release. Mitigation: cache is version-keyed by filename (`archcore-v${VERSION}`), so a pin bump always downloads fresh.
142
141
-**First-run network dependency.** Air-gapped environments that don't pre-install the CLI fail at the first MCP call with a network error. Mitigation: documented `ARCHCORE_BIN` / `ARCHCORE_SKIP_DOWNLOAD=1` workflow in the README.
143
-
-**Cursor remains the multi-host outlier.** Claude Code and Codex CLI both support plugin-shipped MCP configs (via different mechanisms — env-var substitution and `cwd` rebase respectively); Cursor does not. Cursor users still register MCP externally (via project `mcp.json` or Cursor MCP settings). The launcher still works for them — it just isn't wired in via a plugin-shipped `.mcp.json`. This is a deliberate host-by-host rollout; the ADR does not claim parity across all hosts.
142
+
-**Cursor remains the multi-host outlier.** Claude Code and Codex CLI both support plugin-shipped MCP configs; Cursor does not. Cursor users still register MCP externally (via project `mcp.json` or Cursor MCP settings). The launcher still works for them — it just isn't wired in via a plugin-shipped `.mcp.json`. This is a deliberate host-by-host rollout; the ADR does not claim parity across all hosts.
144
143
-**Inverts the Multi-Host Plugin Architecture ADR's "MCP ownership boundary" section.** That section is now historically accurate (rationale at the time) but no longer describes current behavior. See that ADR for the cross-link; this ADR supersedes the "plugin does not ship an MCP server configuration" claim for Claude Code and (by extension) Codex CLI specifically.
145
144
-**Supply-chain surface area.** The launcher executes downloaded binaries. Checksum verification is the only gate. Any compromise of the GitHub Releases signing pipeline compromises the plugin's trust model. Acceptable given the CLI was the trust root already; the launcher doesn't introduce a new trust boundary.
Codex's resolution mechanisms differ between MCP and hooks (canonical record: `plugin/codex-plugin-spawn-semantics.adr.md`). For MCP, Codex does NOT substitute env-var placeholders in `command`/`args` — the resolution happens through a `cwd` rebase: `normalize_plugin_mcp_server_value` rebases a relative `cwd` field against the plugin install root. The plugin's `.codex.mcp.json` therefore sets `command: "./bin/archcore"` paired with `cwd: "."`. For hooks, Codex's hooks engine injects `PLUGIN_ROOT` (canonical, host-neutral) plus `CLAUDE_PLUGIN_ROOT` (a backward-compat alias for porting old Claude plugins) and folds `${KEY}` substitution at spawn time — so `${PLUGIN_ROOT}/bin/...` is the right form for `hooks/codex.hooks.json`. Note: `CODEX_PLUGIN_ROOT` does not exist in Codex.
25
-
26
-
This gives **Codex MCP parity with Claude Code** without requiring a `${CODEX_PLUGIN_ROOT}` env var. Cursor remains the multi-host outlier because it lacks an equivalent path-substitution or cwd-rebase mechanism for plugin-provided MCP.
24
+
Unlike Cursor (which lacks `${CLAUDE_PLUGIN_ROOT}`-equivalent path substitution for plugin-provided MCP), Codex supports plugin-relative paths from the manifest, which means **Codex gets MCP parity with Claude Code** without depending on a `${CODEX_PLUGIN_ROOT}` env var. The plugin ships a Codex-specific plugin-root `.codex.mcp.json` whose command is `./bin/archcore`.
27
25
28
26
## Value
29
27
@@ -41,9 +39,9 @@ Reuse the existing per-host adapter pattern. New components only:
41
39
42
40
1.**Manifest**: `.codex-plugin/plugin.json` — minimal name/version/description plus `interface{}` block for marketplace UI metadata; component pointers `skills: "./skills/"`, `mcpServers: "./.codex.mcp.json"`, `hooks: "./hooks/codex.hooks.json"`.
43
41
44
-
2.**Hooks**: `hooks/codex.hooks.json` — clone of `hooks/hooks.json` with `${PLUGIN_ROOT}/bin/...`substitution (Codex's canonical, host-neutral env var; not the `${CLAUDE_PLUGIN_ROOT}` compat alias) and `apply_patch` added to the edit matcher. Events PascalCase (same as Claude). Block via exit 2 (already supported by `archcore_hook_block`). PostToolUse `additionalContext` via `hookSpecificOutput` (already emitted by `archcore_hook_info`). Runtime execution requires `codex features enable plugin_hooks` (the `plugin_hooks` flag is `under development, false` by default in Codex 0.130.0).
42
+
2.**Hooks**: `hooks/codex.hooks.json` — clone of `hooks/hooks.json` with plugin-relative `./bin/...`commands and `apply_patch` added to the edit matcher. Events PascalCase (same as Claude). Block via exit 2 (already supported by `archcore_hook_block`). PostToolUse `additionalContext` via `hookSpecificOutput` (already emitted by `archcore_hook_info`). Runtime execution requires Codex hooks support and `[features].codex_hooks = true`.
45
43
46
-
3.**MCP wiring**: ship plugin-root `.codex.mcp.json`with `command: "./bin/archcore"`, `args: ["mcp"]`, and `cwd: "."`. The `cwd: "."` is essential — Codex's `normalize_plugin_mcp_server_value` rebases the relative cwd to the plugin install root so the relative command resolves correctly. Without `cwd`, the spawn happens from the user's project CWD and fails with ENOENT. Do NOT reuse Claude's `.mcp.json`, because it contains `${CLAUDE_PLUGIN_ROOT}` which Codex does not substitute.
44
+
3.**MCP wiring**: ship plugin-root `.codex.mcp.json`using the public Codex plugin examples' `{"mcpServers": {...}}` wrapper. Do not reuse Claude's `.mcp.json`, because it contains `${CLAUDE_PLUGIN_ROOT}`.
47
45
48
46
4.**`bin/lib/normalize-stdin.sh`**: add explicit `codex` host detection branch. Codex sends snake_case `hook_event_name` like Claude, so the existing claude-code branch is a working fallback, but explicit detection (e.g., presence of `turn_id` without `conversation_id`/`hookEventName`) gives cleaner separation and future-proofing.
49
47
@@ -57,16 +55,16 @@ Reuse the existing per-host adapter pattern. New components only:
57
55
58
56
## Risks and Constraints
59
57
60
-
**Codex plugin-local hooks.** The official docs describe plugin-bundled lifecycle config, but hooks are behind the `plugin_hooks` feature (`under development, false` by default in Codex 0.130.0) and upstream runtime behavior has been in flux. Mitigation: ship the documented hook config and keep end-to-end hook execution as a smoke-test requirement rather than assuming it from static packaging.
58
+
**Codex plugin-local hooks.** The official docs describe plugin-bundled lifecycle config, but hooks are behind `[features].codex_hooks = true` and upstream runtime behavior has been in flux. Mitigation: ship the documented hook config and keep end-to-end hook execution as a smoke-test requirement rather than assuming it from static packaging.
61
59
62
60
**Plugin-bundled subagents not confirmed.** Codex docs describe subagents in `~/.codex/agents/` (user) and `.codex/agents/` (project), but don't explicitly state plugins can ship `agents/*.toml`. If unsupported, auditor degrades to manual install.
63
61
64
62
**Auditor MCP whitelist coarsening.** Current `archcore-auditor.md` whitelists 8 specific MCP read tools via `tools: [...]`. Codex's `sandbox_mode = "read-only"` blocks file writes but does NOT filter MCP tools. To prevent auditor from calling mutating MCP tools (`create_document`, `update_document`, etc.), need either: (a) `disabled_tools[]` per subagent if Codex supports it, (b) `developer_instructions` enforcement (soft), or (c) a separate read-only MCP server invocation (e.g., `bin/archcore mcp --read-only`).
65
63
66
64
**Skill namespacing.** Codex skill invocation via `@` — unclear if `@archcore/decide` or flat `@decide`. Flat namespace risks collisions with other plugins' similarly-named skills. Spike should confirm.
67
65
68
-
**`.mcp.json` schema divergence.** Resolved for current Codex examples: use `{"mcpServers": {...}}` in a Codex-specific plugin-root `.codex.mcp.json` with `cwd: "."` companion. Existing `.mcp.json` remains Claude-only because it relies on `${CLAUDE_PLUGIN_ROOT}`, which Codex does not substitute.
66
+
**`.mcp.json` schema divergence.** Resolved for current Codex examples: use `{"mcpServers": {...}}` in a Codex-specific plugin-root `.codex.mcp.json`. Existing `.mcp.json` remains Claude-only because it relies on `${CLAUDE_PLUGIN_ROOT}`.
69
67
70
-
**Cursor ADR side-effect.**`bundled-cli-launcher.adr.md` notes Cursor cannot use plugin-shipped MCP because of missing path substitution. Codex CAN — via the `cwd` rebase mechanism — so the ADR's "Multi-host divergence risk" Negative consequence shifts: Cursor remains the outlier, Codex joins Claude Code in zero-setup install.
68
+
**Cursor ADR side-effect.**`bundled-cli-launcher.adr.md` notes Cursor cannot use plugin-shipped MCP because of missing path substitution. If Codex CAN use it, the ADR's "Multi-host divergence risk" Negative consequence shifts: Cursor remains the outlier, Codex joins Claude Code in zero-setup install.
71
69
72
-
**Codex versioning.** Plugin system is recent (v0.117.0, March 2026). API may evolve; users on older Codex versions will hit incompatibility. Document minimum Codex version in README and check at session-start where possible. The `plugin_hooks` feature flag in particular is unstable — track its promotion to `stable` upstream.
70
+
**Codex versioning.** Plugin system is recent (v0.117.0, March 2026). API may evolve; users on older Codex versions will hit incompatibility. Document minimum Codex version in README and check at session-start where possible.
0 commit comments