Skip to content

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 | bash

01/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.

README · Why this exists

02/The scrolling diagram

One task, end to end.

▸ 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_result
  • 02Headless 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.py
  • 03Two-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_task
  • 04Worktree isolation

    A per-task branch and git worktree, auto-committed on success, cleaned up unless you ask to keep it.

    worktree_keep
  • 05Honest permissions

    readonly reverts writes and reports the reverted files. no-network and no-shell are prompt-level, and labelled that way.

    permission
  • 06Reverse 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_poll
  • 07Public-URL mode

    One command exposes MCP through ngrok or a Cloudflare quick tunnel, with MCP_ALLOWED_HOSTS paired automatically.

    install.shstart_public.sh
  • 08Live 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.

    origin
  • 10Production 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.

  1. 01of 03

    Clone and install

    install.sh installs dependencies into .venv, generates or reuses your bearer token, opens the ngrok tunnel, pairs MCP_ALLOWED_HOSTS, prints your connector box, and serves MCP in the foreground.

    curl -fsSL https://taskbridge-landing.pages.dev/install | bash
  2. 02of 03

    Read your connector box

    The public URL plus the bearer token. /mcp and /api/* require it; /health stays open. Rotate any time with ./install.sh --rotate.

  3. 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=…)→ id
    2wait_for(task_ids=["<id>"], timeout_sec=120)→ blocks — no polling
    3get_task_result(task_id="<id>")→ result · structured · usage · diff

Prerequisites: Python 3.12 · opencode on PATH · a free ngrok account for public access.

Full README

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

  1. 05:49:12t_0139pytest -q · 14 passed
  2. 05:49:17t_0142waiting · question posted
  3. 05:49:22t_0140edit src/task_store.py
  4. 05:49:24t_0141claimed · priority 2
  5. 05:49:29t_0138done · diff +12 −3
  6. 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

Star on GitHub2
git clone https://github.com/Parithosh-Varma/TaskBridge.git