Featured

Deploy OpenClaw in 60 seconds — 20% off logoDeploy OpenClaw in 60 seconds — 20% off

Launch OpenClaw on Hostinger in about 60 seconds and keep your agent live 24/7. Our referral link gives you 20% off, no coupon code needed.

Launch on Hostinger
Run your Hermes agent on Hostinger, fully managed logoRun your Hermes agent on Hostinger, fully managed

Launch Hermes on Hostinger in one click, fully managed, no VPS knowledge needed. Use code ZACAARON10 for 10% off.

Launch on Hostinger
Crawl and scrape any site into clean data, 10% off logoCrawl and scrape any site into clean data, 10% off

Firecrawl crawls and scrapes any site into clean markdown for your agent. Get 1,000 free credits, and new users get 10% off their first purchase.

Try Firecrawl free
Your own AI agent, running 24/7 with QwikClaw logoYour own AI agent, running 24/7 with QwikClaw

QwikClaw sets up and runs an always-on OpenClaw agent for you. One click, no config files, no server setup.

Deploy now
One API to scrape, enrich, and extract the internet. logoOne API to scrape, enrich, and extract the internet.

Context.dev gives your agents a single API to scrape, enrich, and extract live web data — no proxies, no parsers, no maintenance.

Start building free
SetupClaw: done-for-you OpenClaw for founders & exec teams logoSetupClaw: done-for-you OpenClaw for founders & exec teams

White-glove OpenClaw for founders and exec teams (4–50+ employees): we install, harden, integrate your tools, and maintain it — secured from day one.

Get it set up for you
SEO data APIs for your agent, $1 free credit logoSEO data APIs for your agent, $1 free credit

DataForSEO gives your agent live access to SERP results, keyword data, backlinks, and on-page SEO data through one API. New accounts get a $1 credit, good for up to 20,000 keyword or backlink lookups.

Try DataForSEO free
Reach 47,000+ AI builders

A flat monthly placement in front of developers actively installing AI tools. No lock-in, cancel anytime.

Advertise here

Works with

Claude CodeClaude DesktopCursorVS CodeClineCodex CLIOpenClaw+ any MCP client

Install to Claude Code

This server doesn't publish a one-line install command. Follow the setup in the source repository.

Summary

Exposes an agent orchestration task queue as an MCP tool interface, allowing agents to submit, list, get, and update tasks with typed validation.

README.md

task-queue-mcp

![Built with Claude Code](https://claude.ai/code) ![License: MIT](https://opensource.org/licenses/MIT)

A FastMCP server that exposes the agent orchestration task queue as an MCP tool interface. Agents submit tasks, check status, and record completions through typed, validated tools instead of raw YAML file writes.

Runs as a Docker container on port 8485. Wired globally into ~/.claude.json so all Claude Code agent sessions have access.

Tools

| Tool | Description | |------|-------------| | submit_task | Create a new task with status: submitted | | list_tasks | List tasks with optional filters; TTL-expired tasks excluded | | get_task | Retrieve a single task by UUID (resolves archived + quarantined) | | update_task | Agent-facing status transition (strict); appends a history entry | | set_task_status | Operator status change — approve, cancel, or advance a missed task (audited override) | | cancel_task | Graceful terminal cancelled state for stale tasks (record kept, never deleted) | | quarantine_task | Isolate a task to quarantine/ (recoverable); drops from list_tasks | | restore_task | Restore a quarantined task to the active queue |

Agents use the strict update_task path; operators (via the HTTP control API) use set_task_status / cancel_task / quarantine_task / restore_task. Agents cannot cancel — cancelled is operator-only.

submit_task

submit_task(
    source_agent="research",
    target_agent="claudebox",  # agent name or "auto" for dispatcher routing
    task_type="build",  # build | deploy | fix | research | review | audit | notify
    summary="Deploy qmd update",
    description="Apply the qmd stack update from build plan...",
    risk_level="low",  # low | medium | high (default: low)
    requires_approval=False,  # explicit override of approval gate
    priority="normal",  # normal | high | urgent (default: normal)
    context_refs=["/home/ted/.claude/projects/research/build-plans/qmd/plan.md"],
    ttl_days=30,
    workflow_mode="semi-auto",  # semi-auto | auto (default: semi-auto)
)
# → {"ok": true, "task_id": "<uuid>", "filename": "<timestamp>-<slug>.yml"}

context_refs must be absolute paths. risk_level and priority are validated against allowlists. workflow_mode controls dispatcher behavior: semi-auto (default) queues the task for operator pickup with a Matrix notification, while auto triggers the dispatcher to launch the target agent headlessly. The server generates the UUID, sets created, and initializes alert_state and retry_policy stubs.

list_tasks

list_tasks(
    target_agent="claudebox",  # optional
    source_agent="research",  # optional
    status="approved,in-progress",  # comma-separated, optional
    task_type="build",  # optional
    include_archived=False,  # include archive/ subdirectory
    limit=20,  # max 200
)
# → list of task dicts, sorted by created descending

Tasks past their ttl_days are excluded. The dispatcher is authoritative for TTL archiving, but list_tasks filters them out proactively so agents don't act on stale items.

get_task

get_task(task_id="a7f3d2c1-1234-5678-abcd-000000000000")
# → full task dict, or {"ok": false, "error": "not found"}

Searches the main queue first, then archive/. Requires a full UUID — no prefix matching.

update_task

update_task(
    task_id="a7f3d2c1-1234-5678-abcd-000000000000",
    status="in-progress",  # see transition table below
    actor="claudebox",
    note="Claimed task, starting build.",
    output=None,  # written to result.output on completed/failed
)
# → {"ok": true, "task_id": "<uuid>"} or {"ok": false, "error": "..."}

Valid transitions:

| From | To | |------|----| | approved | in-progress | | in-progress | completed | | Any non-terminal | failed |

Non-terminal: submitted, pending-approval, approved, in-progress. Terminal: completed, failed, cancelled.

alert_state and retry_policy are dispatcher-owned — update_task never touches them.

Operator transitions (set_task_status)

Broader than update_task but still audited and bounded:

| From | To | Notes | |------|----|-------| | submitted / pending-approval | approved | standard | | Any non-terminal | cancelled | standard (also via cancel_task) | | Any non-terminal | Any non-terminal | requires allow_override=True + a non-empty note (the "advance a missed task" override) |

Terminal tasks are immutable even for operators. Every operator change appends a history entry with actor + note.

Status Lifecycle

submitted → [pending-approval] → approved → in-progress → completed
                                                 ↓
                                              failed

Any non-terminal ──(operator)──> cancelled        # graceful dismissal, record kept
Any task ──(operator quarantine)──> quarantine/    # isolate (recoverable via restore)

The dispatcher owns the submitted → approved/pending-approval transitions. Agents own approved → in-progress → completed (or failed). Operators own cancelled, quarantine/restore, and audited status overrides. Approval gating is controlled by agent manifests and the requires_approval field.

HTTP Control API

Non-MCP clients (the CloudCLI plugin and Matrix bot) can't import the Python core, so all their mutations go through a thin HTTP control API mounted as FastMCP custom routes on the same port 8485. Each endpoint delegates to the tool handlers above, inheriting transition validation, fcntl locking, and atomic writes — so there is exactly one validated write path for the whole system.

| Method | Path | Delegates to | |--------|------|--------------| | POST | /tasks/{id}/approve | set_task_status(approved) | | POST | /tasks/{id}/cancel | cancel_task | | POST | /tasks/{id}/status | set_task_status (body: status, note, allow_override) | | POST | /tasks/{id}/quarantine | quarantine_task | | POST | /tasks/{id}/restore | restore_task |

Body fields: actor (default operator), note, plus status / allow_override for the status route. Responses map the canonical result: 200 ok, 404 not found, 400 validation/transition error.

Auth: custom routes bypass the MCP auth middleware, so a shared-secret header is the gate (defense in depth on top of the loopback-published port):

  • Send X-Task-Queue-Secret: $TASK_QUEUE_API_SECRET on every mutation.
  • The server compares it in constant time (hmac.compare_digest) and fails closed (401) when the secret is missing, wrong, or unconfigured.
  • The secret lives in ~/.secrets/forge.env and is injected via env into the container, bot, and plugin — never committed to source.

Deployment

Docker (production)

services:
  task-queue-mcp:
    image: task-queue-mcp:latest
    container_name: task-queue-mcp
    ports:
      - "8485:8485"
    volumes:
      - /home/ted/.claude/task-queue:/task-queue
    environment:
      - TASK_QUEUE_DIR=/task-queue
      - MCP_HOST=0.0.0.0
      - MCP_PORT=8485
    cap_drop: [ALL]
    security_opt: [no-new-privileges:true]
    read_only: true
    tmpfs: [/tmp]
    user: "1000:1000"
    restart: unless-stopped
    networks:
      - claudebox-net

The container mounts only the task-queue directory read-write. The rest of the filesystem is read-only. /tmp is a tmpfs for transient scratch space.

Claude Code settings.json

{
  "mcpServers": {
    "task-queue-mcp": {
      "type": "url",
      "url": "http://localhost:8485/mcp"
    }
  }
}

Environment Variables

| Variable | Default | Description | |----------|---------|-------------| | TASK_QUEUE_DIR | /task-queue | Path to the task queue directory inside the container | | MCP_HOST | 0.0.0.0 | Bind host for the HTTP server | | MCP_PORT | 8485 | Port for the HTTP server | | TASK_QUEUE_API_SECRET | — | Shared secret for the HTTP control API. Required for any control-API mutation — fails closed (401) if unset. The MCP tools themselves do not use it. |

Building

docker build -t task-queue-mcp:latest .

Development

pip install -r requirements.txt

# Run tests
pytest

# Run server locally (against a local task-queue directory)
TASK_QUEUE_DIR=/home/ted/.claude/task-queue python -m src.server
pip install -r requirements.txt pytest pytest-cov ruff

# Lint + format (Baseline gate)
ruff check .
ruff format --check .

# Tests with coverage (gate: >=80%)
python -m pytest --cov=src --cov-report=term-missing

The test suite covers all eight tools and the HTTP control API — validation edge cases, adversarial YAML strings, illegal transitions, the quarantine/restore round-trip, operator-override auditing, and the shared-secret gate (missing/wrong secret → 401). All writes use yaml.dump — never string interpolation — to prevent YAML injection.

Security

The MCP tool endpoint on port 8485 is unauthenticated and limited to LAN/loopback — the port is not proxied externally via SWAG and the host firewall blocks external access. The HTTP control API mutation routes additionally require a shared-secret header (X-Task-Queue-Secret, constant-time compare, fail-closed) — see HTTP Control API. The container runs as UID 1000 with cap_drop: ALL, no-new-privileges, and a read-only rootfs (only /task-queue is writable).

Trust model

Loopback is the trust boundary. The shared secret gates only the cross-process HTTP control routes (/tasks/...) — it is not the sole barrier to mutation. All MCP tools, including the operator-mutating set_task_status / cancel_task / quarantine_task / restore_task, are reachable via the unauthenticated /mcp/ JSON-RPC endpoint, so any process with loopback access to port 8485 can mutate the queue without the secret. This is intentional: the queue is internal agent-coordination state, the port is loopback-only, and the MCP transport has always been unauthenticated. The secret exists to authenticate the specific cross-process clients (the CloudCLI plugin and Matrix bot) over plain HTTP, not to harden the loopback boundary. If loopback trust ever becomes insufficient, gate the MCP transport with a FastMCP auth provider rather than relying on the control-route secret alone.

Task File Schema

Tasks are YAML files in ~/.claude/task-queue/, named YYYYMMDD-HHMMSS-<uuid-prefix>.yml. All writes are atomic (write to .tmp, then os.rename()). Per-task file locks via fcntl.flock prevent races between concurrent MCP calls and the dispatcher.

For the full schema and lifecycle documentation, see the homelab-agent component doc.

Related

See related servers & alternatives →

Related MCP servers

Browse all →

Related guides

Hand-picked reading to help you choose and use Productivity servers.