Skip to content

Channels 技术参考

构建一个 MCP 服务器,将 Webhook、告警和聊天消息推送到 Claude Code 会话中。本文档涵盖 Channel 协议规范:能力声明、通知事件、回复工具、发送方验证和权限中继。

TIP

Channels 处于研究预览阶段,需要 Claude Code v2.1.80 或更高版本,并需要 claude.ai 登录。不支持 Console 和 API Key 认证。Team 和 Enterprise 组织需明确启用

Channel 是一个将外部事件推送到 Claude Code 会话的 MCP 服务器,使 Claude 能够响应终端之外发生的事情。

你可以构建单向或双向 Channel:

  • 单向:转发告警、Webhook 或监控事件供 Claude 响应
  • 双向:类似聊天桥接,还暴露一个回复工具,让 Claude 可以发送消息

具有可信发送方通道的 Channel 还可以选择中继权限提示,让你可以远程批准或拒绝工具使用。

概览

Channel 是运行在与 Claude Code 同一台机器上的 MCP 服务器。Claude Code 将其作为子进程启动,通过 stdio 通信。你的 Channel 服务器是外部系统与 Claude Code 会话之间的桥梁:

  • 聊天平台(Telegram、Discord):插件在本地运行,轮询平台 API 获取新消息。无需暴露 URL。
  • Webhook(CI、监控):服务器监听本地 HTTP 端口,外部系统 POST 到该端口,服务器将负载推送给 Claude。

前提条件

只需要 @modelcontextprotocol/sdk 包和兼容 Node.js 的运行时。Bun、Node 和 Deno 均可使用。

服务器需要:

  1. 声明 claude/channel 能力,以便 Claude Code 注册通知监听器
  2. 在事件发生时发送 notifications/claude/channel 通知
  3. 通过 stdio transport 连接(Claude Code 将你的服务器作为子进程启动)

示例:构建 Webhook 接收器

这个演示构建一个单文件服务器,监听 HTTP 请求并将其转发到 Claude Code 会话。

typescript
#!/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' },
  {
    // 声明 claude/channel 能力,这是成为 Channel 的关键
    capabilities: { experimental: { 'claude/channel': {} } },
    instructions: '来自 webhook channel 的事件以 <channel source="webhook" ...> 格式到达。它们是单向的:读取并响应,不需要回复。',
  },
)

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')
  },
})

.mcp.json 中注册服务器:

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

测试(研究预览期间需要开发标志):

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

# 另一个终端中模拟 Webhook
curl -X POST localhost:8788 -d "main 分支构建失败:https://ci.example.com/run/1234"

服务器选项

字段类型说明
capabilities.experimental['claude/channel']object必填,始终为 {},注册通知监听器
capabilities.experimental['claude/channel/permission']object可选,声明此 Channel 可接收权限中继请求
capabilities.toolsobject双向 Channel 必填,声明工具能力
instructionsstring建议填写,添加到 Claude 的系统提示中

通知格式

通过 mcp.notification() 推送事件:

字段类型说明
contentstring事件正文,作为 <channel> 标签的内容
metaRecord<string, string>可选,每个键值对变为 <channel> 标签的属性

示例:

typescript
await mcp.notification({
  method: 'notifications/claude/channel',
  params: {
    content: 'main 分支构建失败:https://ci.example.com/run/1234',
    meta: { severity: 'high', run_id: '1234' },
  },
})

Claude 收到的事件格式:

<channel source="your-channel" severity="high" run_id="1234">
main 分支构建失败:https://ci.example.com/run/1234
</channel>

暴露回复工具(双向 Channel)

如果你的 Channel 是双向的(如聊天桥接),暴露一个标准 MCP 工具让 Claude 可以回复。

步骤 1:在服务器构造器中启用工具发现:

typescript
capabilities: {
  experimental: { 'claude/channel': {} },
  tools: {},  // 启用工具发现
},

步骤 2:注册回复工具:

typescript
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: 'reply',
    description: '通过此 Channel 发送消息回复',
    inputSchema: {
      type: 'object',
      properties: {
        chat_id: { type: 'string', description: '要回复的会话' },
        text: { type: 'string', description: '要发送的消息' },
      },
      required: ['chat_id', 'text'],
    },
  }],
}))

步骤 3:更新 instructions 告知 Claude 如何回复。

验证入站消息(防止提示注入)

未验证的 Channel 是提示注入的入口。在调用 mcp.notification() 之前,必须验证发送方:

typescript
const allowed = new Set(loadAllowlist())

// 在消息处理器中,发送前验证:
if (!allowed.has(message.from.id)) {  // 基于发送方身份,而非聊天室
  return  // 静默丢弃
}
await mcp.notification({ ... })

注意:应基于发送方身份(message.from.id)而非聊天室身份(message.chat.id)进行验证。在群聊中,这两者不同,仅验证聊天室会让允许列表内群组的任何人都能向会话注入消息。

中继权限提示

TIP

权限中继需要 Claude Code v2.1.81 或更高版本。

当 Claude 调用需要批准的工具时,本地终端会打开对话框,会话等待。双向 Channel 可以选择接收相同提示,并将其中继到另一台设备。两端同时有效,任意一端回复后即应用。

中继流程

  1. Claude Code 生成一个短请求 ID,通知你的服务器
  2. 你的服务器将提示和 ID 转发到你的聊天应用
  3. 远程用户用该 ID 回复是或否
  4. 你的入站处理器解析回复为裁决,Claude Code 应用

权限请求字段

出站通知方法:notifications/claude/channel/permission_request

字段说明
request_id5 个小写字母(不含 l),用于关联裁决
tool_name工具名称(如 BashWrite
description可读的操作摘要
input_preview工具参数的 JSON 字符串(截断到 200 字符)

裁决通知方法:notifications/claude/channel/permission,包含 request_idbehavior'allow''deny')。

添加权限中继到聊天桥接

步骤 1:声明权限能力:

typescript
capabilities: {
  experimental: {
    'claude/channel': {},
    'claude/channel/permission': {},  // 选择加入权限中继
  },
  tools: {},
},

步骤 2:处理权限请求:

typescript
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 想运行 ${params.tool_name}:${params.description}\n\n` +
    `回复 "yes ${params.request_id}" 或 "no ${params.request_id}"`,
  )
})

步骤 3:拦截入站裁决(在正常聊天转发之前检查):

typescript
// 匹配 "yes abcde" 或 "no abcde" 格式
const PERMISSION_REPLY_RE = /^\s*(y|yes|n|no)\s+([a-km-z]{5})\s*$/i

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  // 已作为裁决处理,不再转发为聊天消息
}

将 Channel 打包为插件

要让 Channel 可安装和共享,将其封装在插件中并发布到市场。用户通过 /plugin install 安装,然后用 --channels plugin:<name>@<marketplace> 启用。

相关资源

  • Channels:安装和使用 Telegram、Discord、iMessage 或 fakechat 演示
  • MCP:Channel 服务器实现的底层协议
  • 插件:将 Channel 打包为可安装的插件