Completed HIGH-001 through HIGH-004: HIGH-001: Discord bot with channel message routing - bot.py: 244 lines with ClaudeCoordinator class - @mention trigger mode for safe operation - Session lifecycle integration with SessionManager - Typing indicators and error handling - 20/20 tests passing HIGH-002: Response formatter with intelligent chunking - response_formatter.py: expanded to 329 lines - format_response() with smart boundary detection - Code block preservation and splitting - 26/26 tests passing HIGH-003: Slash commands for bot management - commands.py: 411 lines with ClaudeCommands cog - /reset with interactive confirmation dialog - /status with Discord embed display - /model for runtime model switching - 18/18 tests passing HIGH-004: Concurrent message handling - Per-channel asyncio.Lock implementation - Same-channel serialization (prevents race conditions) - Cross-channel parallelization (maintains performance) - 7/7 concurrency tests passing Total: 134/135 tests passing (99.3%) Production-ready Discord bot MVP Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 KiB
Discord Bot Usage Documentation
Overview
The Claude Coordinator Discord bot (claude_coordinator/bot.py) provides channel-based message routing to Claude CLI sessions. Each Discord channel maps to a specific project with isolated sessions and custom configurations.
Implementation: 244 lines of Python code
Test Coverage: 20 comprehensive test cases (455 lines), 100% passing
Architecture
Main Components
- ClaudeCoordinator (discord.ext.commands.Bot subclass)
- Message routing and filtering
- Session lifecycle management
- Integration with SessionManager, Config, ClaudeRunner
- Error handling and Discord response formatting
Key Features
- @Mention Trigger Mode: Bot only responds when explicitly mentioned (safe for MVP)
- Channel-to-Project Mapping: Each channel routes to a specific project directory
- Session Persistence: Sessions maintained per-channel across restarts
- Typing Indicator: Shows "Bot is typing..." while Claude processes
- Error Handling: Graceful handling of timeouts, failures, and edge cases
- Response Chunking: Automatically splits long responses at Discord's 2000 char limit
Installation
1. Ensure Dependencies are Installed
The bot requires:
- discord.py >= 2.6.4
- aiosqlite >= 0.22.1
- pyyaml >= 6.0.3
These are already in pyproject.toml and should be installed via uv sync.
2. Configuration
Create ~/.claude-coordinator/config.yaml:
projects:
my-project:
name: "my-project"
channel_id: "1234567890123456789" # Discord channel ID (as string)
project_dir: "/path/to/project"
allowed_tools:
- "Bash"
- "Read"
- "Write"
- "Edit"
system_prompt: "You are a helpful coding assistant for this project."
model: "sonnet" # or "opus", "haiku"
To get a Discord channel ID:
- Enable Developer Mode in Discord (User Settings → Advanced → Developer Mode)
- Right-click a channel → Copy Channel ID
3. Get Discord Bot Token
- Go to https://discord.com/developers/applications
- Create New Application
- Navigate to Bot tab → Add Bot
- Copy the Bot Token
- Set environment variable:
export DISCORD_TOKEN="your-token-here"
4. Invite Bot to Server
- In Discord Developer Portal → OAuth2 → URL Generator
- Select scopes:
bot - Select permissions:
Send Messages,Read Message History,View Channels - Copy generated URL and open in browser
- Select server and authorize
Running the Bot
Manual Execution
cd /opt/projects/claude-coordinator
export DISCORD_TOKEN="your-token-here"
uv run python -m claude_coordinator.bot
As a Service (Recommended)
Create /etc/systemd/system/claude-coordinator.service:
[Unit]
Description=Claude Discord Coordinator Bot
After=network.target
[Service]
Type=simple
User=discord-bot
WorkingDirectory=/opt/projects/claude-coordinator
Environment="DISCORD_TOKEN=your-token-here"
ExecStart=/home/discord-bot/.local/bin/uv run python -m claude_coordinator.bot
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable claude-coordinator
sudo systemctl start claude-coordinator
sudo systemctl status claude-coordinator
View logs:
sudo journalctl -u claude-coordinator -f
Usage
Basic Usage
- Go to a configured Discord channel
- Mention the bot with your message:
@ClaudeCoordinator Can you help me debug this function? - Bot shows typing indicator while processing
- Claude's response appears in the channel
Session Continuity
Sessions are persistent per-channel:
- First message creates a new session
- Subsequent messages resume the same session
- Session history maintained in SQLite database
- Sessions persist across bot restarts
Example Conversation
User: @ClaudeCoordinator Can you list the files in this project?
Bot: I'll list the files in /path/to/project for you.
[Claude's response with file listing]
User: @ClaudeCoordinator Can you read setup.py?
Bot: [Reads and displays setup.py contents, maintaining context from previous message]
Project Isolation
- Each channel has its own isolated session
- Sessions run in the configured
project_dir - Only configured
allowed_toolsare available - Custom
system_promptsets project-specific behavior
Message Filtering Logic
The bot processes a message if ALL of the following are true:
- ✅ Message author is NOT a bot
- ✅ Bot was mentioned in the message (
@ClaudeCoordinator) - ✅ Channel is configured in config.yaml
Otherwise, the message is silently ignored.
Error Handling
Empty Message
If you mention the bot with no content:
User: @ClaudeCoordinator
Bot: ❌ Please provide a message after mentioning me.
Claude Failure
If Claude CLI command fails:
Bot: ❌ **Error running Claude:**
Command failed: invalid syntax
Timeout (>5 minutes)
Bot: ❌ **Timeout:** Claude took too long to respond (>5 minutes).
Unexpected Errors
All exceptions are caught and reported:
Bot: ❌ **Unexpected error:**
[error details]
Testing
Run the test suite:
cd /opt/projects/claude-coordinator
uv run pytest tests/test_bot.py -v
Test Coverage:
- Bot initialization and configuration
- Message filtering (bot messages, mentions, unconfigured channels)
- Message content extraction (removing mentions, whitespace)
- Session management (creation, resumption, persistence)
- Claude integration (config passing, response handling)
- Error handling (empty messages, Claude failures)
- Typing indicator verification
Results: 20/20 tests passing (100%)
Monitoring
Database Location
Session data stored in:
~/.claude-coordinator/sessions.db
Inspect Sessions
cd /opt/projects/claude-coordinator
uv run python << 'EOF'
import asyncio
from claude_coordinator.session_manager import SessionManager
async def main():
async with SessionManager() as sm:
sessions = await sm.get_all_sessions()
for s in sessions:
print(f"Channel: {s['channel_id']}")
print(f" Project: {s['project_name']}")
print(f" Session: {s['session_id']}")
print(f" Messages: {s['message_count']}")
print(f" Last Active: {s['last_active']}")
print()
asyncio.run(main())
EOF
Logs
With systemd service:
# Follow logs in real-time
sudo journalctl -u claude-coordinator -f
# View last 100 lines
sudo journalctl -u claude-coordinator -n 100
# View logs from today
sudo journalctl -u claude-coordinator --since today
Manual execution logs go to stdout:
2026-02-13 18:00:00 - claude_coordinator.bot - INFO - ClaudeCoordinator bot initialized
2026-02-13 18:00:01 - claude_coordinator.bot - INFO - Loaded configuration with 3 projects
2026-02-13 18:00:02 - discord.client - INFO - logging in using static token
2026-02-13 18:00:03 - claude_coordinator.bot - INFO - Logged in as ClaudeBot (ID: 123456789)
2026-02-13 18:00:03 - claude_coordinator.bot - INFO - Connected to 1 guilds
✓ Bot ready: ClaudeBot
Advanced Configuration
Multiple Projects
Configure different channels for different projects:
projects:
web-app:
name: "web-app"
channel_id: "111111111111111111"
project_dir: "/home/cal/projects/web-app"
allowed_tools: ["Bash", "Read", "Write", "Edit", "Grep", "Glob"]
model: "sonnet"
data-pipeline:
name: "data-pipeline"
channel_id: "222222222222222222"
project_dir: "/home/cal/projects/data-pipeline"
allowed_tools: ["Bash", "Read", "Write"] # More restrictive
system_prompt: "You are a data engineering assistant. Focus on Python and SQL."
model: "opus"
docs-site:
channel_id: "333333333333333333"
project_dir: "/home/cal/projects/docs"
allowed_tools: ["Read", "Write", "Edit"] # No Bash execution
system_prompt_file: "/home/cal/prompts/technical-writer.txt"
model: "haiku" # Faster for documentation
External System Prompts
Instead of inline system_prompt, use a file:
projects:
my-project:
# ... other config ...
system_prompt_file: "/path/to/prompt.txt"
The file will be loaded and passed to Claude on each invocation.
Security Considerations
-
Tool Restrictions: Use
allowed_toolsto limit what Claude can do- Production projects: Consider disabling
Bashtool - Read-only access: Only allow
Read,Grep,Glob
- Production projects: Consider disabling
-
Channel Access Control: Use Discord role permissions to control who can access bot channels
-
Environment Variables: Never commit
DISCORD_TOKENto git- Use environment variables or systemd service config
- Store in a secure secrets manager
-
Database Permissions: Session database should only be readable by bot user
chmod 600 ~/.claude-coordinator/sessions.db
Troubleshooting
Bot doesn't respond
- Check bot was mentioned: Must use
@ClaudeCoordinator, not just typing its name - Check channel is configured: Channel ID must be in config.yaml
- Check bot is running:
systemctl status claude-coordinator - Check logs:
journalctl -u claude-coordinator -f
"Timeout" errors
- Claude CLI took >5 minutes to respond
- This is configured in ClaudeRunner (default: 300 seconds)
- Increase timeout in
claude_coordinator/claude_runner.pyif needed
Bot crashes on startup
- Check DISCORD_TOKEN is set:
echo $DISCORD_TOKEN - Check config file exists:
ls ~/.claude-coordinator/config.yaml - Validate config syntax:
uv run python -c "import yaml; yaml.safe_load(open('~/.claude-coordinator/config.yaml'))" - Check database permissions:
ls -la ~/.claude-coordinator/sessions.db
Sessions not resuming
- Check database exists:
ls ~/.claude-coordinator/sessions.db - Verify session was saved (check logs for "Successfully processed message")
- Inspect database to confirm session_id was stored
Architecture Details
Message Flow
Discord Message
↓
on_message() event handler
↓
[Filter: Is bot message?] → Yes → Ignore
↓ No
[Filter: Bot mentioned?] → No → Ignore
↓ Yes
[Filter: Channel configured?] → No → Ignore
↓ Yes
_handle_claude_request()
↓
Extract message content (remove mentions)
↓
[Empty message?] → Yes → Send error
↓ No
Start typing indicator
↓
Get session from SessionManager (None if new)
↓
Run ClaudeRunner with session_id + project config
↓
[Success?] → No → Send error message
↓ Yes
Save/update session in database
↓
Format response (split at 2000 chars if needed)
↓
Send response to Discord channel
Session Lifecycle
User sends first message
↓
SessionManager.get_session(channel_id) → None
↓
ClaudeRunner.run(session_id=None) # New session
↓
Claude CLI creates new session, returns session_id
↓
SessionManager.save_session(channel_id, session_id)
↓
[Subsequent messages]
↓
SessionManager.get_session(channel_id) → session_data
↓
ClaudeRunner.run(session_id=session_data['session_id']) # Resume
↓
SessionManager.update_activity(channel_id) # Update timestamp
Development
Running Tests
# All tests
uv run pytest tests/test_bot.py -v
# Specific test class
uv run pytest tests/test_bot.py::TestMessageFiltering -v
# Single test
uv run pytest tests/test_bot.py::TestMessageFiltering::test_ignores_bot_messages -v
# With coverage
uv run pytest tests/test_bot.py --cov=claude_coordinator.bot
Adding New Features
- Update
claude_coordinator/bot.pywith new functionality - Add tests to
tests/test_bot.py - Run full test suite:
uv run pytest tests/test_bot.py -v - Update this documentation
Code Structure
__init__: Initialize bot with intents and componentssetup_hook: Async initialization (database, config loading)on_ready: Log connection statuson_message: Main event handler with filtering logic_handle_claude_request: Process message, call Claude, send response_extract_message_content: Remove bot mentions from messageclose: Clean shutdown of resources
Future Enhancements
Potential improvements for future versions:
- Slash Commands: Support
/claude <message>in addition to @mentions - Thread Support: Create threads for long conversations
- Reaction Controls: React with ❌ to stop processing, ♻️ to retry
- Usage Tracking: Track API costs per channel/user
- Admin Commands:
/session reset,/session info,/config reload - Rate Limiting: Prevent spam/abuse
- Multi-user Sessions: Track per-user sessions instead of per-channel
- Attachment Support: Process code files attached to messages
Support
For issues or questions:
- Check logs:
journalctl -u claude-coordinator -f - Review test output:
uv run pytest tests/test_bot.py -v - Verify configuration: Ensure config.yaml is valid and channel IDs are correct
- Test manually: Run
uv run python -m claude_coordinator.botto see startup errors