claude-almanac
Integrations

Channels

MCP servers that push events from external systems into a running Claude Code session.

Claude Code Channels

Channels push events from external systems into a running Claude Code session so Claude can react to things happening outside the terminal. They are MCP servers that deliver messages, alerts, and webhooks while the session is open.

Overview

A channel is an MCP server that pushes events into your running Claude Code session. Channels can be one-way (alerts, webhooks) or two-way (chat bridges where Claude replies back through the same channel). Events only arrive while the session is open; for always-on use, run Claude in a background process or persistent terminal.

Requirements:

  • Claude Code v2.1.80 or later
  • claude.ai login (Console and API key auth not supported)
  • Bun runtime (for pre-built plugins)
  • Team/Enterprise: admin must enable channels

Status: Research preview. The --channels flag syntax and protocol may change.

Supported Channels

ChannelTypeSetupToken Required
TelegramTwo-wayCreate bot via BotFatherYes
DiscordTwo-wayCreate bot in Developer PortalYes
iMessageTwo-waymacOS only, reads Messages directlyNo
FakechatTwo-wayLocalhost demo UI on port 8787No

Installing a Channel Plugin

All channels are installed as plugins from the official marketplace:

# Install the plugin
/plugin install telegram@claude-plugins-official

# If not found, refresh marketplace first
/plugin marketplace update claude-plugins-official

# Activate after install
/reload-plugins

Configuring Credentials

Telegram and Discord require bot tokens:

# Telegram
/telegram:configure <token>

# Discord
/discord:configure <token>

Tokens are saved to ~/.claude/channels/<platform>/.env. You can also set TELEGRAM_BOT_TOKEN or DISCORD_BOT_TOKEN in your shell environment.

Starting with Channels Enabled

Exit Claude Code and restart with the --channels flag:

# Single channel
claude --channels plugin:telegram@claude-plugins-official

# Multiple channels (space-separated)
claude --channels plugin:telegram@claude-plugins-official plugin:discord@claude-plugins-official

Pairing (Telegram and Discord)

  1. Send any message to your bot on the platform
  2. The bot replies with a pairing code
  3. In Claude Code, approve the code:
/telegram:access pair <code>
/discord:access pair <code>
  1. Lock down access to your account only:
/telegram:access policy allowlist
/discord:access policy allowlist

iMessage Setup

iMessage requires macOS and reads your Messages database directly:

  1. Grant Full Disk Access to your terminal (System Settings > Privacy & Security > Full Disk Access)
  2. Install: /plugin install imessage@claude-plugins-official
  3. Start: claude --channels plugin:imessage@claude-plugins-official
  4. Text yourself to test (self-chat bypasses access control)
  5. Allow other senders: /imessage:access allow +15551234567

Quickstart: Fakechat Demo

Fakechat runs a chat UI on localhost with no authentication required:

# Install
/plugin install fakechat@claude-plugins-official

# Start (exit and restart)
claude --channels plugin:fakechat@claude-plugins-official

# Open http://localhost:8787 and type a message

Messages arrive in your session as <channel source="fakechat"> events. Claude reads them, does the work, and replies through the chat UI.

Security

Sender Allowlists

Every channel plugin maintains a sender allowlist. Only IDs you have added can push messages; everyone else is silently dropped.

  • Telegram/Discord: Bootstrap via pairing flow
  • iMessage: Self-chat is automatic; add others by handle

Access Controls

  • Being in .mcp.json alone is not enough to push messages; a server must also be named in --channels
  • The allowlist also gates permission relay (anyone who can reply can approve/deny tool use)
  • Only allowlist senders you trust

Permission Relay

Two-way channels can relay permission prompts to your phone or another device. When Claude needs tool approval, the prompt appears both in your terminal and through the channel. The first answer (from either location) is applied.

Requirements: Claude Code v2.1.81+, channel must declare claude/channel/permission capability.

Permission relay covers tool-use approvals (Bash, Write, Edit). Project trust and MCP server consent dialogs only appear in the local terminal.

Building Custom Channels

A custom channel needs to:

  1. Declare the claude/channel capability
  2. Emit notifications/claude/channel events
  3. Connect over stdio transport

Minimal Webhook Receiver (TypeScript/Bun)

#!/usr/bin/env bun
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const mcp = new Server(
  { name: "webhook", version: "0.0.1" },
  {
    capabilities: { experimental: { "claude/channel": {} } },
    instructions:
      'Events from the webhook channel arrive as <channel source="webhook">. Read and act, no reply expected.',
  }
);

await mcp.connect(new StdioServerTransport());

Bun.serve({
  port: 8788,
  hostname: "127.0.0.1",
  async fetch(req) {
    const body = await req.text();
    await mcp.notification({
      method: "notifications/claude/channel",
      params: {
        content: body,
        meta: { path: new URL(req.url).pathname, method: req.method },
      },
    });
    return new Response("ok");
  },
});

Register in .mcp.json:

{
  "mcpServers": {
    "webhook": { "command": "bun", "args": ["./webhook.ts"] }
  }
}

Test with the development flag:

claude --dangerously-load-development-channels server:webhook

# In another terminal:
curl -X POST localhost:8788 -d "build failed on main"

Notification Format

FieldTypeDescription
contentstringEvent body (becomes <channel> tag body)
metaRecord<string, string>Attributes on the <channel> tag (letters/digits/underscores only)

Adding a Reply Tool

For two-way channels, add tools: {} to capabilities and register tool handlers:

import {
  ListToolsRequestSchema,
  CallToolRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "reply",
      description: "Send a message back over this channel",
      inputSchema: {
        type: "object",
        properties: {
          chat_id: { type: "string" },
          text: { type: "string" },
        },
        required: ["chat_id", "text"],
      },
    },
  ],
}));

mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
  if (req.params.name === "reply") {
    const { chat_id, text } = req.params.arguments as {
      chat_id: string;
      text: string;
    };
    // Send to your platform here
    return { content: [{ type: "text", text: "sent" }] };
  }
  throw new Error(`unknown tool: ${req.params.name}`);
});

Server Options

FieldTypeRequiredDescription
capabilities.experimental['claude/channel']objectYesAlways {}. Registers notification listener
capabilities.experimental['claude/channel/permission']objectNoAlways {}. Opts in to permission relay
capabilities.toolsobjectNoTwo-way only. Enables tool discovery
instructionsstringNoAdded to Claude's system prompt. Describe events and reply behavior

Testing During Research Preview

Custom channels are not on the approved allowlist. Use the development flag:

# Plugin you're developing
claude --dangerously-load-development-channels plugin:yourplugin@yourmarketplace

# Bare .mcp.json server
claude --dangerously-load-development-channels server:webhook

The bypass is per-entry. The channelsEnabled org policy still applies.

Enterprise Controls

SettingPurposeDefault
channelsEnabledMaster switch. Must be true for any channel to deliverChannels blocked
allowedChannelPluginsWhich plugins can register (replaces Anthropic allowlist)Anthropic default list applies

Pro and Max users without an organization skip these checks.

Enable for Your Organization

Enable from claude.ai > Admin settings > Claude Code > Channels, or set channelsEnabled: true in managed settings.

Restrict Allowed Plugins

{
  "channelsEnabled": true,
  "allowedChannelPlugins": [
    { "marketplace": "claude-plugins-official", "plugin": "telegram" },
    { "marketplace": "claude-plugins-official", "plugin": "discord" },
    { "marketplace": "acme-corp-plugins", "plugin": "internal-alerts" }
  ]
}

When set, this replaces the Anthropic allowlist entirely. An empty array blocks all allowlisted plugins but --dangerously-load-development-channels can still bypass it.

How Channels Compare

FeatureWhat It DoesGood For
Claude Code on webFresh cloud sandbox cloned from GitHubDelegating self-contained async work
Claude in SlackSpawns web session from @Claude mentionStarting tasks from team conversation context
Standard MCPClaude queries on demand; nothing pushedGiving Claude read/query access to a system
Remote ControlDrive local session from claude.ai or mobile appSteering in-progress session while away from desk
ChannelsPush events from external systems into running sessionChat bridges, CI webhooks, monitoring alerts

Sources

On this page