Pairs with the parallel complete_task commit. claim_task opens an agent_runs row, flips status to in_progress only when it's safe (ready/draft → in_progress, never overwriting a deliberate blocked/done/in_progress), and returns the resolved workflow prompt + source level. Idempotent re-claim by the same actor returns the existing run with reused=true and refreshes notes only — started_at is sacred. Different-actor re-claim errors with ALREADY_CLAIMED naming the existing actor and run id. Tenancy fence: if the backlog item exists but in a different workspace than the resolved handle, we refuse with "doesn't belong to workspace" rather than 404. Prevents cross-tenant existence fishing. All three writes (run insert + status flip + audit insert) happen in one db.transaction() so a partial claim is unreachable. tools/index.ts now registers both claim_task and complete_task. Co-authored-by: Cursor <cursoragent@cursor.com>
5 KiB
5 KiB
| kind | slug | title | plan_slug | epic_slug | status | priority | tenant_id | owner | cursor_todo_id | updated_at |
|---|---|---|---|---|---|---|---|---|---|---|
| task | mcp-claim-task-tool | MCP claim_task tool — open an agent_runs row and return the prompt | agent-coordination | mcp-claim-complete | in_progress | P2 | global | unassigned | null | 2026-06-02 |
Task summary
A new MCP tool claim_task that an agent calls at session start. It flips the backlog item to in_progress, opens an agent_runs row, and returns the effective workflow prompt + run id.
Description
Contract
Register in apps/mcp-server/src/tools/claim-task.ts (mirror the structure of create-object.ts).
Input zod schema:
{
workspace: string, // slug or UUID, resolved via resolveWorkspaceHandle
backlogItemId: string, // uuid
actorUserId?: string, // optional — defaults to null ("system / dev session")
notes?: string, // short opener text the agent can attach to the run
}
Behavior:
- Resolve the workspace handle (existing helper).
- Look up the backlog item; verify it belongs to the resolved workspace. 404 otherwise.
- Check for an existing open
agent_runsrow (finished_at IS NULL) for thisbacklog_item_id:- If one exists with the same
actor_user_id: return it (idempotent re-claim). Refresh itsnotesif provided. - If one exists with a different actor (or actor is null on both sides): return error
ALREADY_CLAIMEDwith the existing actor id (or "anonymous").
- If one exists with the same
- Insert a new
agent_runsrow:workspace_id,backlog_item_id,actor_user_id,started_at=now(),notes. - Update the backlog item:
status='in_progress'(only if currentlyreadyordraft; leave alone if alreadyin_progressordone). - Compute and return:
runId: the newagent_runs.idworkflowPrompt: result ofresolveWorkflowPromptbacklogItem: a minimal snapshot (title,body_markdown,status)
Output shape
Use the existing toolOk / toolErr helpers in apps/mcp-server/src/tools/tool-result.ts. Return as structured tool output so the in-session model can pattern-match.
Audit log
Write an audit_log row (action: "task.claimed", target_type: "agent_run", target_id: runId, metadata: { backlog_item_id, prior_status }). Assumes audit_log from Plan-multitenant-saas-hardening has landed; if not, file a follow-up to add audit writes when it does.
Anti-goals
- No re-fetching from external systems. The tool only touches Echodo's DB.
- No "soft lock" mechanic (timeout-based claims). If a run goes stale, the operator (or
complete_task) ends it explicitly.
Subtasks
- Created
apps/mcp-server/src/tools/claim-task.ts. - Registered in
apps/mcp-server/src/tools/index.ts(along withcomplete_taskin the same commit so both register atomically). - Implemented the 6-step behavior with zod validation (workspace handle, backlogItemId UUID, optional actorUserId UUID nullable, notes max 2000 chars).
- Audit log write inside the same transaction as the insert + status flip. When
actorUserIdis null, stampsmetadata.system_actor = "mcp:claim_task"per the audit conventions. - Operator-side smoke test against a running MCP client — not runnable in the agent's environment without an MCP harness. Documented for the operator.
Design decisions captured
- Tenancy fence on backlog-item lookup. When the resolved workspace doesn't match the item's
workspace_id, we refuse with a "doesn't belong to workspace" error rather than a 404. Prevents cross-tenant existence fishing. shouldTransitionis gated toready/draftonly. Never overwrite a deliberateblocked/done/in_progress. Per-spec.- Idempotent re-claim refreshes
notesonly.started_atis sacred — the second claim still represents the same session window.notesis the only field the agent can usefully amend on a re-claim. - All three writes (run insert + item update + audit insert) happen in one transaction. A partial claim (run exists but item never transitioned, or vice versa) is unreachable.
workflowPromptis resolved on every successful return, including idempotent re-claims. Cheap (three SELECTs max), and a re-claim might span code changes that altered the inheritance chain — always returning the freshest prompt is the safe default.
Owner or assignee
Unassigned
Status
ready
Estimation
M
Acceptance criteria
- Successful claim returns
runId,workflowPrompt,workflowPromptSource,backlogItem, and the resolved workspace summary. - Re-claim by same actor returns the same
runIdwithreused: true. - Claim by different actor errors with
ALREADY_CLAIMED: backlog item is already claimed by <actor> (run <id>). - Backlog item transitions to
in_progressonly if previouslyreadyordraft. Confirmed by inspection of theshouldTransitiongate inclaim-task.ts.
Links to related Epic / Plan
- Epic:
./Epic-mcp-claim-complete.md - Plan:
../Plan-agent-coordination.md