Repository navigation
feat(codex): native Codex plugin — annotate a document, feedback returns to Codex - #1635
Draft
backnotprop wants to merge 4 commits into
Draft
backnotprop wants to merge 4 commits into
backnotprop wants to merge 4 commits into
Conversation
Adds `plannotator mcp`, a dependency-free stdio MCP server whose `annotate` tool resolves a file, folder, or URL exactly like `plannotator annotate`, opens the annotate UI, blocks until the human decides, and returns the plaintext CLI output as the tool result (plus the --json record as structuredContent). Cancellation or stdin EOF stops the session. apps/codex-plugin packages it for Codex: plugin manifest, .mcp.json (tool_timeout_sec raised, PLANNOTATOR_*/SSH env passthrough, since Codex allowlists MCP server env), a plannotator-annotate skill, and a local marketplace. Also ships a small MCP Apps view (ui://plannotator/annotate) for the Codex desktop app, which only posts feedback via ui/message when the host never delivered the tool result.
… ends plannotator mcp outlives each annotate session, so a non-forced Bun server.stop() left the browser tab's SSE stream and keep-alive sockets (with their heartbeat timer) open after every decision or cancel. Add an optional closeActiveConnections flag to the annotate server's stop() and pass it from the MCP session handle; the CLI and OpenCode callers keep the old behavior.
…h env_vars Codex spawns MCP servers with an allowlisted environment, so a user's PLANNOTATOR_AI=disabled (and the agent-terminal remote opt-in, Glimpse size, file-browser limit) never reached plannotator mcp.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First native Codex plugin flow: annotate a document and send the feedback to Codex. Plan review via
submit_planis out of scope for this PR.What's in it
plannotator mcp(apps/hook/server/mcp-*.ts): a stdio MCP server. It is hand-rolled JSON-RPC because@modelcontextprotocol/sdkis not a dependency, and this keeps the compiled binary's footprint at zero. stdout carries only the protocol:console.logis redirected to stderr in this mode.annotate{ target, cwd?, markdown?, noJina?, gate? }. It resolves the target with the CLI's ownresolveAnnotateTarget(file, folder, URL, live app), starts the annotate server, opens the browser, and blocks until the human decides.The user approved.). Where the CLI prints nothing, it returns an explicit sentence instead:The user closed Plannotator without sending feedback., and a separate sentence for an empty submit.structuredContentis the--jsonrecord.notifications/cancelledor stdin EOF stops the annotate server and sends no result.apps/codex-plugin/contains.codex-plugin/plugin.json,.mcp.json, theplannotator-annotateskill, a local marketplace (.agents/plugins/marketplace.json, source./), and a README with install steps.ui://plannotator/annotate, one self-contained HTML string (about 8 KB), not the app. It polls an app-only status tool through host-proxiedtools/call, has an Open Plannotator button (ui/open-link), and shows the state. It posts the feedback withui/messageonly when the session finished and noui/notifications/tool-resultarrived within 5 seconds. A spec-following host therefore never gets the feedback twice, and when the view is not rendered, the tool result is unchanged.apps/codex/README.md, andplannotator mcpin the knowledge skill (required by the freshness test).plugin.jsonversion is added to the release version guard.Codex facts this relies on (checked in Codex source at 26dd19ef47 and against codex-cli 0.157.1)
tool_timeout_sechonored in a plugin.mcp.json?McpServerConfig. The default is 300 s; set here to 345600, matching the Stop hook.-C), not the Codex process cwd. I measured this withlsofwhile Codex ran from/with-C /tmp/pn-e2e/work: the server cwd was/private/tmp/pn-e2e/work, and the relative targetnotes.mdresolved.HOME PATH SHELL USER LANG TERM TMPDIR ...). Withoutenv_vars,PLANNOTATOR_REMOTE,SSH_CONNECTION,PLANNOTATOR_PORTandPLANNOTATOR_DATA_DIRnever reach the server, and remote detection fails silently..mcp.jsonlists them. Verified: withPLANNOTATOR_REMOTE=1 PLANNOTATOR_PORT=19555exported, the session came up on:19555.readOnlyHint: truelets Codex's defaultautomode run the tool without prompting. None appeared incodex exec.httpsURLs (validate_external_url), so that cannot surfacehttp://localhost:19432either. A remote user gets the URL fromplannotator sessionson the remote host. This is documented, but still a gap (see follow-ups).plannotator:plannotator-annotate.Verification
Unit tests (
mcp-annotate.test.ts,mcp-protocol.test.ts) use a tempPLANNOTATOR_DATA_DIR, set and restored per test. They cover:cwd, folder, missing file (the error names the root), and bad input, all through the real resolver;notifications/cancelled, and via transport shutdown (server stopped, no response sent);pinganswered while an annotate call is blocked;ui://resource MIME type.bun test apps/hook/server scripts/check-release-version.test.ts: 357 pass, 0 fail, including the skill-reference freshness test.The full
bun testrun has 60 to 61 failures. The same suites fail on a cleanmainin this environment: Pi vendoring parity (needsvendor.sh), background remote discovery, andPLANNOTATOR_AI/ Pi runtime.Real end to end with the Codex CLI (codex-cli 0.157.1), isolated
CODEX_HOME, dev binary first onPATH:annotate {"target":"/tmp/pn-e2e/work/notes.md"}{"content":[{"type":"text","text":"# File Feedback ... E2E-MARKER-FILE-7731"}],"structured_content":{"decision":"annotated",...}}annotate {"target":"/tmp/pn-e2e/work/docs"}(servermode: annotate-folder)... E2E-MARKER-FOLDER-4410,decision: annotated/annotate {"target":"notes.md"}Relative OK. E2E-MARKER-CWD-2290PLANNOTATOR_REMOTE=1 PLANNOTATOR_PORT=19555http://localhost:19555, feedback returnedReal binary, raw MCP client (one long-lived process, four sequential sessions):
first;second (folder);gate: trueplus/api/approvereturnsThe user approved./{decision:"approved"};/api/exitreturnsThe user closed Plannotator without sending feedback./{decision:"dismissed"}.Cancel: after
notifications/cancelled, no response was sent for the call, the port refuses new connections, the process keeps servingping, and stdin EOF exits with code 0.ui://view, simulated MCP Apps host in headless Chromium (sandboxedsrcdociframe, realplannotator mcpbehind the proxiedtools/call):Waiting for your reviewand the URL. Open sendsui/open-link {url}. After the decision the view showsFeedback sent / Delivered to Codex as the tool result. Noui/message.ui/message {role:"user", content:[{type:"text", text:"Plannotator feedback on notes.md:\n\nScenario B feedback"}]}and showsFeedback posted.Not verified
enable_mcp_appsexperiment, and I did not touch the real~/.codexor the desktop app. The view is checked only against my simulated host, which follows the MCP Apps 2026-01-26 spec and the OpenAI extensions doc. Codex's exact host behavior is unverified: whether it proxiestools/callfor an app-only tool, when it sends tool-input and tool-result relative to a blocking call, howui/open-linkhandles localhost, and how the card sizes.codex execwas used throughout. The TUI uses the same MCP client, so the tool result should be identical.PLANNOTATOR_REMOTE=1.PATH..mcp.jsonrunsplannotatorfromPATH, and app-launched processes may not see~/.local/bin. This is the same caveat the Stop hook README already gives. The workaround is to edit the cached.mcp.jsonto an absolute path.Coexistence with the existing Codex integration
scripts/install.*is untouched, so the installer still writes the CodexStophook (plan review) and the core skills under~/.agents/skills. Those skills shell out toplannotator annotate/review/last. They can live alongside this plugin: the plugin skill is namespacedplannotator:plannotator-annotate, and its tool isannotateon theplannotatorMCP server. Suggested installer follow-up:codex plugin marketplace addpluscodex plugin add;plannotator-annotateskill into the Codex scope, so the model has one annotate path;submit_planlands.A repo-root
.agents/plugins/marketplace.jsonwould also makecodex plugin marketplace add backnotprop/plannotatorwork directly. I left the repo root alone because Codex reads.agents/...ahead of the existing.claude-plugin/marketplace.json.Known gaps / follow-ups
--tailscale(https) support inmcpso a URL elicitation passes the TUI's https check.server.stop(true)in the shared annotate server; I did not change shared shutdown in this PR.approvalNotesSupportedand the abandoned-tab client lease stay off, matching plaintextplannotator annotate.