TaskBridge
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
TaskBridge
Claude plans. OpenCode builds. Dispatch real coding work from Claude to headless OpenCode agents via a durable SQLite queue.
Claude submits tasks over MCP. An auto-worker picks them up in ~60 s, runs opencode run in an isolated worktree, and returns a structured summary, git-aware diff, logs, and usage for Claude to read. Works from Claude Desktop (stdio), any MCP client over HTTP, and in reverse — OpenCode can drive Claude too.
Why this exists
Claude is a powerful planner that can't touch your filesystem. OpenCode runs code locally but lives outside Claude's context. The bridge closes the loop:
Claude stays the interface — breaks work down, delegates, and reads results.
OpenCode does the execution — edits, tests, shell, headlessly and reproducibly.
SQLite is the durable handoff — survives restarts, supports retries, priorities, dependencies, and two-way questions.
Related MCP server: opencode-mcp
Features
17 MCP tools for Claude —
submit_task,submit_batch(up to 20),get_task/get_task_result,get_task_logs(live tail),wait_for,answer_task,list_tasks,queue_status/worker_heartbeat,restart_worker(recover a dead/stale worker through launchd, from the MCP server itself),add_task_note,cancel_task/clear_finished,list_models/set_default_model,ping. No polling loops, full observability.Headless auto-worker (
worker.py) — launchd polls every 60 s, claims oldest-first by priority, streams output totask_logs/<id>.log, returns structured{summary, files_touched, tests_run, open_questions}plus a git-aware diff.Two-way questions — worker can pause with a
questionblock → task goeswaiting+ macOS banner → Claude answers viaanswer_task→ run resumes with the answer in context.Worktree isolation — per-task branch +
git worktree, auto-committed on success, cleaned up unlessworktree_keep.Honest permissions —
readonlyreverts writes and reportsrevertedfiles;no-network/no-shellare prompt-level and explicitly labeled.Reverse bridge (
claude_control_mcp.py) — 6 tools for OpenCode to drive Claude:claude_ask(sync),claude_submit/claude_poll/claude_result/claude_cancel(background),claude_status. Uses Anthropic API ifANTHROPIC_API_KEYis set, elseclaude -p.Public-URL mode (
install.sh/start_public.sh) — one command to expose MCP through a temporary Cloudflare quick tunnel (default, no account needed) or ngrok, with automaticMCP_ALLOWED_HOSTSpairing. The installer asks which tunnel to use.Live dispatch board (
bridge_dashboard.html) — served atGET /on the same host. Tally counts, per-task live logs, reverse jobs, engine tail, and a scrolling feed of fresh output. The Model button sets the worker default model (persisted toworker_config.json) without touching the CLI. Polls/api/*; MCP stays at/mcp.Caller detection —
submit_task/submit_batchstamp the true caller from the MCP handshakeclientInfo, so Claude and ChatGPT dockets stay separate.origincan be overridden explicitly.Production reliability — per-attempt timeouts, 15-min stale-
runningreaper with pid-reuse guard, single-instance flock, heartbeat file, macOS banners (WORKER_NOTIFY=0to silence).
Quickstart
Prerequisites: Python 3.12 and opencode CLI on PATH. Public access needs no account by default — the installer offers a temporary Cloudflare quick tunnel; a free ngrok account is only needed if you pick ngrok (reserved domain / existing Authtoken).
One command — install, tunnel, serve, print your connector box:
git clone https://github.com/Parithosh-Varma/TaskBridge.git
cd TaskBridge
./install.shinstall.sh first asks which tunnel to use — Cloudflare quick tunnel (default, no account, random trycloudflare.com URL) or ngrok (prompts for your Authtoken; fixed domain from .ngrok-domain when present). Skip the prompt with --tunnel cloudflare / --tunnel ngrok. It then installs dependencies into .venv, generates or reuses the bearer token in .bridge-token, opens the tunnel, pairs MCP_ALLOWED_HOSTS, prints the public URL + token box, and serves MCP in the foreground. Ctrl+C stops the server; the tunnel stays up for the next run. Rotate the token with ./install.sh --rotate.
Manual setup (stdio / local dev):
git clone https://github.com/Parithosh-Varma/TaskBridge.git
cd TaskBridge
python3.12 -m venv .venv
./.venv/bin/pip install -r requirements.txtRun the MCP server (stdio — Claude Desktop spawns this):
./.venv/bin/python opencode_bridge_mcp.pyOr serve HTTP locally:
./.venv/bin/python opencode_bridge_mcp.py --transport streamable-http --port 8000
# → http://127.0.0.1:8000/mcpWire into Claude Desktop — merge claude_desktop_config.snippet.json into
~/Library/Application Support/Claude/claude_desktop_config.json under mcpServers
(replace <REPO_PATH> with your checkout path), then restart Claude Desktop.
Verify: ask Claude to list MCP tools — you should see submit_task, get_task, list_tasks under taskbridge.
Authentication (HTTP mode)
install.sh generates the bearer token and serves the public endpoint:
./install.sh # reuse or generate .bridge-token, tunnel + serve
./install.sh --rotate # regenerate the bearer token firstToken is loaded from .bridge-token file or BRIDGE_AUTH_TOKEN env var.
install.sh and start_public.sh (the background / Cloudflare-tunnel variant) both pick it up automatically.
Public URL access requires:
Authorization: Bearer <your-token>/healthis open (monitoring)/dashboard prompts for token (stored in sessionStorage)/api/*and/mcprequire Bearer tokenstdio transport (Claude Desktop) is local-only, no token needed
To rotate token: ./install.sh --rotate
Run one worker pass manually:
./.venv/bin/python worker.pyOr schedule it every 60s with launchd (macOS):
./launchd/install_worker_agent.sh # install/update the user LaunchAgent, then startThat installs ~/Library/LaunchAgents/com.taskbridge.worker.plist (a user agent —
no sudo) from launchd/com.taskbridge.worker.plist.template, with paths for this
checkout. See launchd/README.md for label verification,
update/uninstall, and the restart_worker contract.
Usage
From Claude:
submit_task(title="Add retry tests", instructions="Steps..., paths..., acceptance criteria...", working_dir="/path/to/target-repo")→ returnsidwait_for(task_ids=["<id>"], timeout_sec=120)— blocks instead of pollingget_task_result(task_id="<id>")→result,structured,usage,diff
Batch + follow-ups:
submit_batch(tasks=[...])— up to 20 tasks with per-itemmodel,depends_on,priority,timeout_sec,max_retries/retry_model,tags,idempotency_key,permission,worktree_*get_task_logs(task_id="...", since_offset=0)— tail live outputanswer_task(task_id="...", answer="...")— answer awaitingtask's questionqueue_status()/worker_heartbeat()— queue counts + daemon livenessrestart_worker(force=false, timeout_sec=90)— recover the worker through launchd when it is offline/stale (orforce=trueto restart a healthy one deliberately); returns a verified result withprevious_status,restart_method, heartbeat before/after andnext_stepson failurelist_models(search="")/set_default_model(model="opencode/deepseek-v4.1-flash-free")— change the worker-wide default model (validated againstopencode models, saved toworker_config.json). The dashboard's Model button is the same picker: it callsGET /api/models+POST /api/modelbehind the bearer token.
From OpenCode (manual worker loop):
./.venv/bin/python tasks_cli.py pending
./.venv/bin/python tasks_cli.py show <id>
./.venv/bin/python tasks_cli.py claim <id>
# ... do the work (edits, shell, tests) ...
./.venv/bin/python tasks_cli.py complete <id> --result "..."
./.venv/bin/python tasks_cli.py fail <id> --error "..."Reverse (OpenCode → Claude) via claude_control_mcp.py (registered as an OpenCode MCP server): claude_ask for one-shot answers, claude_submit + claude_poll / claude_result for background jobs.
How it works
flowchart LR
Claude -- MCP stdio/HTTP --> Bridge[opencode_bridge_mcp.py]
Bridge -- read/write --> DB[(tasks.db - SQLite)]
CLI[tasks_cli.py] -- claim/complete --> DB
Worker[worker.py - launchd 60s] -- polls DB, runs opencode run --> Target[working_dir / worktree]
Worker -- streams logs --> Logs[task_logs/ID.log]
Worker -- result/structured/usage --> DB
Claude -- get_task_result / wait_for --> Bridge
Bridge -- restart_worker --> Supervisor[worker_supervisor.py] -- launchctl --> Worker
OpenCode -- claude_ask/submit --> Reverse[claude_control_mcp.py]
Reverse --> ClaudeCLI[claude -p / Anthropic API]Key components:
File | Role |
| MCP server (Claude side), stdio + |
| Shared SQLite logic: submit/claim/complete, retries, deps, notes, heartbeat |
| CLI for manual runs: |
| Headless executor: |
| Independent restart engine behind |
| Reverse MCP server (OpenCode → Claude) |
| One command: deps + bearer token + tunnel choice (Cloudflare quick tunnel or ngrok) + allowlist + serve |
| Background variant: tunnel + allowlisted MCP server (ngrok fixed domain or Cloudflare) |
| Worker LaunchAgent template + install/uninstall script + supervision docs |
| Claude Desktop wiring template ( |
Configuration
Variable | Used by | Purpose |
| bridge, CLI | SQLite path (default: |
| worker | Default model fallback, e.g. |
| worker |
|
| worker |
|
| bridge (HTTP) | Comma-separated tunnel hosts (required behind ngrok/cloudflared, else 421) |
| bridge (HTTP) | Bearer token for MCP/API access (defaults to |
| OAuth consent | Secret for the /approve "this was me" page (defaults to the bridge token) |
| OAuth | Access-token lifetime (default 86400 = 24h) |
| OAuth | Refresh-token lifetime (default 2592000 = 30d) |
| bridge (HTTP) | Force the public origin in OAuth documents when behind unusual proxies |
| reverse bridge | Direct API backend; else |
| reverse bridge | Model override / shadow HOME for CLI backend |
|
| LaunchAgent label (default |
|
| Minimum gap between restart attempts (default 90) |
|
| Default verified-recovery timeout (default 90) |
|
| Max wait for a running pass before |
start_public.sh: uses .ngrok-domain if present (fixed domain), else Cloudflare quick tunnel. It re-pairs MCP_ALLOWED_HOSTS automatically — rerun it whenever the URL changes. If .bridge-token exists, BRIDGE_AUTH_TOKEN is loaded automatically. install.sh is the interactive equivalent — it asks which tunnel to use (Cloudflare quick tunnel by default, ngrok optional), installs cloudflared/ngrok as needed, and serves MCP in the foreground. Set TASKBRIDGE_TUNNEL=cloudflare|ngrok to answer that prompt non-interactively.
Project structure
.
├── opencode_bridge_mcp.py # MCP server (Claude → queue)
├── task_store.py # SQLite queue logic
├── tasks_cli.py # Manual worker CLI (+ restart-worker)
├── worker.py # Auto-worker (launchd, 60s)
├── worker_supervisor.py # restart_worker engine: launchd control, cooldown, verify
├── claude_control_mcp.py # Reverse bridge (OpenCode → Claude)
├── install.sh # One-command installer: deps, token, tunnel, serve
├── start_public.sh # Public tunnel + server launcher (background)
├── launchd/ # Worker LaunchAgent template + install/uninstall docs
├── tests/ # pytest suite (supervisor, MCP tool surface, launchd E2E)
├── claude_desktop_config.snippet.json
├── claude-clean.settings.json
├── requirements.txt # mcp>=1.0,<2
├── requirements-dev.txt # pytest (tests only)
└── .gitignore # excludes .venv/, tasks.db, logs, task_logs/, claude_jobs/, worktrees/Runtime state (never committed): tasks.db, task_logs/, claude_jobs/, worktrees/, *.log, heartbeat.json, .worker.lock, .restart_state.json.
Troubleshooting / FAQ
421 behind tunnel:
MCP_ALLOWED_HOSTSmust exactly match the tunnel host. Run./install.shor./start_public.sh— they set this automatically.Tunnel URL changed (Cloudflare): quick-tunnel hostnames rotate every restart. Rerun
./install.sh(or./start_public.sh), or reserve an ngrok fixed domain via.ngrok-domain.Worker looks stuck: check
./.venv/bin/python tasks_cli.py heartbeatandtask_logs/<id>.log. Stalerunningtasks are reaped after 15 min (per-tasktimeout_sec+ slack honored).Worker offline / heartbeat stale: call MCP
restart_worker()— it restarts through launchd from the MCP server process (which does not depend on the worker), waits for a verified fresh heartbeat, and explains itself (outcome,next_steps). Same engine from a terminal:./.venv/bin/python tasks_cli.py restart-worker [--force]. Full runbook: launchd/README.md.restart_workersaysservice_missing: no LaunchAgent is installed for this checkout — run./launchd/install_worker_agent.sh, then retry.restart_workersaysrestart_timeout: launchctl started the job but no heartbeat appeared.launchctl print "gui/$(id -u)/com.taskbridge.worker" | grep -E 'state|last exit'—last exit code 78(EX_CONFIG) means launchd could not run the configured program (wrong paths in the plist, missing.venv); otherwise read the tail ofworker.launchd.log/worker.log.Double-run after kill: every attempt spawns
opencode run [--standalone] --title br-<id>-a<n>(per-attempt argv marker, ps-visible). Optional flags such as--standaloneare probed from the binary's ownrun --help(-hfallback) once per install and only passed when advertised; if a build still rejects the command (help dump, nonzero exit), the worker retries once without them. Unreadable or older/newer variants just run without the extras — no version tables; the reaper then relies on the pid-tree/argv walk alone. The reaper kills the orphaned CLI child plus its ppid tree (loop-until-empty, hard-capped so a matching bug can't mass-kill); a per-pass sweep catches tagged strays of non-running tasks. Reaper deadline is per-task (max(900s, timeout_sec + 120s)). macOS gives no session/env signal (ps sess=is 0 everywhere,ps -Eshows no env), so ppid walk + argv marker are the whole toolkit — reparented-to-1/setsid grandchildren are the known residual. NOTE: never pass--sessionto the CLI: it makes free-tier models refuse to run.No banners: macOS only. Set
WORKER_NOTIFY=0to silence intentionally; full output is always intasks.db+worker.log.Reverse bridge fails fast (~1s): run
claude loginonce or setANTHROPIC_API_KEY.opencodenot found: put it onPATHor setOPENCODE_BIN=/path/to/opencode.
Limitations
Token counts aren't exposed by the
opencodeCLI —usagereports model, latency, attempts, and sandbox status, not tokens.no-network/no-shellare prompt-level restrictions, not OS-enforced sandboxes (documented inworker.py).Desktop banners and launchd scheduling are macOS-specific; Linux needs its own scheduler (systemd/cron) +
worker.pystill runs manually.restart_workeris likewise macOS/launchd-only — on a host without launchd it returnssupervisor_unavailablewith manual instructions instead of pretending.Long tasks (up to 1800s
timeout_sec) are supported, but the 60s launchd cadence means pickup latency averages ~30s.restart_workercannot recover from a broken plist either: if the configured program can't run, the result isrestart_timeoutwith the launchd diagnostics you need. Queued tasks are untouched either way.
Verified
task_storesubmit → claim → complete round-trip: OKMCP
submit_task/get_task/list_tasksviacall_tool: OKCLI
pending/show/claim/complete: OKsubmit_task→ auto-picked-up in ~60s →get_task_resultwith zero manual steps: OKrestart_workerregistered + schema (forceboolean default false,timeout_secinteger) via MCP tool discovery: OKrestart_workerunit suite (skip/offline/stale/force, missing service, permission denied, launchctl failure, timeout, concurrency, cooldown, queue preservation, label discovery) with a faked launchctl: OKrestart_workeragainst real launchd with a disposable probe LaunchAgent: offline → fresh heartbeat, stale →kickstart -kreplaces the process, healthy +force=false→ skipped, pending task byte-identical after recovery: OK (TASKBRIDGE_RUN_LAUNCHD_E2E=1)
License
Apache-2.0 — see LICENSE. Fork it, self-host it, build on it.
Related MCP Connectors
Shared memory and mail for your AI agents. Verified with Claude Code; other MCP clients in testing.
A task list shared by you and your coding agents (Claude Code, Cursor, Codex and any MCP client). Agents plan each task into steps, report live progress to a board, and block on questions you answer from your phone.
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
Shared memory MCP server for Claude Code, Codex, Gemini CLI and Cursor. Cited, editable.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables MCP clients like Claude Code to delegate coding tasks to a local model through a configurable coding-agent CLI backed by any OpenAI-compatible endpoint, with tools for health checks, task execution, and context-aware file modifications.-
- AlicenseNot gradedqualityAmaintenanceConnects MCP clients such as Claude, Cursor, and VS Code to OpenCode's headless API so users can delegate coding work — asking questions, implementing features, monitoring background jobs, answering prompts and permission requests, and reviewing changes across multiple projects. Supports synchronous runs, asynchronous jobs with polling and recovery, multi-project directory scoping, and configurable server authentication and tool profiles.25,826 npmMIT
- AlicenseNot gradedqualityAmaintenanceConnects MCP clients such as Claude, Cursor, and VS Code to OpenCode's headless API so users can delegate coding tasks, ask questions, run work in the background, and resolve permission or input requests across projects. It also supports monitoring job progress, reviewing resulting changes, and inspecting conversation history.25,826 npmMIT
- AlicenseNot gradedqualityAmaintenanceConnects MCP clients such as Claude, Cursor, and VS Code to an OpenCode server's headless API so coding work can be delegated, answered, and monitored. It supports asking questions, launching foreground or background jobs, supplying required input and permissions, and reviewing resulting changes across projects.25,826 npmMIT