Skip to content

Discover local Codex plugins as stdio MCP presets - #1837

Merged
RhysSullivan merged 21 commits into
mainfrom
codex-plugins-presets
Aug 29, 2026
Merged

Discover local Codex plugins as stdio MCP presets#1837
RhysSullivan merged 21 commits into
mainfrom
codex-plugins-presets

Conversation

@RhysSullivan

@RhysSullivan RhysSullivan commented Aug 29, 2026

Copy link
Copy Markdown
Collaborator

On local builds, the MCP add form's Stdio tab now shows a Codex plugins section: locally installed OpenAI Codex plugins that ship a stdio MCP server (Apple Messages, Computer Use, Computer History, plus anything in the plugin cache with a local-command server), each as a one-click add. Entries whose binaries are missing still render, with an install hint, so the capability is discoverable on machines without Codex. Nothing of OpenAI's is bundled or downloaded — the scanner only reads what the user's own Codex install already put on disk.

  • codex-plugins.ts: server-side scanner over $CODEX_HOME. The three plugins implemented by the shared Codex Computer Use app are curated and spawn the stable client binary (survives plugin-cache version bumps); other cached plugins are scanned from their manifests, newest version wins.
  • GET /mcp/codex-plugins + a Codex plugins section in AddMcpIntegration's stdio tab. One click registers the integration with the reported command/args/cwd/env, auto-connects, and syncs tools.
  • discoverTools now answers elicitation/create with a decline during discovery. The connection advertises the capability, and these plugins elicit for first-use approvals; previously the request had no handler on the discovery path.
  • Gated on dangerouslyAllowStdioMCP; cloud surfaces return an empty list and never load the scanner (dynamic import, stdio-connector pattern).

Testing: unit coverage for the scanner (fixture Codex home: availability, version pick, path resolution, malformed entries) and for the discovery-path decline; e2e scenario local/codex-plugins.test.ts boots the real local server with CODEX_HOME pointed at a fixture and drives list → add → auto-connect → tool discovery, including proof that CODEX_HOME reached the spawned subprocess. Also verified live against a real Codex install: adding Apple Messages from the UI discovered its 6 tools.

Update: the Codex plugins live only in the connect dialog catalog, like every other preset - there is no extra section on the Add MCP screen. Searching "imessage", "texts", or "computer use" finds them; picking one opens a focused single-plugin add screen showing the plugin's own icon (read at runtime from the local Codex install, never shipped), its availability, and one Add action. The catalog metadata shares a module with the scanner so names and summaries cannot drift.

Second update: the focused add screen mirrors the plugin page in Codex - the plugin's own icon, display name, tagline, and long description, all read at runtime from the local install's manifest. Icons render everywhere (connect dialog, command palette, sidebar/integration rows) through a new executor: icon scheme: local auth is deliberately bearer-header-only, so an img tag cannot authenticate; the shared PresetIcon helper and IntegrationFavicon resolve the scheme by fetching /mcp/codex-plugins/:id/icon with the auth header and rendering the returned data URI.

Third update: curated Codex plugins now call tools through a codex app-server bridge. The plugins\x27 service stopped honouring tool calls from non-Codex hosts (direct client spawns can list tools but every call hangs), so the connector now speaks Codex\x27 own app-server protocol: an in-process transport adapter spawns codex app-server, starts a thread, translates tools/list to mcpServerStatus/list and tools/call to mcpServer/tool/call, and forwards app-server elicitations as standard MCP elicitation/create. No model turn is involved - these are direct tool invocations. The adapter owns its stdio wire because the SDK transport validates incoming lines against MCP schemas and drops app-server traffic. Stored configs stay stdio with an optional appServer marker; the ordinary client, discovery, invoke, health, and elicitation paths are unchanged. Verified live: Messages find_chats returns real data through the executor MCP endpoint in ~3s, and Computer History tools execute. Computer Use itself is currently gated inside Codex (its server is never started for app-server threads in the current build), so its card adds but discovers no tools until Codex re-enables it.

Fourth update: the plugins' own approval prompts now bubble up instead of being silently refused. Tools that ask before returning data — read_messages above all — were failing with an unexplained "Read access was not approved", with no prompt anywhere. The cause was the thread the bridge starts: Codex declines every MCP elicitation itself on a thread whose approval policy does not permit them, so the prompt never left Codex. The bridge now starts its thread with an approval policy that routes those prompts to the client, where they arrive as ordinary MCP elicitation/create and executor's existing elicitation bridge handles them. Verified live end to end: reading a chat through the executor MCP endpoint pauses the execution with the plugin's own form ("Once / For this conversation / Always"), and resuming with an accept returns the real messages. The bridge starts no turns, so nothing else can escalate on that thread.

Fifth update: two more Codex plugins, and two fixes behind them.

"For this conversation" approvals now hold. Connections were pooled for remote transports only, so every tool call built a fresh connector — and for the bridge that meant a fresh codex app-server and a fresh thread. The approval grant lives on the Codex thread, so a session-scoped grant was discarded after every call and re-asked on the next one. App-server bridge connections are now poolable, keyed on their spawn recipe, server, and credentials; plain stdio stays unpooled, since a spawn-per-call CLI server is entitled to a fresh process. Two reads of one chat, approved once with "For this conversation", now prompt once. It also removes a full app-server boot per call.

Computer Use works. It is not a broken MCP server — it has no server at all in current Codex. The plugin ships as a node-repl variant whose API is the bundled @oai/sky package driven through the node_repl server's js tool. Rather than hand an agent a raw JavaScript REPL, the bridge projects that API as eleven typed tools with real input schemas (list_apps, get_app_state, click, type_text, …) and compiles each call into the one REPL program that performs it. The raw REPL is never exposed, so there is no second code-execution tool in the catalog.

Chrome, by the same mechanism. The Chrome plugin is skills-only; browser control is its bundled browser-client.mjs, imported into the same REPL. It is projected as fifteen typed tools (list_tabs, new_tab, navigate, read_page, click, type_text, …). Three things this needed: interaction goes through the tab's dom_cua API rather than ax, which the plugin's own reference marks unsupported on the extension backend Chrome uses; the client module is resolved through the latest symlink Codex maintains, so a plugin update does not strand the stored path; and REPL calls carry synthesized Codex turn metadata, without which the client refuses every call. The plugin's own per-site approval surfaces through the normal elicitation path.

OpenAI Developer Docs is a plain server Codex carries itself — search and read the API reference and OpenAPI specs. Curated cards now declare what each one needs on disk, so a machine with Codex but no Computer Use app shows the docs card ready and the others with a setup hint, instead of gating everything on one marker.

Verified live through the executor MCP endpoint: Computer Use lists real apps, Chrome opens a tab and returns its DOM snapshot with node ids, the docs plugin returns real search hits, and Messages reads a chat after one approval. Still no Codex inference on any path — mcpServer/tool/call runs no model turn, and Codex records no rollout or token usage for these calls.

Not included, deliberately: codex_apps (260 connector tools) overlaps executor's own GitHub, Linear, and Google Calendar integrations and deserves a product decision first. MCP resources and server-change notifications are unimplemented in the bridge and are recorded in DESIRES.md.

Sixth update: approval terms, change notifications, and what parity actually means here.

Approval terms now reach the caller. A Codex plugin's prompt can state the terms of the grant rather than just its question — the browser one sends an empty requestedSchema and puts persist: "always" and the origin in its metadata, so accepting means a permanent per-site allow. That metadata was being dropped, leaving a caller consenting to more than the prompt said. FormElicitation/UrlElicitation gained an optional opaque meta, the MCP boundary populates it, and a paused execution now prints an "Approval terms" block and carries them in its structured payload. The prompt is also attributed to the plugin a person recognises ("Browser use") instead of the server it travelled through (node_repl).

Server restarts no longer strand a stale catalog. Codex reports a bridged server's startup transitions; the bridge translates its own server becoming ready into the spec's notifications/tools/list_changed, which executor already acts on by restaling the connection's catalog. Other servers' transitions are ignored.

Two things are deliberately not done, both recorded in DESIRES.md with evidence rather than left implicit. Resources are still unbridged because nothing above the bridge consumes MCP resources yet — adding them today would be a surface with no reader. And approvals persist only for the life of a pooled connection: two calls in one session share a Codex thread and prompt once, but the pool's idle eviction starts a new thread and prompts again. The plugin decides that from its own persisted store, and the app-server elicitation response's _meta is dropped inside codex-app-server-protocol before the plugin sees it, so that is not the channel a third-party host can use.

Verified live after the change: Computer Use lists apps, Chrome opens a tab and reads its DOM, the docs plugin searches, and Messages reads chats — all returning real data, with approvals prompting once per session and not again.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 29, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing 2b40ae6 Commit Preview URL

Branch Preview URL
Aug 29 2026, 10:21 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 29, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud 2b40ae6 Aug 29 2026, 10:22 PM

@pkg-pr-new

pkg-pr-new Bot commented Aug 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@1837

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@1837

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@1837

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@1837

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@1837

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@1837

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@1837

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@1837

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@1837

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@1837

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@1837

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@1837

executor

npm i https://pkg.pr.new/executor@1837

commit: 2b40ae6

@RhysSullivan
RhysSullivan marked this pull request as ready for review August 29, 2026 22:24
@RhysSullivan
RhysSullivan merged commit 7cfcfef into main Aug 29, 2026
43 of 44 checks passed
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