Skip to content

feat: add Claude Code Channels support for real-time Slack messaging#23

Open
marciogranzotto wants to merge 25 commits intoslackapi:mainfrom
marciogranzotto:feat/channels-support
Open

feat: add Claude Code Channels support for real-time Slack messaging#23
marciogranzotto wants to merge 25 commits intoslackapi:mainfrom
marciogranzotto:feat/channels-support

Conversation

@marciogranzotto
Copy link
Copy Markdown

Summary

Adds Claude Code Channels support to the Slack plugin, enabling real-time bidirectional messaging between Slack and Claude Code sessions via Socket Mode.

Closes #22

What it does

  • Real-time messaging: Receives DMs, @mentions, and watched channel messages from Slack and forwards them to Claude Code as channel notifications
  • Reply & react: Claude can send messages and add emoji reactions back to Slack
  • Permission relay: Tool approval prompts are forwarded to Slack with interactive Approve/Deny buttons — enables fully headless operation
  • Access control: Pairing-based sender gating with bootstrap flow for first user, subsequent user pairing via DM
  • Channel watching: Add/remove channels to monitor via manage_channels tool

Architecture

A single Bun process with three layers:

  • Slack Layer (@slack/bolt in Socket Mode) — receives events, sends replies
  • Bridge Layer — transforms events between Slack and MCP notification formats
  • MCP Layer (@modelcontextprotocol/sdk) — claude/channel + claude/channel/permission capabilities

New files

src/
├── index.ts       # Entry point: env validation, module wiring, startup
├── settings.ts    # Settings file read/write with Zod validation
├── gating.ts      # Sender allowlist, pairing codes (crypto.randomBytes)
├── mcp.ts         # MCP server: capabilities, 4 tools, permission relay
├── slack.ts       # Bolt app: Socket Mode, event/action handlers
└── bridge.ts      # Event transformation, tool dispatch, name caching

Test coverage

62 tests across 5 files covering:

  • Settings read/write/corruption handling
  • Gating allowlist, pairing code lifecycle, bootstrap mode
  • Bridge event transformation, verdict parsing, authorization, name caching
  • Reaction filtering, mention gating, source attributes
  • manage_access/manage_channels success paths
  • End-to-end: DM→notification→reply, permission relay cycle, bootstrap pairing

Test plan

  • Unit tests pass (bun test — 62/62)
  • Standalone server connects to Slack via Socket Mode
  • Bootstrap pairing flow (ephemeral code → pair CODE → access granted)
  • DM to bot → Claude receives notification → replies via tool
  • @mention in channel → notification received
  • Watch channel → all messages forwarded
  • Emoji reaction on bot message → notification with original message text
  • React tool adds emoji to Slack message
  • Permission relay with interactive Approve/Deny buttons
  • conversations.join gracefully handles already-in-channel

🤖 Generated with Claude Code

marciogranzotto and others added 19 commits March 26, 2026 19:48
Spec covers architecture (Bolt + MCP SDK), Socket Mode event delivery,
headless operation with permission relay, pairing flow, and sender gating.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix 10 issues from two review rounds:
- Remove pendingCodes from settings schema (in-memory only)
- Define lastActiveContext for permission relay routing
- Limit bootstrap to one active pairing code
- Add pair_user action to manage_access tool
- Document meta key constraints and name resolution caching
- Define reaction event filter (bot messages only)
- Defer workspace gating to Future Work
- Add --dangerously-load-development-channels flag docs
- Specify tool authorization via lastActiveContext check
- Require user IDs in manage_access (resolve handles externally)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
9 tasks covering project setup, settings, gating, MCP server, bridge,
Slack module, entry point, integration tests, and README update.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements settings persistence with atomic writes, Zod schema validation,
and graceful fallback to defaults on missing/corrupt/invalid settings files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Define 4 tools (reply, react, manage_access, manage_channels) and
channel instructions for the Slack channel server. Set up MCP Server
with claude/channel capabilities, request handlers delegating to
Bridge, and permission_request notification relay.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bridge is the central coordinator that transforms events between Slack
and MCP. Implements message handling with gating checks, permission
relay with verdict parsing, tool call dispatch, bootstrap/pairing
flows, and user/channel name resolution caching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ssion relay, pairing flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Correctness & security:
- Add source: 'slack' to all notification meta (spec compliance)
- Widen verdict regex to [a-km-z0-9] for future-proof ID matching
- Add authorization check (require active context) on reply/react tools
- Fetch reacted message text via conversations.history for context
- Fix pair_user to DM target user via conversations.open
- Restrict bootstrap pairing to DMs only (prevent public code echo)
- Fix conversations.join ordering — join before adding to watch list
- Use crypto.randomBytes for pairing codes instead of Math.random
- Add isError: true to tool error responses (MCP protocol)
- Fix resolveChannelName to return empty on failure (not raw ID)

Cleanup:
- Remove dynamic imports in writePairingCodeFile
- Remove unused say parameter in slack.ts
- Fix README: correct .mcp.json example and settings schema
- Fix tsconfig.json: remove invalid bun-types reference
- Add package.json metadata (name, version, type, scripts)

Tests (14 new):
- Reaction handling (bot filter, gating, notification format)
- manage_access/manage_channels success paths
- Mention gating, source attribute, reply/react auth, bootstrap DM

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Catch already_in_channel and missing_scope errors when joining
  channels, so watching still works if the bot is already a member
- Add channels:join and channels:history to required scopes in
  setup docs and README

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace text-based "yes/no <id>" permission prompts with Slack Block Kit
buttons. Clicking a button emits the verdict and updates the message to
show the result. Text-based verdicts still work as a fallback.

Also adds interactivity setup step to the Slack app setup docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CLAUDE.md: add Channel Server section with tools, setup, and running
- .claude-plugin/plugin.json: update description to mention channels,
  fix stale homepage URL
- slack-messaging skill: correct reaction tool availability claim
- .mcp.json: clear credentials from committed config

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 27, 2026 14:28
@salesforce-cla
Copy link
Copy Markdown

Thanks for the contribution! Before we can merge this, we need @marciogranzotto to sign the Salesforce Inc. Contributor License Agreement.

"name": "slack",
"description": "Slack integration for searching messages, sending communications, managing canvases, and more",
"description": "Slack integration for searching messages, sending communications, managing canvases, and real-time bidirectional messaging via Channels",
"version": "1.0.0",
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, version needs to be bumped after merging

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a local Bun-based “slack-channel” MCP server that implements Claude Code Channels for real-time Slack ↔ Claude bidirectional messaging (Socket Mode), including sender gating/pairing and permission-relay workflows.

Changes:

  • Introduces a new channel server stack (src/) that bridges Slack events to notifications/claude/channel, and exposes tools (reply, react, manage_access, manage_channels) + permission relay.
  • Adds settings persistence + allowlist/pairing logic, plus extensive Bun unit/integration tests.
  • Updates plugin/docs/skills to document Channels setup and usage.

Reviewed changes

Copilot reviewed 20 out of 23 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tsconfig.json Adds TS config for the new Bun/TS channel server and tests.
package.json Defines Bun scripts and dependencies for the channel server.
bun.lock Locks dependency versions for the new server.
src/index.ts Entry point wiring settings, gating, Slack (Socket Mode), and MCP (stdio).
src/settings.ts Zod-validated settings read/write with atomic persistence.
src/gating.ts Allowlist + pairing-code lifecycle for sender gating.
src/mcp.ts MCP server capabilities/tools + permission_request notification handler.
src/slack.ts Slack Bolt app initialization + event/action handlers.
src/bridge.ts Core transform layer: Slack events ↔ MCP notifications; tool dispatch; caching.
tests/settings.test.ts Tests settings defaults, corruption handling, and atomic writes.
tests/gating.test.ts Tests allowlist behavior + pairing code generation/expiry/consumption.
tests/mcp.test.ts Verifies MCP tool schemas and channel instructions.
tests/bridge.test.ts Validates event gating/transforms, verdict parsing, caching, and tool auth.
tests/integration.test.ts Smoke-tests DM→notification→reply and permission relay flows end-to-end (mocked).
README.md Documents Channels research-preview setup, config, and pairing.
CLAUDE.md Documents the new channel server and its tools.
docs/slack-app-setup.md Step-by-step Slack app configuration for Socket Mode + scopes/events.
docs/superpowers/specs/2026-03-26-slack-channels-design.md Design spec for the channel server architecture and behavior.
docs/superpowers/plans/2026-03-26-slack-channels.md Implementation plan/checklist for building the feature.
skills/slack-messaging/SKILL.md Updates guidance to reflect the new react capability in the channel server.
.mcp.json Adds the local slack-channel server entry alongside the remote Slack MCP server.
.gitignore Ignores .env and node_modules/.
.claude-plugin/plugin.json Updates plugin metadata to mention Channels + correct homepage URL.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Deep copy DEFAULT_SETTINGS in readSettings to prevent shared-reference
  mutation of nested gating object and watchedChannels array
- Validate allowlist on permission button clicks — extract body.user.id
  from Slack action payload and check gating before emitting verdict
- Show both slack and slack-channel entries in README .mcp.json snippet
  to prevent users from overwriting existing remote server config
- Register event handlers before app.start() to avoid missing events
  during the Socket Mode connection window
- Update spec regex to match implementation: [a-km-z0-9]{5}

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mwbrooks mwbrooks added the discussion M-T: An issue where more input is needed to reach a decision label Mar 30, 2026
@mwbrooks
Copy link
Copy Markdown
Member

Super cool PR @marciogranzotto! 🤯

I've marked this as discussion because we'll need feedback from the Slack MCP Server team before we add support for Claude Code Channels.

While we wait, I'll give the PR some early feedback 😄

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs/ changes look like an accidental commit. We typically reserve docs/ for the public documentation on https://docs.slack.dev, so these files should be removed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right — the docs/superpowers/ files (plans and specs) are development artifacts that shouldn't be in the repo. Removed in a3cf06f.

For docs/slack-app-setup.md, this is the Slack app setup guide for the Channels feature — it's user-facing documentation, not a dev artifact. Let me know if it should be moved elsewhere.

🤖 Generated with Claude Code

.mcp.json Outdated
}
},
"slack-channel": {
"command": "bun",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Why bun? I don't think we can assume all developers have bun installed on their system.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. bun was chosen for its native TypeScript execution (no build step), but you're right that we can't assume it's available. Switched to npx tsx so only Node.js/npm is required. Fixed in bd6036b.

🤖 Generated with Claude Code

bun.lock Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: We'd need to discuss adding bun support to the project. I'd suggest removing this file

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — removed bun.lock and added it to .gitignore. The project now uses package-lock.json via npm. Fixed in bd6036b.

🤖 Generated with Claude Code

package.json Outdated
"version": "0.0.1",
"type": "module",
"scripts": {
"start": "bun src/index.ts",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: I'd suggest npx / npm instead of bun.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — switched to npm-compatible tooling throughout:

  • "start": "tsx src/index.ts" (via tsx as a dependency)
  • "test": "vitest run" (migrated all 5 test files from bun:test to vitest)
  • Replaced @types/bun with @types/node

Fixed in bd6036b and 790e1b9.

🤖 Generated with Claude Code

README.md Outdated

Save the configuration. You will also see a connect button once added. Click that to authenticate into your Slack Workspace.

## Channels (Research Preview)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: This plugin works with Cursor and Claude Code. So we should clarify that this is a Claude Code-specific feature.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Updated the heading to "Channels for Claude Code (Research Preview)" to make it clear this is a Claude Code-specific feature. Fixed in 8323955.

🤖 Generated with Claude Code

marciogranzotto and others added 4 commits April 1, 2026 18:48
Remove docs/superpowers/ planning and spec files that were
accidentally committed. The docs/ directory is reserved for
public documentation on docs.slack.dev.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace bun runtime with tsx for TypeScript execution and
vitest for testing. This removes the bun dependency so only
Node.js/npm is required.

- Replace bun with npx tsx in .mcp.json and package.json
- Replace bun test with vitest in package.json
- Replace @types/bun with @types/node, tsx, vitest
- Remove bun.lock, generate package-lock.json
- Add bun.lock to .gitignore

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace bun:test imports with vitest across all 5 test files.
Replace mock() with vi.fn() in bridge and integration tests.
All matchers are API-compatible between bun:test and vitest.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update README heading to 'Channels for Claude Code (Research Preview)'
to clarify this feature requires Claude Code. Replace all bun references
with npx tsx in README, CLAUDE.md, and slack-app-setup.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla:signed discussion M-T: An issue where more input is needed to reach a decision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Add Claude Code Channels support for real-time Slack messaging

3 participants