7.2 KiB
Discord-Claude Gateway
An event-driven agent runtime that connects Discord to Claude via the Claude Agent SDK. Inspired by OpenClaw's architecture — a gateway in front of an agent runtime with markdown-based personality, memory, and scheduled behaviors.
How It Works
Discord Users ──► Discord Bot ──► Event Queue ──► Agent Runtime ──► Claude Agent SDK
▲ │
Heartbeats ──────────┤ │
Cron Jobs ───────────┤ ┌─────────────────────┘
Hooks ───────────────┘ ▼
Markdown Config Files
(soul, identity, memory, etc.)
All inputs — Discord messages, heartbeat timers, cron jobs, lifecycle hooks — enter a unified event queue. The agent runtime reads your markdown config files fresh on each event, assembles a dynamic system prompt, and calls the Claude Agent SDK. The agent can write back to memory.md to persist facts across sessions.
Prerequisites
- Node.js 18+
- Claude Code CLI — Install Claude Code and sign in with your subscription
- Discord Bot Token — Create a bot with Message Content Intent enabled
Quick Start
# Install dependencies
npm install
# Make sure Claude Code CLI is installed and you're signed in
claude --version
# Set required environment variables
export DISCORD_BOT_TOKEN=your-discord-bot-token
# Create config directory with persona files
mkdir config
Create at minimum config/soul.md and config/identity.md:
# config/identity.md
echo "# Identity
- **Name:** Aetheel
- **Vibe:** Helpful, sharp, slightly witty
- **Emoji:** ⚡" > config/identity.md
# config/soul.md
echo "# Soul
Be genuinely helpful. Have opinions. Be resourceful before asking.
Earn trust through competence." > config/soul.md
# Start the gateway
npm run dev
Configuration
All settings are via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
DISCORD_BOT_TOKEN |
Yes | — | Discord bot token |
CLAUDE_CLI_PATH |
No | claude |
Path to the Claude Code CLI binary |
ALLOWED_TOOLS |
No | Read,Write,Edit,Glob,Grep,WebSearch,WebFetch |
Comma-separated Claude Code tools |
PERMISSION_MODE |
No | bypassPermissions |
Claude Code permission mode |
QUERY_TIMEOUT_MS |
No | 120000 |
Query timeout in milliseconds |
MAX_CONCURRENT_QUERIES |
No | 5 |
Max simultaneous Claude queries |
CONFIG_DIR |
No | ./config |
Path to markdown config directory |
MAX_QUEUE_DEPTH |
No | 100 |
Max events in the queue |
OUTPUT_CHANNEL_ID |
No | — | Discord channel for heartbeat/cron output |
Markdown Config Files
Place these in your CONFIG_DIR (default: ./config/). The gateway reads them fresh on every event — edit them anytime, no restart needed (except agents.md and heartbeat.md which are parsed at startup for cron/heartbeat timers).
| File | Purpose | Required |
|---|---|---|
CLAUDE.md |
Persona: identity, personality, user context, tools — all in one | Yes |
agents.md |
Operating rules, cron jobs, hooks (parsed by gateway) | No |
memory.md |
Long-term memory (agent can write to this) | No (auto-created) |
heartbeat.md |
Proactive check definitions (parsed by gateway) | No |
Missing optional files are created with default headers on first run.
Heartbeat Config (heartbeat.md)
Define proactive checks the agent runs on a timer:
## check-email
Interval: 1800
Instruction: Check my inbox for anything urgent. If nothing, reply HEARTBEAT_OK.
## check-calendar
Interval: 3600
Instruction: Review upcoming calendar events in the next 24 hours.
Interval is in seconds (minimum 60).
Cron Jobs (in agents.md)
Define scheduled tasks with cron expressions:
## Cron Jobs
### morning-briefing
Cron: 0 9 * * *
Instruction: Good morning! Check email, review today's calendar, and give me a brief summary.
### weekly-review
Cron: 0 15 * * 1
Instruction: Review the week's calendar and flag any conflicts.
Hooks (in agents.md)
Define lifecycle hook instructions:
## Hooks
### startup
Instruction: Read memory.md and greet the user.
### shutdown
Instruction: Save any important context to memory.md before shutting down.
Discord Commands
| Command | Description |
|---|---|
@bot <message> |
Send a prompt by mentioning the bot |
/claude <prompt> |
Send a prompt via slash command |
/claude-reset |
Reset the conversation session in the current channel |
Architecture
The system has 5 input types (inspired by OpenClaw):
- Messages — Discord mentions and slash commands
- Heartbeats — Timer-based proactive checks
- Cron Jobs — Scheduled events with cron expressions
- Hooks — Internal lifecycle triggers (startup, shutdown, agent_begin, agent_stop)
- Webhooks — External system events (planned)
All inputs enter a unified FIFO event queue → processed sequentially by the agent runtime → state persists to markdown files → loop continues.
Project Structure
src/
├── index.ts # Entry point
├── gateway-core.ts # Main orchestrator
├── config.ts # Environment variable loader
├── discord-bot.ts # Discord.js wrapper
├── event-queue.ts # Unified FIFO event queue
├── agent-runtime.ts # Core processing engine
├── markdown-config-loader.ts # Reads config files from disk
├── system-prompt-assembler.ts# Assembles system prompt from configs
├── session-manager.ts # Channel-to-session bindings
├── channel-queue.ts # Per-channel sequential processing
├── response-formatter.ts # Message splitting for Discord's 2000 char limit
├── error-formatter.ts # Safe error formatting
├── heartbeat-scheduler.ts # Recurring timer events
├── cron-scheduler.ts # Cron-expression scheduled events
├── hook-manager.ts # Lifecycle hook events
├── bootstrap-manager.ts # First-run file validation/creation
└── shutdown-handler.ts # Graceful SIGTERM/SIGINT handling
Development
# Run tests
npm test
# Run in dev mode
npm run dev
# Build
npm run build
# Start production
npm start
Claude Code CLI vs API Key
This gateway uses the Claude Code CLI (claude -p) instead of the Anthropic API directly. This means:
- You use your existing Claude Code subscription — no separate API key needed
- Just sign in with
claudein your terminal and you're good to go - The gateway shells out to
claude -p "prompt" --output-format jsonfor each query - Set
CLAUDE_CLI_PATHifclaudeisn't in your PATH
License
MIT