This feature implements the Claude Code Channels extension — an experimental MCP protocol extension using
notifications/claude/channel.Why Channels?
Channels fill a gap that tools, resources, and prompts cannot address — server-initiated push notifications:| Aspect | Tool | Resource | Channel |
|---|---|---|---|
| Purpose | Execute actions | Provide data | Push real-time events |
| Direction | Client triggers | Client pulls | Server pushes |
| Side effects | Yes | No | No (notification only) |
| Two-way | Request/response | Read-only | Optional reply tool |
| Use case | Actions, calculations | Context loading | Alerts, chat bridges, monitoring |
- CI/CD alerts — deploy status, build failures, PR notifications
- Chat bridges — WhatsApp, Telegram, Slack, Discord messaging with Claude
- Error monitoring — forward application errors for debugging assistance
- Agent notifications — notify when AI agents complete background tasks
- Job completion — alert when background jobs or workflows finish
Creating Channels
Class Style
Use class decorators for channels that need complex message transformation or two-way communication:Function Style
Use thechannel() builder for simple transform-only channels:
Channel Sources
Every channel has a source that determines how events flow into it:| Source | Trigger | Config |
|---|---|---|
webhook | HTTP POST to a path | { type: 'webhook', path: '/hooks/deploy' } |
app-event | In-process event bus | { type: 'app-event', event: 'error' } |
agent-completion | Agent finishes | { type: 'agent-completion', agentIds?: ['reviewer'] } |
job-completion | Job completes | { type: 'job-completion', jobNames?: ['daily-report'] } |
service | Persistent connection | { type: 'service', service: 'whatsapp-business' } |
manual | Programmatic push | { type: 'manual' } |
Webhook Source
External services send HTTP POST requests to the configured path:App Event Source
Your application emits events to theChannelEventBus:
Agent & Job Completion
Auto-subscribe to system events with optional filtering:Service Connector
Service connectors maintain persistent connections to external messaging services. Claude sends messages via channel-contributed tools, and incoming responses arrive as channel notifications. This is the most powerful channel pattern — it turns Claude Code into a full messaging client.- Claude calls
send-whatsapp({ to: "+1234567890", text: "Hi Alice!" }) - Message is delivered to Alice’s WhatsApp
- Alice replies “Got it, will review the PR”
onConnect()’s listener fires →pushIncoming()→onEvent()transforms it- Claude sees:
<channel source="whatsapp" sender="Alice">Alice: Got it, will review the PR</channel> - Claude can continue the conversation by calling
send-whatsappagain
Service connector channels call
onConnect() during scope initialization and onDisconnect() during teardown. The connection persists for the lifetime of the server.File Watcher
Watch file system paths and notify Claude when files change. UsesonConnect() to start watching.
Manual Push
Push notifications programmatically from any code with scope access:Replay Buffer
By default, events are lost when no Claude Code sessions are connected. Enable replay to buffer events:- Events arrive and are processed normally via
onEvent() - Each notification is also stored in an in-memory ring buffer
- When a new session connects,
replayBufferedEvents(sessionId)sends all buffered events - Replayed events include
replayed: "true"in their meta
Two-Way Communication
SettwoWay: true to enable Claude Code to reply back through the channel. This auto-registers a channel-reply tool.
channel-reply tool:
Registration
In an App
Enable at Server Level
Wire Protocol
Channel notifications use the experimentalnotifications/claude/channel JSON-RPC method:
experimental: { 'claude/channel': {} } in its capabilities. Only sessions whose client capabilities include this extension receive channel notifications.
Session Isolation
Channel notifications are session-scoped to prevent data leaking between connected agents.How It Works
- When a session initializes with
claude/channelcapability, it is auto-subscribed to all channels - Notifications are only delivered to sessions subscribed to the specific channel
- Session-targeted events (job completions, agent results) go ONLY to the originating session
- Global events (webhooks, file changes) go to all subscribed sessions
- Channel subscriptions are cleaned up automatically on disconnect
Delivery Matrix
| Source | Target | Delivery |
|---|---|---|
| Webhook, file-watcher, app-event | All subscribers | Every subscribed session |
| Agent completion | Originating session | ONLY the session that triggered the agent |
| Job completion | Originating session | ONLY the session that triggered the job |
| Manual push | Configurable | send() = all subscribers, sendToSession() = one session |
Selective Subscriptions
Sessions can subscribe to specific channels instead of all:Security
Sender Gating
For chat bridges, always validate individual sender identity:Meta Key Restrictions
Meta keys must be valid identifiers (letters, digits, underscores). Invalid keys are rejected at startup:Related
@Channel Decorator
Full decorator API reference
ChannelContext
Context class API reference
ChannelRegistry
Registry API reference
Claude Code Channels
Official Claude Code channels specification