Skip to content

Channels

Push chat messages, webhooks and alerts into a running Claude Code session through channel plugins for Telegram, Discord, iMessage or your own server.

Most integrations make Claude go and fetch something. A channel works the other way round: an MCP server pushes an event into the Claude Code session you already have open, and Claude reacts. Channels can also be two-way, so Claude can answer back through the same route, which turns Telegram or Discord into a remote chat window for a session running on your own machine against your real files.

Note: Channels are a research preview. They need Anthropic authentication (a claude.ai login or a Console API key) and do not work on Amazon Bedrock, Google Vertex AI or Microsoft Foundry. Team and Enterprise organisations must switch them on first; see Admin controls.

Two things to understand before you start:

  • Events only arrive while the session is running. For an always-listening setup, keep Claude in a persistent terminal (tmux, screen) or a background session.
  • Replies are shown on the other platform, not in your terminal. Locally you see the inbound message, then a tool call and a short confirmation such as "sent".

You install a channel as a plugin and configure it with your own credentials. Telegram, Discord and iMessage ship in the preview, plus a local demo called fakechat. To build your own, see the channels reference.

Try it locally with fakechat

Fakechat runs a chat page on localhost. No accounts, no tokens, so it is the quickest way to see the mechanics.

You need:

  • Claude Code installed and signed in with a claude.ai account or Console API key.
  • Bun, because the bundled channel plugins are Bun scripts. Check with bun --version.
  • On Team, Enterprise or a managed Console organisation, channels enabled by your admin.

1. Install the plugin. Start claude and run:

/plugin install fakechat@claude-plugins-official

Choose the user scope when asked so it is available in every project. If you see Marketplace "claude-plugins-official" not found, run /plugin marketplace add anthropics/claude-plugins-official and try again. If the plugin itself is not found, check the spelling. You can ignore a Run /reload-plugins to apply. message here because the next step restarts anyway.

2. Restart with the channel switched on.

claude --channels plugin:fakechat@claude-plugins-official

The startup screen shows a notice that messages from that plugin will be injected into this session. If the plugin is missing or not on the approved list, a warning line below the notice says so. You can pass several plugins to --channels, separated by spaces.

3. Send something. Open http://localhost:8787 and type, for example:

how many TODO comments are left in src/?

In the terminal it shows as an inbound line like ← fakechat · web: how many TODO comments.... The model receives it as a <channel source="plugin:fakechat:fakechat"> event (the source uses the plugin's scoped server name). Claude does the work and calls fakechat's reply tool; approve that first reply if prompted, and the answer appears in the browser.

Connecting a real platform

Every platform follows the same shape: get credentials, install the plugin, configure, restart with --channels, then restrict who can talk to it. All three need Bun. Install problems are handled exactly as in the fakechat steps above. If the install summary says Run /reload-plugins to apply. for Telegram or Discord, run it so the plugin's configure command becomes available before you restart (see the plugin CLI reference).

Telegram

  1. In Telegram, message @BotFather with /newbot, pick a display name and a username ending in bot, and copy the token.
  2. /plugin install telegram@claude-plugins-official (user scope).
  3. /telegram:configure <token> saves it to ~/.claude/channels/telegram/.env. Alternatively export TELEGRAM_BOT_TOKEN before launching.
  4. Restart: claude --channels plugin:telegram@claude-plugins-official. The plugin starts polling your bot.
  5. Message the bot. It replies with a pairing code (only while Claude is running with --channels). In Claude Code run /telegram:access pair <code>, then /telegram:access policy allowlist so only you get through.

Discord

  1. In the Discord Developer Portal create a New Application, then in Bot set a username, click Reset Token and copy the token.
  2. Under Privileged Gateway Intents, turn on Message Content Intent.
  3. In OAuth2 > URL Generator select the bot scope with these permissions: View Channels, Send Messages, Send Messages in Threads, Read Message History, Attach Files, Add Reactions. Open the generated URL to add the bot to your server.
  4. /plugin install discord@claude-plugins-official (user scope).
  5. /discord:configure <token> saves it to ~/.claude/channels/discord/.env, or export DISCORD_BOT_TOKEN.
  6. Restart: claude --channels plugin:discord@claude-plugins-official.
  7. DM the bot to get a pairing code, run /discord:access pair <code>, then /discord:access policy allowlist.

iMessage (macOS only)

This one needs no bot or external service: it reads the Messages database directly and sends replies with AppleScript.

  1. Full Disk Access. ~/Library/Messages/chat.db is protected. Allow the prompt the first time (it names whichever app launched Bun: Terminal, iTerm, your IDE). If you missed it, add your terminal under System Settings > Privacy & Security > Full Disk Access. Without it the server exits with authorization denied.
  2. /plugin install imessage@claude-plugins-official (user scope).
  3. Restart: claude --channels plugin:imessage@claude-plugins-official.
  4. Text yourself from any device on your Apple ID. Self-chat skips access control entirely. The first reply triggers a macOS Automation prompt asking whether your terminal may control Messages; click OK.
  5. To let someone else in: /imessage:access allow +447700900123 or an Apple ID email. Handles are +country phone numbers or emails.

Working unattended

If Claude hits a permission prompt while you are away, the session simply waits. Channels that declare the permission relay capability can forward the prompt to you on the platform so you can approve or deny from your phone (details in the channels reference).

For fully unattended use you can start with --dangerously-skip-permissions, but only somewhere you trust completely, and some actions still prompt in every mode (see permission modes). In -p headless mode, tools that need terminal input, such as multiple-choice questions and plan approval, are switched off so nothing stalls.

My own pattern: a dedicated tmux window running Claude with the Telegram channel in a throwaway worktree, with tight allow rules for the test and lint commands I expect it to need. Anything outside that list waits for me, and permission relay lets me answer from the train.

Security model

Three layers decide whether a message gets in:

  1. Sender allowlist per plugin. Only IDs on the list can push messages; everyone else is dropped silently. Telegram and Discord build the list by pairing: you message the bot, it returns a code, you approve the code in Claude Code, and your sender ID is added. iMessage lets your own messages through automatically and adds others with /imessage:access allow.
  2. Per-session opt-in. A server only delivers channel events if you name it in --channels. Being listed in .mcp.json is not enough.
  3. Organisation policy. channelsEnabled and allowedChannelPlugins in managed settings.

Warning: If a channel supports permission relay, anyone on its allowlist can approve or deny tool calls in your session. Only allowlist people you would trust at your keyboard.

Admin controls

Two managed settings govern channels, and users cannot override them.

SettingWhat it doesIf unset
channelsEnabledMaster switch. Must be true for any channel, including development channels, to deliver messagesclaude.ai Team/Enterprise: blocked. Console: allowed, unless the organisation deploys managed settings, in which case blocked until set
allowedChannelPluginsReplaces the Anthropic-maintained list of plugins allowed to register as channelsAnthropic's default list applies

Pro and Max users outside an organisation skip both checks and just opt in with --channels. Whatever the policy, nothing runs until a user names a channel with --channels.

Switching channels on

An Owner can enable them at Organization settings > Claude Code > Channels on claude.ai, or you can set channelsEnabled: true in managed settings. While disabled, the MCP server still connects and its tools still work, but channel messages do not arrive, and a startup warning tells the user to ask an admin.

Restricting which plugins may run

Set allowedChannelPlugins to approve specific official plugins, plugins from your internal marketplace, or both:

{
  "channelsEnabled": true,
  "allowedChannelPlugins": [
    { "marketplace": "claude-plugins-official", "plugin": "discord" },
    { "marketplace": "northwind-internal", "plugin": "pagerduty-bridge" }
  ]
}

This requires channelsEnabled: true. A plugin passed to --channels that is not listed does not register; Claude Code starts normally and the startup notice explains why. An empty array blocks every allowlisted plugin, but --dangerously-load-development-channels can still load something for local testing. To block everything including that flag, leave channelsEnabled unset.

On the v2 MCP client runtime, a channel can also fail to register because Claude Code does not register a channel server that negotiates protocol revision 2026-07-28. See MCP.

Research preview caveats

  • Neither --channels nor --dangerously-load-development-channels appears in claude --help during the preview, but both work.
  • The flag syntax and protocol may change.
  • --channels only accepts plugins on the effective allowlist: Anthropic's default set (the channel plugins in the claude-plugins-official marketplace) or your organisation's allowedChannelPlugins.
  • To test a channel you are building, use --dangerously-load-development-channels with plugin:<name>@<marketplace> or server:<name>.

Choosing between channels and similar features

FeatureDirectionWhere the work runsBest for
ChannelsExternal event pushed into your open sessionYour machineChat bridges, CI or monitoring webhooks landing where Claude already has context
Remote ControlYou drive the local session from claude.ai or mobileYour machineSteering a session while away from your desk
Standard MCP serverClaude pulls on demandYour machineQuerying a system during a task
Claude in SlackAn @Claude mention starts a sessionCloudKicking off work from a team thread
Cloud sessionsYou hand off a taskFresh cloud sandboxSelf-contained async work
Scheduled tasksClaude polls on a timerYour machineChecking something periodically when nothing can push

The webhook case is the one I find most valuable: a failed deploy or a new error-tracker issue arrives in a session that already has the repository open and remembers what you were debugging, instead of spinning up a cold session.