Claude Code 的 channels:把 CI 告警和聊天消息推进正在运行的会话,以及走中转站时为什么收不到
channels 让外面的事件自己进到一个开着的 Claude Code 会话里:CI 挂了、监控报警、你在 Telegram 上发的一句话。本文整理它是什么、和远程控制等方式的区别、官方插件怎么配、怎么自己写一个、安全上要守住什么。本站通过中转站试了一次,结果是用不了,文中给出看到的提示。
本文根据公开分享整理,本站尚未实测。操作前请对照文末的原始来源。
这篇教程解决一个问题:Claude Code 的会话开着,你人不在电脑前。CI 挂了、线上报警了,或者你想从手机上问它一句,怎么让这些消息自己进到那个会话里?
官方的答案叫 channels,目前是研究预览。本文按官方文档整理,并在服务器上通过中转站试了一次。先说结论:走中转站用不了,原因和看到的提示在第三节。用 claude.ai 账号或 Console 的 API key 登录的读者,后面几节照着做就行。
一、它是什么
一个 channel 就是一个 MCP 服务器,只多了一样本事:它能主动往会话里推消息。普通的 MCP 服务器只能等 Claude 来调用。
几个要点:
- 它跑在你自己的机器上。 Claude Code 把它作为子进程启动,两边通过标准输入输出通信。
- 事件进的是你已经开着的那个会话。 Claude 手边有你的文件,也记得你刚才在查什么。
- 可以是双向的。 Claude 读到事件后,能通过同一个 channel 把回复发回去,像一座聊天的桥。
- 会话关了就收不到。 想一直在线,要把 Claude Code 放在后台进程或者不会断的终端里。
两种常见的形态:
| 形态 | 怎么工作 | 例子 |
|---|---|---|
| 聊天桥 | 插件在本机轮询聊天平台的接口,有人给机器人发消息就转给 Claude。不用对外暴露地址 | Telegram、Discord、iMessage |
| Webhook 接收器 | 服务器在本机监听一个端口,外部系统往这个端口发 POST | CI、报错平台、发布流水线 |
回复通过 channel 发出去时,终端里看得到进来的消息,看不到回复的正文,只有一次工具调用和一个“sent”。回复的内容在对面的平台上。
二、和别的方式有什么不同
Claude Code 有好几种“连到终端以外”的方式,文档给了一张对照:
| 方式 | 做什么 | 适合 |
|---|---|---|
| 云端会话 | 在云上新开一个沙箱,从 GitHub 克隆代码去跑 | 把一件独立的事交出去,回头再看 |
| Slack 里的 Claude | 在频道里 @Claude,起一个云端会话 | 从团队的讨论里直接起任务 |
| 普通的 MCP 服务器 | Claude 干活时去查它,它不会主动推 | 让 Claude 按需读某个系统 |
| Remote Control | 你从网页或手机上操作本机的会话 | 人不在座位上,但想自己接着指挥 |
| channels | 把别的系统的事件推进本机已经开着的会话 | 让会话自己对外面发生的事做反应 |
一句话区分 Remote Control 和 channels:前者是你在远处操作会话,后者是别的系统往会话里送消息。
如果只是想隔一段时间看一眼,做一个定时运行的智能体去轮询就够了,不需要 channel。
三、你能不能用
| 条件 | 说明 |
|---|---|
| 登录方式 | 要用 claude.ai 账号或 Console 的 API key。Amazon Bedrock、Google Cloud、Microsoft Foundry 上不提供 |
| 个人套餐(Pro、Max) | 直接可用,每次会话用启动参数点名要开哪个 |
| Team、Enterprise | 默认关着,要组织的 Owner 先打开 |
| Console(API key) | 默认可用;组织下发了托管设置时,要在里面显式打开 |
| 官方插件的运行环境 | 都是 Bun 脚本,要先装 Bun |
| 状态 | 研究预览。启动参数的写法和协议都可能变;claude --help 里暂时不列这两个参数,但能用 |
本站通过中转站试的结果:不可用
测试机上的 Claude Code 是 2.1.296,用令牌加自定义接口地址接的酷站AI 中转站。本站写了一个带回复工具的 channel 服务器,登记在项目的 .mcp.json 里,然后这样启动:
claude --channels server:webhook
启动画面上多了两行:
▎ --channels ignored (server:webhook)
▎ Channels are not currently available
接着看了三件事:
- MCP 服务器照常连上了。
/mcp里显示✔ webhook 1 tool,它的工具能用。 - 事件发出去了,会话没有任何反应。 用
curl往它监听的端口发了一条“main 分支构建失败”,服务器返回ok,终端里什么也没出现。 - 调试日志里有一句原因。 加
--debug重启后,日志里是:
MCP server "webhook": Channel notifications skipped: channels feature is not currently available
这和文档的两处说法对得上:channels 要用 Anthropic 自己的登录方式;会话没有把服务器登记成 channel 时,事件会被悄悄丢掉,服务器那边收不到任何错误。
所以走中转站的读者目前用不了 channels。想要类似的效果,有两条不依赖它的路:用非交互模式让 CI 在失败时直接起一次 claude -p;或者用定时任务轮询。
四、先用现成的试一下
官方准备了一个叫 fakechat 的演示 channel:在本机起一个聊天页面,不用注册任何平台。
/plugin install fakechat@claude-plugins-official
退出,带上参数重新启动:
claude --channels plugin:fakechat@claude-plugins-official
然后打开 http://localhost:8787,在页面上打一句话。按文档的描述,终端里会出现一行 ← fakechat · web: …,Claude 干完活后调用 fakechat 的 reply 工具,答案出现在聊天页面上。第一次回复时如果问你要不要批准,点同意。
要同时开几个 channel,在 --channels 后面用空格隔开写。
三个正式的插件
| Telegram | Discord | iMessage | |
|---|---|---|---|
| 事先准备 | 找 BotFather 建一个机器人,拿到 token | 在开发者后台建应用和机器人,打开 Message Content Intent,把机器人拉进你的服务器 | 只能在 macOS 上用,不需要 token |
| 安装 | telegram 插件 |
discord 插件 |
imessage 插件 |
| 配置 | /telegram:configure <token> |
/discord:configure <token> |
第一次读消息库时 macOS 会要权限,点允许 |
| 认人 | 给机器人发条消息拿配对码,再在会话里确认 | 同左 | 自己给自己发的消息直接通过 |
三个插件都装自 claude-plugins-official,安装和启动的写法和 fakechat 一样,把名字换掉就行。
Telegram 和 Discord 配对完,记得再执行一次把名单锁死,之后只有名单里的人能发消息:
/telegram:access pair <配对码>
/telegram:access policy allowlist
token 存在 ~/.claude/channels/ 下各插件自己的 .env 文件里,也可以用环境变量给。iMessage 要加别的联系人,用 /imessage:access allow 加上对方的手机号或 Apple ID 邮箱。
五、安全:谁能往你的会话里说话
channel 等于给会话开了一个入口。文档的原话是:没有把关的 channel 就是一条提示词注入的通道,谁够得着你的端口,谁就能把文字放到 Claude 面前。
有三层限制:
- 发送者名单。 每个官方插件都维护一份名单,名单外的消息直接丢掉,不回任何东西。
- 每次会话点名。 服务器写在
.mcp.json里不够,还得出现在这次启动的--channels里才能推消息。 - 组织开关。 见第七节。
另外两件事要有数:
- 碰到要批准的操作,会话会停下来等你。 你不在终端前,它就一直等。channel 可以声明“转发审批请求”,让你在手机上答复。也可以用跳过权限检查的模式,但那只适合你完全信任的环境。
- 能在 channel 里回话的人,就能批准或拒绝你会话里的工具调用。 开了审批转发之后,名单里只放你愿意把这个权力交出去的人。
用 -p 非交互模式跑 channel 时,需要在终端里作答的工具(比如多选题、计划模式的批准)会被关掉,免得会话卡死。
六、自己写一个
系统没有现成的插件时,可以自己写。硬性要求只有一个包:@modelcontextprotocol/sdk,Node.js、Bun、Deno 都能跑。
服务器要做三件事:
- 声明
claude/channel这个能力,Claude Code 才会为它登记监听。 - 有事发生时发一条
notifications/claude/channel通知。 - 用标准输入输出和 Claude Code 连接。
官方的例子用 Bun。下面是本站照着改的 Node.js 版,一个文件,监听本机 8788 端口,把收到的 POST 转给 Claude:
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import http from 'node:http'
const ALLOWED = new Set(['ci', 'tony']) // 只转发这两个发送者的消息
const mcp = new Server(
{ name: 'webhook', version: '0.0.1' },
{
capabilities: { experimental: { 'claude/channel': {} } }, // 有这一项才算 channel
instructions: '来自 webhook 通道的事件是 <channel source="webhook" ...>。单向:读完后处理,不用回复。',
},
)
await mcp.connect(new StdioServerTransport())
http.createServer(async (req, res) => {
let body = ''
for await (const chunk of req) body += chunk
const sender = String(req.headers['x-sender'] ?? '')
if (!ALLOWED.has(sender)) { res.writeHead(403).end('forbidden\n'); return } // 先看发送者
await mcp.notification({
method: 'notifications/claude/channel',
params: { content: body, meta: { sender, path: req.url } },
})
res.end('ok\n')
}).listen(8788, '127.0.0.1') // 只听本机
{ "mcpServers": { "webhook": { "command": "node", "args": ["./webhook.mjs"] } } }
这段代码本站验证到哪一步: 原样放到测试机上,Claude Code 能把它启动并连上(/mcp 里显示 ✔ webhook),端口能收到请求,名单外和不带发送者的请求得到 403,名单内的得到 ok。事件真正送进会话这一步没有验证,因为测试机上 channels 不可用。另外不要在这个文件里用 console.log:标准输出是它和 Claude Code 通信的通道。
事件长什么样
content 是正文,meta 里的每一项变成标签上的一个属性,source 自动取服务器的名字。模型看到的是:
<channel source="webhook" sender="ci" path="/">
main 分支构建失败:test_calc.py::test_div
</channel>
几条容易踩的规矩:
meta的键只能用字母、数字和下划线。 带连字符的键会被悄悄丢掉。- 没有送达确认。 通知写出去就算完,Claude Code 不回执。会话没登记这个 channel,或者组织策略不允许,事件直接丢,服务器不会收到错误。本站第三节看到的就是这种情况。
- 事件按顺序排队。 Claude 忙的时候来了好几条,会在下一轮一起交给它。要并行处理互不相干的事件流,开多个会话。
instructions值得认真写。 它会在服务器连上时交给 Claude,告诉它会收到什么事件、要不要回复、用哪个工具回。
让它能回话
双向的 channel 多三样东西,都是普通的 MCP 写法:
- 能力里加一项
tools: {}。 - 注册一个回复工具(官方例子叫
reply,参数是chat_id和text),在处理函数里调你的平台接口把消息发出去。 - 在
instructions里告诉 Claude:回复时用这个工具,chat_id从进来的标签上照抄。
把关要按“人”,不按“群”
发通知之前先查发送者在不在名单里。文档特别提醒:按发送者的身份查,不要按聊天室的身份查。 群聊里这两个不是一回事,按群放行,群里任何人都能往你的会话里塞话。
转发审批请求
这是可选的,而且只有双向、认得出发送者的 channel 才应该开。流程是四步:
- Claude Code 弹出审批对话框时,生成一个短编号,同时通知你的服务器。
- 服务器把请求和编号转到你的聊天软件上。
- 你回复“同意”或“拒绝”,带上那个编号。
- 服务器把答复变成一条裁决通知发回去。编号对得上一个还开着的请求,Claude Code 才采纳。
终端里的对话框一直开着,两边谁先答算谁的。涉及的名字:
| 值 | |
|---|---|
| 要声明的能力 | claude/channel/permission |
| Claude Code 发来的通知 | notifications/claude/channel/permission_request |
| 通知里的字段 | request_id、tool_name、description、input_preview |
| 你发回去的通知 | notifications/claude/channel/permission |
| 发回去的字段 | request_id,以及取值为 allow 或 deny 的 behavior |
几个细节:
- 编号是五个小写字母,不含
l,免得在手机上和1、I混。终端的对话框上不显示这个编号,只有你的服务器知道。 description只是对这次调用的概括,不是命令本身。有地方显示的话,把input_preview也带上,Bash 的命令在那里面。- 这两个字段要当成不可信的内容。2.1.234 起 Claude Code 会把认得出的密钥替换成
[REDACTED],但没有固定前缀的密钥不会被遮住。 - 项目信任和 MCP 服务器授权这两种对话框不转发,只在本机终端出现。
- 答复的格式不对,会被当成普通消息交给 Claude;编号不对,会被悄悄丢掉。两种情况下本机的对话框都还开着。
测试和发布
研究预览期间,自己写的 channel 不在批准名单上,用 --channels 带不起来。官方给的办法是一个开发用的启动参数:
claude --dangerously-load-development-channels server:webhook
按文档的说明,它会先弹一个全屏的警告让你确认,并且只能在交互会话里用,-p 和 Agent SDK 会忽略它。它只跳过批准名单,组织的总开关照样管着。本站没有用这个参数启动过,这一段是照文档写的。
要分享给别人,把它包成插件发到插件市场。发在自己的市场上仍然不在批准名单里,对方还是要用开发参数;Team 和 Enterprise 的管理员可以把它加进组织自己的名单。
还有一处和 MCP 版本有关的坑:文档说,在新的 MCP 客户端运行时上,服务器如果协商到 2026-07-28 这一版协议,就送不了 channel 消息,Claude Code 不会把它登记成 channel。要让服务器留在旧的握手方式上,设环境变量 MCP_PROTOCOL_NEGOTIATION=legacy。
七、组织怎么管
管理员用两项托管设置控制,用户改不了:
| 设置 | 作用 | 不设的时候 |
|---|---|---|
channelsEnabled |
总开关。关着时任何 channel 都收不到消息,开发参数也不行 | Team、Enterprise:全部禁用 |
allowedChannelPlugins |
哪些插件可以登记成 channel。设了就替换掉 Anthropic 维护的默认名单 | 用默认名单 |
{
"channelsEnabled": true,
"allowedChannelPlugins": [
{ "marketplace": "claude-plugins-official", "plugin": "telegram" },
{ "marketplace": "acme-corp-plugins", "plugin": "internal-alerts" }
]
}
总开关也可以在 claude.ai 的组织设置里开(Claude Code 下的 Channels),要 Owner 角色。开关没开时,MCP 服务器照样连得上、工具照样能用,只是消息进不来,启动时有一条提醒。
这篇没有验证的地方
- channels 本身没有跑通过。 测试机走中转站,功能不可用。事件送达、回复工具、审批转发、配对流程,都是按文档整理的。
- 没有用开发参数启动过,不知道它在中转站下会不会有不同的结果。
- 四个官方插件(fakechat、Telegram、Discord、iMessage)一个都没有装过,测试机上也没有 Bun。
- “不可用”只在一家中转站、一个版本上看到。 启动提示和日志没有说明具体原因,本站是根据文档对登录方式的要求来理解的。
- 组织管控的两项设置没有试。