Claude Code Channels 参考 - MCP server 协议详解
构建一个 MCP 服务器,把 webhook、警报和聊天消息推送到 Claude Code 会话——频道契约的参考:能力声明、通知事件、回复工具、发送者门控和权限中继。
ℹ️ Channels 处于研究预览阶段,需要 Claude Code v2.1.80 或更高版本。它们需要 claude.ai 登录。不支持控制台和 API 密钥身份验证。Team 和 Enterprise 组织必须明确启用它们。
Channel 是一个 MCP 服务器,它将事件推送到 Claude Code 会话中,以便 Claude 可以对终端外发生的事情做出反应。
您可以构建单向或双向频道。单向频道转发警报、webhooks 或监控事件供 Claude 处理。双向频道(如聊天桥接)也公开回复工具,以便 Claude 可以发送消息回复。具有受信任发送者路径的频道也可以选择加入中继权限提示,以便您可以远程批准或拒绝工具使用。
本页涵盖:
- 概述:频道如何工作
- 您需要什么:要求和一般步骤
- 示例:构建 webhook 接收器:最小单向演练
- 服务器选项:构造函数字段
- 通知格式:事件有效负载
- 公开回复工具:让 Claude 发送消息回复
- 门控入站消息:发送者检查以防止提示注入
- 中继权限提示:将工具批准提示转发到远程频道
要使用现有频道而不是构建一个,请参阅 Channels。Telegram、Discord、iMessage 和 fakechat 包含在研究预览中。
概述
Channel 是一个在与 Claude Code 相同的机器上运行的 MCP 服务器。Claude Code 将其作为子进程生成并通过 stdio 进行通信。您的频道服务器是外部系统和 Claude Code 会话之间的桥梁:
- 聊天平台(Telegram、Discord):您的插件在本地运行并轮询平台的 API 以获取新消息。当有人向您的机器人发送 DM 时,插件接收消息并将其转发给 Claude。无需公开 URL。
- Webhooks(CI、监控):您的服务器在本地 HTTP 端口上侦听。外部系统 POST 到该端口,您的服务器将有效负载推送到 Claude。
您需要什么
唯一的硬性要求是 @modelcontextprotocol/sdk 包和 Node.js 兼容的运行时。Bun、Node 和 Deno 都可以工作。研究预览中的预构建插件使用 Bun,但您的频道不一定要使用。
您的服务器需要:
- 声明
claude/channel能力,以便 Claude Code 注册通知侦听器 - 当发生某事时发出
notifications/claude/channel事件 - 通过 stdio transport 连接(Claude Code 将您的服务器作为子进程生成)
服务器选项和通知格式部分详细介绍了每一项。
在研究预览期间,自定义频道不在批准的允许列表上。使用 --dangerously-load-development-channels 在本地测试。
示例:构建 webhook 接收器
本演练构建一个单文件服务器,该服务器侦听 HTTP 请求并将其转发到您的 Claude Code 会话中。最后,任何可以发送 HTTP POST 的东西,如 CI 管道、监控警报或 curl 命令,都可以将事件推送到 Claude。
此示例使用 Bun 作为运行时,用于其内置的 HTTP 服务器和 TypeScript 支持。您可以改用 Node 或 Deno。
创建项目
创建一个新目录并安装 MCP SDK:
mkdir webhook-channel && cd webhook-channel
bun add @modelcontextprotocol/sdk
编写频道服务器
创建一个名为 webhook.ts 的文件。这是您的整个频道服务器:它通过 stdio 连接到 Claude Code,并在端口 8788 上侦听 HTTP POST。当请求到达时,它将主体作为频道事件推送到 Claude。
#!/usr/bin/env bun
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
// 创建 MCP 服务器并将其声明为频道
const mcp = new Server(
{ name: 'webhook', version: '0.0.1' },
{
// 这个键使其成为频道 — Claude Code 为其注册侦听器
capabilities: { experimental: { 'claude/channel': {} } },
// 添加到 Claude 的系统提示,以便它知道如何处理这些事件
instructions: 'Events from the webhook channel arrive as <channel source="webhook" ...>. They are one-way: read them and act, no reply expected.',
},
)
// 通过 stdio 连接到 Claude Code(Claude Code 生成此进程)
await mcp.connect(new StdioServerTransport())
// 启动一个 HTTP 服务器,将每个 POST 转发给 Claude
Bun.serve({
port: 8788, // 任何开放端口都可以
// 仅限本地主机:此机器外的任何东西都无法 POST
hostname: '127.0.0.1',
async fetch(req) {
const body = await req.text()
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: body, // 成为 <channel> 标签的主体
// 每个键都成为标签属性,例如 <channel path="/" method="POST">
meta: { path: new URL(req.url).pathname, method: req.method },
},
})
return new Response('ok')
},
})
该文件按顺序执行三项操作:
- 服务器配置:使用
claude/channel在其能力中创建 MCP 服务器,这是告诉 Claude Code 这是一个频道的原因。instructions字符串进入 Claude 的系统提示。 - Stdio 连接:通过 stdin/stdout 连接到 Claude Code。
- HTTP 侦听器:在端口 8788 上启动本地 Web 服务器。每个 POST 主体都通过
mcp.notification()作为频道事件转发给 Claude。
向 Claude Code 注册您的服务器
将服务器添加到您的 MCP 配置中,以便 Claude Code 知道如何启动它:
{
"mcpServers": {
"webhook": { "command": "bun", "args": ["./webhook.ts"] }
}
}
Claude Code 在启动时读取您的 MCP 配置并将每个服务器作为子进程生成。
测试它
在研究预览期间,自定义频道不在允许列表上,因此使用开发标志启动 Claude Code:
claude --dangerously-load-development-channels server:webhook
在单独的终端中,通过向您的服务器发送带有消息的 HTTP POST 来模拟 webhook:
curl -X POST localhost:8788 -d "build failed on main: https://ci.example.com/run/1234"
有效负载作为 <channel> 标签到达您的 Claude Code 会话中:
<channel source="webhook" path="/" method="POST">build failed on main: https://ci.example.com/run/1234</channel>
在研究预览期间测试
在研究预览期间,每个频道都必须在批准的允许列表上才能注册。开发标志在确认提示后绕过特定条目的允许列表。此示例显示两种条目类型:
# 测试您正在开发的插件
claude --dangerously-load-development-channels plugin:yourplugin@yourmarketplace
# 测试裸 .mcp.json 服务器(尚无插件包装器)
claude --dangerously-load-development-channels server:webhook
ℹ️ 此标志仅跳过允许列表。
channelsEnabled组织政策仍然适用。不要使用它来运行来自不受信任来源的频道。
服务器选项
频道在 Server 构造函数中设置这些选项。instructions 和 capabilities.tools 字段是标准 MCP;capabilities.experimental['claude/channel'] 和 capabilities.experimental['claude/channel/permission'] 是频道特定的添加:
| 字段 | 类型 | 描述 |
|---|---|---|
capabilities.experimental['claude/channel'] | object | 必需。始终为 {}。存在注册通知侦听器。 |
capabilities.experimental['claude/channel/permission'] | object | 可选。声明此频道可以接收权限中继请求。 |
capabilities.tools | object | 仅双向。始终为 {}。标准 MCP 工具能力。 |
instructions | string | 推荐。添加到 Claude 的系统提示。 |
要创建单向频道,请省略 capabilities.tools。此示例显示双向设置:
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
const mcp = new Server(
{ name: 'your-channel', version: '0.0.1' },
{
capabilities: {
experimental: { 'claude/channel': {} },
tools: {},
},
instructions: 'Messages arrive as <channel source="your-channel" ...>. Reply with the reply tool.',
},
)
要推送事件,请使用方法 notifications/claude/channel 调用 mcp.notification()。
通知格式
您的服务器使用两个参数发出 notifications/claude/channel:
| 字段 | 类型 | 描述 |
|---|---|---|
content | string | 事件主体。作为 <channel> 标签的主体传递。 |
meta | Record<string, string> | 可选。每个条目成为 <channel> 标签上的属性。键必须是标识符:仅字母、数字和下划线。 |
await mcp.notification({
method: 'notifications/claude/channel',
params: {
content: 'build failed on main: https://ci.example.com/run/1234',
meta: { severity: 'high', run_id: '1234' },
},
})
事件在 Claude 的上下文中到达,包装在 <channel> 标签中:
<channel source="your-channel" severity="high" run_id="1234">
build failed on main: https://ci.example.com/run/1234
</channel>
公开回复工具
如果您的频道是双向的,公开一个标准 MCP 工具,Claude 可以调用它来发送消息回复。回复工具有三个组件:
- 您的
Server构造函数能力中的tools: {}条目 - 定义工具的架构并实现发送逻辑的工具处理程序
- 您的
Server构造函数中的instructions字符串
启用工具发现
capabilities: {
experimental: { 'claude/channel': {} },
tools: {},
},
注册回复工具
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', description: 'The conversation to reply in' },
text: { type: 'string', description: 'The message to send' },
},
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(`Reply to ${chat_id}: ${text}`)
return { content: [{ type: 'text', text: 'sent' }] }
}
throw new Error(`unknown tool: ${req.params.name}`)
})
更新说明
instructions: 'Messages arrive as <channel source="webhook" chat_id="...">. Reply with the reply tool, passing the chat_id from the tag.'
门控入站消息
未门控的频道是提示注入向量。任何可以到达您的端点的人都可以在 Claude 前面放置文本。侦听聊天平台或公共端点的频道需要在发出任何内容之前进行真正的发送者检查。
在调用 mcp.notification() 之前,根据允许列表检查发送者:
const allowed = new Set(loadAllowlist())
if (!allowed.has(message.from.id)) {
return // 静默删除
}
await mcp.notification({ ... })
根据发送者的身份而不是聊天或房间身份进行门控:示例中的 message.from.id,而不是 message.chat.id。在群组聊天中,这些不同,根据房间进行门控会让允许列表中的任何人向会话注入消息。
中继权限提示
ℹ️ 权限中继需要 Claude Code v2.1.81 或更高版本。较早的版本忽略
claude/channel/permission能力。
当 Claude 调用需要批准的工具时,本地终端对话打开,会话等待。双向频道可以选择加入以在并行接收相同的提示,并将其中继到您的另一台设备。两者都保持活动:您可以在终端或手机上回答,Claude Code 应用先到达的任何答案并关闭另一个。
中继涵盖工具使用批准,如 Bash、Write 和 Edit。项目信任和 MCP 服务器同意对话不中继;这些仅在本地终端中出现。
中继如何工作
当权限提示打开时,中继循环有四个步骤:
- Claude Code 生成一个短请求 ID 并通知您的服务器
- 您的服务器将提示和 ID 转发到您的聊天应用
- 远程用户使用该 ID 回复是或否
- 您的入站处理程序将回复解析为判决,Claude Code 仅在 ID 匹配开放请求时应用它
权限请求字段
来自 Claude Code 的出站通知是 notifications/claude/channel/permission_request。params 对象有四个字符串字段:
| 字段 | 描述 |
|---|---|
request_id | 从 a-z 中抽取的五个小写字母,不包括 l。 |
tool_name | Claude 想要使用的工具的名称,例如 Bash 或 Write。 |
description | 此特定工具调用执行的操作的人类可读摘要。 |
input_preview | 工具的参数作为 JSON 字符串,截断为 200 个字符。 |
您的服务器发送回的判决是 notifications/claude/channel/permission:request_id 回显上面的 ID,behavior 设置为 'allow' 或 'deny'。
向聊天桥接添加中继
向双向频道添加权限中继需要三个组件:
- 您的
Server构造函数中experimental能力下的claude/channel/permission: {}条目 notifications/claude/channel/permission_request的通知处理程序- 您的入站消息处理程序中的检查
仅在您的频道验证发送者时声明该能力。
声明权限能力
capabilities: {
experimental: {
'claude/channel': {},
'claude/channel/permission': {},
},
tools: {},
},
处理传入请求
import { z } from 'zod'
const PermissionRequestSchema = z.object({
method: z.literal('notifications/claude/channel/permission_request'),
params: z.object({
request_id: z.string(),
tool_name: z.string(),
description: z.string(),
input_preview: z.string(),
}),
})
mcp.setNotificationHandler(PermissionRequestSchema, async ({ params }) => {
send(
`Claude wants to run ${params.tool_name}: ${params.description}\n\n` +
`Reply "yes ${params.request_id}" or "no ${params.request_id}"`,
)
})
在您的入站处理程序中拦截判决
const PERMISSION_REPLY_RE = /^\s*(y|yes|n|no)\s+([a-km-z]{5})\s*$/i
async function onInbound(message: PlatformMessage) {
if (!allowed.has(message.from.id)) return
const m = PERMISSION_REPLY_RE.exec(message.text)
if (m) {
await mcp.notification({
method: 'notifications/claude/channel/permission',
params: {
request_id: m[2].toLowerCase(),
behavior: m[1].toLowerCase().startsWith('y') ? 'allow' : 'deny',
},
})
return
}
// 不匹配判决格式:落入正常聊天路径
await mcp.notification({
method: 'notifications/claude/channel',
params: { content: message.text, meta: { chat_id: String(message.chat.id) } },
})
}
打包为插件
要使您的频道可安装和可共享,请将其包装在插件中并将其发布到市场。用户使用 /plugin install 安装它,然后使用 --channels plugin:<name>@<marketplace> 按会话启用它。
发布到您自己的市场的频道仍然需要 --dangerously-load-development-channels 来运行,因为它不在批准的允许列表上。要将其添加,将其提交到官方市场。频道插件在被批准之前经过安全审查。在 Team 和 Enterprise 计划上,管理员可以改为将您的插件包含在组织自己的 allowedChannelPlugins 列表中。
另请参阅
- Channels 安装和使用 Telegram、Discord、iMessage 或 fakechat 演示
- 工作频道实现 用于具有配对流、回复工具和文件附件的完整服务器代码
- MCP 用于频道服务器实现的基础协议
- Plugins 打包您的频道,以便用户可以使用
/plugin install安装它
本文翻译自 Anthropic Claude Code 官方文档,最近一次同步:2025-05-01。