feat(coordinator): MCP orchestration engine, REST API hardening, and task model#120
feat(coordinator): MCP orchestration engine, REST API hardening, and task model#120brooksc wants to merge 1 commit into
Conversation
|
Thank you very much for your work on this! <3 Follow-up review after splitting the pass across remote auth, coordinator lifecycle/persistence, and MCP execution paths. I think these need attention before merge:
Secondary follow-ups from the pass: |
…task model Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2465ab6 to
b43a427
Compare
|
Thank you for the thorough review — all five issues and both secondary items are addressed in the latest push. Additional correctness issues surfaced during internal review and are also fixed; listed below. 1. Connect Phone cannot authenticate with the new mobile token The WS auth handler in 2. Connect Phone can return unreachable LAN URLs while MCP is active
3. Coordinator state is typed but not persisted/restored
4. Deregistering a coordinator drops live child-task backend state
5. Sub-task agent args are Claude-specific even when the coordinator agent is not Claude
Secondary: Addressed in item 2 above — Secondary:
Additional fixes from internal review
|
|
Thank you very much for your ongoing work on this! <3 Additional review of The mobile WebSocket/auth and loopback remote-status changes look good from the additional pass. I still think these need attention before merge:
|
Overview
This is PR 2 of 4 in the coordinator series splitting #100 as requested in the round-4 review. It is stacked on PR 1 (
coordinator-1-security) and should be merged after that one. The diff shown here includes PR 1's content; the meaningful delta for this PR is the coordinator engine and REST hardening described below.PR sequence:
coordinator-1-securitycoordinator-2-mcp-backendcoordinator-3-store-ipccoordinator-4-uiPRs 3–4 are stacked on this one. Nothing coordinator-related is user-visible until PR 4 adds the
NewTaskDialogcheckbox and Settings toggle (coordinatorModeEnableddefaults tofalse).What's in this PR (delta over PR 1)
MCP orchestration engine (
electron/mcp/)coordinator.ts— core orchestrator singletonsignal_done/waitForSignalDonewith replay cache (requestIddedup for safe retries)atomic.tssetTaskControl/blockedByHumanControlstate machine (coordinator ↔ human hand-off)cleanupTaskwith double-resolve guard onanySignalResolversserver.ts— MCP stdio entry point\r/\nin--coordinator-id/--task-id(header injection prevention)create_task,list_tasks,get_task_status,get_task_diff,get_task_output,send_prompt,wait_for_idle,wait_for_signal_done,merge_task,close_task,review_and_merge_task,signal_doneconfig.ts— MCP JSON config generationselectMcpJsonDir: selects the right directory for the per-coordinator configwriteMcpConfig: writes config (mode0o600) atomicallypreamble.ts— preamble injection and strip<sub-task-mode>block to CLAUDE.md, AGENTS.md, GEMINI.md,.agent.md, orsettings.local.jsondepending on agent typestripPreambleFromBranch: atomically strips the block on merge/closebuildNormalizedPreambleFileDiff:git diff --no-indexwith anchored-regex path rewriting (prevents false substitutions when tmpdir path appears in file content)sub-task-preamble.ts— sub-task-side preamble injectionprompt-detect.ts— sliding-window idle/prompt detectorreplay-cache.ts— deduplicateswait_for_signal_doneretries byrequestIdclient.ts—MCPClientused by the MCP stdio process to call the REST APImcp-tool-list.ts— builds the tool list advertised to coordinator vs. sub-task rolestypes.ts— shared types:CoordinatedTask,ApiTaskSummary,ApiTaskDetail, token classes, etc.REST API hardening (
electron/remote/server.ts)New coordinator task routes (all require auth):
/api/tasks/api/tasks/api/tasks/:id/api/tasks/:id/prompt/api/tasks/:id/wait/api/tasks/:id/diff/api/tasks/:id/output/api/tasks/:id/merge/api/tasks/:id/review-merge/api/tasks/:id/api/tasks/:id/doneX-Done-Token)/api/wait-signalsignal_doneAuth scoping hardening:
callerCoordinatorIdextracted from verifiedX-Coordinator-Idheader and enforced before all coordinator routes (includingwait-signal)create_task: bodycoordinatorTaskIdignored; header is authoritative; mismatch → 403wait-signal: bodycoordinatorTaskIdignored entirely; header-only/api/agentsonly — task routes removed (mobile token is embedded in a QR-code URL reachable by anyone on the local network)X-Coordinator-Id→ 403 on all task routestask.name: 200-char max, control characters stripped (prompt injection prevention)IPC handlers (
electron/ipc/register.ts)New handlers wired up:
StartMCPServer,StopMCPServer,GetMCPStatus,GetMCPLogs,HydrateCoordinatedTask,MCP_CoordinatedTaskClosed,MCP_TaskHydrated,MCP_CoordinatorNotificationAck,MCP_CoordinatorNotificationDropAck,MCP_CoordinatorRestageAfterUserSendStore / type changes (
src/store/)New
Taskfields:coordinatorMode,coordinatedBy,controlledBy,mcpConfigPath,preambleFileExistedBefore,signalDone*,needsReview,mcpStartupStatus/ErrorNew
PersistedState/AppStorefields:coordinatorModeEnabled,coordinatorNotificationDelayMs,coordinatorControlHintDismissed,MCPStatuscore.ts,remote.ts,persistence.ts,store.ts,ui.ts— wired up coordinator store state with defaults and persistence round-tripOpenSpec
openspec/changes/coordinator-mcp-backend/proposal.md— documents the MCP orchestration server, REST task API, three-token auth model, signal/wait lifecycle, preamble injection, and new IPC channels perCLAUDE.mdrequirementTests
electron/mcp/coordinator.test.tswaitForIdle, signal/wait, notifications,hydrateTask,setTaskControl,cleanupTaskelectron/mcp/coordinator-sequence.test.tselectron/mcp/config.test.tsselectMcpJsonDir,writeMcpConfigelectron/mcp/mcp-tool-list.test.tselectron/mcp/prompt-detect.test.tselectron/remote/coordinator-scoping.test.tscreate_taskbody-vs-header scopingelectron/ipc/register-mcp.test.tsStartMCPServerinput validationelectron/ipc/register.test.tselectron/ipc/docker-config.test.tselectron/mcp/docker.integration.test.tsTotal: 295+ test cases.
npm test→ 859 pass, 12 skipped.Test plan
npm run compile && npm run typecheck && npm run lint && npm run format:check— passnpm test— 859 pass, 12 skipped (Docker integration skipped without daemon)git diff --check johannesjo/main...HEAD— clean🤖 Generated with Claude Code