A self-hosted chat bridge that connects Discord and Slack to either
Claude Code or OpenAI Codex. Claude Code uses MCP Channels; Codex uses
the official codex app-server protocol while reusing the same Discord/Slack
MCP tools. Both backends use your existing CLI login, so no API key is required.
Choose the backend during compact-bot init, or set
AGENT_PROVIDER=claude|codex.
Claude Code ships with a built-in Channels plugin — but CLI-native commands like /compact and /clear simply don't work through it. They need to be typed directly into the interactive terminal, and MCP servers have no way to do that.
This project started as a fix for that one problem: a wrapper that owns the Claude Code process and can forward PTY commands on your behalf. It then grew into a full-featured bridge with multi-platform support, session management, and more.
| Official Plugin | This Project | |
|---|---|---|
| Platforms | Discord only | Discord + Slack (simultaneous) |
| Session | Ephemeral (new context per invocation) | Persistent (always-on, shared across platforms) |
| Session control | None | /new, /clear, /compact |
| Model switching | Manual restart | /model sonnet (auto-restart) |
| Working directory | Fixed at launch | /cwd /path (auto-restart) |
| Runtime capture | N/A | /capture (Claude terminal / Codex transcript) |
| System prompt | Not configurable | Claude append / Codex developer instructions |
| Message customization | N/A | All bot messages overridable via JSON |
| Auto-recovery | N/A | Auto-respawn on unexpected exit |
The official plugin is spawned by Claude Code as a child MCP server. This means:
- No PTY access — CLI commands (
/compact,/clear) can't be forwarded because there's no terminal to type into - No lifecycle control — when Claude Code stops, everything stops, and there's no way to restart from chat
- No state across restarts — switching models or working directories requires manually killing and restarting
Compact Bot solves this by inverting the relationship — a wrapper owns and controls the agent process. Claude Code keeps the original PTY architecture:
wrapper.ts (always-on)
├── Manages Claude Code lifecycle (spawn / kill / respawn)
├── Virtual terminal buffer (@xterm/headless) for screen capture
├── IPC socket for bidirectional control
└── Claude Code (node-pty)
├── Discord MCP server
└── Slack MCP server
The wrapper can restart Claude Code on command (/new, /model), forward CLI commands (/compact, /clear), and auto-respawn on crashes — all without losing the chat connection.
For Codex, the wrapper uses its structured app-server API instead of scraping a terminal:
wrapper.ts (always-on)
├── Codex app-server JSON-RPC client
│ ├── thread/start, turn/start, turn/steer
│ ├── thread/read transcript capture
│ ├── thread/compact/start, turn/interrupt, thread goals
│ └── approval + request_user_input relay
└── Codex app-server
├── Discord MCP server
└── Slack MCP server
- Multi-platform — Discord and Slack run as independent MCP servers, conditionally enabled by token
- Dual agent backend — choose Claude Code or Codex without changing the chat platform setup
- Shared session — Both platforms share the same selected-agent context
- Attachments — Images/files sent with a message are downloaded automatically; their local paths are inlined into the prompt. The
download_attachmenttool fetches older attachments found viafetch_messages - Reply context — Discord message references and Slack threads are preserved
- MCP tools —
reply,react,edit_message,fetch_messages,download_attachment - Reasoning effort control — Inspect or change Codex effort from chat with
/effort - Customizable messages — Override any bot message via
data/messages.json - Custom system prompt — Append instructions via
SYSTEM_PROMPT_PATH
- Node.js >= 20
- At least one authenticated agent CLI:
- Claude Code (
claude --version,claude login) - Codex (
codex --version,codex login)
- Claude Code (
- Build tools —
build-essential/python3fornode-ptycompilation - At least one of: Discord Bot Token or Slack Bot Token
npx @serin511/compact-bot init # interactive setup → ~/.config/compact-bot/.env
npx @serin511/compact-bot # run from anywhereThe setup wizard asks whether to use claude or codex. Existing
installations remain on Claude Code unless AGENT_PROVIDER=codex is added.
git clone https://github.com/Serin511/Compact-Bot.git
cd Compact-Bot
npm install
cp .env.example .env # edit with your tokens
npm run build
npm start- Go to Discord Developer Portal → New Application
- Bot → Enable all Privileged Gateway Intents:
- Presence Intent
- Server Members Intent
- Message Content Intent (required)
- Copy the bot token → paste into
.envasDISCORD_BOT_TOKEN - OAuth2 → URL Generator → Scopes:
bot→ Permissions:- Send Messages / Send Messages in Threads
- Read Message History
- Attach Files / Add Reactions
- Open the generated URL to invite the bot to your server
- Go to Slack API → Create New App → From scratch
- Socket Mode → Enable → Create App-Level Token (
connections:write) → copyxapp-...token - OAuth & Permissions → Add Bot Token Scopes:
channels:history,channels:read,groups:history,groups:readim:history,im:read,im:write,mpim:history,mpim:readchat:write,files:read,files:write,reactions:write,users:read
- Install to Workspace → copy Bot User OAuth Token (
xoxb-...) - Event Subscriptions → Enable → Subscribe to bot events:
message.channels,message.groups,message.im,message.mpim
- App Home → Enable Messages Tab and allow messages from users
- Paste tokens into
.envasSLACK_BOT_TOKENandSLACK_APP_TOKEN - Invite the bot to channels:
/invite @botname
# Agent backend
AGENT_PROVIDER=codex # claude (default) or codex
# CODEX_PATH=codex # Codex mode; defaults to "codex"
# CLAUDE_PATH=claude # Claude mode; defaults to "claude"
# Platform tokens (at least one required)
DISCORD_BOT_TOKEN=
SLACK_BOT_TOKEN=xoxb-...
SLACK_APP_TOKEN=xapp-...
# Optional
# DEFAULT_MODEL= # empty = selected CLI default
# DEFAULT_REASONING_EFFORT=ultra # Codex only; empty = Codex config
# DEFAULT_CWD=~/projects # empty = current directory
MAX_TURNS=50 # Claude Code only; 0 = unlimited
ALLOWED_CHANNEL_IDS= # comma-separated, empty = all
SLACK_ALLOWED_CHANNEL_IDS=
# DANGEROUSLY_SKIP_PERMISSIONS=true # Claude flag / Codex never + full access
SYSTEM_PROMPT_PATH=data/system-prompt.txt
VERBOSE=falseAll commands work from both Discord and Slack.
| Command | Description |
|---|---|
/new |
Start a fresh session/thread |
/clear |
Clear current context (Claude CLI command / fresh Codex runtime and thread) |
/compact [hint] |
Compress context. Claude forwards the hint to its CLI; Codex injects it into thread history before compaction |
/model <name> |
Switch model. Claude supports sonnet/opus/haiku aliases; Codex accepts a full Codex model ID |
/effort [level] |
Show or change Codex reasoning effort. Supported levels are validated against the current model |
/cwd <path> |
Change the agent's working directory |
/capture [--all] |
Claude: capture the CLI screen/scrollback. Codex: capture the last 50 lines/full current-thread transcript |
/esc |
Claude: send ESC. Codex: interrupt the active turn |
/raw <text> |
Claude: type into the CLI. Codex: submit/steer a normal turn (not a Codex TUI slash command) |
/goal <condition> |
Set the agent goal; /goal clear clears it |
/help |
Show available commands |
Any other message is forwarded to the selected agent.
In Codex mode, /capture uses structured app-server thread items rather than a
literal terminal viewport. It includes available metadata, user/agent messages,
reasoning summaries, plans, command output, file changes, and MCP calls. The
active turn's streamed progress is merged in while it is running. The default
form returns the last 50 rendered transcript lines to approximate a CLI
viewport; /capture --all returns the available transcript for the entire
current thread.
/effort is Codex-only. A change applies to the next new turn without
restarting the thread; a turn already in progress keeps its original effort.
For example, /effort ultra works when the selected model advertises ultra
through Codex's model catalog. When switching models, an effort that the new
model does not advertise is reset to that model's default.
Run Compact Bot under an always-on process manager. For source checkouts,
tmux or screen is sufficient (and keeps Claude Code's PTY attached):
tmux new-session -d -s claude-bot 'npm start'
# Attach to view logs
tmux attach -t claude-botCreate data/messages.json to override any bot message:
{
"newSession": "Starting fresh session...",
"captureRequested": "Capturing screen..."
}See src/messages.ts for all available keys and template variables.
Create a text file and point SYSTEM_PROMPT_PATH to it:
echo "Always respond in English. Be concise." > data/system-prompt.txtThe content is injected via --append-system-prompt for Claude Code and as
developerInstructions when a Codex runtime starts. Compact Bot reads the file
when it starts or restarts the agent runtime; editing it does not change an
already-running session. /new, /clear, /model, and /cwd restart the
Codex runtime and therefore load the current file contents.
Bot doesn't respond to messages
- Verify Message Content Intent is enabled (Discord Developer Portal → Bot)
- Check
ALLOWED_CHANNEL_IDS— empty means all channels - Ensure the bot has read/write permissions in the channel
Claude CLI auth error
claude auth status
claude loginCodex CLI or app-server error
codex --version
codex loginIf codex is not on PATH, set CODEX_PATH to the executable. On macOS,
Compact Bot also checks the Codex binary bundled with the Codex and ChatGPT
desktop apps.
node-pty build fails
sudo apt install build-essential python3
npm rebuild node-ptySession feels stuck
- Send
/escto interrupt the active turn - Send
/newto start a fresh agent session - Send
/clearto reset the current context
MIT