@pablospe/claude-code-bridge
v0.2.1
Published
Bridge that drives interactive Claude Code sessions from external UIs via the channels MCP surface. Provides a managed launch path (bridge spawns claude via node-pty), an observational hook relay (PreToolUse / PostToolUse / Stop surfaced as tool.event rec
Readme
Claude Code Bridge
A TypeScript library + CLI that lets external UIs and orchestrators drive an already-running, already-authenticated Claude Code session — without restarting it, without scraping the terminal, and without talking to the model API directly.
The idea
Channels for inbound general content; MCP tools for outbound. A consumer pushes a message in via a Claude Code channel (notifications/claude/channel); Claude responds by calling one of three MCP tools (bridge_reply / bridge_progress / bridge_done); every turn is persisted as an append-only JSONL event log.
+-------------------------------------------+
| Consumer |
| ccb serve, T3 Code, agtx, ... |
+-------------------------------------------+
| ^
| sendMessage() | events(sessionId)
v |
+-------------------------------------------+
| Bridge |
| session state + event log |
+-------------------------------------------+
|| ^^
|| channels (inbound) || MCP tools (outbound)
vv ||
+-------------------------------------------+
| Claude Code |
| claude + ccb plugin |
+-------------------------------------------+This is not an ACP replacement and not a universal agent runtime. It exists for the specific case where you want an already-authenticated interactive claude to be the runtime.
See docs/ARCHITECTURE.md for the full design, process topology, and a library-usage example.
Demo
Screnshot
Install
1. Install the CLI globally (bun ≥ 1.3 must be on PATH):
> bun add -g @pablospe/claude-code-bridge2. Register the plugin inside a claude session:
/plugin marketplace add https://github.com/pablospe/claude-code-bridge
/plugin install ccb@claude-code-bridgeThe plugin connects a normal claude session back to the bridge
automatically — including the hook relay, so you get tool.event visibility
out of the box.
- Four bins land on your PATH:
ccb(the CLI),ccb-channel-serverandccb-hook-relay(spawned by Claude Code), andccb-launcher(a Node-side launcher; see limitations). - npm name: published as
@pablospe/claude-code-bridge, scoped under the author's namespace; the GitHub repo name staysclaude-code-bridge. - Global install must come first: the plugin manifest invokes
ccb-channel-server/ccb-hook-relayby bare name, so without them on PATH claude reportscommand not found. - Channels is a research preview: it must be enabled / allowlisted for
your account — see
docs/SMOKE.mdfor that one-time step.
Usage: two terminals
The bridge is the consumer side; your claude session is the runtime —
you drive claude from the bridge and watch the structured event stream.
1. Start the bridge (Terminal 1). It mints a session id, writes a
per-session MCP config, and prints the exact claude command for terminal 2:
ccb serve
# listening on 127.0.0.1:18484
# bridge_uuid: 4f3b6e10-…
# …
# in a second terminal, start claude pointed at this bridge:
# claude --dangerously-load-development-channels server:ccb \
# --mcp-config /tmp/ccb-serve-4f3b6e10-….mcp.json \
# --allowed-tools "mcp__ccb__bridge_reply mcp__ccb__bridge_progress mcp__ccb__bridge_done"2. Start claude (Terminal 2). Paste the command ccb serve printed.
3. Send a prompt (back in Terminal 1) — it's pushed to claude as a channel notification:
what is 11 squared?Terminal 1 streams the round-trip as structured events:
[message.sent] m1 "what is 11 squared?"
[tool.event] PreToolUse Bash "…"
[tool.event] PostToolUse Bash (12 B)
[agent.reply final=true] "11 squared is 121."
[agent.done]- The dev-channels flag is what enables inbound. It loads the
ccbchannel from the generated--mcp-config(whose env carries the endpoint + session id); a plain plugin launch gives outbound tools + hooks only. - Need a session id without copy-paste? Bun is already installed:
bun -e 'console.log(crypto.randomUUID())', or useuuidgenif you have it, and pass the same value toccb serve --session-idandexport CCB_SESSION_ID.
For the full verified walkthrough — including enabling the channels preview
and the alternative ccb-launcher flow — see docs/SMOKE.md.
Usage: as an API server (ccb api)
The bridge can also serve a warm claude session pool behind an HTTP API that
speaks both the OpenAI and Anthropic wire formats — so LiteLLM, the OpenAI
SDK, the official anthropic SDK, and Instructor all work against your
interactive session with zero client changes:
ccb api
# ccb api listening on http://127.0.0.1:18485/v1Each request becomes one turn in a live session; /clear is injected between
requests, so the pool stays warm instead of cold-restarting. Sessions launch
clean (no user plugins, hooks, or MCP servers) and in raw-model mode (claude's
own tools disabled — it answers instead of acting).
# OpenAI dialect — litellm, openai SDK, instructor
import litellm
litellm.completion(model="openai/ccb-claude",
api_base="http://127.0.0.1:18485/v1", api_key="ccb",
messages=[{"role": "user", "content": "hello"}])
# Anthropic dialect — official SDK, or export ANTHROPIC_BASE_URL
from anthropic import Anthropic
client = Anthropic(api_key="ccb", base_url="http://127.0.0.1:18485")
client.messages.create(model="ccb-claude", max_tokens=1024,
messages=[{"role": "user", "content": "hello"}])Streaming, tool calling (including forced tool_choice for structured
output), and crash-retry are supported in both dialects. Sampling parameters
and max_tokens are accepted but ignored, and usage is estimated — see the
design doc for the honest
limitations and docs/SMOKE.md for the verified smokes.
Did it install? (no claude needed)
A mock supervisor runs the whole event pipeline in-process, with no real
claude and no channels — handy as a smoke check:
ccb demo --supervisor=mock "what is 11 squared?"[session.started] d91356a6-…
[message.sent] 916c268f-… "what is 11 squared?"
[agent.progress] "thinking"
[agent.reply final=true] "echo: what is 11 squared?"
[session.ended]Development
From a source checkout — also the way to run the bridge before the npm package is published:
git clone https://github.com/pablospe/claude-code-bridge
cd claude-code-bridge
bun install
bun test
# the CLI runs straight from source (no global install):
bun apps/ccb/src/cli.ts demo --supervisor=mock "hello world"--channels=dev-flag exercises the real-claude channel surface without the
plugin (uses claude --dangerously-load-development-channels), so you can
test against your local source without publishing. To exercise the plugin
path against local source, bun link @pablospe/claude-code-bridge in this
checkout and bun link --global @pablospe/claude-code-bridge where you run
claude; the plugin manifest references bin names, so PATH resolution finds
the linked bins.
Requirements & limitations
The real-claude paths need Claude Code v2.1.80+, authenticated, with the
channels research preview enabled for your account. The preview is gated
server-side (the tengu_harbor flag) and isn't on for everyone yet — see
docs/SMOKE.md for the availability diagnostics.
- Inbound is dev-flag-only. The full round-trip needs the
--dangerously-load-development-channels+--mcp-configpath (whatccb serveprints). The plugin path gives outbound tools + hooks but not inbound on individual accounts. - Managed launch needs
node-pty.ccb demo --supervisor=claudespawnsclaudevianode-pty(works under both Node and Bun); ifnode-ptycan't load on the host, use the two-terminal flow or theccb-launcherbin instead.
Where to go next
docs/ARCHITECTURE.md— full design, process topology, library-usage example, and channel-direction nuance.docs/CLI.md—ccbCLI command reference (demo,mcp-config,serve).docs/SMOKE.md— real-claudeverification procedure (plugin, dev-flag, and two-terminal launcher).docs/ROADMAP.md— milestones, planned work, and consumer-gated follow-ups.
License
MIT.
