Claude Code扩展工具——Channels
背景
项目进入维护阶段后,很多需求其实只是小改动——文案调整、配置修改、局部逻辑优化。这类改动如果仍走”产品提需求、开发实现、测试验证、上线”的完整流程,会让简单问题的处理周期被拉长,增加沟通与排期成本。
Claude 这类工具已经具备一定的代码理解和修改能力,能够辅助完成相对简单的开发工作。于是有一个值得尝试的方向:对于维护阶段的大量小需求,能否借助 AI 缩短中间链路,让需求更快落地。
但这件事不能简单理解为”把 Claude 交给产品去用”。一方面 Claude 的使用偏代码层面,产品同学通常不具备直接操作代码的能力;另一方面让非研发角色直接进入开发环境,本身存在管理与安全风险。所以关键不在于工具能力够不够,而在于能否围绕现有协作方式,设计一套适合维护场景的流程机制,让 AI 在可控前提下真正参与到小需求处理中。
这篇笔记记录整套方案的设计,以及其中最核心的一环——用 Claude Channels 打通 Web 平台与开发机上 Claude 的实现。
整体方案 整套流程分为五步,把产品的需求一路送到 Claude,再把改动经测试、提交、流水线送上线。
1. 搭建 ClaudeWeb 平台
开发一个 ClaudeWeb 平台,统一管理多个处于维护阶段的项目。产品经理在平台中选择对应项目,直接与该项目绑定的 Claude 交互,描述需求和修改内容。
2. 打通 ClaudeWeb 与 Claude 的通信
ClaudeWeb 负责在产品经理和 Claude 之间建立通信,把需求准确传递给 Claude,同时接收 Claude 的执行结果和反馈,打通产品与项目代码之间的使用壁垒。
3. 由 Claude 在项目内完成代码修改
Claude 在对应项目的运行环境中工作,根据需求完成代码更新。它的操作要受项目规则和权限范围限制:提前配置好 skills、开发规范、rules、hooks 等机制,用于代码检查、测试执行和开发约束,尽量避免异常修改和不规范代码进入项目。
4. 产品经理在测试环境验收改动
代码改完后,产品经理通过测试地址查看效果,确认是否符合预期。验证通过后,再由产品经理在页面上发起提交 Git 的操作。
5. 通过流水线完成发布上线
流水线持续监听 Git 仓库变更,检测到新的版本提交后自动执行后续发布流程,完成上线。
其中第 2 步”打通 ClaudeWeb 与 Claude 的通信”是最关键也最难的一环——只有这条链路建立起来,产品经理在 Web 端提出的需求才能真正传递到具体项目里的 Claude 并由它执行。这一步的解决方案就是下面要讲的 Claude Channels 。
Claude Channels 简介 这是什么 Claude Channels 本质上是一套基于 MCP(Model Context Protocol) 的插件机制。它通过 MCP 协议,在 Claude Code 和外部消息平台之间建立一条通信通道,让原本只在本地开发环境中工作的 Claude,具备远程接收消息、理解指令并返回结果的能力。
可以把它理解成一座桥:一边连接开发机上的 Claude,另一边连接 ClaudeWeb 这样的上层平台。平台侧发出的需求和指令通过这座桥传给 Claude,Claude 的执行结果、状态反馈和处理过程再返回到平台侧。
也就是说,Claude Channels 解决的不是”让 Claude 更聪明”,而是”让 Claude 能接入现有流程”,从而成为整个维护链路中一个可用的节点。
消息流转过程 1 2 3 4 5 6 7 8 9 10 11 消息平台(Telegram / Discord / iMessage / 自建 Web) ↓ MCP Server(插件) ↓ <channel> 事件包装 ↓ Claude Code 会话 ↓ 本地环境处理(FS / Git / MCP) ↓ 通过暴露的工具回复
上游消息进入 MCP Server 后,被包装成 <channel> 事件推给 Claude Code 会话;Claude 在本地环境完成文件、Git、MCP 等操作后,再通过插件暴露的 reply 工具把结果回传。整条链路的两个方向——“消息进”和”结果出”——就是后面代码要实现的核心。
代码实践 前期准备
Claude 客户端 :参考 Claude Code 基础概念及使用手册 ,版本需为最新版(v2.1.80+)。
Go 代码库 :用于开发 MCP Server 及 HTTP Server。
vue-element-admin :用于开发前端。
android_analysis_platform :维护阶段的一个真实项目,用作测试对象。
通信方式 :这里 MCP 采用 stdio 方式,比较方便,也可以换成其他方式。
消息流转流程
Channel MCP Server 开发 MCP Server 是整座桥的中枢,它同时干两件事:作为 MCP 插件挂在 Claude Code 上(stdio),又作为 WebSocket 服务端连着前端。下面只保留最能说明桥接原理的核心片段。
注册工具与通知(mcp.go) 关键在两处:注册一个 reply 工具供 Claude 回话,以及在收到前端消息时用 notifications/claude/channel 通知把消息推给 Claude。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 type ChannelServer struct { mcpServer *server.MCPServer wsManager *WSManager session *Session port int } func NewChannelServer (port int ) *ChannelServer { sess := NewSession() cs := &ChannelServer{wsManager: NewWSManager(sess), session: sess, port: port} mcpSrv := server.NewMCPServer( "claude-web" , "0.0.1" , server.WithToolCapabilities(false ), server.WithInstructions("Messages arrive as <channel source=\"claude-web\" ...>. Reply with the reply tool, passing the chat_id from the tag." ), server.WithExperimental(map [string ]any{ "claude/channel" : map [string ]any{}, "claude/channel/permission" : map [string ]any{}, }), ) replyTool := mcp.NewTool("reply" , mcp.WithDescription("Send a message back to the web UI" ), mcp.WithString("chat_id" , mcp.Required(), mcp.Description("The conversation to reply in" )), mcp.WithString("text" , mcp.Required(), mcp.Description("The message to send" )), ) mcpSrv.AddTool(replyTool, cs.handleReply) cs.mcpServer = mcpSrv cs.wsManager.SetOnChat(cs.handleChatFromBrowser) return cs } func (cs *ChannelServer) handleReply(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error ) { args := req.GetArguments() cs.wsManager.Broadcast(WSMessage{ Type: "chat_reply" , Payload: map [string ]string { "message" : args["text" ].(string ), "from" : "claude" , "chat_id" : args["chat_id" ].(string ), }, }) return mcp.NewToolResultText("sent" ), nil } func (cs *ChannelServer) handleChatFromBrowser(message, sender, chatID string ) { cs.mcpServer.SendNotificationToAllClients( "notifications/claude/channel" , map [string ]any{ "content" : message, "meta" : map [string ]string {"sender" : sender, "chat_id" : chatID}, }, ) }
服务启动时同时跑两个监听:一个 HTTP/WebSocket 服务(ListenAndServe)对接前端,一个 stdio MCP 服务(stdioSrv.Listen)对接 Claude,这部分是样板代码,此处省略。
会话归属控制(session.go) 多个用户可能同时连上同一个项目的 Claude,需要一个”谁在用”的归属锁,避免指令互相干扰。核心是 Claim / Release 两个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 type Session struct { mu sync.RWMutex owner string users map [string ]bool } func (s *Session) Claim(user string ) bool { s.mu.Lock() defer s.mu.Unlock() if s.owner != "" && s.owner != user { return false } s.owner = user return true } func (s *Session) Release(user string ) bool { s.mu.Lock() defer s.mu.Unlock() if s.owner != user { return false } s.owner = "" return true }
前端通信(websocket.go) WebSocket 侧按消息 type 分发,核心是 register(注册在线用户)、chat(发消息,带归属校验)、claim(抢占会话)三类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 type WSMessage struct { Type string `json:"type"` Payload map [string ]string `json:"payload"` } func (wm *WSManager) handleMessage(conn *websocket.Conn, msg WSMessage) { switch msg.Type { case "register" : wm.clients[conn] = &client{conn: conn, user: msg.Payload["user" ]} wm.session.AddUser(msg.Payload["user" ]) wm.BroadcastStatus() case "chat" : sender := msg.Payload["sender" ] if owner := wm.session.GetOwner(); owner != "" && owner != sender { wm.sendTo(conn, WSMessage{Type: "error" , Payload: map [string ]string {"message" : fmt.Sprintf("当前被 %s 占用" , owner)}}) return } chatID := fmt.Sprintf("%d" , nextChatID()) wm.Broadcast(WSMessage{Type: "chat_echo" , Payload: map [string ]string { "message" : msg.Payload["message" ], "sender" : sender, "chat_id" : chatID}}) wm.onChat(msg.Payload["message" ], sender, chatID) case "claim" : if !wm.session.Claim(msg.Payload["user" ]) { wm.sendTo(conn, WSMessage{Type: "error" , Payload: map [string ]string {"message" : "占用失败" }}) return } wm.BroadcastStatus() } }
Broadcast、SendToOwner、BroadcastStatus 等发送方法与 nextChatID 等辅助函数是样板代码,此处省略。
注册自定义 MCP 插件 在测试项目 android_analysis_platform 下创建 .mcp.json,把上面编译出的 Server 作为 stdio 类型的 MCP 插件注册进去。
1 2 3 4 5 6 7 8 9 10 11 12 { "mcpServers" : { "claude-web" : { "args" : [ "channel" ] , "command" : "/data/cuixiaogang/service/data/claude_web/server/claude-web-server" , "env" : { "CHANNEL_PORT" : "15422" } , "type" : "stdio" } } }
启动带该 channel 插件的 Claude:
1 claude --dangerously-load-development-channels server:claude-web
如果不希望 Claude CLI 一直占着当前终端不能停,可以用 tmux 把它放到后台会话里跑。
前端事件处理 前端就是一个聊天窗口,额外要能接收权限授权事件。核心是 _handleMessage:按后端推来的消息 type 分发到不同处理——Claude 回复、用户消息回显、会话状态、权限请求、错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 _handleMessage (msg ) { switch (msg.type ) { case WS_MSG_TYPE .CHAT_REPLY : this .store .commit ('claude/ADD_MESSAGE' , { role : 'claude' , content : msg.payload .message , timestamp : Date .now () }) break case WS_MSG_TYPE .CHAT_ECHO : this .store .commit ('claude/ADD_MESSAGE' , { role : 'user' , content : msg.payload .message , sender : msg.payload .sender , timestamp : Date .now () }) break case WS_MSG_TYPE .STATUS : this .store .commit ('claude/SET_SESSION_STATUS' , msg.payload ) break case WS_MSG_TYPE .PERMISSION_REQUEST : this .store .commit ('claude/ADD_PERMISSION_REQUEST' , msg.payload ) break case WS_MSG_TYPE .ERROR : this .store .commit ('claude/SET_ERROR' , msg.payload .message ) break } }
建连与注册(_doConnect 里 onopen 时发 register、onclose 时重连)是常规 WebSocket 样板,此处省略。
最终成果
小结
环节
要点
方案目标
维护阶段的小需求,借 AI 缩短”产品→开发→测试→上线”链路,且全程可控
核心难点
打通 Web 平台与开发机上 Claude 的通信,即第 2 步
Claude Channels
基于 MCP 的插件机制,在 Claude Code 与外部消息平台间架一条双向通道
MCP Server
中枢:一边 stdio 挂在 Claude 上,一边 WebSocket 连前端
两个方向
前端→Claude 用 channel 通知推入;Claude→前端用 reply 工具回传
安全控制
session 归属锁(Claim/Release)+ 权限审批中继 + 项目内 rules/hooks 约束
延伸方向:给 Claude 配置项目级 skills / hooks 做代码质量卡点,以及把提交 Git、触发流水线也包装成工具接入这条链路。