Multi-platform chat bridge for Claude Code. Connect Claude to Discord, Slack, or both simultaneously via MCP.
┌─────────────┐ ┌─────────────┐
│ Discord │ │ Slack │
│ Gateway │ │ Gateway │
│ (discord.js)│ │(@slack/bolt)│
└──────┬──────┘ └──────┬──────┘
│ │
└────────┬──────────┘
│
┌────────▼────────┐
│ Daemon │ Single process per platform.
│ (daemon.ts) │ Holds gateway connection,
│ │ routes messages, manages
│ unix socket │ sessions and access control.
└────────┬────────┘
│ newline-delimited JSON
┌────────▼────────┐
│ Bridge │ Thin MCP relay. One per
│ (bridge.ts) │ Claude session. Platform-
│ │ agnostic — doesn't import
│ stdio ↔ socket │ any chat SDK.
└────────┬────────┘
│ MCP (stdio)
┌────────▼────────┐
│ Claude Code │ Full Claude with tools,
│ │ memory, file access, etc.
└─────────────────┘
Key design decisions:
- One gateway connection per platform. Prevents token race conditions (Discord) and simplifies state.
- Daemon ↔ Bridge separation. The daemon is long-lived; Claude sessions come and go. The bridge reconnects automatically.
- Platform selection via env var. Set
CHAT_PLATFORM=discordorCHAT_PLATFORM=slack. Default:discord. - Simultaneous platforms. Run two daemons on different state dirs for Discord + Slack at the same time.
- Bun —
curl -fsSL https://bun.sh/install | bash - tmux —
brew install tmux - Claude Code —
npm install -g @anthropic-ai/claude-code
# Install dependencies
bun install
# Create .env with your bot token
mkdir -p ~/.claude/channels/discord
cat > ~/.claude/channels/discord/.env << 'EOF'
DISCORD_BOT_TOKEN=your-token-here
EOF
# Install watchdog + verify setup
bun cli/hydra.ts install discord --cwd ~/your/project
# Start
bun cli/hydra.ts up discord- Discord Setup — bot creation, token, permissions, pairing
- Slack Setup — app manifest, Socket Mode, tokens
All operations go through the hydra CLI (bun cli/hydra.ts or alias to hydra).
hydra install <platform> # Generate launchd watchdog, run preflight
hydra uninstall <platform> # Remove launchd watchdog
hydra preflight <platform> # Verify deployment is readyhydra up <platform> # Start daemon + byte
hydra down <platform> # Stop byte + daemon
hydra restart <platform> # Restart daemon (picks up code changes)hydra spawn <prompt> # Spawn a new session
hydra list # List active sessions
hydra status <name> # Session details
hydra kill <name> # Kill a session
hydra health # Daemon diagnostics
hydra clear-key <key> # Clear a stuck idempotency key--daemon <name> Target a specific daemon (when multiple running)
--json Output raw JSON
Set in ~/.claude/channels/<platform>/.env:
# Discord
DISCORD_BOT_TOKEN=MTIz...
# Slack
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...access.json controls who can message the bot. Lives in the state dir (~/.claude/channels/discord/ by default).
See ACCESS.md for full reference.
# Install both
hydra install discord
hydra install slack
# Start both
hydra up discord
hydra up slackEach platform gets its own daemon, state dir, and watchdog. Use different CLAUDE_CONFIG_DIR values for separate logins.
| Tool | Description |
|---|---|
reply |
Send a message. Takes chat_id + text, optionally reply_to for threading and files for attachments (max 10, 25MB each). Auto-chunks long messages. |
react |
Add emoji reaction to a message. |
edit_message |
Edit a previously sent message. |
fetch_messages |
Pull recent history (up to 100). |
download_attachment |
Download attachments from a message to local inbox. |
create_thread |
Create a thread on a message or standalone. |
spawn_session |
Spawn a new Claude session for a topic (main session only). |
list_sessions |
List active spawned sessions (main session only). |
kill_session |
Kill a spawned session (main session only). |
Spawn isolated Claude sessions from chat:
| Command | Action |
|---|---|
spawn: <topic> |
Create a new session with a thread |
kill: <name> |
Kill a session by name |
/sessions |
List active sessions |
listen / pause |
Toggle auto-routing in a session thread |
help / commands |
Show all available commands |
Sessions get cute names (spark, pixel, nova...) and run in their own tmux sessions. State persists across daemon restarts.
| File | Purpose |
|---|---|
gateway.ts |
ChatGateway interface and shared types |
discord-gateway.ts |
Discord implementation (discord.js) |
slack-gateway.ts |
Slack implementation (@slack/bolt Socket Mode) |
daemon.ts |
Platform-agnostic message router and session manager |
bridge.ts |
MCP relay between Claude and daemon (unix socket ↔ stdio) |
cli/hydra.ts |
CLI entry point — routes commands |
cli/helpers.ts |
Config resolution, tmux wrappers, socket comms, compile check |
cli/lifecycle.ts |
Lifecycle commands: up/down/restart/watchdog/preflight/install |
Logs land at ~/hydra-<platform>-daemon.log and ~/hydra-<platform>-byte.log.
{ "dmPolicy": "pairing", // pairing | allowlist | disabled "allowFrom": ["user-id-here"], // platform user IDs "groups": { // channel-level policies "channel-id": { "requireMention": true, "allowFrom": [], "threadReply": true } }, "ackReaction": "👀", "replyToMode": "first", // first | all | off "textChunkLimit": 2000, "chunkMode": "newline" // newline | length }