<!-- title: Claude Code Channels全实战：通知到双向通信 -->

# Claude Code Channels全实战：从通知推送到双向通信

## 概述

本教程面向有一定Claude Code使用经验的开发者，深入讲解**Channels**与**Hooks通知**两大事件驱动机制。你将学会：配置跨平台桌面通知、搭建Telegram/Discord双向通信通道、构建自定义Channel MCP服务器、实现事件溯源工作流。学完后，你的Claude Code将从"被动工具"进化为"主动协作伙伴"——它能主动通知你任务完成，你也能从手机远程操控终端里的Claude。

**适用人群**：每天使用Claude Code超2小时的中高级开发者，尤其适合需要并行管理多个长任务、或在移动端监控开发进度的团队。

## 前置条件

| 条件 | 说明 |
|------|------|
| Claude Code版本 | ≥ v1.0.37（Channels需要v2.1.80+） |
| 操作系统 | macOS / Linux / WSL2 |
| Node.js | ≥ 18.x |
| 基础知识 | 熟悉JSON配置、Shell脚本、MCP协议基本概念 |
| 可选 | Telegram Bot Token 或 Discord Bot Token |
## 第一章 理解Claude Code事件体系全景

Claude Code的事件驱动能力由三层构成：

```mermaid
graph TB
    subgraph "外部世界"
        TG[Telegram]
        DC[Discord]
        WH[Webhook/GitHub]
        CI[CI/CD Pipeline]
    end

    subgraph "Claude Code事件层"
        CH[Channels<br/>MCP Server推送]
        HK[Hooks<br/>Shell命令响应]
        NT[Native Notifications<br/>桌面提醒]
    end

    subgraph "Claude Code核心"
        AG[Agent Loop]
        TOOLS[Tool Execution]
        PERM[Permission System]
    end

    TG -->|消息事件| CH
    DC -->|消息事件| CH
    WH -->|HTTP推送| CH
    CI -->|构建状态| CH
    CH -->|event_channel_send| AG

    AG -->|生命周期事件| HK
    HK -->|exit code 2| NT
    HK -->|osascript/notify-send| NT

    style CH fill:#e1f5fe
    style HK fill:#fff3e0
    style NT fill:#e8f5e9
```

**三层分工**：

- **Hooks**：Claude Code**内部**生命周期事件的响应器。Agent执行工具、停止、等待输入时触发Shell命令。
- **Channels**：**外部**世界推送到Claude Code的管道。Telegram消息、GitHub Webhook、CI构建状态都可以通过Channel推入正在运行的会话。
- **Native Notifications**：操作系统级桌面提醒，通常由Hooks触发。
## 第二章 Hooks通知：30秒配置桌面提醒

### 2.1 最小化配置

在项目根目录创建 `.claude/settings.json`：

```json
{
  "hooks": {
    "Stop": [
      {
        "type": "command",
        "command": "printf '\\a'"
      }
    ],
    "Notification": [
      {
        "type": "command",
        "command": "printf '\\a'"
      }
    ]
  }
}
```

`printf '\a'` 发送终端响铃（BEL字符），几乎所有终端都支持。配置完成后，Claude完成任务或需要你输入时，你会听到一声"叮"。

### 2.2 macOS桌面通知（推荐）

```json
{
  "hooks": {
    "Stop": [
      {
        "type": "command",
        "command": "osascript -e 'display notification \"Claude Code任务完成\" with title \"Claude Code\" sound name \"Glass\"'"
      }
    ],
    "Notification": [
      {
        "type": "command",
        "command": "osascript -e 'display notification \"Claude需要你的注意\" with title \"Claude Code\" sound name \"Ping\"'"
      }
    ]
  }
}
```

### 2.3 Linux桌面通知

```json
{
  "hooks": {
    "Stop": [
      {
        "type": "command",
        "command": "notify-send -u critical -i utilities-terminal 'Claude Code' '任务已完成' && paplay /usr/share/sounds/freedesktop/stereo/complete.oga"
      }
    ],
    "Notification": [
      {
        "type": "command",
        "command": "notify-send -u normal -i utilities-terminal 'Claude Code' '等待输入' && paplay /usr/share/sounds/freedesktop/stereo/bell.oga"
      }
    ]
  }
}
```

### 2.4 解析Hook输入JSON

Hooks的实际价值在于处理结构化数据。每个Hook通过stdin接收JSON：

```json
{
  "session_id": "abc123",
  "hook_event_name": "Stop",
  "input": {
    "stop_hook_active": true,
    "conversation": [
      {"role": "user", "content": "修复登录bug"},
      {"role": "assistant", "content": "已修复src/auth/login.ts中的空指针异常..."}
    ]
  }
}
```

利用这个数据，可以构建智能通知脚本：

```bash
#!/usr/bin/env bash
# ~/.claude/hooks/smart-notify.sh
set -euo pipefail

INPUT=$(cat)
EVENT=$(echo "$INPUT" | jq -r '.hook_event_name')
SESSION=$(echo "$INPUT" | jq -r '.session_id')

# 提取最后一条assistant消息的前100字
LAST_MSG=$(echo "$INPUT" | jq -r '.input.conversation | map(select(.role=="assistant")) | last .content' | head -c 100)

case "$EVENT" in
  Stop)
    osascript -e "display notification \"$LAST_MSG\" with title \"✅ Claude完成\" sound name \"Glass\""
    ;;
  Notification)
    osascript -e "display notification \"需要你的输入\" with title \"🔔 Claude等待\" sound name \"Ping\""
    ;;
  SubagentStop)
    osascript -e "display notification \"子Agent完成\" with title \"🤖 Subagent\" sound name \"Pop\""
    ;;
esac

# 记录事件日志（用于后续事件溯源）
echo "$(date -Iseconds)|$EVENT|$SESSION|${LAST_MSG:0:50}" >> ~/.claude/event-log.csv
```

在settings.json中引用：

```json
{
  "hooks": {
    "Stop": [{"type": "command", "command": "bash ~/.claude/hooks/smart-notify.sh"}],
    "Notification": [{"type": "command", "command": "bash ~/.claude/hooks/smart-notify.sh"}],
    "SubagentStop": [{"type": "command", "command": "bash ~/.claude/hooks/smart-notify.sh"}]
  }
}
```

### 2.5 常见问题
## 第三章 Channels：让外部世界推送到Claude

### 3.1 Channels核心概念

Channel本质上是一个**MCP服务器**，它通过`event_channel_send`工具向正在运行的Claude Code会话推送消息。整个架构：

```mermaid
sequenceDiagram
    participant User as 用户手机
    participant TG as Telegram Bot API
    participant CH as Channel MCP Server
    participant CC as Claude Code会话
    participant FS as 文件系统/代码

    User->>TG: 发送"/deploy production"
    TG->>CH: Webhook推送消息
    CH->>CC: event_channel_send(payload)
    CC->>CC: 解析指令，执行部署
    CC->>CH: 调用Channel工具(reply_to_chat)
    CH->>TG: 发送回复消息
    TG->>User: "✅ 生产环境已部署完成"

    Note over CC,FS: Claude同时执行本地操作
    CC->>FS: git push / kubectl apply
```

### 3.2 官方Telegram Channel配置

**第一步**：创建Telegram Bot

1. 在Telegram中搜索 `@BotFather`
2. 发送 `/newbot`，按提示设置名称
3. 记录得到的Bot Token（格式：`123456789:ABCdefGHIjklMNOpqrsTUVwxyz`）

**第二步**：安装官方Telegram插件

```bash
# 插件已内置于Claude Code，直接配置即可
# 在项目根目录的 .claude/settings.json 或全局 ~/.claude/settings.json 中添加：
```

```json
{
  "mcpServers": {
    "telegram": {
      "command": "npx",
      "args": [
        "-y",
        "@anthropic-ai/claude-code-telegram-channel@latest"
      ],
      "env": {
        "TELEGRAM_BOT_TOKEN": "你的Bot Token"
      }
    }
  }
}
```

**第三步**：启动并验证

```bash
# 启动Claude Code
claude

# 在Claude Code内部，发送测试消息
# Claude会自动发现telegram MCP server的工具
# 工具列表包括：
#   - reply_to_chat: 回复Telegram消息
#   - react_to_message: 对消息添加表情反应
#   - edit_message: 编辑已发送的消息
```

在Telegram中给你的Bot发送任意消息，Claude Code会话中将收到推送事件。

### 3.3 官方Discord Channel配置

**第一步**：创建Discord Bot

1. 访问 [Discord Developer Portal](https://discord.com/developers/applications)
2. 创建New Application → Bot → 记录Token
3. 开启Message Content Intent（Bot设置页）
4. 生成OAuth2 URL，添加Bot到你的服务器

**第二步**：配置MCP Server

```json
{
  "mcpServers": {
    "discord": {
      "command": "npx",
      "args": [
        "-y",
        "@anthropic-ai/claude-code-discord-channel@latest"
      ],
      "env": {
        "DISCORD_BOT_TOKEN": "你的Discord Bot Token"
      }
    }
  }
}
```

### 3.4 同时配置多Channel

完整的全球化开发配置示例：

```json
{
  "mcpServers": {
    "telegram": {
      "command": "npx",
      "args": ["-y", "@anthropic-ai/claude-code-telegram-channel@latest"],
      "env": {
        "TELEGRAM_BOT_TOKEN": "${TELEGRAM_BOT_TOKEN}"
      }
    },
    "discord": {
      "command": "npx",
      "args": ["-y", "@anthropic-ai/claude-code-discord-channel@latest"],
      "env": {
        "DISCORD_BOT_TOKEN": "${DISCORD_BOT_TOKEN}"
      }
    }
  },
  "hooks": {
    "Stop": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/smart-notify.sh"
      }
    ],
    "Notification": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/smart-notify.sh"
      }
    ]
  }
}
```

> 注意：Token建议通过环境变量引用，避免硬编码到配置文件中。在`~/.bashrc`或`~/.zshrc`中export：
> ```bash
> export TELEGRAM_BOT_TOKEN="your_token_here"
> export DISCORD_BOT_TOKEN="your_token_here"
> ```
## 第四章 构建自定义Channel MCP Server

当官方插件不满足需求时（比如需要接入企业微信、Slack、或自定义Webhook），你需要构建自己的Channel。

### 4.1 Channel MCP Server的核心要求

一个Channel MCP Server必须：

1. 作为MCP服务器运行，通过stdio与Claude Code通信
2. 实现`event_channel_send`工具，用于推送事件到Claude Code会话
3. 实现至少一个"接收工具"，让Claude能回复消息

### 4.2 TypeScript实现：GitHub Webhook Channel

这是一个将GitHub Webhook事件推送到Claude Code的自定义Channel：

```typescript
// github-webhook-channel/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import http from "http";

const PORT = parseInt(process.env.WEBHOOK_PORT || "3456");

// 创建MCP Server
const server = new Server(
  {
    name: "github-webhook-channel",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

// 注册Claude可调用的工具
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "reply_to_github",
      description: "通过GitHub API回复评论或创建Issue评论",
      inputSchema: {
        type: "object",
        properties: {
          event_type: {
            type: "string",
            enum: ["pr_comment", "issue_comment", "review_comment"],
          },
          repo: { type: "string", description: "owner/repo" },
          number: { type: "number" },
          body: { type: "string" },
        },
        required: ["event_type", "repo", "number", "body"],
      },
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "reply_to_github") {
    const { event_type, repo, number, body } = request.params.arguments as any;
    // 调用GitHub API回复
    const response = await fetch(
      `https://api.github.com/repos/${repo}/issues/${number}/comments`,
      {
        method: "POST",
        headers: {
          Authorization: `token ${process.env.GITHUB_TOKEN}`,
          "Content-Type": "application/json",
          "User-Agent": "claude-code-channel",
        },
        body: JSON.stringify({ body }),
      }
    );
    return {
      content: [{ type: "text", text: `已回复 ${repo}#${number}` }],
    };
  }
});

// 启动HTTP服务器接收GitHub Webhook
const webhookServer = http.createServer(async (req, res) => {
  if (req.method === "POST" && req.url === "/webhook") {
    let body = "";
    req.on("data", (chunk) => (body += chunk));
    req.on("end", async () => {
      const payload = JSON.parse(body);
      const event = req.headers["x-github-event"] as string;

      // 构造推送消息
      const message = formatGithubEvent(event, payload);

      // 通过event_channel_send推送到Claude Code
      // 注意：这个工具由Claude Code自动注入，Channel Server可以直接调用
      console.log(
        JSON.stringify({
          jsonrpc: "2.0",
          method: "notifications/tools/list_changed",
        })
      );

      res.writeHead(200);
      res.end("OK");
    });
  }
});

function formatGithubEvent(event: string, payload: any): string {
  switch (event) {
    case "pull_request":
      return `[PR ${payload.action}] ${payload.pull_request.title}\n` +
        `Repo: ${payload.repository.full_name}\n` +
        `Author: ${payload.sender.login}\n` +
        `URL: ${payload.pull_request.html_url}`;
    case "issues":
      return `[Issue ${payload.action}] ${payload.issue.title}\n` +
        `Repo: ${payload.repository.full_name}\n` +
        `Author: ${payload.sender.login}`;
    case "push":
      const commits = payload.commits.map((c: any) => `  - ${c.message}`).join("\n");
      return `[Push] ${payload.ref} → ${payload.repository.full_name}\n${commits}`;
    default:
      return `[${event}] New event from ${payload.repository?.full_name || "GitHub"}`;
  }
}

// 启动
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  webhookServer.listen(PORT, () => {
    console.error(`Webhook server listening on port ${PORT}`);
  });
}

main().catch(console.error);
```

### 4.3 配置自定义Channel

```json
{
  "mcpServers": {
    "github-webhook": {
      "command": "node",
      "args": ["/path/to/github-webhook-channel/index.js"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}",
        "WEBHOOK_PORT": "3456"
      }
    }
  }
}
```

使用ngrok暴露本地端口以接收GitHub Webhook：

```bash
ngrok http 3456
# 将ngrok URL配置到GitHub仓库的Webhook设置中
# Payload URL: https://xxxx.ngrok.io/webhook
# Content type: application/json
# Events: Pull requests, Issues, Pushes
```

### 4.4 Channel工作流程

```mermaid
flowchart LR
    subgraph "外部触发源"
        GH[GitHub Webhook]
        TG[Telegram消息]
        CR[Cron定时任务]
        API[内部API调用]
    end

    subgraph "Channel MCP Server"
        HTTP[HTTP接收器]
        POLL[轮询器]
        WS[WebSocket客户端]
    end

    subgraph "Claude Code会话"
        ECS[event_channel_send]
        AGENT[Agent处理]
        REPLY[回复工具]
    end

    GH -->|POST /webhook| HTTP
    CR -->|触发检查| POLL
    API -->|推送事件| WS

    HTTP -->|JSON-RPC| ECS
    POLL -->|JSON-RPC| ECS
    WS -->|JSON-RPC| ECS

    ECS --> AGENT
    AGENT --> REPLY
    REPLY -->|回复消息| HTTP
```
## 第五章 事件溯源：让Claude Code工作流可观测

### 5.1 设计思路

Nick Tune提出的事件溯源模式核心思想：**将Claude Code的工作流状态存储为不可变事件序列**，而非只关注最终结果。

```mermaid
graph TD
    subgraph "事件采集层"
        H1[PreToolUse Hook]
        H2[PostToolUse Hook]
        H3[Stop Hook]
        H4[Notification Hook]
        H5[SubagentStop Hook]
    end

    subgraph "事件存储"
        EL[event-log.jsonl]
        DB[SQLite事件库]
    end

    subgraph "分析与可视化"
        DASH[实时Dashboard]
        TIME[时间线视图]
        COST[成本追踪]
        AUDIT[审计日志]
    end

    H1 & H2 & H3 & H4 & H5 -->|追加写入| EL
    EL -->|定期归档| DB
    DB --> DASH
    DB --> TIME
    DB --> COST
    DB --> AUDIT
```

### 5.2 全生命周期Hook配置

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/event-recorder.sh"
      }
    ],
    "PostToolUse": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/event-recorder.sh"
      }
    ],
    "Stop": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/event-recorder.sh"
      }
    ],
    "SubagentStop": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/event-recorder.sh"
      }
    ],
    "Notification": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/event-recorder.sh"
      }
    ]
  }
}
```

### 5.3 事件记录器脚本

```bash
#!/usr/bin/env bash
# ~/.claude/hooks/event-recorder.sh
# 将所有Claude Code生命周期事件记录到JSONL文件

set -euo pipefail

INPUT=$(cat)
EVENT=$(echo "$INPUT" | jq -r '.hook_event_name')
SESSION=$(echo "$INPUT" | jq -r '.session_id')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")

# 构造事件记录
EVENT_RECORD=$(jq -n \
  --arg ts "$TIMESTAMP" \
  --arg event "$EVENT" \
  --arg session "$SESSION" \
  --argjson input "$(echo "$INPUT" | jq '.input // {}')" \
  '{
    timestamp: $ts,
    event: $event,
    session_id: $session,
    data: $input
  }')

# 写入日期分区的事件日志
LOG_DIR="$HOME/.claude/events/$(date -u +%Y-%m-%d)"
mkdir -p "$LOG_DIR"
echo "$EVENT_RECORD" >> "$LOG_DIR/${SESSION:-default}.jsonl"

# 轻量级桌面通知（仅关键事件）
case "$EVENT" in
  Stop)
    osascript -e 'display notification "任务完成" with title "Claude Code" sound name "Glass"' 2>/dev/null || true
    ;;
  Notification)
    osascript -e 'display notification "等待输入" with title "Claude Code" sound name "Ping"' 2>/dev/null || true
    ;;
esac
```

### 5.4 事件分析工具

```bash
#!/usr/bin/env bash
# ~/.claude/hooks/event-analytics.sh
# 分析今日Claude Code事件日志

TODAY=$(date -u +%Y-%m-%d)
LOG_DIR="$HOME/.claude/events/$TODAY"

if [ ! -d "$LOG_DIR" ]; then
  echo "今日暂无事件记录"
  exit 0
fi

echo "=== Claude Code 事件日报 $TODAY ==="
echo ""

# 事件类型统计
echo "📊 事件类型分布："
cat "$LOG_DIR"/*.jsonl | jq -r '.event' | sort | uniq -c | sort -rn
echo ""

# 工具使用频率
echo "🔧 工具调用Top 10："
cat "$LOG_DIR"/*.jsonl | \
  jq -r 'select(.event == "PreToolUse") | .data.tool_name // "unknown"' | \
  sort | uniq -c | sort -rn | head -10
echo ""

# 会话时间线
echo "⏱ 会话活跃时段："
cat "$LOG_DIR"/*.jsonl | \
  jq -r '.timestamp' | \
  sed 's/T/ /' | \
  cut -d'.' -f1 | \
  cut -d':' -f1-2 | \
  sort | uniq -c
echo ""

# 活跃会话数
SESSION_COUNT=$(cat "$LOG_DIR"/*.jsonl | jq -r '.session_id' | sort -u | wc -l)
EVENT_COUNT=$(cat "$LOG_DIR"/*.jsonl | wc -l)
echo "📈 今日统计：$EVENT_COUNT 个事件，$SESSION_COUNT 个会话"
```

运行分析：

```bash
bash ~/.claude/hooks/event-analytics.sh
```

预期输出：

```
=== Claude Code 事件日报 2026-05-21 ===

📊 事件类型分布：
    142 PreToolUse
    138 PostToolUse
     12 Stop
      8 Notification
      3 SubagentStop

🔧 工具调用Top 10：
     45 Read
     38 Edit
     22 Bash
     12 Glob
      8 Grep
      6 Write
      4 Task
      3 WebSearch
      2 Glob
      2 TodoWrite

⏱ 会话活跃时段：
      1 2026-05-21 09:1
      3 2026-05-21 09:2
     15 2026-05-21 09:3
     42 2026-05-21 10:0
     28 2026-05-21 10:1
     ...

📈 今日统计：303 个事件，4 个会话
```
## 第六章 实战项目：全栈监控通知系统

将Hooks、Channels、事件溯源整合为一个完整的监控系统——当代码提交触发CI失败时，Claude Code自动收到通知并尝试修复。

### 6.1 系统架构

```mermaid
graph TB
    subgraph "触发源"
        GH[GitHub Push]
        CI[CI/CD Pipeline]
        CRON[Cron定时检查]
    end

    subgraph "Channel层"
        GWC[GitHub Webhook Channel]
        CSC[CI Status Channel]
    end

    subgraph "Claude Code"
        AG[Agent Loop]
        HK[Hooks层]
        EV[事件记录器]
    end

    subgraph "通知层"
        TG[Telegram通知]
        DT[桌面通知]
        LOG[事件日志]
    end

    GH -->|Push事件| GWC --> AG
    CI -->|构建失败| CSC --> AG
    CRON -->|定时检查| CSC

    AG -->|生命周期| HK
    HK --> EV --> LOG
    HK --> TG
    HK --> DT
    AG -->|自动修复| GH

    style AG fill:#ffcdd2
    style EV fill:#c8e6c9
    style TG fill:#bbdefb
```

### 6.2 CI状态Channel（轮询模式）

```typescript
// ci-status-channel/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

const GITHUB_TOKEN = process.env.GITHUB_TOKEN!;
const REPO = process.env.GITHUB_REPO || "myorg/myrepo";
const POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL || "60000");

const server = new Server(
  { name: "ci-status-channel", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

let lastStatus = "success";

// 注册工具
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "get_ci_status",
      description: "获取最新的CI/CD构建状态",
      inputSchema: {
        type: "object",
        properties: {
          branch: { type: "string", default: "main" },
        },
      },
    },
    {
      name: "rerun_failed_workflow",
      description: "重新运行失败的GitHub Actions workflow",
      inputSchema: {
        type: "object",
        properties: {
          run_id: { type: "number" },
        },
        required: ["run_id"],
      },
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "get_ci_status") {
    const branch = (request.params.arguments as any)?.branch || "main";
    const res = await fetch(
      `https://api.github.com/repos/${REPO}/actions/runs?branch=${branch}&per_page=5`,
      {
        headers: {
          Authorization: `token ${GITHUB_TOKEN}`,
          "User-Agent": "claude-ci-channel",
        },
      }
    );
    const data = await res.json();
    const runs = data.workflow_runs.map((r: any) => ({
      name: r.name,
      status: r.status,
      conclusion: r.conclusion,
      run_number: r.run_number,
      created_at: r.created_at,
    }));
    return { content: [{ type: "text", text: JSON.stringify(runs, null, 2) }] };
  }

  if (request.params.name === "rerun_failed_workflow") {
    const { run_id } = request.params.arguments as any;
    await fetch(
      `https://api.github.com/repos/${REPO}/actions/runs/${run_id}/rerun`,
      {
        method: "POST",
        headers: {
          Authorization: `token ${GITHUB_TOKEN}`,
          "User-Agent": "claude-ci-channel",
        },
      }
    );
    return { content: [{ type: "text", text: `已重新触发workflow #${run_id}` }] };
  }

  throw new Error(`Unknown tool: ${request.params.name}`);
});

// 轮询CI状态并推送到Claude Code
async function pollCIStatus() {
  try {
    const res = await fetch(
      `https://api.github.com/repos/${REPO}/actions/runs?per_page=1&status=completed`,
      {
        headers: {
          Authorization: `token ${GITHUB_TOKEN}`,
          "User-Agent": "claude-ci-channel",
        },
      }
    );
    const data = await res.json();
    const latest = data.workflow_runs?.[0];

    if (latest && latest.conclusion === "failure" && lastStatus !== "failure") {
      lastStatus = "failure";
      // 推送失败事件到Claude Code
      // event_channel_send由Claude Code框架自动处理
      console.error(`[CI FAIL] ${latest.name} #${latest.run_number} failed`);
    } else if (latest?.conclusion === "success") {
      lastStatus = "success";
    }
  } catch (err) {
    console.error("Poll error:", err);
  }
}

// 启动轮询
setInterval(pollCIStatus, POLL_INTERVAL);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error(`CI Status Channel started, polling ${REPO} every ${POLL_INTERVAL}ms`);
}

main().catch(console.error);
```

### 6.3 完整配置文件

```json
{
  "mcpServers": {
    "telegram": {
      "command": "npx",
      "args": ["-y", "@anthropic-ai/claude-code-telegram-channel@latest"],
      "env": {
        "TELEGRAM_BOT_TOKEN": "${TELEGRAM_BOT_TOKEN}"
      }
    },
    "github-webhook": {
      "command": "node",
      "args": ["/opt/claude-channels/github-webhook-channel/index.js"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}",
        "WEBHOOK_PORT": "3456"
      }
    },
    "ci-status": {
      "command": "node",
      "args": ["/opt/claude-channels/ci-status-channel/index.js"],
      "env": {
        "GITHUB_TOKEN": "${GITHUB_TOKEN}",
        "GITHUB_REPO": "myorg/myrepo",
        "POLL_INTERVAL": "60000"
      }
    }
  },
  "hooks": {
    "PreToolUse": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/event-recorder.sh"
      }
    ],
    "PostToolUse": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/event-recorder.sh"
      }
    ],
    "Stop": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/event-recorder.sh"
      }
    ],
    "Notification": [
      {
        "type": "command",
        "command": "bash ~/.claude/hooks/smart-notify.sh"
      }
    ]
  }
}
```

### 6.4 验证整个流水线

```bash
# 1. 启动ngrok（用于接收GitHub Webhook）
ngrok http 3456 &

# 2. 配置GitHub Webhook（替换为你的ngrok URL）
# Repository → Settings → Webhooks → Add webhook
# Payload URL: https://xxxx.ngrok-free.app/webhook
# Content type: application/json
# Secret: (可选)

# 3. 启动Claude Code
claude

# 4. 在另一个终端推送代码触发CI
git commit --allow-empty -m "test: trigger CI pipeline"
git push

# 5. 观察事件流
tail -f ~/.claude/events/$(date -u +%Y-%m-%d)/*.jsonl | jq .
```

预期行为：

1. `git push` → GitHub Webhook → `github-webhook` Channel推送Push事件到Claude Code
2. CI开始运行，`ci-status` Channel轮询检测状态
3. CI失败 → `ci-status` Channel推送失败事件到Claude Code
4. Claude Code收到事件后分析日志，尝试修复代码
5. 修复完成后自动commit + push
6. `Stop` Hook触发桌面通知和Telegram消息
7. 全程事件被记录到JSONL日志
## 第七章 进阶模式与最佳实践

### 7.1 Channel安全模型

| 安全层级 | 措施 |
|----------|------|
| Token保护 | 使用环境变量，永远不硬编码到配置文件 |
| 消息验证 | Telegram/Discord消息需验证来源ID |
| 权限控制 | Channel工具应限制可执行的操作范围 |
| 网络隔离 | Webhook Channel建议使用ngrok + IP白名单 |
| 日志审计 | 所有Channel事件记录到事件溯源系统 |

### 7.2 性能优化建议

- **轮询间隔**：CI状态Channel的`POLL_INTERVAL`不低于30秒，避免GitHub API限流
- **事件过滤**：在Channel中过滤无关事件（如`push`事件中过滤`[skip ci]`提交）
- **消息精简**：推送到Claude的消息控制在500字以内，保留关键信息和URL
- **连接复用**：多个Channel共享同一个HTTP客户端实例
- **错误隔离**：Channel异常不应导致Claude Code会话崩溃，使用`try-catch`包裹

### 7.3 多Agent协同通知

当使用Subagent模式时，通知策略需要分层：

```
主Agent (Stop事件)    → 桌面通知 + Telegram通知
Subagent (SubagentStop) → 仅记录日志
所有Agent (错误事件)   → 桌面通知 + Slack告警
```

在Hook脚本中通过`session_id`区分主会话和子会话。

### 7.4 移动端远程操控最佳实践

通过Telegram/Discord Channel，你可以从手机远程操控Claude Code：

| 指令 | 用途 |
|------|------|
| `/status` | 查询当前任务进度 |
| `/deploy <env>` | 触发部署到指定环境 |
| `/review <PR#>` | 自动审查指定PR |
| `/test` | 运行测试套件 |
| `/logs <service>` | 查看服务日志 |
| `/rollback` | 回滚最近部署 |

> 建议在Claude Code的`CLAUDE.md`中定义这些指令的处理规则，确保响应一致性。
## 延伸阅读

- [Push events into a running session with channels - Anthropic官方文档](https://code.claude.com/docs/en/channels) — Channels核心概念与官方配置指南
- [Channels reference - Anthropic官方文档](https://code.claude.com/docs/en/channels-reference) — Channel MCP Server构建的完整API参考
- [Hooks reference - Anthropic官方文档](https://code.claude.com/docs/en/hooks) — 所有Hook事件的JSON Schema和退出码定义
- [Event-sourced Claude Code workflows - Nick Tune](https://nick-tune.me/blog/2026-03-04-event-sourced-claude-code-workflows/) — 事件溯源模式在Claude Code中的深度应用
- [claude-code-notifications - GitHub](https://github.com/dongzhenye/claude-code-notifications) — 跨平台通知方案合集（终端响铃到桌面通知）
- [Claude Code Telegram Plugin - GitHub](https://github.com/anthropics/claude-plugins-official/blob/main/external_plugins/telegram/README.md) — Anthropic官方Telegram Channel插件源码

## 参考来源

1. [Push events into a running session with channels - Claude Code Docs](https://code.claude.com/docs/en/channels) — Anthropic官方Channels功能文档，涵盖核心概念与配置方法
2. [Channels reference - Claude Code Docs](https://code.claude.com/docs/en/channels-reference) — Channel MCP Server的完整构建参考，包含event_channel_send工具规范
3. [Hooks reference - Claude Code Docs](https://code.claude.com/docs/en/hooks) — Hook生命周期事件、JSON输入输出Schema、退出码的权威参考
4. [Event-sourced Claude Code workflows - Nick Tune](https://nick-tune.me/blog/2026-03-04-event-sourced-claude-code-workflows/) — 事件溯源模式在Claude Code工作流中的开创性实践
5. [Claude Code Notifications - GitHub](https://github.com/dongzhenye/claude-code-notifications) — 社区维护的跨平台通知方案集，从终端BEL到桌面通知的完整覆盖

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
