Model Context Protocol · SQLite queue · Headless agents

Claude dispatches.
OpenCode executes.

Claude submits coding tasks over MCP. An auto-worker picks them up in about a minute, runs them with opencode run, and writes structured results back for Claude to read.

16 MCP tools~60s pickupSQLite durable queue2-way questions
live relay · task t-3f2a
Claudesubmit_task
tasks.dbSQLite queue
workeropencode run
t-3f2a · pending
t-3f2a · running
t-3f2a · completed
✓ claimed · priority 5 · model cat-jam-free
◷ streaming → task_logs/t-3f2a.log
✓ completed · 14 files · diff attached

Why it exists

Planners plan.
Runners run.

Claude is a great planner but can't touch your machine. OpenCode runs code locally but lives outside Claude's context. The bridge connects them with a queue that survives restarts, supports retries and priorities, and lets either side ask questions mid-task.

Delegate, don't babysit

One submit_task call fans out to a headless agent. Poll once with wait_for instead of looping.

Answers, not just logs

Every completion carries a structured summary, files touched, tests run, open questions — plus a git-aware diff.

Stuck? It asks back

The worker can pause a task with a question. You answer, it resumes with your answer in context.

Under the hood

Sixteen tools, one queue.

submit_tasksubmit_batch ×20wait_forget_task_resultget_task_logsanswer_tasklist_tasksqueue_statusworker_heartbeatadd_task_notecancel_taskclear_finishedlist_modelsset_default_modelping+ reverse: claude_ask ×6

Per-task isolation

Each task can run on its own branch in a git worktree, auto-committed on success and cleaned up after.

Honest permissions

readonly reverts writes and reports what was reverted. Network/shell limits are prompt-level and labeled as such — never oversold.

Taste

Three calls. Done.

claude → bridge
# 1. dispatch
submit_task(title="Add retry tests", working_dir="/path/to/repo",
  instructions="Cover task_store retries. pytest must pass.")  →  id: t-3f2a

# 2. wait (no poll loop)
wait_for(task_ids=["t-3f2a"], timeout_sec=120)

# 3. read the structured result
get_task_result(task_id="t-3f2a")  →  { summary, files_touched, tests_run, diff }