Skip to content

feat(codex): add isolated Cortex plugin package - #344

Merged
cdeust merged 4 commits into
mainfrom
agent/add-codex-plugin
Aug 2, 2026
Merged

feat(codex): add isolated Cortex plugin package#344
cdeust merged 4 commits into
mainfrom
agent/add-codex-plugin

Conversation

@cdeust

@cdeust cdeust commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • add a dedicated cortex-codex-plugins repository marketplace
  • package hypermnesia-mcp-codex as an isolated, MCP-only native Codex plugin
  • expose the published local stdio server on the exact 10-tool lean profile
  • give the measured clean-cache uvx startup a 180-second manifest ceiling
  • document repository installation and the separate public-directory boundary

Anthropic compatibility invariant

Claude Code remains Cortex's primary interface. This change does not modify
the Claude marketplace, manifest, hooks, custom agent, launcher, or complete
tool profile.

The Codex .mcp.json is deliberately confined to
plugins/hypermnesia-mcp-codex/. A repository-root .mcp.json is forbidden
because Claude Code would also discover it as project-scoped configuration
when Cortex itself is open, double-registering the server and potentially
shadowing the healthy Claude plugin process.

The new .agents/ marketplace and Codex sub-plugin are excluded from the
Anthropic MCPB bundle.

Scope boundary

This PR provides local repository-marketplace installation for Codex and the
ChatGPT desktop Codex runtime. It does not claim ChatGPT web/public-directory
availability. A public plugin would require a separately designed production
HTTPS Streamable HTTP service, authentication, tenant isolation, domain
verification, operations, and privacy/legal review.

References:

Verification

  • OpenAI plugin validator passes on the isolated sub-plugin
  • Codex 0.146 resolves the documented cdeust/Cortex GitHub shorthand,
    discovers, and installs hypermnesia-mcp-codex@cortex-codex-plugins
  • Codex parses the bundled stdio command and 180-second startup ceiling
  • exact clean-cache uvx command completes initialize, tools/list, and
    memory_stats with 10 lean tools in 110.46 seconds locally (macOS 26.5.1
    arm64, uv 0.8.19) and 23.87 seconds on clean ubuntu-latest CI
  • 31 focused package, Claude-collision, protocol, and profile tests pass
  • six real MCP lifecycle cases pass: Claude, Gemini, and Codex on full and
    lean profiles
  • Ruff, documentation claims, badge, marketplace-pin, YAML, and diff gates
    pass
  • rebuilt MCPB has 561 entries and excludes the Codex package plus the
    deprecated Claude migration shim

The broader local suite reached 7,043 passing tests. Fifty AST tests could not
write macOS's Tree-sitter cache under the sandbox; the two remaining hook
tests fail identically on the untouched checkout because the local specialist
configuration is absent. GitHub CI runs in a clean environment and is the
authoritative full-suite result.

The review follow-up also tightens the repository methodology: measured claims
must now record before/after values, the exact command, code revision,
environment, and experimental conditions.

@cdeust cdeust left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff (9 files, +256/-2) against the host manifests #341 also touched. Scope note: this PR is Codex-only — Gemini was already covered by gemini-extension.json; only its version is re-asserted here.

What holds

The .mcp.json isolation is the right invariant, anchored in a test. plugins/hypermnesia-mcp-codex/.mcp.json never at the repository root, and test_codex_plugin_is_confined_to_a_dedicated_subdirectory asserts not (REPO_ROOT / ".mcp.json").exists(). That is the 2026-06-12 root cause pinned down: a repository-root .mcp.json gets picked up by Claude Code as project-scoped config, where ${CLAUDE_PLUGIN_ROOT} is never substituted, giving -32000 Connection closed on a shadow duplicate. Regression-proofed rather than documented.

The "exact 10-tool lean profile" claim is verifiable, not decorative. LEAN_TOOL_NAMES in mcp_server/tool_profiles.py holds exactly ten names and is_tool_allowed returns tool_name in LEAN_TOOL_NAMES for the lean profile.

CI actually exercises the changed path. The added lines land in the mcp-host-config job ("Validate MCP host configurations"), which passed in 24s on run 30764132033. The job is gated on head.repo.fork == false; this is a same-repository PR, so it really ran. This is the job that exercises the change, not an unrelated fast green.

Claude non-regression is pinned. test_codex_package_does_not_weaken_the_primary_claude_plugin locks hooks, the custom agent, the python3 launcher.py command, and the absence of --profile on the Claude side.

What should be addressed before merge

1. The recommended path is never started

CI runs plugin marketplace addplugin add → asserts installed and enabled. It performs no MCP handshake against this package. The "six real MCP lifecycle cases" in the PR description come from scripts/verify_mcp_hosts.py, which drives the installed console entry point (hypermnesia-mcp), not the uvx command line this plugin ships.

Net effect: the path the README promotes as recommended carries strictly less execution proof than the path it demotes to fallback. Installation is proven; startup is not.

Suggested fix: extend the job to spawn the plugin's exact command (uvx --from "hypermnesia-mcp[sqlite]" hypermnesia-mcp --profile lean) through one initialize + tools/list round-trip and assert the ten lean tool names — ideally with a cold UV_CACHE_DIR, so the measurement covers the first-run case real users hit.

2. The README contradicts itself on cold start

The fallback paragraph states, verbatim: "install the executable first so the first MCP handshake is not spent downloading the Python environment". The recommended path directly above it ships uvx --from "hypermnesia-mcp[sqlite]" with no pre-installation and no startup timeout.

Mitigating: gemini-extension.json already uses this uvx pattern, so this PR does not introduce it. Still, Codex enforces a startup timeout — the direct-config test on the line above pins startup_timeout_sec=30 precisely because of this — and the plugin .mcp.json format appears to offer no equivalent knob. A cold first launch overrunning the host default is the most likely failure mode of this change, and nothing currently measures it. Either the fallback's warning applies to the recommended path too and should be repeated there, or it is obsolete and should be dropped from both.

3. The documented command is not the tested command

README: codex plugin marketplace add cdeust/Cortex
CI: codex plugin marketplace add "$GITHUB_WORKSPACE"

The GitHub short-form is exercised nowhere. If Codex 0.146 does not resolve owner/repo for marketplace add, the first command in the install guide fails. Worth one explicit check, since it is the entry point of the documented flow.

4. Duplicated version gate (boy-scout)

tests_py/scripts/test_cross_host_manifests.py::test_cross_host_manifest_versions_match_the_release already asserts gemini-extension.json, server.json, manifest.json, and .claude-plugin/plugin.json against package.json. The new test_codex_version_matches_all_release_manifests re-asserts those same four plus the Codex manifest — two lists to keep in sync, one a strict superset of the other. Prefer either adding the Codex manifest to the existing test, or narrowing the new one to the Codex manifest alone.

Related: this brings the manually-bumped identity surfaces to ten. There is no bump script — the invariant is held by tests only — so a redundant list is a real maintenance cost, not a stylistic one.

5. Question on .mcpbignore

The diff adds plugins/hypermnesia-mcp-codex/, but plugins/cortex-deprecated (the v4.15.0 migration shim) is still not listed and therefore ships inside the 562-entry MCPB bundle. Pre-existing, but this PR touches the file — intentional, or an omission worth folding in here?

6. Question without a source

"authentication": "ON_INSTALL" in .agents/plugins/marketplace.json, for a local stdio server that authenticates nothing. Deliberate, or copied from a template? I have not read the OpenAI marketplace schema, so I am not asserting this is wrong — only that the value is unexplained.


None of this challenges the design: the isolation boundary is correct and the Claude contract is protected by test rather than by convention. The gap is execution proof on the promoted path (1–3), not architecture.

@cdeust cdeust left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at 20a30c30. All six points are addressed, and the blocking one is addressed with execution proof rather than with an assertion about intent.

Blocking point — CI proved plugin INSTALL, never STARTUP

Resolved. The mcp-host-config step now does the three things that were missing:

  1. After codex plugin add, it asserts through codex mcp list --json that the registered server is uvx with startup_timeout_sec == 180.0. This is the part I could not take on faith: it proves Codex actually honours a startup_timeout_sec key declared inside a plugin .mcp.json, rather than silently dropping an unknown field.
  2. It reads both the command and the timeout from the manifest itself (mapfile + python -c over plugins/hypermnesia-mcp-codex/.mcp.json), so the gate cannot drift away from the shipped artifact the way a transcribed literal would.
  3. It replays verify_mcp_hosts.py against that command under a fresh UV_CACHE_DIR/UV_TOOL_DIR in $RUNNER_TEMP, so the bootstrap is genuinely cold.

Evidence, run 30765134054 / job 91542387856:

PASS codex-cli/lean: initialize + discovery + memory_stats (10 tools, 23.87s)

The recommended path now carries more execution proof than the fallback, which was the asymmetry I objected to. Two design choices worth noting as correct rather than incidental: the CI gate is stricter than the real constraint (the 180 s budget here covers initialize + tools/list + memory_stats, whereas Codex only bounds startup), and --allow-bootstrap-network is scoped to this one cold case, so the SOCKS regression fixture still guards every warm run — the 2026-06-12 -32000 RCA keeps its regression proof.

The other five

  • README self-contradiction on cold uvx. Fixed by hoisting uv tool install ahead of both paths and stating the prerequisite once. The framing is accurate and does not overclaim: uv tool install populates the shared artifact cache that the plugin's uvx --from then reuses; it does not skip environment creation, and the text does not claim it does.
  • marketplace add cdeust/Cortex documented but only $GITHUB_WORKSPACE tested. CI now exercises the documented form with --ref "${GITHUB_HEAD_REF:-$GITHUB_REF_NAME}"; the log confirms resolution through the real marketplace path (installedRoot: ~/.codex/.tmp/marketplaces/cortex-codex-plugins). I had a concern about fork PRs, where GITHUB_HEAD_REF would name a branch absent from cdeust/Cortex — it is void: the job already carries if: ... head.repo.fork == false.
  • Duplicated version gate. Removed from test_codex_plugin_contract.py and folded into test_cross_host_manifests.py, which is where the other five manifests are already checked. No orphan references to the deleted test remain, and all six manifests agree at 4.17.2.
  • plugins/cortex-deprecated missing from .mcpbignore. Added and asserted. The directory does exist on this branch, so the omission was real, not theoretical.
  • Unexplained authentication: ON_INSTALL. The explanation holds and I verified the source rather than the link's existence: OpenAI's page states "Always include policy.installation, policy.authentication, and category on each plugin entry" and shows "authentication": "ON_INSTALL" in its example. It enumerates values for policy.installation (AVAILABLE, INSTALLED_BY_DEFAULT, NOT_AVAILABLE) but documents no "no authentication required" value, so "schema-required timing metadata, not an added authentication mechanism" is the correct reading of what is actually published. The non-interactive install in CI is the empirical half of that claim.

Two cosmetic residuals, non-blocking

  1. docs/codex-plugin.md cites 110.46 s for the clean-cache run and 28.19 s for the warm one, both dated but with no environment stated, while CI measures 23.87 s cold on ubuntu-latest. The gap is plausible network variance and neither figure is wrong, but a reader hitting 23.87 s in CI and 110.46 s in the doc has no way to reconcile them. Naming the local environment in one clause would close it. (coding-standards §8: measured on <date> in <environment>.)
  2. The 28.19 s warm figure is local-only; CI exercises the cold path exclusively. That is the right choice — cold is the worst case — so this is a note, not a request.

Neither blocks. CI is green apart from Test (Python 3.12), still running at the time of writing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant