Let your team talk to Claude Code directly from Slack. Claude runs locally on your machine using your existing Claude plan — no API keys, no per-token costs. Your codebase, your CLAUDE.md, your MCP tools — all accessible through a Slack message.
You (Slack): @Claude What files are in the src directory?
Bot (Slack): Found 12 files in src/: index.ts, app.ts, config.ts...
Mention @YourBot in any channel, DM it directly, or use the optional /claude slash command. Reply in threads and it remembers the full conversation.
┌──────────────────────────┐
│ Slack (your workspace) │
│ │
│ /claude <prompt> │
│ @bot <message> │
└──────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ Worker (Railway / any host) │
│ │
│ FastAPI webhook receiver │
│ Slack signature verification │
│ Quick commands (help, status)│
│ │
│ AI tasks → writes JSON to │
│ GitHub tasks/ directory │
└──────────────┬───────────────┘
│
GitHub repo (tasks/)
│
▼
┌──────────────────────────────┐
│ Daemon (your Mac, always on) │
│ │
│ Polls GitHub every 5s │
│ Picks up task JSON files │
│ Runs: claude -p "prompt" │
│ Posts result to Slack │
│ Deletes task file │
│ Auto-commits workspace │
└──────────────────────────────┘
Why this architecture?
- Claude Code CLI runs locally with your subscription (no API key costs)
- Claude has full access to your codebase, CLAUDE.md, and MCP tools
- Your workspace stays on your machine — the worker only routes messages
- Thread context is preserved (the daemon fetches Slack thread history)
The daemon and your project don't need to live in the same folder. The daemon installs to its own directory (~/claude-code-slack/ by default), and WORKSPACE_DIR in its .env file points to wherever your repo clone lives. They're completely independent.
Claude Code runs inside WORKSPACE_DIR, so it automatically picks up any CLAUDE.md file in that repo. This means you can give Claude project-specific instructions that apply to every Slack command:
# CLAUDE.md (in your project repo root)
This is a Next.js 14 app with App Router.
- Tests are in __tests__/ — run with `npm test`
- Never modify .env or .env.local
- API routes are in app/api/
- We use Tailwind CSS and shadcn/uiWhen someone asks @Bot how do I add a new API route?, Claude already knows your project structure and conventions.
- Slash commands —
/claude <anything>from any channel - @mentions —
@YourBot summarize the READMEin channels - DMs — Message the bot directly for private conversations
- Thread context — Reply in threads and the bot sees prior messages
- Markdown conversion — Claude's markdown output is converted to Slack's mrkdwn format
- Auto-sync — Workspace changes are committed and pushed after each task
- Menu bar status — Green/red circle shows daemon health at a glance (macOS)
- Workspace isolation — Claude runs in a separate clone, not your working directory
| Requirement | Version | Check |
|---|---|---|
| macOS | 10.15+ | sw_vers |
| Python | 3.9+ | python3 --version |
| Node.js | 18+ | node --version |
| Claude Code CLI | Latest | claude --version |
| GitHub CLI | Latest | gh --version |
| A Slack workspace | — | You must be an admin or able to install apps |
| A Railway account | — | railway.app — free tier works, or use any container host |
# Node.js (via nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
nvm install --lts
# Claude Code CLI
npm install -g @anthropic-ai/claude-code
# GitHub CLI
brew install gh # or download from https://github.com/cli/cli/releases
gh auth login
# Python dependencies (daemon)
pip3 install httpx python-dotenv rumpsThis is the repo Claude Code will operate on. It can be an existing project or a new one.
# Option A: Use an existing repo
# Just note the owner/name, e.g. "youruser/my-project"
# Option B: Create a dedicated workspace repo
gh repo create my-claude-workspace --privateCreate a tasks/ directory with a .gitkeep:
cd my-claude-workspace
mkdir tasks
touch tasks/.gitkeep
git add tasks/.gitkeep && git commit -m "Add tasks directory" && git pushThe daemon needs a separate clone — not your working directory. This prevents Claude from accessing personal files.
gh repo clone youruser/my-project ~/claude-workspace- Go to https://github.com/settings/tokens
- Generate new token (classic) with
reposcope - Copy the token — you'll need it for both daemon and worker
- Go to https://api.slack.com/apps
- Click Create New App → From scratch
- Name it (e.g., "Claude Code") and select your workspace
Navigate to OAuth & Permissions and add these Bot Token Scopes:
| Scope | Purpose |
|---|---|
app_mentions:read |
Respond when @mentioned |
chat:write |
Post messages to channels |
commands |
Handle slash commands |
im:history |
Read DM history |
im:read |
Access DMs |
im:write |
Send DMs |
Navigate to Event Subscriptions:
- Toggle Enable Events to On
- Set Request URL to:
https://your-railway-url.up.railway.app/slack/events(You'll get this URL after deploying the worker — come back to this step) - Under Subscribe to bot events, add:
app_mentionmessage.im
Navigate to Slash Commands and create one:
| Field | Value |
|---|---|
| Command | /claude (or whatever you prefer) |
| Request URL | https://your-railway-url.up.railway.app/slack/events |
| Description | Talk to Claude Code |
| Usage Hint | [your question or command] |
- Navigate to Install App
- Click Install to Workspace
- Copy the Bot User OAuth Token (
xoxb-...) - Go to Basic Information and copy the Signing Secret
Railway is the easiest option, but any container host works (Fly.io, Render, etc.).
- Fork or push this repo to GitHub
- Go to https://railway.app and create a new project
- Select Deploy from GitHub repo
- Set the Root Directory to
worker/ - Add environment variables:
| Variable | Value |
|---|---|
PORT |
8080 |
GITHUB_TOKEN |
ghp_... (your PAT) |
GITHUB_REPO |
youruser/your-repo |
GITHUB_BRANCH |
main |
SLACK_BOT_TOKEN |
xoxb-... |
SLACK_SIGNING_SECRET |
From Slack app Basic Info |
- Deploy. Copy the public URL (e.g.,
https://your-app.up.railway.app) - Go back to your Slack app settings and update the Request URL for both Events and Slash Commands to:
https://your-app.up.railway.app/slack/events
cd worker
docker build -t claude-code-slack-worker .
docker run -p 8080:8080 --env-file .env claude-code-slack-workercd daemon
# Create .env from template
cp .env.example .env
# Edit with your values
# IMPORTANT: Set WORKSPACE_DIR to the CLONED repo path, not your working directory
nano .envpip3 install -r requirements.txt
chmod +x install.sh start-daemon.sh
./install.shThis will:
- Copy daemon files to
~/claude-code-slack/ - Auto-detect Claude CLI and Node.js paths
- Generate LaunchAgent plists
- Start the daemon and status bar
# Must set PATH to include node
PATH="$(dirname $(which node)):$PATH" python3 main.py- Check the menu bar for a green circle (daemon running)
- In Slack, type:
/claude what files do you see? - You should get a response within 10-30 seconds
| Variable | Required | Default | Description |
|---|---|---|---|
GITHUB_TOKEN |
Yes | — | GitHub PAT with repo scope |
GITHUB_REPO |
Yes | — | owner/repo format |
GITHUB_BRANCH |
No | main |
Branch to watch for tasks |
SLACK_BOT_TOKEN |
Yes | — | Slack bot token (xoxb-...) |
WORKSPACE_DIR |
Yes | — | Path to cloned repo |
CLAUDE_BIN |
No | claude |
Full path to Claude CLI |
CLAUDE_TIMEOUT |
No | 300 |
Max seconds per task |
POLL_INTERVAL |
No | 5 |
Seconds between task checks |
| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 8080 |
Server port |
GITHUB_TOKEN |
Yes | — | Same PAT as daemon |
GITHUB_REPO |
Yes | — | Same repo as daemon |
GITHUB_BRANCH |
No | main |
Same branch as daemon |
SLACK_BOT_TOKEN |
Yes | — | Same token as daemon |
SLACK_SIGNING_SECRET |
Yes | — | From Slack app settings |
| Symptom | Cause | Fix |
|---|---|---|
env: node: No such file or directory |
PATH not set for LaunchAgent | Re-run install.sh — it auto-detects Node.js path |
Claude Code CLI not found |
CLAUDE_BIN not set or wrong path |
Set full path: which claude |
| Menu bar shows red | Daemon process crashed | Check logs: tail -f ~/Library/Logs/claude-code-slack-daemon.log |
| Daemon starts then immediately exits | Missing or invalid .env |
Verify all required vars are set in ~/claude-code-slack/.env |
| Symptom | Cause | Fix |
|---|---|---|
| Slack says "dispatch_failed" | Worker URL wrong or not deployed | Verify Railway URL in Slack app settings |
| 401 on every request | Signing secret wrong | Copy signing secret from Slack > Basic Information |
| Railway deploy fails | Missing requirements | Check worker/requirements.txt is committed |
| Port conflict on Railway | PORT not set | Railway sets PORT automatically — ensure your code reads os.getenv("PORT", 8080) |
| Symptom | Cause | Fix |
|---|---|---|
| Bot doesn't respond to @mentions | Missing app_mentions:read scope |
Add scope, reinstall app |
| Bot doesn't respond in DMs | Missing im:history + im:read scopes |
Add scopes, reinstall app |
| "not_in_channel" error | Bot not in the channel | Invite bot: /invite @YourBot |
| Slash command shows nothing | Worker not responding within 3s | Worker must send immediate acknowledgment |
| Symptom | Cause | Fix |
|---|---|---|
| Tasks not picked up | Daemon not running or wrong repo | Check daemon logs and GITHUB_REPO in both .env files |
| Multiple responses | Multiple daemon processes running | Kill extras: pkill -f "python3.*main.py" |
| Git push fails | Clone is stale or has conflicts | Re-clone: rm -rf ~/claude-workspace && gh repo clone ... |
| 404 on task file | Task already deleted | Usually harmless — means another process handled it |
| Symptom | Cause | Fix |
|---|---|---|
| LaunchAgent not starting after reboot | Plist not loaded | launchctl load ~/Library/LaunchAgents/com.claude-code-slack.daemon.plist |
| "Operation not permitted" | macOS privacy settings | System Preferences > Privacy > Full Disk Access > Terminal |
| Status bar icon missing | Statusbar LaunchAgent not loaded | Check: `launchctl list |
# View logs
tail -f ~/Library/Logs/claude-code-slack-daemon.log
# Restart daemon
launchctl kickstart -k gui/$(id -u)/com.claude-code-slack.daemon
# Stop daemon
launchctl unload ~/Library/LaunchAgents/com.claude-code-slack.daemon.plist
# Start daemon
launchctl load ~/Library/LaunchAgents/com.claude-code-slack.daemon.plist
# Full uninstall
cd daemon && ./install.sh uninstallWhen you reply in a Slack thread, the daemon fetches the full thread history and injects it as context for Claude. This means Claude "sees" the prior conversation:
User: @Bot What does the auth module do?
Bot: The auth module handles JWT token generation and validation...
User: (in thread) Can you refactor it to use sessions instead?
Bot: Based on our discussion about the auth module, here's how to
refactor from JWT to sessions...
The context is reconstructed from Slack's API on each request — no local state needed.
- Workspace isolation — Always point
WORKSPACE_DIRat a separate clone, not your working directory. This prevents Claude from accessing personal files, credentials, or other repos. - Slack signature verification — The worker validates every request using Slack's HMAC-SHA256 signature. Never disable this in production.
- GitHub token scope — Use a fine-grained PAT scoped to only the repos you need.
- No secrets in responses — Be mindful that Claude's output is posted to Slack. If your codebase contains secrets, Claude might reference them. Use
.gitignoreand.envfiles properly.
MIT
Built by Alcanah Partners — AI operations for growing businesses.