clawborrator-channel
v0.18.4
Published
Claude Code channel app for clawborrator: an stdio MCP server that bridges to a remote clawborrator hub via WebSocket so collaborators can prompt the same Claude Code session from a browser.
Maintainers
Readme
clawborrator channel
Claude Code side of clawborrator. A stdio MCP server that dials out to a clawborrator hub over WebSocket and relays prompts, replies, and tool-permission verdicts in both directions.
The hub lives at clawborrator/hub.
Claude Code only. This channel uses Claude Code's experimental
<channel>notification protocol (--dangerously-load-development-channels). It does not work with Claude Desktop, the Claude API, or any other client — Claude Desktop'sclaude_desktop_config.jsonMCP slot routes tool calls but doesn't deliver inbound channel notifications, so the remote operator's prompts never reach the model. If you wire the channel intoclaude_desktop_config.jsonyou'll see the MCP server connect but nothing will flow.
Use
No clone, no npm install. Sign in at https://clawborrator.com with GitHub, mint a channel token at /settings, then add this to .mcp.json in your project root (or ~/.claude.json for user-level — note: ~/.claude.json, not ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"clawborrator": {
"command": "npx",
"args": ["-y", "clawborrator-channel"],
"env": {
"CLAWBORRATOR_HUB_URL": "wss://clawborrator.com",
"CLAWBORRATOR_TOKEN": "ck_live_..."
}
}
}
}npx -y clawborrator-channel fetches the latest published version on first run, caches it, and uses the cache on subsequent runs.
Then start Claude Code with custom channels enabled (research-preview flag):
claude --dangerously-load-development-channels server:clawborratorEnv vars
| Var | Default | Purpose |
| ------------------------- | --------------------- | -------------------------------------------------------------------------------- |
| CLAWBORRATOR_HUB_URL | ws://localhost:8787 | Hub WebSocket URL. Use wss://clawborrator.com for the hosted hub. |
| CLAWBORRATOR_TOKEN | (required) | Bearer token for WS upgrade. Mint at <hub>/settings. Channel exits on missing. |
| CLAWBORRATOR_SESSION_ID | random UUID | Stable session id (see below). |
| CLAWBORRATOR_SOURCE | clawborrator | <channel source="…"> attribute Claude sees. |
Durable session IDs
By default each Claude Code restart spawns a fresh session row in the hub dashboard (a new random UUID per process). That's usually what you want — each terminal instance is a real, distinct session.
If you'd rather keep the same dashboard row across restarts (e.g. you
restart Claude Code several times a day on the same project and don't
want a new "session" entry each time), set CLAWBORRATOR_SESSION_ID
to a UUID of your choosing in .mcp.json:
{
"mcpServers": {
"clawborrator": {
"command": "npx",
"args": ["-y", "clawborrator-channel"],
"env": {
"CLAWBORRATOR_HUB_URL": "wss://clawborrator.com",
"CLAWBORRATOR_TOKEN": "ck_live_...",
"CLAWBORRATOR_SESSION_ID": "8b7d2a3a-9c0e-4ff4-9c40-1a2b3c4d5e6f"
}
}
}
}The hub keys session state by this id, so the same channel ID will
re-attach to the same hub-side session on reconnect (chat history,
shares, queue all preserved). One CLAWBORRATOR_SESSION_ID per
project: set it in the project's .mcp.json and don't reuse the
same value across different projects.
Generate a fresh UUID with uuidgen (macOS/Linux) or [guid]::NewGuid()
(PowerShell).
File attachments (v0.9+)
Inbound — operator → Claude
When a remote operator sends a prompt with files attached (paperclip button or pasted images on the dashboard), the channel:
- Downloads each file via a short-lived signed GCS URL the hub mints.
- Writes it to
<cwd>/.clawborrator/attachments/<chatId>/<filename>. - Prepends an
<attachments-from-operator>tag to the prompt content listing the local paths so Claude knows to read them.
Stale staging dirs (older than 1 hour) are reaped on each hub
reconnect. Add .clawborrator/ to .gitignore to keep the staged
files out of any repo this CC is running in.
Outbound — Claude → operator (v0.10+)
Two new MCP tools let Claude attach files to its replies:
attach_file({ path, label? })— Reads a file from inside cwd, uploads to the hub via the channel token, returns afileId.reply({ chat_id, text, attachments? })— The existing reply tool now accepts an optionalattachments: [fileId, ...]array.
Typical agent flow when the operator asks for a file:
operator: "send me oil-spec.md from your data folder"
agent: attach_file({ path: 'data/oil-spec.md' })
→ { fileId: "abc123", filename: "oil-spec.md", ... }
agent: reply({
chat_id: "...",
text: "Here's the file you asked for. ...",
attachments: ["abc123"]
})The operator's dashboard renders a download chip on the reply row; clicking it streams the file from GCS via a freshly-minted signed URL.
Path safety: attach_file rejects any path that resolves outside
process.cwd(). The agent can attach project files but can't walk
out to e.g. ~/.ssh/.
Permanent corpus updates — owner only (v0.11+)
save_to_corpus({ staged_path, target_relative_path }) promotes a
staged attachment into the agent's permanent project tree (e.g.
docs/shop-manuals/1997-gts.pdf). The file becomes part of the
agent's repo and is available on future queries — useful for
RAG-style memory agents that accumulate knowledge over time.
Only the agent owner can use this. The hub stamps a
creator_session="1" attribute on the inbound <channel> tag when
the prompt comes from the session owner's own dashboard (not from
a routed prompt, not from a shared collaborator). The agent's
CLAUDE.md should refuse save_to_corpus calls when this attribute
is absent. The channel-side tool runs the file copy regardless —
the semantic gate lives in the prompt.
Channel-side sanitization rejects:
- Absolute paths
..segments- Dotfile-prefixed segments (no
.env,.git/,.ssh/) node_modules/,dist/,build/as the top segment- Paths deeper than 5 segments
- Source paths outside the project directory
Sample CLAUDE.md snippet for an agent that wants to support corpus extension:
If the inbound <channel> tag has creator_session="1" and the message
asks you to add a file to your knowledge base, call save_to_corpus
with the staged path and a sensible target like docs/<topic>/<file>.
If creator_session is absent, refuse and explain that only the agent
owner can extend the corpus — they should ask via their own session.Transcript-tail hooks (optional)
By default the hub only sees prompts you send and Claude's final replies.
The intermediate work — every Read, Edit, Bash invocation — happens inside
your local Claude Code process and never crosses the channel. Install the
transcript-tail hooks to fan tool calls out to the hub so remote
operators see them inline as ▸ Read foo.js / ▸ Bash npm test (12s, ok)
in the chat log.
Auto-installed by default. When the channel boots as an MCP server it
installs (or refreshes) the hooks idempotently — no command to run, just
add the channel to your .mcp.json. On a fresh project the hooks won't
fire until the next CC restart since Claude Code reads
.claude/settings.json at session start, before MCP servers spawn — so
the very first session has zero ▸ rows; restart and they're live.
To opt out, add "CLAWBORRATOR_DISABLE_HOOK_INSTALL": "true" to your
.mcp.json env block. The channel will then skip the install entirely
and never modify your .claude/ directory.
You can also install manually if you'd rather see the verbose status output:
npx -y clawborrator-channel install-hooksThe install (manual or auto) is idempotent and writes two things:
.claude/hooks/clawborrator-tail.mjs— a copy of the package'shook-template.mjs. Self-contained Node script that POSTs each hook event to the hub..claude/settings.json— adds entries underhooks.PreToolUse,hooks.PostToolUse,hooks.Stop,hooks.SubagentStop, andhooks.Notificationthat point at the script.
Restart Claude Code after installing so the new hook config loads.
To remove the hooks later:
npx -y clawborrator-channel uninstall-hooksThe hook script reads the same CLAWBORRATOR_* env vars from your
.mcp.json — no extra setup. Each event POSTs with a 800 ms / 2 s
timeout (tighter for PreToolUse since that one blocks Claude). Failures
log to stderr and exit 0; the hook never blocks your turn.
A breadcrumb log is written to your tmpdir on every fire as
clawborrator-tail.<sessionId>.log — separate file per Claude Code
session so concurrent projects don't trample each other's debug output.
Use it to diagnose missing tool rows in the hub's chat log.
What flows through:
| Hook event | Renders as |
| -------------- | ---------- |
| PreToolUse | ▸ Read foo.js (placeholder while the tool runs) |
| PostToolUse | folds into the matching PreToolUse line, adding (ok, 12s) or (err) |
| Stop | late assistant text — only shows when Claude finished a turn without calling the reply MCP tool |
| SubagentStop | summary text from a Task subagent |
| Notification | idle / waiting / thinking transitions |
Server-side redaction strips common secrets (API keys, GitHub tokens,
AWS keys, KEY=value patterns) from previews, and previews truncate to
2 KB.
Development
If you're hacking on the channel itself, clone the repo and point args at the local file instead:
{
"command": "node",
"args": ["/abs/path/to/clawborrator/channel/channel.js"]
}Files in this package:
| File | Role |
| ----------------------- | ---- |
| channel.js | The MCP stdio server. Connects to the hub, routes prompts/replies/permissions, exposes the reply and xroute tools. |
| install-hooks.mjs | Subcommand handler for install-hooks / uninstall-hooks. Also called from channel.js on MCP boot for the auto-install path. |
| hook-template.mjs | Source-of-truth for the per-event hook script. Copied verbatim into <project>/.claude/hooks/clawborrator-tail.mjs on install. Edit this, not the installed copy — install will overwrite the project copy on next channel boot. |
