--- kind: task slug: mcp-complete-task-tool title: MCP complete_task tool — close an agent_runs row and finalize status plan_slug: agent-coordination epic_slug: mcp-claim-complete status: ready priority: P2 tenant_id: global owner: unassigned cursor_todo_id: null updated_at: "2026-06-01" --- # Task summary A new MCP tool `complete_task` that an agent calls at session end. It closes the `agent_runs` row with an outcome and token totals, and optionally flips the backlog item to `done` (or another terminal status). ## Description ### Contract Register in `apps/mcp-server/src/tools/complete-task.ts`. Input zod schema: ```typescript { runId: string, // uuid of the agent_runs row to close outcome: "succeeded" | "failed" | "cancelled" | "stalled", tokensInput?: number, tokensOutput?: number, tokensTotal?: number, notes?: string, // freeform summary, capped at ~2000 chars error?: string, // optional failure message finalStatus?: "done" | "blocked" | "ready" | "in_progress" | "cancelled", // optional override for the backlog item's status } ``` Behavior: 1. Look up the run; verify it's still open (`finished_at IS NULL`). If already closed, error `RUN_ALREADY_FINISHED`. 2. Resolve the backlog item and confirm its workspace matches the run's workspace. 3. Update the run row: `finished_at=now()`, `outcome`, tokens, `notes`, `error`. 4. Compute the final backlog status: - If `finalStatus` provided: use it (validate it's a legal `markdown_backlog_items.status` value). - Else if `outcome === "succeeded"`: set status to `done`. - Else if `outcome === "failed"`: set status to `blocked`. - Else: leave status as-is. 5. Write `audit_log` row (`action: "task.completed"`, `metadata: { outcome, final_status }`). 6. Return `{ runId, finishedAt, finalStatus, tokensTotal }`. ### Token semantics Take the agent at its word for `tokensTotal` — don't recompute from input+output. This matches Symphony's "prefer absolute thread totals" rule (`SPEC.md` §13.5) and avoids double-counting when models report cumulative totals natively. If `tokensTotal` is omitted but `tokensInput` and `tokensOutput` are provided, compute total as `input + output` and store. If all three are present and inconsistent, prefer `tokensTotal` and don't error. ### Idempotency Closing an already-closed run is an error, not a silent no-op. The agent should know it tried to close something twice. If the operator wants to amend a closed run, they can do it via a future tRPC procedure — not through this tool. ### Anti-goals - No streaming updates. This tool runs once at session end. - No "extend" or "renew" semantics. A long session that the agent thinks is still going should keep its run open by *not* calling `complete_task`. Stall detection is the orchestrator's job (deferred). ## Subtasks - [ ] Create `apps/mcp-server/src/tools/complete-task.ts`. - [ ] Register in `apps/mcp-server/src/tools/index.ts`. - [ ] Implement the 6-step behavior with zod validation. - [ ] Audit log write. - [ ] Verify with a manual end-to-end loop: `claim_task` → do nothing → `complete_task` and confirm the run row is closed. ## Owner or assignee Unassigned ## Status ready ## Estimation M ## Acceptance criteria - [ ] Successful close updates `finished_at`, `outcome`, tokens, and (when appropriate) the backlog item's status. - [ ] Double-close errors with `RUN_ALREADY_FINISHED`. - [ ] Token inconsistency is resolved by preferring `tokensTotal`. ## Links to related Epic / Plan - Epic: `./Epic-mcp-claim-complete.md` - Plan: `../Plan-agent-coordination.md`