# Claude Bridge

> Real-time cross-machine MCP relay for Claude Code agents

- **Type:** MCP server
- **Install:** `agentstack add mcp-constripacity-claude-bridge`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [constripacity](https://agentstack.voostack.com/s/constripacity)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [constripacity](https://github.com/constripacity)
- **Source:** https://github.com/constripacity/Claude-Bridge

## Install

```sh
agentstack add mcp-constripacity-claude-bridge
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Claude Bridge

**Real-time cross-machine communication for Claude Code agents.**

---

Claude Code's native [Agent Teams](https://code.claude.com/docs/en/agent-teams) coordinate multiple instances on the **same machine**. Claude Bridge fills the gap — it lets Claude Code agents on **different machines** communicate in real time over a shared MCP relay server.

```
Windows PC (Claude Code)         MacBook (Claude Code)
         |                                |
         |   SSE · Tailscale / LAN        |  ← server runs here
         +-------> Claude Bridge  **Why the PyPI name differs from the project name:** `claude-bridge` was already taken on PyPI by an unrelated project. The distribution name is `claude-code-bridge`; the import name (`import claude_bridge`) and the CLI command (`claude-bridge`) are unchanged.

Or from a clone if you'd like to hack on it:
```bash
git clone https://github.com/constripacity/Claude-Bridge.git
cd Claude-Bridge
pip install -e .[dev]              # editable install with test/lint deps
```

Or run the official container image (published to GHCR on every release):
```bash
docker run -p 8765:8765 -v claude-bridge-data:/data \
  ghcr.io/constripacity/claude-bridge:latest
# with auth, retention, and the audit log:
docker run -p 8765:8765 -v claude-bridge-data:/data \
  -e CLAUDE_BRIDGE_AUTH_TOKEN=secret \
  ghcr.io/constripacity/claude-bridge:latest --host 0.0.0.0 --retention-days 30 --audit-log
```
The image binds `0.0.0.0` by default (publishing the port is already an opt-in to network exposure) and stores its SQLite DB in the `/data` volume. Tags: `latest` + `MAJOR.MINOR` + exact `X.Y.Z` per release, plus a moving `edge` from `main`.

### 2. Start the server

```bash
claude-bridge                       # defaults: 127.0.0.1:8765, ./claude-bridge.db
# accept connections from other machines on the network:
claude-bridge --host 0.0.0.0
# or pick a custom port / db path:
claude-bridge --port 9000 --db /var/lib/claude-bridge/bridge.db
# or disable the web dashboard if you only want the MCP transport:
claude-bridge --no-dashboard
# or run as a pure stdio MCP server (no HTTP, no dashboard, no banner):
claude-bridge --stdio
```

> The default bind changed in v0.7.3 from `0.0.0.0` to `127.0.0.1` so a fresh install on a laptop doesn't silently expose the bridge to whatever network it's on. Pass `--host 0.0.0.0` (or a specific interface IP) when you actually want cross-machine reach. Combine with [Authentication](#authentication) for anything less trusted than your own LAN/tailnet.

```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Claude Bridge — General MCP Relay Server
  Version: 0.9.1
  http://localhost:8765/             ← Dashboard
  http://localhost:8765/sse          ← Local MCP config
  http://:8765/sse    ← Remote machines (LAN/Tailscale)
  http://localhost:8765/api/state    ← JSON state for dashboard
  http://localhost:8765/status       ← Health check
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```

### 3. Connect each Claude Code session to the bridge

Use the `claude mcp add` CLI on every machine that should use the bridge — including the host. Don't edit `~/.claude/settings.json` directly; current Claude Code rejects the legacy `mcpServers` block at the schema level.

**Host machine** — points at the local server:
```bash
claude mcp add --transport sse -s user claude-bridge http://localhost:8765/sse
```

**Remote machines** — point at the host's reachable address (LAN IP, Tailscale IP, or any other network route). Make sure the host bridge was started with `--host 0.0.0.0` (or the specific interface IP) — the default `127.0.0.1` only accepts local connections.
```bash
claude mcp add --transport sse -s user claude-bridge http://:8765/sse
```

**Single-machine / stdio mode** — when there's no need for cross-machine reach, Claude Code can spawn the bridge as a subprocess and talk to it over stdin/stdout, no HTTP at all:
```bash
claude mcp add -s user claude-bridge -- claude-bridge --stdio
# share the SQLite store with an HTTP instance if you also run one:
claude mcp add -s user claude-bridge -- claude-bridge --stdio --db /path/to/shared.db
```

Use `-s user` to share the entry across all your projects, or `-s local` to scope it to one. Verify with `claude mcp list` — `claude-bridge` should show as `✓ Connected`. If a Claude Code session is already running, type `/mcp` inside it to re-handshake (or restart the session) so the new tools register.

That's it. Every connected Claude Code session now has six new tools.

---

## MCP Tools

| Tool | Description |
|------|-------------|
| `bridge_send` | Send a message to a named channel |
| `bridge_receive` | Read messages — pass `since_id` for incremental polling |
| `bridge_channels` | List all active channels and message counts |
| `bridge_ping` | Health check + server stats |
| `bridge_clear` | Clear all messages from a channel |
| `bridge_status` | Cross-channel overview with recent messages |

---

## Usage

Agents communicate over **named channels**. Convention: `:`.

**Machine A (orchestrator):**
```
bridge_send(
  channel="myproject:orchestrator",
  sender="windows",
  content='{"type":"task","phase":1,"action":"run_tests"}'
)
```

**Machine B (worker):**
```
bridge_receive(channel="myproject:orchestrator")
→ gets the task

bridge_send(
  channel="myproject:worker",
  sender="mac",
  content='{"type":"result","phase":1,"status":"complete","tests_run":61,"failures":0}'
)
```

**Machine A polls for results:**
```
bridge_receive(channel="myproject:worker", since_id="")
```

The `since_id` parameter ensures each agent only processes new messages on every poll.

If the cursor goes stale — for example after another agent runs `bridge_clear` and the message your `since_id` referred to no longer exists — `bridge_receive` (and `GET /api/messages`) returns an empty result with a `since_id_not_found` warning instead of silently dumping the channel from the start. Drop the bad cursor and call again without `since_id` to re-sync.

---

## Channel Naming

Channels are created on first write — no registration needed.

```
:orchestrator   →  A sends tasks to B
:worker         →  B sends results to A
:events         →  shared event log
:debug          →  verbose diagnostics
general:sync             →  cross-project coordination
```

---

## Networking

The bridge is plain HTTP + Server-Sent Events. As long as the client machine can reach the host's `:8765`, it works — pick whatever connectivity fits your setup:

- **Single machine** — use `localhost`. Nothing to set up.
- **Same LAN** — use the host's LAN IP (e.g. `192.168.1.42`). No port forwarding needed.
- **Different networks** (the original motivating case) — use a private overlay between machines:
  - [Tailscale](https://tailscale.com) is the simplest and what this project is tested against. Install on both machines, join the same tailnet, use the host's tailnet IP in the remote MCP config, and keep `:8765` firewalled to the tailnet — no public exposure.
  - Other mesh VPNs (ZeroTier, Nebula, headscale) work the same way.
  - A reverse proxy with auth on a public host works too. If you need to expose the bridge directly to a less-trusted network, enable the built-in [Bearer token](#authentication) instead of running bare.

---

## Why not use Agent Teams?

Claude Code's built-in Agent Teams (experimental, requires `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`) coordinate agents on the **same machine**. They share a process, a filesystem, and a local network. There's no mechanism for two agents running on separate physical machines to talk to each other.

Claude Bridge is the missing layer. It's intentionally minimal — a relay, not an orchestrator. Your agents stay in control of their own logic.

---

## CLAUDE.md

A ready-to-use `CLAUDE.md` is included. Drop it in your project root or add it to your global `~/.claude/CLAUDE.md` to give every Claude Code session full context on how to use the bridge, which channels belong to which project, and what each machine's role is.

---

## Web Dashboard

Open `http://localhost:8765/` in any browser for a live monitor of channels, messages, and senders. Messages appear the instant they land — no 2-second lag. Click into any message for a JSON-highlighted inspector. Includes a send composer (pick a sender, type or paste JSON, ⌘↵ / Ctrl↵ to send) and a per-channel clear button. Adapts to mobile viewports automatically.

The dashboard uses a live SSE stream for the active channel's message feed and continues polling `/api/state` every 2 seconds for channel list and counts.

The JSON API (polling endpoints remain available for scripts and external tools):

| Endpoint | Purpose |
|----------|---------|
| `GET /api/state` | All channels + counts + senders + uptime in one call |
| `GET /api/messages?channel=X[&since_id=Y][&limit=N]` | Feed for one channel |
| `GET /api/messages/{id}` | Full message detail (parsed JSON, byte size) |
| `POST /api/send` `{channel,sender,content}` | Same effect as `bridge_send` |
| `POST /api/clear` `{channel}` | Drop all messages on a channel |
| `GET /events/channel/[?since_id=Y]` | Live SSE stream for one channel |
| `GET /api/audit[?limit=N]` | Recent audit events (when `--audit-log` is on) |

Sends from the dashboard are indistinguishable from MCP `bridge_send` calls — they share the same INSERT path.

---

## Live event stream

`GET /events/channel/` is a Server-Sent Events endpoint. Open it and you get a real-time push of every message and clear event on that channel with ~0 ms latency. The polling JSON endpoints remain unchanged for backwards compatibility.

**Events emitted:**

| Event | Payload | When |
|-------|---------|------|
| `message` | `{seq, id, channel, sender, content, timestamp}` | On every new message |
| `clear` | `{channel, cleared}` | When `bridge_clear` or `POST /api/clear` runs |
| `cursor_stale` | `{since_id}` | Reconnect cursor no longer exists — re-sync via `/api/messages` |
| `replay_truncated` | `{limit}` | Backlog > 500 rows — re-sync via `/api/messages` |

**Browser (`EventSource`):**
```javascript
const es = new EventSource('/events/channel/myproject:worker');
es.addEventListener('message', e => console.log(JSON.parse(e.data)));
es.addEventListener('clear',   e => console.log('channel cleared'));
```

The browser `EventSource` API cannot send custom headers, so when auth is enabled, pass the token as a query parameter instead:
```javascript
const es = new EventSource(`/events/channel/myproject:worker?token=${encodeURIComponent(token)}`);
```

> **Access-log note:** query-parameter tokens appear in server access logs. If you run the bridge behind a reverse proxy (nginx, Caddy), add a log-scrub rule for `/events/.*[?&]token=` to avoid leaking tokens into your log retention.

**Backlog replay on reconnect:** browsers send `Last-Event-ID` automatically on reconnect (using the `id:` field from each SSE frame). TUI clients can pass `?since_id=` explicitly. Up to 500 messages are replayed; if more exist, a `replay_truncated` event fires and the client should re-fetch via `/api/messages`.

**Caps (env-tunable, no restart needed):**

| Variable | Default | What it limits |
|----------|---------|----------------|
| `CLAUDE_BRIDGE_MAX_SSE` | 100 | Total concurrent subscribers across all channels |
| `CLAUDE_BRIDGE_MAX_SSE_PER_CHANNEL` | 25 | Subscribers per channel |
| `CLAUDE_BRIDGE_SSE_REPLAY_LIMIT` | 500 | Backlog rows replayed on reconnect |

Requests past a cap get `503 Service Unavailable`. A comment-line keepalive is sent every 15 s to survive the 30–60 s idle cutoff most reverse proxies enforce.

---

## Terminal UI

If you live in a terminal, run the TUI companion instead of (or alongside) the web dashboard. Install the `[tui]` extra and use the module entry point:

```bash
pip install claude-bridge[tui]
python -m claude_bridge.tui
# or:  python -m claude_bridge.tui --url http://:8765 --sender mac
```

It's a [Textual](https://textual.textualize.io) app that talks to the same JSON API as the dashboard, so they're always in sync. Channels in a sidebar, live-polled feed with sender/type colouring, a JSON-highlighted inspector, send composer, filter, clear, and pause — all keyboard-driven (`?` for help, `q` to quit).

Design reference for every layout (full / compact / narrow / states) lives in `docs/design/terminal/` — open `index.html` to browse the artboards.

---

## Authentication

The bridge runs **without auth by default** — anyone who can reach `:8765` can read or write any channel. That's fine for `localhost`, a trusted LAN, or a tailnet. If you need to expose the bridge to a less-trusted network, set a token:

```bash
# Recommended: env var (token never appears in `ps` output)
export CLAUDE_BRIDGE_AUTH_TOKEN="$(openssl rand -hex 32)"
claude-bridge

# Or read it from a file (safer than --auth-token on shared hosts)
echo "$(openssl rand -hex 32)" > /etc/claude-bridge/token
chmod 600 /etc/claude-bridge/token
claude-bridge --auth-token-file /etc/claude-bridge/token

# Or the literal value on the CLI — easy but the value shows up in `ps`
# output and /proc//cmdline; fine for local dev, avoid on shared hosts
claude-bridge --auth-token "$(openssl rand -hex 32)"
```

Precedence: `--auth-token > --auth-token-file > CLAUDE_BRIDGE_AUTH_TOKEN`. When set, every endpoint except `/status` (the healthcheck) requires `Authorization: Bearer `. Constant-time comparison; no other state. Unset everything and the bridge runs exactly as it did before — auth is fully opt-in.

**Connecting clients with auth on:**

```bash
# claude mcp add — pass the header on registration:
claude mcp add --transport sse -s user claude-bridge \
    http://:8765/sse \
    --header "Authorization: Bearer $CLAUDE_BRIDGE_AUTH_TOKEN"

# TUI — picks up CLAUDE_BRIDGE_AUTH_TOKEN automatically, or pass --token:
python -m claude_bridge.tui --url http://:8765 \
    --token "$CLAUDE_BRIDGE_AUTH_TOKEN"

# Web dashboard — open the URL in a browser. On the first /api/* call it
# prompts you for the token, stores it in localStorage, and attaches it as
# a Bearer header to every subsequent request. A green "Auth ✓" badge in
# the top-right with a `clear` button lets you rotate or forget.
```

**stdio mode is unaffected** — `claude-bridge --stdio` is a subprocess pipe, not a network surface, so it skips the auth check entirely.

---

## TLS / HTTPS

By default the bridge serves plain HTTP and the recommended cross-machine setup terminates TLS upstream — a reverse proxy (nginx, Caddy) or an overlay like Tailscale. For a self-contained HTTPS listener with no proxy, point the bridge at a cert + key:

```bash
claude-bridge --host 0.0.0.0 --tls-cert /etc/claude-bridge/cert.pem \
                              --tls-key  /etc/claude-bridge/key.pem
```

Both flags are required together; the cert lifecycle (issuance, renewal) is the operator's responsibility. The banner switches to `https://` URLs when TLS is on. stdio mode ignores these flags.

---

## Audit log

Opt-in forensic trail. With `--audit-log` (or `CLAUDE_BRIDGE_AUDIT_LOG=1`) the bridge records security-relevant events to a separate `audit` table:

| Event | Logged when |
|-------|-------------|
| `auth_failure` | A protected request is rejected (`401`) |
| `oversize_reject` | A request body exceeds the size cap (`413`) |
| `channel_clear` | A channel is cleared (records the count) |
| `channel_create` | The first message lands on a new channel |

Each row carries a UTC timestamp and the client IP. Read recent events via `GET /api/audit?limit=N` (auth-protected when a token is set; returns `{"enabled": false, "events": []}` when the log is off). Audit rows are **not** removed by message retention — forensic logs usually need to outlive content.

---

## Persistence

Messages are persisted to a local SQLite database (`./claude-bridge.db` by default) so they survive server restarts. Override the path with the `CLAUDE_BRIDGE_DB` environment variable:

```bash
CLAUDE_BRIDGE

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [constripacity](https://github.com/constripacity)
- **Source:** [constripacity/Claude-Bridge](https://github.com/constripacity/Claude-Bridge)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-constripacity-claude-bridge
- Seller: https://agentstack.voostack.com/s/constripacity
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
