MCP Task Orchestrator · Field guidePlate 5 of 9
One Docker image, one SQLite file. It runs as a throwaway container per session or as a persistent local server shared by every project, and the same gate logic answers both the MCP tools and the REST API.
/mcp endpoint itself is never authenticated, so keep it on loopback or behind your own network controls.Dashboards, CI jobs and the plugin's own hooks use HTTP under /api/v1, switched on with API_ENABLED=true. An advance over REST runs the same gate logic as the MCP tool.
| Auth mode | How a caller proves itself |
|---|---|
bearer | Static tokens from a secrets file. Each has its own capabilities, scope and expiry. Loaded at startup. |
jwks | Signed JWTs checked against a key set, issuer and audience. Capabilities and scope travel as claims. |
none | An explicit double opt-in. Every request acts as admin. For a loopback port only. |
| Capability | Allows |
|---|---|
read | Every GET, and the event stream |
write-items, write-notes | Creating, editing and deleting items and notes |
advance | Role transitions |
manage-dependencies | Adding and removing edges |
write-config | Pushing per-project config, plans and rules |
admin | All of the above, plus attribution fields and lease overrides |
A token can also be limited to certain project roots and tags. A single item outside that scope returns 403, and lists are filtered.
| To do this | Route under /api/v1 |
|---|---|
| Read or change items | GET /items/{id} · POST /items · PATCH /items/{id} |
| Write a note | PUT /items/{id}/notes/{key} |
| Move an item, or ask what blocks it | POST /items/{id}/advance · GET /items/{id}/gate |
| Search | GET /search · GET /notes/search |
| Push or inspect project config | PUT /roots/{rootId}/config · GET /roots/{rootId}/config/effective |
| Audit who did what | GET /transitions?since=… |
| Inspect or release leases | GET /resources/leases · DELETE /resources/leases/{key} |
| Check liveness, with no auth | GET /health |
| Follow changes live | GET /events |
item.created, item.updated, item.deleted, item.advanced, note.upserted, note.deleted, dependency.added, dependency.removed, scope.entered, scope.left. Two control events always arrive: sync.lost and auth.expired.Editing an item requires If-Match with its current ETag, so a stale client gets a 412 and cannot overwrite. Edits are JSON merge-patch. An Idempotency-Key makes a retried write replay its first result.
Every error is { error, message, details }. A failed gate is 422 gate_blocked with the missing notes. A contended lease is 409 resource_unavailable with a Retry-After header.
The actor is always the token's identity, and one supplied in the body is dropped. A REST advance is not bound by claim ownership, though it is logged, and is still bound by leases. Lease administration and the event stream exist only here.
The server works with any MCP client. The plugin wires it into a Claude Code session through hooks, in the order a feature normally unfolds.
| Three more hooks | What each does |
|---|---|
| Before a note is written | If the note's schema names a skill and the body looks thin, suggests running that skill first. Advisory only. |
When config.yaml changes | Re-syncs the project config to the server mid-session. |
| When a turn ends | Nudges a retrospective if an item finished on its own and nothing else prompted one. |
| Skills, by purpose | Slash commands under /task-orchestrator: |
|---|---|
| Get set up | quick-start (guided onboarding) · init (project or personal root) · configure-server (transport, REST, config-sync) · adopt-project-scope (migrate an existing database) |
| Shape the rules | manage-schemas (create, edit and validate schemas, traits and identity policy) |
| Day-to-day work | work-summary (dashboard) · create-item · status-progression (which trigger, what is missing) · dependency-manager · batch-complete |
| Run many items | run-wave (attended) · ralph (unattended queue drain) |
| Improve the process | session-retrospective · review-proposals |
Also shipped: agent definitions for implementer, reviewer, planner and test author, the orchestrate skill and four workflow scripts. Plate 8 covers how they run a feature.
When a schema declares seats (plate 4), run-wave reads them to build each item's stages and picks the agent and model for each seat. The stop guard is seat-aware too: it only sends a sub-agent back for notes its own seat owns.
| Topology | Claims | Fits |
|---|---|---|
| Pure orchestration | None | Single developer, sequential pipelines |
| Pure claim, flat items | Every item | Independent parallel work with no hierarchy |
| Pure claim, chains | Every item | Rarely. When a blocker completes, every agent races for the next link. |
| Hybrid: claim the parent, orchestrate the sub-tree | Feature level only | Feature-based development with many agents |
success, already_claimed (pick another item), queue_empty (nothing matches, stop or wait) and none_eligible (matches exist but none can be claimed now, retry later). One claim per call, and a requestId is required.
Any transition into terminal clears the claim, and a reopened item starts unclaimed. A crashed agent's claim simply expires. There is no background sweeper.
Lists and searches show only whether an item is claimed. The holder's identity appears in one place: get_context for that item.
{ id, kind, parent }. What changes is whether anyone checked it.actor_authentication:
enabled: true # the hook requires an actor
degraded_mode_policy: reject # what to do when unverified
verifier:
type: jwks
oidc_discovery: "https://idp.example/.well-known/openid-configuration"
audience: "task-orchestrator"
algorithms: ["EdDSA", "RS256"]
| Policy when a proof cannot be verified | Identity the server uses |
|---|---|
accept-cached, the default | The verified id when only the key fetch failed and cached keys still validate it. Otherwise the self-reported id, with a warning logged. |
accept-self-reported | Always the id the caller sent. For local development. |
reject | None. Claims return rejected_by_policy. |
enabled makes the plugin's hook refuse a write with no actor. verifier makes the server validate the proof. A call can pass the first and fail the second.
An OIDC discovery URL, a JWKS URL, a local key file for air-gapped hosts, or per-agent decentralized identifiers from an allowlist. Proofs must expire, within 24 hours by default.
Identity settings live only in the global config. A project cannot loosen them. The DEGRADED_MODE_POLICY environment variable overrides the file, and an unknown value stops the server from starting.
Always on. Transitions, gates, dependencies and cascades work with no further setup.
Opt-in. Agents take time-limited ownership of an item with claim_item, and the server then requires the holder's identity on its transitions.
Opt-in and separate from claims. Verifies the actor on each write against a JWKS key set, so attribution is proven and no longer self-reported.