Two-way Slack channel for Claude Code via Socket Mode + MCP
View the Project on GitHub jeremylongshore/claude-code-slack-channel
Two-way Slack channel for the Claude Code — chat from Slack DMs and channels, just like the terminal.
Uses Socket Mode (outbound WebSocket, no public URL) to bridge Slack messages into a running Claude Code session via MCP stdio. Five defense layers prevent prompt injection, token exfiltration, and unauthorized access. Three runtime options: Bun, Node.js, Docker.
If you’re using Claude Code, you’re chained to the terminal. Step away from your desk and the conversation stops. Your phone has Slack — but Slack can’t talk to your Claude session.
Anthropic shipped channels for Telegram, Discord, and a localhost demo (fakechat), but nothing for Slack — the tool most teams already live in. The existing Slack plugin in the official repo is a tool server: Claude can post to Slack, but Slack can’t talk back. It’s one-way.
Meanwhile, the channel protocol (claude/channel) is documented in the Anthropic channels reference but no community Slack channel exists yet. If you want to DM your Claude session from Slack, nothing exists to do it.
A single-file MCP server (~850 lines TypeScript) that connects Slack to Claude Code bidirectionally. Inbound messages arrive via Socket Mode (Slack’s outbound WebSocket — no public URL, no webhook endpoint, works behind any firewall). Outbound replies go through the Slack Web API. Everything runs locally as a subprocess of Claude Code.
The architecture mirrors the official Discord channel exactly — same gate/pairing/allowlist pattern — adapted for Slack’s APIs (timestamps as message IDs, mrkdwn instead of Markdown, dual tokens, files.uploadV2). Security is defense-in-depth: inbound gate drops ungated messages before MCP, outbound gate restricts replies, file exfiltration guard blocks state directory leaks, and system prompt hardening tells Claude to refuse manipulation attempts from messages.
| Aspect | Details |
|---|---|
| Who | Developers using Claude Code who collaborate via Slack |
| What | MCP channel server — pushes Slack events into Claude Code sessions, replies back |
| Where | Runs locally (your machine), connects to Slack via Socket Mode WebSocket |
| When | AFK coding — review PRs from your phone, check build status from the couch, pair with Claude from any device with Slack |
| Why | Only Slack channel for Claude Code. No public URL needed. Five security layers. Matches official plugin patterns exactly. |
| Layer | Technology | Purpose |
|---|---|---|
| Runtime | Bun / Node.js / Docker | Flexible execution — pick your preference |
| Protocol | MCP (stdio) | Standard Claude Code channel transport |
| Connection | @slack/socket-mode v2 | Outbound WebSocket to Slack — no public URL |
| API | @slack/web-api v7 | Send messages, upload files, add reactions |
| Security | Custom gate + allowlist | 5-layer defense: inbound gate, outbound gate, exfiltration guard, prompt hardening, token security |
anthropics/claude-plugins-official patterns exactly (MIT, same file structure, same conventions as Discord/Telegram)claude-code-slack-channel is a fully built, single-file MCP server that bridges Slack workspaces to Claude Code sessions. The entire implementation lives in server.ts (844 lines) with two skill files for configuration and access management. It uses three dependencies (@modelcontextprotocol/sdk, @slack/web-api, @slack/socket-mode) — no frameworks, no middleware, no build step for Bun.
The security architecture is the most substantial part of the design. Every inbound message passes through a gate function that checks sender identity against an allowlist before anything reaches the MCP notification layer. Outbound messages are similarly gated. State files (tokens, access config) are locked to 0o600 permissions with atomic writes. The system prompt explicitly warns Claude about prompt injection patterns.
This is v0.1.0 — feature-complete for the core use case (DM chat + channel monitoring) but pre-release. CI pipeline is configured and passing. No test suite yet. The immediate next step is real-world testing against a Slack workspace, then PR to anthropics/claude-plugins-official.
| Category | Technology | Version | Purpose |
|---|---|---|---|
| Runtime | Bun | 1.x | Primary execution runtime (also supports Node.js via tsx) |
| Protocol | @modelcontextprotocol/sdk | 1.27.x | MCP server + stdio transport |
| Connection | @slack/socket-mode | 2.0.x | Outbound WebSocket to Slack (no public URL) |
| API | @slack/web-api | 7.15.x | Slack REST API (messages, files, reactions) |
| Language | TypeScript | 5.9.x | Strict mode, type-checked |
| Container | Docker (oven/bun:1-slim) | — | Optional isolated runtime |
┌────────────────────────────┐
│ Slack Workspace │
│ (cloud, api.slack.com) │
└────────────┬───────────────┘
│ WebSocket (Socket Mode)
│ outbound from local machine
┌────────────▼───────────────┐
│ server.ts │
│ │
│ ┌──────────────────────┐ │
│ │ Socket Mode Client │ │ ← receives events
│ └──────────┬───────────┘ │
│ │ │
│ ┌──────────▼───────────┐ │
│ │ gate() │ │ ← drops ungated messages
│ │ (inbound security) │ │
│ └──────────┬───────────┘ │
│ │ deliver │
│ ┌──────────▼───────────┐ │
│ │ MCP Notification │──┼──→ Claude Code session (stdio)
│ └──────────────────────┘ │
│ │
│ ┌──────────────────────┐ │
│ │ Tools (reply, etc) │←─┼── Claude calls tools
│ │ + outbound gate │ │
│ │ + assertSendable() │ │
│ └──────────┬───────────┘ │
│ │ │
│ ┌──────────▼───────────┐ │
│ │ Web API Client │ │ ← sends messages back
│ └──────────┬───────────┘ │
└─────────────┼──────────────┘
│ HTTPS
┌─────────────▼──────────────┐
│ Slack Workspace │
└────────────────────────────┘
| Decision | Chosen | Over | Why | Revisit When |
|---|---|---|---|---|
| Connection | Socket Mode | Webhooks | No public URL needed, works behind NAT/firewalls | Never — this is a hard requirement for local-only |
| Framework | Raw SDK | @slack/bolt | Fewer deps, smaller surface, direct event control | If event routing gets complex (10+ event types) |
| State | JSON file | SQLite/DB | Zero deps, simple read/write, atomic via rename | If access lists exceed ~1000 entries |
| Architecture | Single file | Multi-module | Matches official plugins pattern, easy to review | If server exceeds ~1500 lines |
| Auth | Dual token (bot + app) | OAuth flow | Socket Mode requires app-level token; simplest setup | If distributing as installable Slack app |
Socket Mode over webhooks is the defining choice. It means the server is local-only — no DNS, no TLS cert, no port forwarding. The tradeoff is that Socket Mode requires an app-level token with connections:write, which is an extra setup step. Worth it for the security posture.
claude-code-slack-channel/
├── .claude-plugin/
│ └── manifest.json # Plugin metadata (name, version, description)
├── skills/
│ ├── configure.md # /slack-channel:configure — token setup
│ └── access.md # /slack-channel:access — pairing, allowlist, channels
├── server.ts # MCP server — all logic (844 lines)
├── package.json # 3 runtime deps, 2 dev deps
├── .mcp.json # MCP server registration (bun server.ts)
├── Dockerfile # Optional Docker runtime
├── tsconfig.json # TypeScript strict config
├── ACCESS.md # Access control schema docs
├── README.md # Setup guide + security overview
├── CONTRIBUTING.md # Contribution guidelines
├── SECURITY.md # Vulnerability reporting
├── CHANGELOG.md # Keep a Changelog format
├── CODE_OF_CONDUCT.md # Contributor Covenant 2.1
└── LICENSE # MIT
| Capability | Command | Notes |
|---|---|---|
| Install deps | bun install |
Or npm install for Node.js |
| Typecheck | bun run typecheck |
tsc --noEmit — strict mode |
| Run (Bun) | bun server.ts |
Primary runtime |
| Run (Node) | npx tsx server.ts |
Fallback runtime |
| Run (Docker) | docker build -t claude-slack-channel . && docker run --rm -i -v ~/.claude/channels/slack:/state claude-slack-channel |
Isolated runtime |
| Launch channel | claude --channels plugin:slack-channel@claude-code-plugins |
Current marketplace |
| Dev mode | claude --dangerously-load-development-channels server:slack |
Bypasses plugin allowlist |
| Configure | /slack-channel:configure xoxb-... xapp-... |
Writes tokens to ~/.claude/channels/slack/.env |
| Pair user | /slack-channel:access pair <code> |
Approves pending pairing code |
claude/channel capability (844 lines, type-checked)tsc --noEmit passes)