Architecture
Claude talks to an MCP server. The server reads and writes SQLite. A worker executes. Nothing else is load-bearing.
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
| File | Role |
|---|---|
| opencode_bridge_mcp.py | MCP server, Claude side. Transports: stdio, streamable-http, sse. Host allowlist for tunnels. |
| task_store.py | Shared SQLite logic — submit, claim, complete, retries, dependencies, notes, heartbeat. |
| tasks_cli.py | Manual worker CLI: pending, list, show, claim, complete, fail, logs, heartbeat, answer. |
| worker.py | Headless executor. 60s cadence, priority order, timeouts, stale-task reaper, worktrees, macOS banners. |
| claude_control_mcp.py | Reverse bridge. Six tools so OpenCode can drive Claude: ask, submit, poll, result, cancel, status. |
Configuration
| Variable | Default | Purpose |
|---|---|---|
| TASK_DB_PATH | <repo>/tasks.db | SQLite location |
| WORKER_MODEL | opencode/cat-jam-free | Default execution model |
| OPENCODE_BIN | opencode | Binary on PATH, overridable |
| WORKER_NOTIFY | 1 | macOS 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
Per-attempt budgets enforced even on silent hangs. Each retry gets the full budget again.
Tasks stuck in running past 15 min are requeued — and their orphaned process groups are killed.
Overlapping launchd and manual passes skip cleanly via flock instead of double-finalizing.