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
- In Telegram, message @BotFather with
/newbot, pick a display name and a username ending inbot, and copy the token. /plugin install telegram@claude-plugins-official(user scope)./telegram:configure <token>saves it to~/.claude/channels/telegram/.env. Alternatively exportTELEGRAM_BOT_TOKENbefore launching.- Restart:
claude --channels plugin:telegram@claude-plugins-official. The plugin starts polling your bot. - 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 allowlistso only you get through.
Discord
- In the Discord Developer Portal create a New Application, then in Bot set a username, click Reset Token and copy the token.
- Under Privileged Gateway Intents, turn on Message Content Intent.
- In OAuth2 > URL Generator select the
botscope 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. /plugin install discord@claude-plugins-official(user scope)./discord:configure <token>saves it to~/.claude/channels/discord/.env, or exportDISCORD_BOT_TOKEN.- Restart:
claude --channels plugin:discord@claude-plugins-official. - 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.
- Full Disk Access.
~/Library/Messages/chat.dbis 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 withauthorization denied. /plugin install imessage@claude-plugins-official(user scope).- Restart:
claude --channels plugin:imessage@claude-plugins-official. - 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.
- To let someone else in:
/imessage:access allow +447700900123or an Apple ID email. Handles are+countryphone 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:
- 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. - Per-session opt-in. A server only delivers channel events if you name it in
--channels. Being listed in.mcp.jsonis not enough. - Organisation policy.
channelsEnabledandallowedChannelPluginsin 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.
| Setting | What it does | If unset |
|---|---|---|
channelsEnabled | Master switch. Must be true for any channel, including development channels, to deliver messages | claude.ai Team/Enterprise: blocked. Console: allowed, unless the organisation deploys managed settings, in which case blocked until set |
allowedChannelPlugins | Replaces the Anthropic-maintained list of plugins allowed to register as channels | Anthropic'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
--channelsnor--dangerously-load-development-channelsappears inclaude --helpduring the preview, but both work. - The flag syntax and protocol may change.
--channelsonly accepts plugins on the effective allowlist: Anthropic's default set (the channel plugins in theclaude-plugins-officialmarketplace) or your organisation'sallowedChannelPlugins.- To test a channel you are building, use
--dangerously-load-development-channelswithplugin:<name>@<marketplace>orserver:<name>.
Choosing between channels and similar features
| Feature | Direction | Where the work runs | Best for |
|---|---|---|---|
| Channels | External event pushed into your open session | Your machine | Chat bridges, CI or monitoring webhooks landing where Claude already has context |
| Remote Control | You drive the local session from claude.ai or mobile | Your machine | Steering a session while away from your desk |
| Standard MCP server | Claude pulls on demand | Your machine | Querying a system during a task |
| Claude in Slack | An @Claude mention starts a session | Cloud | Kicking off work from a team thread |
| Cloud sessions | You hand off a task | Fresh cloud sandbox | Self-contained async work |
| Scheduled tasks | Claude polls on a timer | Your machine | Checking 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.