Skip to main content
Glama
JosiahSiegel

claude-bridge

by JosiahSiegel

claude-bridge

tests python license

An MCP server that lets host-side Claude Cowork dispatch work into Claude Code running inside your devcontainer, over stdio. Cowork launches the bridge with docker exec -i; the bridge shells to claude -p and returns a structured result.

Install

Inside the devcontainer:

# from PyPI (once published — see CHANGELOG.md):
pip install claude-bridge-mcp

# or pin to the latest main branch:
pip install "git+https://github.com/JosiahSiegel/claude-bridge.git@main"

# or for development (editable + dev deps):
git clone https://github.com/JosiahSiegel/claude-bridge.git
cd claude-bridge
pip install -e ".[dev]"

Note on names: the PyPI distribution is claude-bridge-mcp (the bare claude-bridge name was already taken by an unrelated HTTP gateway). The Python import (import claude_bridge), the CLI (claude-bridge), and the MCP server name (claude-bridge) are unchanged — only the pip install argument differs.

A reference Dockerfile and devcontainer.json for downstream projects live in examples/devcontainer/.

Related MCP server: Clanker

Quickstart

Three things have to be true: the bridge installed in the container, its absolute path in the Claude Desktop config on the host, and Claude Desktop fully restarted.

1. Install in the container.

pip install claude-bridge-mcp
which claude-bridge      # copy this path — you'll paste it in step 2

2. Register with Claude Desktop on the host.

Open via Settings → Developer → Edit Config, or edit the file directly:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

  • Linux: ~/.config/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "devcontainer-claude": {
      "command": "docker",
      "args": [
        "exec", "-i",
        "-e", "CLAUDE_BRIDGE_CWD=/path/to/clean/dir",
        "<container-name-from-docker-ps>",
        "/absolute/path/from/which/claude-bridge"
      ]
    }
  }
}

The CLAUDE_BRIDGE_CWD line is important — see Recommended pattern below for why.

3. Fully quit Claude Desktop (⌘Q / system-tray exit, not close window) and relaunch. In a Cowork session, ask Claude to call list_channels. {"channels": {}} means it works.

4. Dispatch.

For quick prompts (under ~60s round trip):

dispatch(prompt="say ok", channel="smoke")

For real work (anything that might exceed the MCP transport's per-call ceiling — persona runs, refactors, work in a busy project):

job = dispatch_async(
  prompt="audit the auth middleware",
  channel="auth-audit",
  cwd="/workspace",          # the project you want claude to work in
  timeout_seconds=900
)
# Then poll wait_dispatch(job["job_id"], max_wait_seconds=50) in a loop.

For watch-this-condition-over-hours work, use schedule_dispatch — each tick is its own short job, the bridge owns the cron loop, and the prompt can self-cancel by emitting [BRIDGE_STOP_SCHEDULE].

schedule_dispatch(
  prompt="gh pr list --state open. If all merged, end with [BRIDGE_STOP_SCHEDULE]. Otherwise summarize.",
  channel="pr-watcher",
  interval_seconds=300,
  until_seconds=14400,        # stop trying after 4 hours
  permission_mode="bypassPermissions"
)

See Tools → Polling pattern for the long-job loop and Recurring for schedules.

For agents using the bridge programmatically: call bridge_help() first. It returns a structured guide to every tool, the four canonical workflows, and the gotchas. Designed to be the single discoverability entry point so agents don't have to skim the README.

The bridge runs claude -p in its own cwd by default. If that cwd is a busy project workspace (with .claude/settings.local.json enabling several MCP servers and plugins, plus a large CLAUDE.md), every dispatch pays a 30s+ cold-start cost — sometimes long enough to time out.

The fix is to anchor the bridge in a clean directory and retarget individual dispatches at the project they actually want:

  • Set CLAUDE_BRIDGE_CWD in the MCP config to a clean dir (e.g. the bridge's own checkout, or any directory without .claude/).

  • Pass cwd="/workspace" (or whatever) per dispatch when you need project context.

  • Bump timeout_seconds for those calls — first invocation in a project has to load all its MCP servers.

list_channels and a bare dispatch(prompt="say ok") then stay snappy; heavy project work gets routed to the right cwd on demand.

Running unattended (max permissions)

For autonomous operation — letting Cowork drive the bridge without permission prompts blocking dispatches — set the bridge's default permission mode to bypassPermissions in the MCP config:

{
  "mcpServers": {
    "devcontainer-claude": {
      "command": "docker",
      "args": [
        "exec", "-i",
        "-e", "CLAUDE_BRIDGE_CWD=/home/vscode/claude-bridge",
        "-e", "CLAUDE_BRIDGE_DEFAULT_PERMISSION_MODE=bypassPermissions",
        "<container>",
        "/absolute/path/to/claude-bridge"
      ]
    }
  }
}

Now every dispatch(...) without an explicit permission_mode runs as bypassPermissions — no Bash gates, no workspace sandbox. Cowork doesn't need to know the policy; it's enforced at the bridge. Individual dispatches can still opt into stricter behavior by passing permission_mode="acceptEdits" (or "plan") per call.

Only do this when the container is the trust boundary — the container's network and filesystem isolation is what's keeping bypassed permissions safe. Treat the MCP config as privileged: anyone who can edit it can run arbitrary commands in your container as the bridge user. See the official devcontainer guide for the recommended firewall config.

Tools

For agents reading this: call bridge_help() once at the start of a session. It returns a structured map of every tool, when to use each, the four canonical workflows, and the gotchas that have actually bitten users. Faster than skimming this section.

The tool surface comes in six groups:

  • Discovery: bridge_help.

  • Synchronous: dispatch — short prompts under the MCP ceiling.

  • Asynchronous: dispatch_async + wait_dispatch / get_dispatch / cancel_dispatch / list_jobs — anything longer. Optional webhook on terminal state.

  • Recurring: schedule_dispatch + list_schedules / get_schedule / cancel_schedule — fire a prompt every N seconds on a channel until a deadline or the prompt emits the stop sentinel. Supports chaining (after_schedule_id) and webhooks (notify_url).

  • Event log: list_events — cursor-paged stream of every notable state transition. The "what happened while I was offline?" answer.

  • Completion polling: list_completions, wait_any_completion — answer "anything new since I last looked?" for finished jobs. Use list_events instead when you need schedule context.

  • Channel admin: list_channels, reset_channel.

Synchronous

dispatch(prompt, channel="default", timeout_seconds=300, permission_mode=None, cwd=None)

Run a prompt against claude -p and return the full result. Channels pin to one Claude Code session each — first call starts fresh, subsequent calls on the same channel --resume it. Distinct channels run in parallel; same-channel calls serialize.

  • permission_mode: default, acceptEdits, plan, or bypassPermissions. Defaults to acceptEdits (override via CLAUDE_BRIDGE_DEFAULT_PERMISSION_MODE).

  • cwd: per-call override of the working directory. Use it to retarget a single dispatch at a different repo without standing up a second bridge.

Returns {ok, channel, duration_ms, result, session_id, raw} on success, or {ok: false, channel, duration_ms, error, exit_code} on failure. Both shapes include stderr when claude wrote to it (e.g. project-MCP-server warnings). Failures never raise — the MCP layer always sees a result.

Asynchronous (long-running)

dispatch_async(prompt, channel="default", timeout_seconds=300, permission_mode=None, cwd=None, notify_url=None, notify_on=None, notify_headers=None)

Kick off a dispatch in the background; return a job_id immediately. Channel locking still applies — concurrent dispatch_async on the same channel queue up. Empty prompts surface as {"ok": false, "error": ...} synchronously (no orphan job).

Optional webhook on terminal state: pass notify_url to have the bridge POST a JSON payload when the job ends. notify_on selects which terminal states notify (values: done, error, cancelled, abandoned; default ["done"]). notify_headers adds auth. Delivery is fire-and-log; failures are recorded as webhook_failed events. Payload shape: {event, job_id, channel, status, started_at, finished_at, ok, result_preview, error} (result_preview is truncated to 4KB).

Returns {ok: true, job_id, channel}.

get_dispatch(job_id)

Non-blocking status read. status is one of:

  • running — work in flight; also includes elapsed_ms for progress.

  • done — full sync-style result keys (ok, result, session_id, duration_ms, raw, stderr, …) plus job_id and started_at.

  • cancelledcancel_dispatch was called by the user; subprocess was SIGTERMed.

  • abandoned — the asyncio task running the dispatch was cancelled by the runtime (transport timeout, FastMCP shutdown, loop teardown). The subprocess kept running; a watcher will finalize it when it exits, transitioning the status to done (or error). Poll again.

  • error — programmer error in the dispatcher itself, or output that couldn't be parsed; should be rare.

  • orphaned — bridge restarted but the subprocess and output files are gone; result was lost. The channel pinning is auto-reset so the next dispatch starts fresh.

Unknown job_id returns {ok: false, error: ...}. Works for both live jobs and ones loaded from disk after a restart.

wait_dispatch(job_id, max_wait_seconds=50)

Block up to max_wait_seconds for a job, then return whatever get_dispatch would return. Default 50s is intentionally below the typical MCP transport ceiling — Cowork polls this in a loop until status != "running". The underlying job is shielded from cancellation, so an aborted poll doesn't kill the work in flight.

cancel_dispatch(job_id)

Request cancellation. The task's CancelledError handler kills the underlying claude -p subprocess before propagating, so we don't leave orphan workers. Idempotent: {cancelled: true} if cancellation was requested, {cancelled: false, reason: "already_finished"} otherwise.

list_jobs()

Diagnostics only — returns one summary dict per tracked job (running and recently finished). Job retention is bounded by max_completed_jobs (default 1000), so this is safe to call on a long-lived bridge.

Recurring (long-running watch patterns)

schedule_dispatch(prompt, channel, interval_seconds, until=None, until_seconds=None, after_schedule_id=None, notify_url=None, notify_on=None, notify_headers=None, ...)

Fires prompt on channel every interval_seconds, until a deadline or until the prompt emits the literal stop sentinel [BRIDGE_STOP_SCHEDULE] in its result. Each tick is its own dispatch_async job — short individually, collectively long-running. The bridge owns the loop, persists schedules to disk, and resumes them after a restart without burst-firing missed ticks (only one tick fires on the first iteration after a long gap).

  • interval_seconds minimum is 10s.

  • until is ISO 8601 ("2026-04-27T20:00:00Z"); until_seconds is relative seconds-from-now. Mutually exclusive.

  • If a tick is still running when the next interval fires, the bridge skips that tick (no stacking). Schedules use the same channel for every tick, so ticks share session continuity.

  • Self-cancellation: have the prompt end with [BRIDGE_STOP_SCHEDULE] when the watched condition resolves. Example:

    prompt = "gh pr list --state open. If all merged, end with [BRIDGE_STOP_SCHEDULE]. Otherwise summarize."
    schedule_dispatch(prompt, channel="pr-watcher", interval_seconds=300, until_seconds=14400)
  • Pipelines — pass after_schedule_id to chain schedules. The new schedule starts in waiting and transitions to active when the predecessor reaches a terminal state (completed, cancelled, or error). Cycles are detected and rejected at creation:

    a = schedule_dispatch(prompt="wave A merge…",  channel="wave-a", interval_seconds=300, until_seconds=14400)
    b = schedule_dispatch(prompt="post-merge hygiene", channel="hygiene",
                           interval_seconds=600, until_seconds=3600,
                           after_schedule_id=a["schedule_id"])
  • Webhooks — pass notify_url to get a JSON POST when the schedule reaches notable transitions. notify_on selects events: tick, tick_with_sentinel, tick_error, schedule_end (default ["schedule_end"]). notify_headers adds auth. Delivery is fire-and-log; failures are logged as webhook_failed events. Payload includes event, schedule_id, channel, tick_count, status, last_tick_result (truncated to 4KB), last_job_id.

list_schedules() / get_schedule(id) / cancel_schedule(id)

Inspect or stop schedules. cancel_schedule fires the schedule_end webhook (if configured) and does not cancel the in-flight tick — use cancel_dispatch(last_job_id) for that.

Event log (turn-level "what happened while I was offline?")

list_events(since=0, limit=100, types=None, notable_only=False)

Bridge-wide structured event stream. Records every state transition: dispatch lifecycle, schedule lifecycle, webhook outcomes, recovery actions. The buffer is bounded (default 1000) and persisted to events.json so it survives restarts.

Two read modes:

  • Debug mode (notable_only=False, default): every event, including chatter like dispatch_start and schedule_tick. Right for forensic post-mortem.

  • Surfacing mode (notable_only=True): only state transitions worth surfacing to a human. Drops dispatch_start, schedule_tick, schedule_created, webhook_sent, bridge_init_subprocess_alive. Keeps every terminal transition and every failure. This is what an orchestrator wants for "what should I tell the user about?"

Cursor pattern: pass since=0 for everything, then track the largest ts you've seen. types is an explicit allow-list and composes with notable_only (intersection). Returns oldest-first.

events = list_events(since=last_seen_ts, notable_only=True)
for e in events: surface(e)
last_seen_ts = max(e["ts"] for e in events) if events else last_seen_ts

Call bridge_help() and read notable_event_types to see the curated set programmatically.

Heads-up: events are only recorded from the moment the event-log feature is running. Anything that fired on a prior bridge process (or before this feature shipped) is gone. Going forward, every state transition is captured.

Completion polling (turn-level "anything new?")

list_completions(since=0, limit=50)

Jobs whose finished_at > since, oldest first. Use since=0 for "everything that ever finished". For ongoing polling, track the largest finished_at you've seen and pass it as the next since. Cheap, non-blocking — safe at the top of every turn.

wait_any_completion(since=0, max_wait_seconds=50)

Long-poll up to max_wait_seconds for any new completion since the cursor. Same MCP-ceiling-aware default as wait_dispatch. Returns immediately if any are already available.

Channel admin

list_channels()

{"channels": {channel: session_id, ...}}. Doesn't invoke claude. Always cheap — Cowork can call this safely while a long dispatch is running.

reset_channel(channel)

Drops a channel's pinned session so the next dispatch starts fresh. {"reset": true|false, "channel": ...}. Useful when a project MCP server (playwright, neon, etc.) has wedged inside the channel's claude session and you want a clean reconnect.

Polling pattern (the canonical long-running flow)

job = dispatch_async(prompt="...", channel="...", cwd="/workspace",
                     permission_mode="bypassPermissions",
                     timeout_seconds=900)

while True:
    res = wait_dispatch(job["job_id"], max_wait_seconds=50)
    if res["status"] != "running":
        break
    # optional: surface elapsed_ms, log, or just keep polling

wait_dispatch returning at the 50s mark with status="running" is the common case for real work; the loop just goes around again. When the job finishes, wait_dispatch returns immediately with the full result.

Durability guarantees (long workflows)

The bridge is designed so Cowork can trust it across hours-long workflows that span Claude Desktop restarts, container/bridge process restarts, MCP transport hiccups, and Cowork's own per-call timeouts. The contract:

What persists

  • Channel→session pinning in sessions.json, atomic temp+rename writes.

  • Every job's state in jobs.json (same directory) on every transition: spawn (with PID and output-dir), completion, cancellation, abandonment. Concurrent writes are serialized through an asyncio.Lock so parallel dispatches can't lose each other's updates.

  • Subprocess output in <state>.parent/job-output/<job_id>/{stdout,stderr}, written by claude -p directly (not via pipes the bridge has to drain). Output survives bridge crashes and asyncio task cancellations.

Scheduler + watcher liveness is decoupled from FastMCP

The scheduler task and any orphan-reaping watchers are bootstrapped by ensure_watchers_running(), which runs at every async tool entry and on a wall-clock cadence from a daemon supervisor thread. The thread lives outside FastMCP's asyncio task lifecycle, so even if FastMCP cancels every in-flight task on a transport disconnect, the supervisor revives the scheduler within one poll interval (~5s by default).

Without this, an abandoned tick during a transport blip could leave a schedule "active but not firing" until the next external MCP call woke things up — see issue notes in CLAUDE.md, invariant 25.

Subprocess lifetime is decoupled from the bridge

For dispatch_async, the subprocess is spawned with start_new_session=True (its own session, immune to SIGHUP) and stdin redirected to /dev/null. Three follow-on guarantees:

  • MCP transport timeouts can't kill the work. If FastMCP cancels the asyncio task running a dispatch (because Cowork's MCP request hit a transport timeout, or the connection blipped), the subprocess keeps running and writing output. The job is marked abandoned rather than cancelled, and a watcher coroutine reaps it when it finishes — finalizing the result from the on-disk output files.

  • Bridge crashes don't kill the work. If the bridge process dies (OOM, supervisor restart, manual kill), the subprocess is parented to PID 1 and keeps running. On bridge restart:

    • If the PID is still alive, the job stays running and a watcher is re-spawned.

    • If the PID has exited and the output files are intact, the job is finalized — get_dispatch(job_id) returns the recovered result.

    • If neither, the job becomes orphaned and its channel is unpinned so a new dispatch can't race a stray subprocess on the same session.

  • cancel_dispatch is the only way to actually stop the work. It sets a cancel_requested flag, SIGTERMs the subprocess by PID (then SIGKILLs if it's still alive on the watcher's next tick), and cancels the asyncio task. Status becomes cancelled. Anything else that ends a task (transport timeout, runtime cancellation, asyncio loop teardown) becomes abandoned, not cancelled.

wait_dispatch is shielded

If Cowork's MCP call to wait_dispatch is cancelled by the transport (e.g. exceeded the per-call ceiling), the underlying job survives — the inner task is wrapped in asyncio.shield. Cowork retries with the same job_id and gets the next slice of state.

Failures never raise out of the MCP layer

Subprocess errors, timeouts, missing binary, malformed JSON, runtime cancellation — all become structured ok: false results. The MCP transport always sees a clean tool response.

What this does not cover

  • Container death. If the container itself dies, all subprocesses die with it. Nothing for the bridge to recover.

  • Output-file corruption (e.g. disk full). If claude -p's stdout is truncated, the recovery path treats it as a parse failure and marks the job as error.

  • Subprocess exit code on recovery. When the bridge wasn't the parent at exit, we can't read the return code. Recovery treats well-formed JSON as success and unparseable output as failure — the exit code is informative but not load-bearing.

Configuration (env vars in the container)

Variable

Default

Purpose

CLAUDE_BRIDGE_STATE

~/.claude-bridge/sessions.json

Channel→session map (atomic writes). The same directory holds jobs.json, schedules.json, job-output/<job_id>/{stdout,stderr} and (if enabled) the JSONL log

CLAUDE_BRIDGE_CWD

bridge process cwd

Default working dir for claude -p. Per-call cwd= overrides

CLAUDE_BRIDGE_CLAUDE_BIN

claude

Override claude binary location

CLAUDE_BRIDGE_DEFAULT_PERMISSION_MODE

acceptEdits

Default if caller omits permission_mode

CLAUDE_BRIDGE_LOG

unset

If set to a path, writes one JSONL line per state transition (dispatch_start, dispatch_end, dispatch_cancelled, dispatch_error, bridge_init_orphans). Helps when something looks wrong at the bridge layer

CLAUDE_BRIDGE_PERSIST_PROMPTS

unset

Set to 1 to include the prompt text in jobs.json. Off by default; opt in for post-mortem debugging

CLAUDE_BRIDGE_LOG_PROMPTS

unset

Set to 1 to include prompts in the JSONL log too. Off by default

Set these via your MCP config (docker exec -e KEY=value ...) or your devcontainer's containerEnv. They are not negotiated over MCP.

Authentication

The bridge has no auth opinion — it just shells to claude -p and inherits the container's environment. Anything that makes claude -p "hi" --output-format json succeed in your container shell will work here, including:

  • ANTHROPIC_API_KEY

  • CLAUDE_CODE_OAUTH_TOKEN (from claude setup-token)

  • On-disk ~/.claude/.credentials.json (from claude /login)

Test (validated by test_auth_env_passes_through_to_subprocess): the bridge does not pass env= to the subprocess, so the container's env reaches claude unchanged.

Troubleshooting

Symptom

Cause

Fix

OCI runtime exec failed: ... "claude-bridge": executable file not found in $PATH

Bridge installed in a venv or ~/.local/bin not on docker exec's default PATH

Use the absolute path from which claude-bridge in the MCP config

dispatch hangs or times out, but list_channels returns instantly

The bridge's cwd has a heavy .claude/. claude -p is loading project MCP servers/plugins and stalling

Anchor the bridge in a clean dir with CLAUDE_BRIDGE_CWD, pass cwd=... and bump timeout_seconds per call. See Recommended pattern

Cowork reports "lost the response handle" or hits a ~60s MCP ceiling on dispatch

The MCP transport caps individual tool calls; long claude runs exceed it

Use dispatch_async + wait_dispatch, not dispatch. Each wait_dispatch returns within 50s by design, and the underlying job survives across calls. See Tools → Polling pattern

A project MCP server (playwright, neon, cloudflare, …) disconnects mid-session and the channel keeps failing

The wedged MCP server is bound to the pinned claude -p session for that channel

reset_channel("<name>"), then dispatch again — the next claude -p reconnects all its project MCP servers from scratch

Cowork doesn't see the server after editing the config

Window-close ≠ quit

Fully quit Claude Desktop and relaunch

Edits to the config produce no servers and no error

JSON syntax error

python -m json.tool < claude_desktop_config.json to validate. Claude Desktop silently ignores malformed files

claude_desktop_config.json Settings → Connectors UI doesn't list the bridge

That UI is for remote HTTP MCP servers only. Local stdio servers go in the JSON file

(Working as intended — you're not missing anything)

D:/Program Files/Git/... in the path when running docker exec from Windows Git Bash

MSYS rewrote your /... arg

MSYS_NO_PATHCONV=1 docker exec ... or double the leading slash (//path). Doesn't affect Claude Desktop's invocation, only your interactive testing

Verifying the bridge directly (optional)

For interactive smoke tests, the only useful CLI check is an MCP initialize round-trip — there's no --help:

echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
  | docker exec -i <container> /absolute/path/to/claude-bridge
# Windows Git Bash: prefix the path with //

Expect a one-line JSON response with "serverInfo":{"name":"claude-bridge",...}. Don't bother running docker exec ... claude -p "hi" as a sanity check — that's a different code path than the bridge uses, and it has Windows/Git Bash quirks of its own.

How it works

┌──────────────────────┐       ┌──────────────────────────────┐
│ HOST                 │ stdio │ DEVCONTAINER                 │
│ Claude Cowork ───────┼──────▶│ claude-bridge ─▶ claude -p   │
│ (MCP client)         │       │ (MCP server)    --resume sid │
└──────────────────────┘       └──────────────────────────────┘
   docker exec -i …
  • Transport: MCP stdio over docker exec -i. No network sockets, no port forwarding, no shared bind mounts.

  • Sessions: each channel pins to one Claude Code session id. First call uses --session-id <new-uuid>; later calls use --resume. We never use --continue — that's the race the original file-queue prototype had to serialize around.

  • Concurrency: distinct channels run in parallel; same-channel calls serialize behind an asyncio.Lock so message ordering is preserved.

  • State: channel→session map persisted atomically (temp + os.replace) to ~/.claude-bridge/sessions.json.

  • Failure surface: subprocess errors, timeouts, missing binary, bad JSON — all return structured ok: false results, never raise. The MCP layer turns raises into opaque ToolErrors, so this matters.

Security

  • The container is the trust boundary. Anyone who can docker exec into it can drive claude with whatever auth lives there. Same goes for whoever can edit claude_desktop_config.json on the host.

  • acceptEdits (the default) auto-accepts file edits but still prompts for Bash. Fine when the prompt doesn't need shell.

  • bypassPermissions removes all gates including the workspace sandbox — only safe when the container's network/filesystem isolation is the thing keeping you safe. See Running unattended.

Development

See CONTRIBUTING.md for the full guide. TL;DR:

python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest             # 83 tests, ~20s

Tests use a fake claude binary (a Python stub in tests/conftest.py) so they verify real subprocess behavior — argv formation, JSON parsing, exit codes, timeouts, env passthrough, per-call cwd, cancellation handling, schedule firing, and webhook delivery against a loopback HTTP server — all without a real Anthropic API key.

CI (GitHub Actions) runs the test matrix on Python 3.11 / 3.12 / 3.13 plus a sanity wheel build.

Releases

See CHANGELOG.md. Versioning follows Semantic Versioning once 0.1.0 is published. The single source of truth for the version is src/claude_bridge/__init__.py; pyproject.toml reads it via hatch's dynamic version mechanism.

Reporting issues / security

Available Tools

16 tools
bridge_helpA

Return a structured guide to every tool, recommended workflow, and common gotcha. Call this once when you start using the bridge, or whenever you're unsure which tool fits a situation.

The shape:

  • tools: name → {group, summary, when_to_use, when_not_to_use}

  • workflows: list of named multi-step patterns with example invocations.

  • concepts: short definitions of channel, session pinning, permission_mode, cwd, the stop sentinel, and the durability model.

  • gotchas: things that have actually bitten users.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so description must cover behavior. It describes the output structure in detail, implying a read-only information retrieval. However, it does not explicitly state that it has no side effects or permissions needed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is moderately concise with clear front-loading. It uses two paragraphs effectively but could be slightly tighter.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema, but description thoroughly explains the return shape (tools, workflows, concepts, gotchas) with field names. This is complete for a help tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has zero parameters, so no param info needed. Baseline is 4, and description does not mention any parameters, which is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns a structured guide covering tools, workflows, concepts, and gotchas. It distinguishes itself from sibling tools by being a meta-guide rather than an operational tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly recommends calling once when starting or when unsure which tool fits. Provides clear context for use without needing to infer.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

cancel_dispatchA

User-cancel a running job. SIGTERMs the subprocess by PID, then cancels the asyncio task. Status becomes cancelled (vs the abandoned state for runtime/transport-induced cancellations).

Returns {cancelled: true} if the cancel was actionable, or {cancelled: false, reason} for jobs that have already finished or have no live task.

ParametersJSON Schema
NameRequiredDescriptionDefault
job_idYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description thoroughly explains behaviors: it sends SIGTERM, cancels the asyncio task, and details return values for actionable vs. non-actionable cases. The state distinction between 'cancelled' and 'abandoned' adds valuable context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, with two sentences that front-load the purpose and then provide essential details. No unnecessary words or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple one-parameter tool and no output schema, the description covers all critical aspects: action, process, state distinction, and return values. It is fully adequate for agent invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the description does not describe the job_id parameter. It only implies its use via context. The description fails to add meaning beyond the schema's type declaration, leaving a gap for the agent.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it cancels a running job, specifies the mechanism (SIGTERM, asyncio task cancellation), and distinguishes the resulting state as 'cancelled' vs 'abandoned', which differentiates it from sibling tools like cancel_schedule.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly implies when to use: to cancel a user-initiated job. It distinguishes cancellation states but does not explicitly mention when not to use (e.g., for scheduled jobs) or name alternatives like cancel_schedule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

cancel_scheduleA

Stop a schedule from firing further ticks. In-flight ticks are not cancelled — use cancel_dispatch(last_job_id) for that.

ParametersJSON Schema
NameRequiredDescriptionDefault
schedule_idYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, but the description discloses that the tool cancels future ticks but not in-flight ones, and references the related tool. It is clear about its scope but lacks details on permissions or error handling.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no wasted words, front-loaded with the primary action, and includes important distinction and alternative in the second sentence.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with one parameter and no output schema, the description adequately covers what the tool does and its limitations. It could be improved by hinting at the source of schedule_id (e.g., from list_schedules) but is still sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description does not add any meaning to the 'schedule_id' parameter beyond its type, leaving the agent to infer that it is the identifier of the schedule to cancel.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Stop a schedule from firing further ticks' with a specific verb and resource, and it distinguishes from the sibling 'cancel_dispatch' by noting that in-flight ticks are not cancelled and directing to use that alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to use (stop future ticks) and when not to use (for in-flight ticks), and provides an alternative tool 'cancel_dispatch' for the latter case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

dispatchA

Run a prompt against Claude Code in this devcontainer; return the result.

Use for short prompts (under ~60s round trip). For anything longer use dispatch_async so the MCP transport's per-call ceiling doesn't abort your tool call before claude finishes.

Args: prompt: The natural-language task for Claude Code. channel: Logical conversation thread. Same channel = shared session (subsequent calls --resume it). Default "default" — pick a stable channel name per logical thread (e.g. "feature-auth"). timeout_seconds: Wall-clock seconds before the call is aborted. Default 300. permission_mode: default | acceptEdits | plan | bypassPermissions. Defaults to CLAUDE_BRIDGE_DEFAULT_PERMISSION_MODE. cwd: Per-call working directory override. Use it to retarget at a specific project (e.g. /workspace) while keeping the bridge anchored in a clean directory for fast cold starts.

Returns: Success: {ok: true, channel, duration_ms, result, session_id, raw, stderr}. Failure: {ok: false, channel, duration_ms, error, exit_code?}. Failures never raise — the MCP layer always sees a result.

ParametersJSON Schema
NameRequiredDescriptionDefault
promptYes
channelNodefault
timeout_secondsNo
permission_modeNo
cwdNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so the description carries full burden. It explains that failures never raise (always return a result) and describes timeout behavior. However, it could be more explicit about side effects like state changes in the devcontainer, but overall it gives good insight into behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a front-loaded purpose, a usage guideline line, and a clear Args/Returns layout. It is concise given the number of parameters and the need for behavioral explanation, though slightly longer than ideal.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (5 params, no output schema, sibling tools), the description is complete. It explains the return value format (success/failure fields), when to use this vs dispatch_async, and covers parameter details thoroughly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description thoroughly explains each parameter in the Args section, adding meaning beyond the bare schema. It covers prompt, channel, timeout_seconds, permission_mode, and cwd with usage details.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool runs a prompt against Claude Code and returns the result, specifying the verb, resource, and scope. It distinguishes itself from the sibling dispatch_async by mentioning short vs long prompts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use this tool (for short prompts under ~60s) and when to use the alternative dispatch_async (for longer prompts) to avoid transport ceiling issues. Also explains the channel parameter for session continuity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

dispatch_asyncA

Spawn a dispatch in the background, return a job_id immediately.

Use this for anything that may exceed the MCP transport's per-call ceiling (~60s in Cowork) — persona runs, refactors, anything in a busy project. The subprocess is spawned with its own session (start_new_session=True) and its stdout/stderr go to files on disk, so the work survives transport timeouts and bridge restarts.

Then poll with wait_dispatch(job_id) in a loop until status != 'running'.

Args mirror dispatch. Empty prompts return a structured error synchronously without creating a job.

Optional webhook notification (notify_url): The bridge POSTs a JSON payload to notify_url when the job reaches a terminal state matching notify_on. notify_on values: done, error, cancelled, abandoned. Default ["done"]. Delivery is fire-and-log — failures are recorded in the event log as webhook_failed but don't affect the job. Add auth via notify_headers.

Payload shape (truncated to 4KB on result_preview)::

{"event": "done", "job_id": "...", "channel": "...",
 "status": "done", "ok": true, "started_at": ...,
 "finished_at": ..., "result_preview": "...", "error": null}

Returns: Success: {ok: true, job_id, channel}. Validation failure: {ok: false, error}.

ParametersJSON Schema
NameRequiredDescriptionDefault
promptYes
channelNodefault
timeout_secondsNo
permission_modeNo
cwdNo
notify_urlNo
notify_onNo
notify_headersNo

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully bears the burden of behavioral disclosure. It reveals that the subprocess runs with its own session, stdout/stderr go to disk, the job survives transport timeouts and bridge restarts, webhook delivery is fire-and-log, and empty prompts return a synchronous error without creating a job. This is comprehensive for a tool with no annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with paragraphs and a bullet-like section for webhook details. It front-loads the core purpose and proceeds logically. While slightly long, every sentence adds essential information, and the structure aids readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 8 parameters, no output schema, and no annotations, the description is nearly complete. It covers return values (success and failure shapes), background behavior, survival, polling instructions, and webhook details. The only gap is that it relies on 'Args mirror dispatch' without explicitly listing all parameters, but the context is rich enough for an agent to use the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage, so the description must compensate. It adds meaning for notification parameters (notify_url, notify_on, notify_headers) with details on payload and behavior, but it only says 'Args mirror dispatch' for the other 5 parameters, leaving them to the agent's knowledge of the sibling tool. This partial compensation is adequate but not fully comprehensive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states that the tool spawns a background dispatch and returns a job_id immediately. It distinguishes itself from siblings like dispatch (synchronous) and wait_dispatch (polling) by focusing on long-running tasks that exceed MCP transport limits.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear guidance: 'Use this for anything that may exceed the MCP transport's per-call ceiling (~60s in Cowork) — persona runs, refactors, anything in a busy project.' It also instructs to poll with wait_dispatch. However, it does not explicitly state when not to use it or list alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_dispatchA

Non-blocking read of a job's current state.

status values:

  • running — work in flight; elapsed_ms included for progress.

  • done — full sync-style result keys.

  • cancelled — user called cancel_dispatch.

  • abandoned — runtime cancel (transport timeout, FastMCP shutdown). Subprocess kept running; watcher will transition this to done or error shortly. Poll again.

  • error — dispatcher-internal error or unparseable output.

  • orphaned — subprocess and output both lost on a restart.

Unknown job_id returns {ok: false, error: ...}. Works for both live jobs and ones loaded from disk after a bridge restart.

ParametersJSON Schema
NameRequiredDescriptionDefault
job_idYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description must disclose behavior. It explains all possible statuses (running, done, cancelled, abandoned, error, orphaned), handling of unknown job_id, and that it works for live and disk-loaded jobs after restart. It does not mention authorization or rate limits, but it covers key behavioral traits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise: a single line for the main purpose followed by a bulleted list of statuses. No redundant information; every sentence adds value. Front-loads the key action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 1 parameter, no output schema, and no annotations, the description covers the tool's behavior comprehensively: status explanations, error handling for unknown IDs, and persistence across restarts. It is sufficient for an AI agent to understand what the tool does and what to expect.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter job_id has 0% schema description coverage. The description adds meaning by specifying that an unknown job_id returns {ok: false, error: ...}, which is not in the schema. This compensates well for the lack of schema-level documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description starts with 'Non-blocking read of a job's current state,' which clearly specifies the action (read) and resource (job's current state). This distinguishes it from siblings like dispatch (create) and wait_dispatch (blocking wait).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'Non-blocking' implies when to use (avoid waiting) but does not explicitly contrast with alternatives like wait_dispatch. There are no when-not-to-use or exclusionary statements, though the context is reasonably clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_scheduleC

Detail view of one schedule. Includes last_job_id so you can follow up with get_dispatch or list_completions.

ParametersJSON Schema
NameRequiredDescriptionDefault
schedule_idYes

TDQS

C2.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must carry the full behavioral burden. It implies a read operation but does not explicitly state read-only nature, side effects, or authorization needs. The description is too brief to adequately disclose behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise at two sentences and front-loads the purpose. However, it could be restructured to include parameter guidance or behavioral notes without becoming verbose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple input and lack of output schema, the description leaves out critical details like what fields the response contains (beyond last_job_id). An agent would need to infer the response structure, making it incomplete for reliable tool invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 0% description coverage for its only parameter (schedule_id). The description does not mention the parameter or explain how to obtain the schedule_id. It adds no meaning beyond the schema, which itself lacks a description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it provides a 'detail view of one schedule', indicating retrieval of a single schedule. It distinguishes from sibling tools like list_schedules by implying a specific schedule is targeted, but does not explicitly mention the schedule_id parameter.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description suggests follow-up actions using get_dispatch or list_completions based on the returned last_job_id. However, it does not specify when to use this tool versus alternatives, or provide any prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_channelsA

channel → pinned session_id. Doesn't tell you whether work is in flight on a channel — use list_jobs for that.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses that the tool returns a pinned session_id and does not indicate work status, but it lacks details on side effects, data freshness, or whether it is a read-only operation. This is adequate but not comprehensive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise, consisting of two short sentences that convey the essential information with no wasted words. It is front-loaded and easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (no parameters, no output schema), the description is complete. It explains the output (pinned session_id) and provides an important limitation with a cross-reference to a sibling tool. No further details are needed for an agent to use it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are zero parameters, and the input schema has no properties. According to the rubric, a baseline of 4 is appropriate for 0 parameters. The description does not need to add parameter details, and it does not detract from clarity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool lists channels and returns a pinned session_id, effectively communicating its purpose. It also distinguishes itself from the sibling tool list_jobs by clarifying what it does not provide.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly advises when not to use this tool (when checking for in-flight work) and directs the agent to use list_jobs as an alternative, providing clear usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_completionsA

Jobs whose finished_at > since, oldest first.

Use since=0 for "everything that ever finished". For ongoing polling, track the largest finished_at you've seen and pass it as the next since. Cheap and non-blocking — safe to call at the start of every turn / iteration to surface "anything new?".

Returns {completions: [<get_dispatch shape>, ...]} with finished_at present on each entry. raw is omitted; fetch full payloads via get_dispatch(job_id).

ParametersJSON Schema
NameRequiredDescriptionDefault
sinceNo
limitNo

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. States it's cheap and non-blocking, explains return shape (list of get_dispatch shape with finished_at, raw omitted). Could elaborate on any side effects or auth requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three concise paragraphs, each sentence adds value. Front-loaded with purpose, no wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 2 optional parameters, no output schema, and no annotations, description is fairly complete. Covers return shape, typical polling pattern, and limitation (raw omitted). Could mention pagination for limits.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but description adds meaning to parameters: since is a threshold for finished_at, limit defaults to 50. Explains usage patterns that go beyond schema structure.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it lists completed jobs filtered by finished_at > since, oldest first. Verb 'list' with resource 'completions' is specific and distinct from sibling tools like list_jobs or get_dispatch.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly provides usage patterns: use since=0 for all completions, track largest finished_at for polling. Advises on caller frequency (start of every turn) and contrasts with get_dispatch for full payloads.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_eventsA

Return bridge events whose ts > since, oldest first.

The event log records dispatch starts/ends, schedule ticks, sentinel hits, schedule completions, webhook outcomes, and recovery actions. The buffer is bounded (default 1000 events) and persisted across bridge restarts.

Two read modes:

  • Debug mode (notable_only=False, the default): every event. Good for forensic post-mortem of a workflow.

  • Surfacing mode (notable_only=True): only state transitions worth reporting to a human — terminal dispatch states, schedule completions / cancellations / errors, webhook failures, recovery actions. Skips dispatch_start, schedule_tick, schedule_created, webhook_sent, and bridge_init_subprocess_alive. This is what an orchestrator wants for "what should I tell the user about?".

Cursor pattern: pass since=0 for everything, then on each subsequent call pass the largest ts you saw.

types is an explicit allow-list — composes with notable_only (intersection).

Common types:

  • Dispatch lifecycle: dispatch_start, dispatch_end, dispatch_cancelled, dispatch_abandoned, dispatch_error, dispatch_orphan_finalized.

  • Schedule lifecycle: schedule_created, schedule_tick, schedule_activated, schedule_self_cancelled, schedule_cancelled, schedule_completed, schedule_tick_error.

  • Webhook outcomes: webhook_sent, webhook_failed.

  • Recovery: bridge_init_recovery, bridge_init_subprocess_alive.

Each event has ts (epoch seconds), event (type), plus type-specific fields (job_id, schedule_id, channel, ok, duration_ms, etc.).

Note: events generated before this feature shipped are gone — the in-memory buffer only captures from the current bridge process onward. Anything you persisted before then lives in the optional CLAUDE_BRIDGE_LOG JSONL file (if you enabled it).

ParametersJSON Schema
NameRequiredDescriptionDefault
sinceNo
limitNo
typesNo
notable_onlyNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations were provided, so the description fully carries the burden. It discloses the bounded buffer, persistence, event loss before feature shipped, optional JSONL log, and event structure. This is thorough behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is detailed and well-structured with sections for modes, common types, and notes. It is front-loaded with the main purpose, though slightly long due to necessary detail. Every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, but the description adequately describes event structure (ts, event, type-specific fields). It covers usage patterns, edge cases (buffer, persistence, limitations), and provides a complete picture for effective tool use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description adds deep meaning to all 4 parameters: 'since' cursor usage, 'limit' default, 'types' as allow-list with common types, and 'notable_only' with detailed mode definitions. It fully compensates for the schema gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool returns bridge events with filtering, using a specific verb and resource. It distinguishes from sibling tools like list_completions or list_schedules by focusing on the generic event log.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly describes two read modes (debug vs. surfacing) with guidance on when to use each, explains the cursor pattern, and details how the 'types' parameter composes with 'notable_only'. This provides clear when-to-use and when-not-to-use advice.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_jobsA

All tracked jobs (running and finished). raw is stripped from done-state summaries to keep the response cheap; use get_dispatch(job_id) for the full payload.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description discloses a key behavioral trait: 'raw is stripped from done-state summaries to keep the response cheap.' This adds useful context beyond a simple listing, though it could mention the response format (e.g., list of objects with summary fields).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no wasted words. The key information is front-loaded: what it lists, the trade-off, and the alternative. Perfectly concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no parameters, no output schema, and no annotations, the description covers everything needed: purpose, behavioral caveat, and fallback. It is complete for a simple list tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are zero parameters, so baseline is 4. The description does not need to add parameter details and doesn't repeat schema information. It mentions the output characteristic (cheap, stripped raw) which indirectly relates to parameter semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists all tracked jobs (running and finished) using a specific verb ('list') and resource ('jobs'). It distinguishes itself from sibling tool 'get_dispatch' by noting the omission of raw data for cheapness.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells when to use this tool (for a cheap overview) and when to use an alternative (get_dispatch for full payload). This guides the agent effectively.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_schedulesB

Every schedule the bridge knows about — active, completed, cancelled, error.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description must disclose behavior. It states the tool returns all schedules with their statuses, but does not mention read-only nature, potential pagination, ordering, or lack of side effects. The description is minimal.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence that is front-loaded with the core purpose ('Every schedule the bridge knows about') and includes relevant statuses. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple list tool with no parameters or output schema, the description is adequate. It covers what the tool returns. However, it does not clarify if results are paginated or if any default ordering exists.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has no parameters, and schema description coverage is 100% (trivial). Baseline score of 4 applies as the description adds no parameter info, which is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists all schedules (active, completed, cancelled, error), which aligns with the tool name 'list_schedules'. It distinguishes it from sibling tools like 'get_schedule' which retrieves a single schedule. However, it could be more explicit that the verb is 'list'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives such as 'get_schedule' or 'cancel_schedule'. The description assumes the agent knows to use this for a general overview but lacks explicit context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

reset_channelA

Drop a channel's pinned session so the next dispatch starts a fresh Claude Code session. Useful when a project MCP server has wedged inside the channel's session and you want a clean reconnect.

Does not cancel running work — use cancel_dispatch for that.

ParametersJSON Schema
NameRequiredDescriptionDefault
channelYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, so the description carries the full burden. It discloses that it drops a pinned session, does not cancel running work, and affects future dispatches. However, it does not mention side effects, permissions, or return behavior, leaving minor gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences deliver all key points: action, use case, and limitation/alternative. No wasted words. Front-loaded with the primary action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with one parameter and no output schema, the description covers purpose, usage, and behavioral distinction. Missing are return values, error cases, or prerequisites, but the tool's simplicity reduces the need for exhaustive detail.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single 'channel' parameter has 0% schema description coverage, and the description does not elaborate on its meaning, format, or where to obtain it. The implied meaning from the tool name and description is minimal.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('Drop a channel's pinned session') and clearly identifies the resource and outcome. It also distinguishes itself from the sibling 'cancel_dispatch' by stating what it does not do.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit usage context is provided: 'when a project MCP server has wedged inside the channel's session'. It also tells when not to use it and directs to an alternative ('use cancel_dispatch for that').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

schedule_dispatchA

Recurring dispatch: fire prompt on channel every interval_seconds, until a deadline or until the prompt emits the literal stop sentinel [BRIDGE_STOP_SCHEDULE] in its result.

Each tick is an independent dispatch_async job — ticks are short individually, but the schedule can run for hours. The bridge owns the loop, persists schedules to disk, and resumes them after a restart (without burst-firing missed ticks).

Args: prompt: The text fired at each tick. channel: Channel for every tick. Same-session continuity across ticks (each tick --resumes the prior). interval_seconds: Seconds between ticks. Minimum 10. until: Absolute end time, ISO 8601. Prefer including a timezone (e.g. "2026-04-27T20:00:00Z"); naive datetimes without a timezone are interpreted as UTC, not local time, so the same string yields the same instant regardless of which container the bridge runs in. Mutually exclusive with until_seconds. until_seconds: Relative end time in seconds from now (e.g. 14400 = 4 hours). timeout_seconds: Per-tick timeout. Default 300. permission_mode: Same as dispatch. cwd: Same as dispatch. after_schedule_id: Chain this schedule to start only after the named predecessor terminates (completed, cancelled, or error). Useful for pipelines: "after wave A merges, run hygiene check wave B." This schedule starts in waiting status and transitions to active automatically. Cycles are detected and rejected. notify_url: HTTPS endpoint to POST event payloads to. Optional. Bridge fires fire-and-log POSTs; the destination is the user's relay (Slack/Pushover/email/etc.) — bridge does not retry. notify_on: List of event names to push. Values: tick (every tick fired — chatty), tick_with_sentinel (the tick that triggered self-cancel), tick_error (a tick failed to spawn), schedule_end (any terminal transition: completed, cancelled, error). Default ["schedule_end"]. notify_headers: Extra request headers (e.g. auth). Sent on every webhook POST.

Returns: {ok: true, schedule_id, schedule} or {ok: false, error}.

Self-cancellation: if any tick's result text contains [BRIDGE_STOP_SCHEDULE], the schedule cancels after that tick. Use this in your prompt for "watch X until Y" patterns:

"Check open PRs. If all merged, end your reply with
[BRIDGE_STOP_SCHEDULE]. Otherwise summarize."

Webhook payload shape (result_preview truncated to 4KB)::

{"event": "schedule_end", "schedule_id": "...",
 "channel": "...", "tick_count": 17, "status": "cancelled",
 "last_tick_at": ..., "last_tick_result": "...",
 "last_job_id": "...", "error": null}
ParametersJSON Schema
NameRequiredDescriptionDefault
promptYes
channelYes
interval_secondsYes
untilNo
until_secondsNo
timeout_secondsNo
permission_modeNo
cwdNo
after_schedule_idNo
notify_urlNo
notify_onNo
notify_headersNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavioral traits: each tick is independent, schedule persists to disk and resumes after restart, self-cancellation via sentinel, webhook payload shape, and per-tick timeout. It provides rich context for agent decision-making.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a summary paragraph, Args list, Returns, and additional details. It is comprehensive but slightly verbose, though every section earns its place given the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers all aspects: parameters, return value, webhook payload, self-cancellation, persistence, and chaining. With no output schema, it provides the return structure. It is complete for a scheduling tool with 12 parameters.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the description compensates by explaining every parameter in detail, including mutual exclusivity (until vs. until_seconds), timezone interpretation, chaining behavior, and webhook options. Examples like '14400 = 4 hours' add clarity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Recurring dispatch: fire prompt on channel every interval_seconds...' It distinguishes from siblings like dispatch (one-off) and dispatch_async (async single) by explicitly using 'recurring' and detailing scheduling behavior.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for recurring tasks and provides a self-cancellation pattern example. However, it does not explicitly contrast with siblings or state when not to use it, leaving implicit guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

wait_any_completionA

Long-poll up to max_wait_seconds for any new completion since the cursor. Returns immediately if any are already available; otherwise waits.

Default 50s is below the MCP transport ceiling so you can re-enter in a loop. Useful when watching schedule ticks land without polling each tick's job_id separately.

ParametersJSON Schema
NameRequiredDescriptionDefault
sinceNo
max_wait_secondsNo

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It discloses the long-poll behavior: returns immediately if any completion is available, otherwise waits up to max_wait_seconds. The loop re-entry and default time rationale are explained. However, it does not detail what constitutes a 'completion' or any potential side effects, though for a poll tool this is acceptable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise with three sentences, each serving a purpose: first defines the core behavior, second explains the immediate-return condition, third provides practical usage guidance. No extraneous words. Front-loaded with the primary action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given this is a simple long-poll tool with no output schema and minimal parameters, the description covers all essential aspects: action, parameters, behavior, and use case. It is complete for an agent to correctly select and invoke the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 0% description coverage, so the description must compensate. It adds meaning to 'since' as a cursor and states that 'max_wait_seconds' default is below the MCP transport ceiling. This provides context beyond the schema, though it could specify the format of 'since' (e.g., timestamp).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Long-poll up to max_wait_seconds for any new completion since the cursor', specifying the exact verb (long-poll/wait) and resource (any completion). It distinguishes itself from sibling tools like wait_dispatch by focusing on any completion rather than a specific dispatch.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Useful when watching schedule ticks land without polling each tick's job_id separately', providing a clear use case. It also explains the default timing strategy: 'Default 50s is below the MCP transport ceiling so you can re-enter in a loop', guiding when and how to use this tool effectively.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

wait_dispatchA

Long-poll a job for up to max_wait_seconds.

Default 50s is intentionally below the typical MCP transport ceiling (~60s) — call this in a loop and break when status != 'running'. The underlying job is shielded from cancellation, so if your MCP call is aborted at the ceiling the work keeps running and you can re-enter with the same job_id.

ParametersJSON Schema
NameRequiredDescriptionDefault
job_idYes
max_wait_secondsNo

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses that the job is shielded from cancellation and that work continues if MCP call is aborted. No annotations provided, so description carries burden. Explains why default 50s is chosen.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise paragraphs with no filler. Front-loaded with main purpose, each sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema, but description implies response contains status field. Covers polling pattern and error recovery. Could mention return format or error codes.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%; description adds meaning for max_wait_seconds by explaining its default and rationale. job_id is not explained further, remaining vague.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states 'Long-poll a job' and specifies the parameter max_wait_seconds. Distinguishes from siblings like get_dispatch (simple fetch) and cancel_dispatch (cancels).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit instructions to call in a loop and break on status != 'running'. Mentions re-entering with same job_id if aborted. Could be more explicit about when to use alternatives like get_dispatch.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 16 tool updatesv0.1.0
    • First observedbridge_help
    • First observedcancel_dispatch
    • First observedcancel_schedule
    • First observeddispatch
    • First observeddispatch_async
    • First observedget_dispatch
    • First observedget_schedule
    • First observedlist_channels
    • First observedlist_completions
    • First observedlist_events
    • First observedlist_jobs
    • First observedlist_schedules
    • First observedreset_channel
    • First observedschedule_dispatch
    • First observedwait_any_completion
    • First observedwait_dispatch

TDQS

A4.1/5.0

Scored across 16 tools

Disambiguation5/5

Each tool has a clearly distinct purpose, from dispatching and monitoring jobs to managing schedules and channels. Detailed descriptions further clarify any potential overlap.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case (e.g., cancel_dispatch, list_schedules, wait_dispatch). The main dispatch tools are named appropriately as core actions.

Tool Count5/5

16 tools is well-scoped for a bridge to Claude Code, covering help, dispatch, scheduling, monitoring, and channel management without being excessive or insufficient.

Completeness5/5

The tool surface provides comprehensive coverage for the bridge's purpose: running prompts, handling async jobs, scheduling, monitoring, and cancellation. No obvious gaps are present.

Maintenance

ActivityInactive
ResponsivenessSyncing

Related MCP Connectors

Related MCP Servers