MCP Task Orchestrator

repository·main·Indexed 42 days ago

https://github.com/jpicklyk/task-orchestrator

An MCP server that provides AI agents with a persistent work item graph and server-enforced workflow discipline. It moves structural concerns like dependency ordering and quality gates from prompts into a configurable server-side layer using a Phase Model (queue, work, review, blocked, terminal) and customizable schemas in `.taskorchestrator/config.yaml`.

Tokens
134.2K
Snippets
283
Records
444
Agent score
51%

What's inside MCP Task Orchestrator

  1. Choose a Fleet Topology Pattern

    main

    Select a topology based on your workload requirements and concurrency needs:

    Scenario A: Pure Orchestration

    • Mechanism: A single orchestrator agent calls advance_item to move children through the pipeline. No claim_item is used.
    • Best for: Single-developer or low-concurrency sequential pipelines.

    Scenario B: Pure Claim, Flat Items

    • Mechanism: Independent items with no hierarchy. Agents call claim_item(selector=...) to drain a shared queue.
    • Best for: Horizontally scalable, truly independent parallel tasks.

    Scenario C: Pure Claim, Chains (Anti-pattern)

    • Mechanism: Dependent items (A→B→C) where each is independently claimed.
    • Warning: Suffers from a post-completion race. Once A completes, B becomes immediately available to all agents, potentially causing the wrong agent to pick up B and losing context continuity.
    • Best for: Simple chains where notes provide sufficient handoff and races are tolerable.
    • Mechanism: Agents claim at the feature level (top-level items) using claim_item. They then orchestrate the sub-tree using advance_item (not claim_item) for all children.
    • Benefits: Prevents post-completion races, ensures full chain-context continuity, and protects sub-trees from being accidentally claimed by other agents.
    • Best for: Feature-based development work.
  2. Understand resource leasing and contention

    main

    When an item's traits declare resources:, entering the work role via start or resume triggers resource acquisition.

    Lease Modes

    • mode: exclusive: Acquires an exclusive lease for the resource key. This is an atomic operation (all keys or none). The lease is released upon any exit from the work role (complete, cancel, block/hold, reopen, or terminal cascade).
    • mode: advisory: Does not lock the resource but records the key into consumedCredentials for audit visibility.

    Contention Handling

    If an exclusive resource is already held, the transition is rejected as transient. This is not a gate failure; you should wait and retry rather than attempting to add notes. The response will include errorKind: "transient", errorCode: "resource_unavailable", and a retryAfterMs value.

    Note: For security/privacy, the identity of the current resource holder is never disclosed in the error response. To diagnose stuck leases, use get_context(itemId=...) or the ADMIN REST endpoint /api/v1/resources/leases.

    {
      "results": [
        {
          "itemId": "uuid",
          "trigger": "start",
          "applied": false,
          "error": "Resource lease contended: staging-db",
          "errorKind": "transient",
          "errorCode": "resource_unavailable",
          "retryAfterMs": 30000,
          "contendedResources": ["staging-db"]
        }
      ],
      "summary": { "total": 1, "succeeded": 0, "failed": 1 }
    }
  3. Implement delegation chains via Actor Attribution

    main

    While the server does not enforce delegation logic, it is a best practice to follow a convention to build traceable delegation chains for post-mortem analysis:

    1. Orchestrator: Include actor: { id: "orch-1", kind: "orchestrator" }.
    2. Dispatching: When an orchestrator dispatches a subagent, it should pass its own ID as context.
    3. Subagent: The subagent should include its own ID and the orchestrator's ID as the parent: actor: { id: "sub-1", kind: "subagent", parent: "orch-1" }.

    To ensure subagents follow this, include explicit instructions in their delegation prompts:

    Include an "actor" object on every advance_item and manage_notes call:
    { "id": "your-agent-name", "kind": "subagent", "parent": "orchestrator-id" }
  4. Understand Tiered Claim Disclosure for Identity Security

    main

    The orchestrator implements a tiered disclosure model for claimedBy identity to prevent identity leakage, claim sniping, and jealousy patterns in multi-agent fleets.

    • Full Identity Disclosure: Only available via get_context(itemId). This is intended as an operator diagnostic tool.
    • Limited/Boolean Disclosure: Most other surfaces (like get_next_item, query_items, or claim_item failure responses) only expose whether an item is claimed (isClaimed: boolean) or provide a retryAfterMs value. They do not reveal which agent holds the claim.
    • Summary Disclosure: Global health checks and overview queries return counts only (claimSummary: { active, expired, unclaimed }).
  5. Understand Scope Filtering for API Tokens

    main

    Tokens can be restricted using two types of scope: root_ids and tags_include.

    root_ids (Hierarchical)

    Tokens with scope.root_ids can only access items within those specific root subtrees. The system performs an ancestor walk: an item is accessible if it or any of its ancestors are in the allowed root_ids set.

    tags_include (Item-level)

    Tokens with scope.tags_include are restricted to items carrying at least one of the listed tags. This is item-level only; it does not walk the ancestor chain. The item's own tags must match the allowlist.

    Behavior by Endpoint

    • Single-item access (GET /items/{id}, Write endpoints): Returns 403 scope_forbidden if the item is outside the scope.
    • Collection endpoints (GET /items, GET /items/roots, search, etc.): These never return 403. Instead, they return 200 OK with a filtered list (or an empty list if no items match).
    • Breadcrumbs (GET /items/{id}/breadcrumbs): The chain is truncated at the caller's scope root. Ancestors failing tags_include are dropped.
    • Dependencies/Backlinks: The subject item is checked normally. The counterparty item is checked against the scope; if the counterparty is out of scope, that specific row is simply dropped from the response (200 OK with a filtered list).
    • SSE (GET /api/v1/events): tags_include is enforced per-event for both live streams and replays.
  6. Understand Verifier Type Behaviors

    main

    The verifier.type determines how actor claims are processed:

    • noop (or absent): All actor claims are accepted as unchecked. No cryptographic validation occurs.
    • jwks: Validates JWT tokens in actor.proof against the configured JWKS.
      • Valid token $\rightarrow$ status: verified.
      • Invalid/Expired/Wrong claims $\rightarrow$ status: rejected (includes reason and metadata.failureKind).
      • Missing proof $\rightarrow$ status: absent.
      • Network/Fetch errors $\rightarrow$ status: unavailable.
  7. Understand the automatic Hooks and the Plan-Mode Pipeline

    main

    The plugin uses automatic hooks to enforce a structured workflow without manual invocation. This creates a "Plan-Mode Pipeline" that ensures every plan is tracked and structured.

    Automatic Hooks

    • Session Start (SessionStart): Injects the 13-tool surface (e.g., manage_items, query_notes, advance_item), the role lifecycle (queue → work → review → terminal), and a session tip.
    • Pre-Plan (PreToolUse on EnterPlanMode): Injects an instruction to run the pre-plan-workflow internal skill. This allows the agent to check existing MCP state and note schemas before writing a plan.
    • Post-Plan (PostToolUse on ExitPlanMode): Injects an instruction to run the post-plan-workflow internal skill after a plan is approved. This materializes work trees, dependencies, and notes in the MCP before implementation begins.
    • Subagent Start (SubagentStart): Injects the Agent-Owned-Phase Protocol into any subagent launched via the Agent tool.

    The Pipeline Flow

    1. User describes feature
    2. Enter Plan Mode $\rightarrow$ Pre-plan hook $\rightarrow$ pre-plan-workflow (checks state/schemas)
    3. Agent writes plan (with MCP awareness)
    4. User approves plan $\rightarrow$ Post-plan hook $\rightarrow$ post-plan-workflow (materializes items/trees/deps)
    5. Implementation begins (items and dependencies already exist in MCP)
  8. Manage Resource Leasing for Shared External Resources

    main

    Resource leasing is a primitive used to serialize access to shared external resources (e.g., staging slots, test databases, deployment mutexes) across different work items.

    Key Concepts:

    • Separation from Claims: A work-item claim leases an item to an actor; a resource lease serializes access to a resource. An agent can hold both simultaneously.
    • Item-keyed Holder Model: The lease is tied to the holding work item, not the actor. You cannot release a lease by matching an actor ID.
    • Release Paths: Leases are released via (1) the holding item transitioning out of WORK, (2) TTL expiry, or (3) an explicit REST API force-release.
    • TTL (Time-To-Live): Default is 3600s. Precedence: requirement.ttlSeconds > registry.defaultTtlSeconds > 3600. Maximum is 86400s. Unlike claims, resource leases do not use heartbeats; size the TTL to accommodate the expected duration of the work.
  9. Use the needs-prod-access trait

    main

    The needs-prod-access trait can be applied to workflows requiring production environment interaction. It enforces documentation of access justification and provides an advisory audit trail for credentials.

    • access-justification: A required work role note explaining why access was needed and who approved it.
    • prod-change-credential: An advisory mode resource that provides an audit trail of the credentials used without serializing the incident work itself.
    traits:
      needs-prod-access:
        notes:
          - key: access-justification
            role: work
            required: true
            description: "Why production access was needed and who approved it."
        resources:
          - key: prod-change-credential
            mode: advisory
  10. Understand Schema Matching Rules

    main

    The orchestrator resolves which schema applies to an item using a specific hierarchy:

    1. Type-first lookup: If the item has a type field, the orchestrator looks for an exact match in work_item_schemas.
    2. Tag fallback (Legacy): If no type is present or no type-based schema matches, it looks for the first matching tag in the note_schemas block.
    3. Default fallback: If no matches are found, it looks for a schema named default in either section.
    4. Schema-free: If no match is found, the item has no schema and no gate enforcement is applied.

    Key Constraints:

    • Matching is exact and case-sensitive.
    • Only one schema applies per item.
    • Setting type on an item is the preferred method to activate a schema.
  11. Understand resourceLeases in item mode

    main

    When inspecting an item, the resourceLeases array (optional) shows the resource requirements declared by the item's traits or leases it currently holds. This is used for identity diagnostics of resource ownership.

    Key Fields:

    • key: The resource identifier (e.g., staging-db).
    • mode: Either "exclusive" or "advisory". If a lease is held for a key that is no longer declared in traits, it defaults to "advisory".
    • held: true only if the item itself holds the active lease.
    • holderItemId / acquiredByActorId / expiresAt: Present only if a lease is active for that key. If the item holds its own lease, these fields prioritize the self-lease information.
    {
      "resourceLeases": [
        {
          "key": "staging-db",
          "mode": "exclusive",
          "held": true,
          "holderItemId": "uuid",
          "acquiredByActorId": "agent-worker-42",
          "expiresAt": "2026-07-25T13:00:00Z"
        },
        { "key": "deploy-credential", "mode": "advisory", "held": false }
      ]
    }
  12. Handle Note body length limits and enforcement

    main

    The orchestrator enforces length limits on note bodies based on the resolved schema for a note's key. Enforcement behavior is controlled by the note_limits.mode configuration:

    • warn (default): The note is accepted, but a warning field is added to the note's result in the response, specifying the limit and the actual size.
    • reject: The note fails and is included in the failures array of the response with the error code NOTE_BODY_TOO_LONG. The error object includes key, maxLength, and actualLength.

    Configuration Layering:

    • A manage_project_config push that sets note_limits.mode for a specific root takes precedence.
    • If a per-root document omits note_limits, it falls back to the global mode.