Architecture

One queue.
Two directions.

Claude talks to an MCP server. The server reads and writes SQLite. A worker executes. Nothing else is load-bearing.

data flow
Claude app ──MCP stdio/HTTP──▶ opencode_bridge_mcp.py ──read/write──▶ tasks.db (SQLite)
OpenCode agent ──shell──▶ tasks_cli.py ──claim/complete──▶ tasks.db
worker.py ──polls DB, runs `opencode run --auto`──▶ working_dir / git worktree
worker.py ──streams──▶ task_logs/<id>.log ──result+diff──▶ tasks.db ◀── Claude reads
OpenCode ──claude_ask/submit──▶ claude_control_mcp.py ──▶ claude -p / Anthropic API

Components

Five files do the work.

FileRole
opencode_bridge_mcp.pyMCP server, Claude side. Transports: stdio, streamable-http, sse. Host allowlist for tunnels.
task_store.pyShared SQLite logic — submit, claim, complete, retries, dependencies, notes, heartbeat.
tasks_cli.pyManual worker CLI: pending, list, show, claim, complete, fail, logs, heartbeat, answer.
worker.pyHeadless executor. 60s cadence, priority order, timeouts, stale-task reaper, worktrees, macOS banners.
claude_control_mcp.pyReverse bridge. Six tools so OpenCode can drive Claude: ask, submit, poll, result, cancel, status.

Configuration

Seven knobs. All env.

VariableDefaultPurpose
TASK_DB_PATH<repo>/tasks.dbSQLite location
WORKER_MODELopencode/cat-jam-freeDefault execution model
OPENCODE_BINopencodeBinary on PATH, overridable
WORKER_NOTIFY1macOS banners; 0 silences
MCP_ALLOWED_HOSTS—Tunnel hosts, required behind ngrok/cloudflared
ANTHROPIC_API_KEY—Reverse bridge: API backend, else Claude CLI
CLAUDE_API_MODEL—Reverse bridge model override

Reliability

Built for unattended runs.

Timeouts that hold

Per-attempt budgets enforced even on silent hangs. Each retry gets the full budget again.

Stale-task reaper

Tasks stuck in running past 15 min are requeued — and their orphaned process groups are killed.

Single-instance lock

Overlapping launchd and manual passes skip cleanly via flock instead of double-finalizing.