MCP Task Orchestrator · Field guidePlate 2 of 5
Every work item sits in one of five roles. Agents never edit status directly. They call advance with a trigger, and that call is where the server checks required notes.
start passes it. Two triggers are not drawn: complete jumps from queue, work or review straight to terminal and checks the required notes of every phase, and cancel jumps to terminal from any role that is not terminal, blocked included, with no check at all.A simplified model of the server's rules for one item, using the server's own error wording. Fire triggers in any order and read what comes back. The highlighted station above follows along.
type feature-task · no status label
| Trigger | From | To | Gate |
|---|---|---|---|
start | queue, work or review | the next role on the line | Required notes of the current phase |
complete | queue, work or review | terminal | Required notes of every phase. A blocked item must resume first. |
block / hold | any non-terminal | blocked | None. Saves the previous role. |
resume | blocked | the saved role | None |
cancel | any non-terminal | terminal | None. Sets the status label to cancelled. |
reopen | terminal | queue | Bypassed. A terminal parent goes back to work. |
The role is what the server reasons about. Alongside it, each transition sets a status label: display text for people and dashboards. The five roles never change. The labels can be renamed per project.
| Trigger | Default label |
|---|---|
start | in-progress |
complete | done |
block | blocked |
cancel | cancelled, always |
| a cascade | done |
resume | the label from before the block |
reopen | cleared, always |
status_labels: start: "working" complete: "finished" block: "on-hold" cascade: "auto-completed"
The label comes back on every transition result and on item queries. Filter and gate on the role. Show the label.
Every failure carries a kind that says whether retrying can help, and a code to branch on. An agent never has to parse the message text.
detail, such as a gate's missing notes or the blockers of a dependency, and a fix sentence for codes that define one. A claim_held error names the item and when the claim expires, never the holder.{
"ok": false,
"error": {
"code": "claim_held",
"kind": "transient",
"message": "...",
"detail": { "itemId": "550e8400-...", "expiresAt": "2026-10-16T13:15:00Z", "retryAfterMs": 420000 },
"retryAfterMs": 420000
}
}
Each element of a write call may carry an idempotencyKey. The server records a committed element for 24 hours per principal and tool and returns its stored result again (replayed: true) instead of running it twice. The same key with a different element is idempotency_mismatch.
No response arrived, such as a timeout: resend with the same key. Only an element that committed is recorded, so an element that failed runs again when you retry with the same key.