MCP task bridge · SQLite queue· Apache-2.0
Claude plans. OpenCode builds.
Dispatch real coding work from Claude to headless OpenCode agents via a durable SQLite queue. Structured results, git-aware diffs, receipts.
curl -fsSL https://taskbridge-landing.pages.dev/install | bash01/Out of context
Two good tools, standing on opposite platforms.
Claude is a powerful planner that can't touch your filesystem. OpenCode runs code locally, but it lives outside Claude's context. Each is missing the half the other has.
TaskBridge closes the loop. Claude writes a task to a queue over MCP. A worker on your machine picks it up, runs it in an isolated worktree, and files a receipt Claude can read: what changed, what ran, what is still open.
The planner
Claude stays the interface. It breaks work down, delegates, and reads results.
The executor
OpenCode does the execution — edits, tests, shell — headlessly and reproducibly.
The queue
SQLite is the durable handoff. It survives restarts and supports retries, priorities, dependencies, and two-way questions.
Claude stays the interface. OpenCode does the execution. SQLite is the durable handoff.
02/The scrolling diagram
One task, end to end.
Claude ──MCP──▶ [opencode_bridge_mcp.py] ──write──▶ ⛁ tasks.db ▲ │ [tasks_cli.py / worker.py] ──┘ ▼ │ ~60 s · priority ▼ oldest-first ┌── opencode run ── worktree ──┐ │ edits · shell · tests │ └────── structured result ─────┘ │ ▼ { summary, files_touched, tests_run, open_questions } + diff │ ▼ Claude: get_task_result OpenCode ╌╌claude_ask╌╌▶ Claude reverse bridge · 6 tools
▸ submit—Claude calls submit_task over MCP — stdio from Claude Desktop, or HTTP from any MCP client.
03/The spec sheet
What ships in the box.
Ten line items from the README. Tool names are the real ones.
0116 MCP tools for Claude
Submit, batch up to 20, wait, tail logs, answer, annotate, cancel, pick models. No polling loops, full observability.
submit_tasksubmit_batchwait_forget_task_result02Headless auto-worker
launchd polls every 60 s, claims oldest-first by priority, streams output to task_logs/<id>.log, and returns a structured result plus a git-aware diff.
worker.py03Two-way questions
The worker can pause with a question block. The task goes waiting, a macOS banner fires, Claude answers, and the run resumes with the answer in context.
answer_task04Worktree isolation
A per-task branch and git worktree, auto-committed on success, cleaned up unless you ask to keep it.
worktree_keep05Honest permissions
readonly reverts writes and reports the reverted files. no-network and no-shell are prompt-level, and labelled that way.
permission06Reverse bridge
Six tools for OpenCode to drive Claude, sync or in the background. Uses the Anthropic API when ANTHROPIC_API_KEY is set, otherwise claude -p.
claude_askclaude_submitclaude_poll07Public-URL mode
One command exposes MCP through ngrok or a Cloudflare quick tunnel, with MCP_ALLOWED_HOSTS paired automatically.
install.shstart_public.sh08Live dispatch board
Served at GET / on the same host: tally counts, per-task live logs, reverse jobs, engine tail, and a scrolling feed of fresh output.
GET //api/*09Caller detection
Submissions are stamped with the true caller from the MCP handshake clientInfo, so Claude and ChatGPT dockets stay separate.
origin10Production reliability
Per-attempt timeouts, a 15-minute stale-running reaper with a pid-reuse guard, a single-instance flock, a heartbeat file, and banners you can silence.
worker_heartbeatWORKER_NOTIFY=0
04/Three moves
From clone to first receipt.
- 01of 03
Clone and install
install.shinstalls dependencies into.venv, generates or reuses your bearer token, opens the ngrok tunnel, pairsMCP_ALLOWED_HOSTS, prints your connector box, and serves MCP in the foreground.curl -fsSL https://taskbridge-landing.pages.dev/install | bash - 02of 03
Read your connector box
The public URL plus the bearer token.
/mcpand/api/*require it;/healthstays open. Rotate any time with./install.sh --rotate.┌─ TaskBridge connector ─────────────────────────────┐ │ │ │ URL https://<your-domain>.ngrok.app/mcp │ │ Token •••••••••••••••••••••••••••••••• │ │ │ └────────────────────────────────────────────────────┘
- 03of 03
Dispatch from Claude
Add the URL as an MCP connector — or wire stdio into Claude Desktop — then ask Claude to hand work over.
1submit_task(title=…, instructions=…, working_dir=…)→ id2wait_for(task_ids=["<id>"], timeout_sec=120)→ blocks — no polling3get_task_result(task_id="<id>")→ result · structured · usage · diff
Prerequisites: Python 3.12 · opencode on PATH · a free ngrok account for public access.
05/Demo
A task with a question in the middle.
Scroll to run it. The worker pauses, Claude answers, the run resumes, and the receipt comes back.
claude · taskbridge mcp · illustrative session
› › › ›
┌ dispatch board
worker online
- pending
- 03
- running
- 01
- waiting
- 01
- done
- 12
engine tail
fresh output
- 05:49:12t_0139pytest -q · 14 passed
- 05:49:17t_0142waiting · question posted
- 05:49:22t_0140edit src/task_store.py
- 05:49:24t_0141claimed · priority 2
- 05:49:29t_0138done · diff +12 −3
- 05:49:34t_0143pending · depends_on t_0141
a stylized view of a real one — the actual board ships at GET / on the same host.
06/Troubleshooting
When the line goes quiet.
Straight from the README's troubleshooting notes.
MCP_ALLOWED_HOSTS must exactly match the tunnel host. Run ./install.sh or ./start_public.sh — both set it for you.
Cloudflare quick-tunnel hostnames rotate on every restart. Rerun ./install.sh (or ./start_public.sh), or reserve a fixed ngrok domain in .ngrok-domain.
Check ./.venv/bin/python tasks_cli.py heartbeat and task_logs/<id>.log. Stale running tasks are reaped after 15 minutes, and a task's own timeout_sec plus slack is honoured.
Every attempt runs opencode with a per-attempt argv marker (br-<id>-a<n>) visible in ps. The reaper kills the orphaned CLI child plus its ppid tree, hard-capped so a matching bug can't mass-kill, and a per-pass sweep catches tagged strays. macOS gives no session or env signal, so the ppid walk and argv marker are the whole toolkit — grandchildren reparented to 1 or setsid'd are the known residual. Never pass --session to the CLI: free-tier models refuse to run with it.
Banners are macOS only. WORKER_NOTIFY=0 silences them on purpose. Full output is always in tasks.db and worker.log.
Run claude login once, or set ANTHROPIC_API_KEY.
Put it on your PATH, or set OPENCODE_BIN=/path/to/opencode.
07/Receipts
What it won't do
Every bridge has a load limit. Here's ours, up front.
✕ 01
No token counts
The opencode CLI doesn't expose them. usage reports model, latency, attempts, and sandbox status — not tokens.
✕ 02
Restrictions are prompt-level
no-network and no-shell are instructions to the agent, not OS-enforced sandboxes. worker.py documents this.
✕ 03
macOS-first scheduling
launchd scheduling and desktop banners are macOS-specific. On Linux, bring your own scheduler (systemd or cron); worker.py still runs by hand.
✕ 04
Pickup is not instant
Tasks can run up to 1800 s, but the 60 s launchd cadence means pickup latency averages around 30 s.
Fork it. Self-host it.
Build on it.
Apache-2.0
git clone https://github.com/Parithosh-Varma/TaskBridge.git