
1. 先想清楚Slack 里跑 Claude Code 到底图什么1.1 为什么非要把终端里的东西塞进聊天框先聊个很现实的问题Claude Code 本身是个跑在终端里的命令行工具你写代码的时候打开终端输入claude它就在你的项目目录里帮你读代码、改文件、跑测试。那为什么还要把它搬进 Slack我自己的第一反应是这不是脱裤子放屁吗终端里用得好好的何必绕一圈。但真在团队环境里用过之后我发现 Slack 接入 Claude Code 的价值完全不在“替代终端”而在四个字场景外溢。举个例子。你正在改一个支付模块突然产品经理在 Slack 里问“这个订单状态下个月要加个自动退款逻辑你评估下要动哪些文件、大概多久”这种时刻你不可能在聊天窗口里贴一堆代码上下文也不可能叫产品去看你的终端。但如果你在 Slack 里配好了 Claude Code 入口直接往频道里丢一句/cc 帮我看下这个仓库里订单表和退款逻辑在哪些文件列一下改动点Claude Code 会直接读取项目、搜索代码、给出结论然后把结果回到 Slack 里。整个过程不需要你离开聊天窗口不需要你手动打开终端切目录更不需要你复制粘贴近乎零上下文的散碎代码。这就是 Slack 版 Claude Code 的定位把 AI 编程助手从“个人终端工具”变成“团队协作工具”。它服务的不是你一个人在终端里高效写码而是让团队在沟通现场就能获得代码层面的即时响应。1.2 适合谁用不适合谁别硬上聊完价值再泼盆冷水。这东西不是所有人、所有团队都适合。我觉得比较适合的场景是团队日常沟通在 Slack且项目代码都在本地或固定的开发机上成员需要频繁讨论代码相关细节。你有很多“看一眼代码就能回答”的咨询类问题比如“这个函数谁在调用”“这个配置在哪个文件”“上次改的定时任务在哪”这类问题让 Claude Code 去查非常省时间。非技术人员也要能触发 AI 查询。产品、测试、运营不需要装 Node 环境他们有 Slack 就行在频道里发一条指令就能得到结果。不适合的情况也很明确重度代码修改场景。Claude Code 本身就是为交互式、多轮、可确认的操作设计的跑在聊天机器人里会让这种交互大打折扣。你让它改代码它改完了你没法直接在聊天框里 review diff体验非常拧巴。项目特别大、启动特别慢每次问答都要等半分钟以上体验就很差。团队 Slack 本来就很吵。把机器人和业务消息混在同一个频道里指令识别和会话隔离都会很麻烦。我自己实测下来的感受是Slack 接入 Claude Code 最适合做“代码问答 任务拆解 轻量修改”不适合做“深度结对编程”。定位清楚了后面思路就不会歪。1.3 官方集成和自建网关我该怎么选目前把 Claude Code 接进 Slack 有两种路线一是用官方/半官方提供的集成方式二是自己写一个桥接服务。官方集成的优点是省事配置好应用和权限后直接在 Slack 里像 一个同事那样 Claude它会基于你配置的代码仓库做回答。适合不想折腾基础设施、只想要一个“能聊代码的机器人”的团队。自建网关则更像是“自己掌控一切”你在自己的服务器或开发机上跑一个 Node.js 服务监听 Slack 的 Socket Mode 事件收到消息后用child_process调用本地的claude命令再把输出回传。这样做的好处是可以完全复用你现有的本地环境、模型配置、MCP 服务。可以自定义指令前缀、权限控制、会话隔离。可以把 Slack 消息转发给 Claude Code 做复杂任务比如“在这个仓库里新增一个接口”。坏处也很直接你得自己维护。网络断了要排查进程崩溃要重启并发多了要排队。这篇教程主要讲自建网关的方案因为它的灵活度最高而且能真正用上你本机或服务器上已经调好的 Claude Code。后面涉及的所有代码和配置我都会给出完整的可落地版本。2. 准备工作环境、权限、Slack 应用配置2.1 你需要的环境清单在动手之前先把环境确认好。我踩过不少坑都是因为环境版本不匹配所以这里列一个自查清单项目要求说明Node.js18.0 以上运行桥接服务必需建议用 20 LTSClaude Code CLI已安装并可用运行claude --version确认Claude Code 登录状态已登录且有可用额度运行claude能正常进入交互Slack 工作区有权限创建应用需要管理员或开发者权限开发机/服务器能访问 Slack APISocket Mode 下只需要能出网请求 Slack 域名如果你还没有安装 Claude Code先别往下读去把这一环补上。安装方式很简单在终端里执行npm install -g anthropic-ai/claude-code装完确认一下claude --version如果输出版本号说明安装成功。接下来测试登录claude看到交互式提示符后随便提一个问题确认模型能正常回答。这一步很重要因为后面 Slack 机器人本质上是在替你调用这个命令如果命令行本身有问题后面所有排查都会很痛苦。2.2 在 Slack 后台创建一个应用接下来去 Slack 的 App 管理后台操作。网址是https://api.slack.com/apps登录你的工作区账号。点击Create New App选From scratch给它起个名字比如Claude Bridge选择目标工作区然后创建。创建完成后你会进入这个 App 的配置页面。这里我按顺序带你走一遍重点配置项。第一步开启 Socket Mode在左侧菜单找到Socket Mode打开开关。开启时系统会要求你生成一个App-Level Token给这个 token 起名比如claude-socket-token作用域选connections:write然后创建。这个 token 很重要一会写服务配置时会用到它形如xapp-1-...。第二步配置 Bot Token 作用域左侧菜单进OAuth Permissions往下翻到Scopes区域。Bot Token Scopes 至少要添加这几个chat:write往频道/私聊中发送消息im:history读取私聊消息im:write向用户私聊发送消息channels:history读取公开频道消息groups:history读取私密频道消息如果要用reactions:write可选用于给消息加个“正在处理”的标记这里我推荐把私聊IM和频道Channel的历史读取都配上这样机器人既能处理和用户的私聊也能响应频道里被 的消息。然后点击页面顶部的Install to Workspace完成安装授权。安装完成后你会得到一个Bot User OAuth Token形如xoxb-...。把它复制保存好。第三步配置事件订阅左侧菜单进入Event Subscriptions打开开关。这里要选消息事件消息类型选择message.im用户与机器人的私聊消息再选message.channels频道里提到了机器人的消息如果有私密频道需求再加上message.groups选好后点击保存。系统会提示你配置请求 URL但因为我们在用 Socket Mode所以不需要填那串 URL直接忽略即可。此时你应该已经拿到两个核心 TokenBot Token: xoxb-... App Token: xapp-...收好它们下面写代码的时候要用。2.3 机器人身份和权限边界很多新手在配置完 Slack App 之后直接把机器人加进公开频道就开始用这会埋下不少坑。首先是机器人入频道的方式。在 Slack 客户端里点频道名 - 集成 - 添加应用把刚才创建的机器人加进去。如果不开 Socket Mode机器人只能响应频道里的 提及这是 Slack 的安全机制避免它接收频道里所有消息。其次是消息可见性。即便机器人在频道里它默认也读不到历史消息除非配置了channels:history并开启相应事件。如果你想让它“进频道就能总结最近 100 条聊天记录”那必须确认历史读取权限。最后是权限越大风险越大。如果你的桥接服务部署在公网服务器上不要给机器人的 Token 太宽泛的权限。按最小必要原则只授予它“读消息、发消息”的权限就好千万不要加admin或users:write之类的作用域。2.4 自建网关的整体架构在写代码之前先理解整个请求链路。用户会在 Slack 里发一条私聊消息或是在频道里 机器人。Slack 客户端通过 Socket Mode 把这起事件推到我们的服务进程中。服务进程解析消息文本看它是不是一条以特定前缀开头的指令比如/cc。如果是就拼接参数调用claude命令行在项目目录下执行一次非交互式请求。Claude Code 处理完毕后会把结果输出到 stdout服务进程捕获输出再通过 Slack API 发回聊天窗口。画成流程大致这样用户消息 - Slack API - Socket Mode 网关 - Node 服务 | child_process | claude CLI 进程 | 结果回传 Slack这套架构的好处是简单直接不依赖第三方框架也不依赖订阅外部回调地址。只要你能出网服务往哪跑都行。3. 实操用 Node.js 搭一个 Slack 到 Claude Code 的桥接服务3.1 初始化项目与安装依赖我在本地建了一个空目录名字叫slack-claude-bridge。之后的演示都基于这个目录。mkdir slack-claude-bridge cd slack-claude-bridge npm init -y然后安装关键依赖npm install slack/socket-mode slack/web-api dotenv简单解释一下这三个包是干嘛的slack/socket-mode负责与 Slack 建立 WebSocket 连接接收事件。也就是上面说的 Socket Mode 网关。slack/web-api负责调用 REST API 发送消息。dotenv用来管理环境变量避免在代码里硬编码 Token。装完继续创建.env文件SLACK_APP_TOKENxapp-1-xxxx SLACK_BOT_TOKENxoxb-xxxx CLAUDE_PROJECT_PATH/path/to/your/project视你自己项目的实际绝对路径把/path/to/your/project换成 Claude Code 要操作的代码仓库路径。3.2 建立 Socket 连接并监听消息事件创建入口文件index.jsrequire(dotenv).config(); const { App } require(slack/socket-mode); const { WebClient } require(slack/web-api); const socketMode new App({ appToken: process.env.SLACK_APP_TOKEN, socketMode: true, }); const web new WebClient(process.env.SLACK_BOT_TOKEN); socketMode.start(); console.log(Socket Mode 已启动);这个基础上我们要监听消息事件。Socket Mode 下的事件结构和普通 Event API 类似但多了一个类型字段。比较稳妥的方式是监听message事件然后根据event.channel_type判断是频道还是私聊。const COMMAND_PREFIX /cc; socketMode.event(message, async ({ event }) { const text event.text || ; const channel event.channel; if (!text.startsWith(COMMAND_PREFIX)) return; if (event.subtype event.subtype ! bot_message) return; const prompt text.slice(COMMAND_PREFIX.length).trim(); if (!prompt) { await web.chat.postMessage({ channel, text: 用法/cc 你想让 Claude Code 干的事, }); return; } // 后续处理调用 Claude Code 并回传结果 });这串代码有几个细节值得说。为什么要用/cc前缀因为你可能不想让机器人响应频道里所有消息前缀相当于一个触发器只有消息以/cc开头时才触发避免误伤业务聊天。为什么要检查subtypeSlack 的消息事件里有很多子类型比如消息被编辑、被删除、机器人自己的消息等。如果不加这个判断可能会造成消息回环也就是机器人收到了自己发出的消息又去触发一次 Claude Code形成一个死循环。我第一版就吃过这个亏机器人一直重复执行因为它的输出消息也带/cc。3.3 调用 Claude Code CLI 并捕获输出这是核心环节。关键在于怎么用命令行跑 Claude Code。Claude Code 官方提供了非交互模式可以在一条命令里直接给出提示并输出结果。先说说我在实践里用的方式const { exec } require(child_process); function runClaude(prompt, projectPath) { return new Promise((resolve, reject) { const command claude -p ${prompt.replace(//g, \\)} --output-format text; exec(command, { cwd: projectPath, maxBuffer: 10 * 1024 * 1024, timeout: 120000, env: { ...process.env, CLAUDE_CODE_SKIP_CONFIRM: 1, }, }, (error, stdout, stderr) { if (error) { reject(new Error(Claude 进程异常: ${error.message})); return; } resolve(stdout.trim()); }); }); }这里的重点参数是-p表示 prompt 模式也就是一次性输入指令不进入交互式会话。--output-format text让输出以纯文本返回而不是 JSON。这两项组合就是我实测下来最稳定的非交互调用方式。还有几个注意事项prompt.replace(//g, \\)这个转义必须做否则提示词里如果包含双引号命令会被拆断。我遇到过提示词里有中文引号没问题但英文引号直接切掉半个命令的错误。maxBuffer设置大一点默认 1MBClaude Code 的输出经常能到几万字符太小会报stdout maxBuffer exceeded。timeout给了 120 秒。不是每条任务都需要那么久但碰到大仓库或者复杂任务给足余量才能避免中途被杀。设置CLAUDE_CODE_SKIP_CONFIRM1是为了跳过交互确认让它在非交互模式下不会卡在“是否执行文件修改”之类的问句上。把调用流程串起来socketMode.event(message, async ({ event }) { const text event.text || ; const channel event.channel; if (!text.startsWith(COMMAND_PREFIX)) return; if (event.subtype event.subtype ! bot_message) return; const prompt text.slice(COMMAND_PREFIX.length).trim(); if (!prompt) { await web.chat.postMessage({ channel, text: 用法/cc 你想让 Claude Code 干的事 }); return; } // 先给用户一个“正在处理”的反馈 const ackRes await web.chat.postMessage({ channel, text: 收到正在让 Claude Code 处理${prompt}, }); try { const result await runClaude(prompt, process.env.CLAUDE_PROJECT_PATH); await web.chat.postMessage({ channel, text: 结果如下\n\\\\n${result.slice(0, 3500)}\n\\\, }); } catch (error) { await web.chat.postMessage({ channel, text: 执行出错${error.message}, }); } });这里我特意把结果截断到 3500 字符。Slack 单条消息有 40000 字符限制但在聊天界面里发超长文本体验非常差截断成代码块能让消息更清爽。3.4 让 Claude Code 返回更友好的格式你可能会觉得上面那版输出太朴素了。评论区确实有人问我能不能让 Claude Code 在 Slack 里直接以 Markdown 或列表形式返回可以但没必要。Slack 的消息格式和 Markdown 不是完全兼容的尤其是表格、标题、嵌套列表这些发过去经常一团糟。我的习惯是让它返回纯文本尤其是代码相关的结果直接用代码块包裹。如果你想试更好的格式runClaude里的--output-format可以改成json。这样 Claude Code 会返回结构化的 JSON里面含result、cost、usage等字段。你可以在服务端解析出更精致的内容再发到 Slack比如把它消耗的 token 数一起发出来方便团队做成本监控。像这样改const resultJson await runClaude(prompt, process.env.CLAUDE_PROJECT_PATH, json); const parsed JSON.parse(resultJson); const resultText parsed.result; const costInfo parsed.cost ? \n\n本次调用约消耗 $${parsed.cost} : ;不过这里有个坑Claude Code 在不同版本里 JSON 输出的字段名可能不太一样。有的版本是result有的版本是result下还嵌套body。如果你在解析上出问题先手动在终端跑一次命令用console.log看下完整结构再适配。3.5 启动服务并测试在package.json的scripts里加一个启动命令{ scripts: { start: node index.js } }然后在目录下运行npm start看到终端打印出Socket Mode 已启动说明连接成功。这时候打开 Slack找到机器人给它发一条私聊消息/cc 帮我看下这个项目的 package.json 里有哪些依赖机器人的反应顺序应该是先回一句“收到正在让 Claude Code 处理”等待几秒或几十秒然后回传 Claude Code 的结果。如果中间出了问题常见的有两类一是 Socket 没连上二是 claude 命令执行失败。这两种问题的排查我都会在第 5 章单独展开。4. 让它更顺手会话隔离、并发控制、安全设置4.1 指令前缀和会话隔离你有多少个用户在用这个机器人如果只有你自己那很简单。但团队共用时就有一个非常头疼的问题会话互相污染。举个例子。A 用户在频道里发/cc 帮我查一下订单模块的代码B 用户接着发/cc 顺便看看退款逻辑。如果机器人每次启动独立的claude -p进程没有记忆那它们互不干扰。但如果你的设计是复用同一个交互会话那 B 的请求就会把 A 的结果覆盖掉。我的建议是每个用户、每个频道维护独立的会话标识。最简单的做法是用消息里带的user和channel作为标识拼接成一条唯一 key比如U123456_C123456。服务端可以用一个哈希表存储这个 key 对应的会话状态。不过考虑到claude -p是非交互模式每次调用都是冷启动没有上下文记忆这反而帮我们省掉了会话管理的复杂度。如果你想要多轮对话可以把历史消息拼进 prompt 里类似于const fullPrompt [ 以下是之前对话的摘要, conversationHistory.join(\n), , 当前问题, prompt, ].join(\n);这种方式维护成本低也足够应付大多数问答场景。4.2 并发限制和排队机制你自己在终端里跑 Claude Code 没问题但团队里好几个人同时用并发就成了问题。Claude Code 进程在同一台机器上跑多个实例会互相争抢资源而且极易触发 API 的频率限制。我踩过的一个真实例子是同事在 Slack 连发了 5 条指令机器人瞬间开了 5 个claude进程结果五个请求全部超时。原因就是同时启动的进程太多又都对着同一个模型 API 发请求配上限流直接把任务拖垮了。解决方案是给服务加上一个简单的任务队列。用现成库可以自己写也行。我自己实现了一个简单版const queue []; let isProcessing false; async function processQueue() { if (isProcessing) return; isProcessing true; while (queue.length 0) { const { prompt, channel, user } queue.shift(); try { const result await runClaude(prompt, process.env.CLAUDE_PROJECT_PATH); await web.chat.postMessage({ channel, text: ${user} 结果如下\n${result.slice(0, 3500)} }); } catch (err) { await web.chat.postMessage({ channel, text: 执行出错${err.message} }); } } isProcessing false; } function enqueue(prompt, channel, user) { queue.push({ prompt, channel, user }); processQueue(); }这样无论什么时间段涌入多少请求服务都会一次只跑一个任务其余的排队等待。虽然单个任务响应变慢但整体稳定性好很多。这个队列是内存队列服务重启会丢任务。对于内部工具来说完全够用不需要引入 Redis。4.3 权限控制和防止滥用把 Claude Code 暴露给整个 Slack 之后第一个要考虑的是谁能调用。我在内部部署时设置了两个门槛。第一个门槛是白名单。只有指定的用户 ID 可以触发/cc指令。获取用户 ID 的方式很简单在 Slack 里点击用户头像菜单里能看到用户 ID或者通过event.user字段直接取。const ALLOWED_USERS [U123456, U789012]; socketMode.event(message, async ({ event }) { if (!ALLOWED_USERS.includes(event.user)) return; // 后续逻辑 });第二个门槛是调用频率限制。防止某个人狂刷指令把资源耗尽。你可以用时间戳记录每个用户最后一次触发的时间间隔不足 10 秒就拒绝并提示“操作太快请稍候”。这个逻辑非常简单我用的是一个lastReceived对象const lastReceived {}; function throttleCheck(user, interval 10000) { const now Date.now(); if (lastReceived[user] now - lastReceived[user] interval) { return false; } lastReceived[user] now; return true; }4.4 敏感操作和审核策略Claude Code 的能力不止是问答它可以改代码、跑命令。如果机器人被滥用就相当于把一个能改代码的终端暴露给了所有能发消息的人。我的处理原则是聊天机器人只做“读”和“说”不做“写”和“执行”。具体操作是在调用 Claude Code 时加参数禁止它去修改文件系统claude -p 你只能阅读代码并回答问题不要修改任何文件 --permission-mode read-only新版 Claude Code 里有--permission-mode参数设成read-only后就只能读取不能写文件、不能执行命令。这对团队内部使用来说是更稳妥的默认值。如果你确实需要它执行改代码或者跑命令建议单独设一个“操作专用频道”只允许管理员在其中触发高级指令普通频道的机器人统一用只读模式。5. 常见问题与排查记录5.1 Socket 连接失败或频繁断开现象服务启动后终端打印Socket Mode 已启动但过几分钟就断线或者从来没成功连上。排查步骤确认SLACK_APP_TOKEN是以xapp-开头的 App-Level Token不是xoxb-的 Bot Token。确认 Socket Mode 在应用配置里是开启状态。试一下在其它设备上能否访问 Slack 的域名。Socket 模式走的是一个长连接有些公司的网络策略会拦截 WebSocket 连接。如果频繁断开注意slack/socket-mode库的日志。可以在初始化时打开日志const socketMode new App({ appToken: process.env.SLACK_APP_TOKEN, socketMode: true, logLevel: 2, });日志级别设为 2 之后能看到连接断开的原因码。最常见的几个原因码是1006异常断开、1000正常关闭、1011服务端错误。如果日志里反复出现reconnecting优先检查网络稳定性。5.2 机器人收到消息但没有任何反应这个问题十有八九出在事件订阅上。你先打开 Slack App 后台的Event Subscriptions确认三个消息事件有没有配置message.im、message.channels、message.groups。这三个事件分别对应私聊、公开频道、私密频道。缺哪一个哪一个场景就收不到消息。还有一个隐蔽的问题机器人要不要回应自己的消息。如果你的服务把/cc开头的消息返回结果返回结果里也带了text这时如果你的消息监听不区分subtype就会把自己发出的消息也当成一次触发。我在 3.2 节里加了subtype ! bot_message的判断就是干这个用的。如果确认事件没问题再检查一下socketMode.event(message)注册有没有生效。有些版本的事件监听是用socketMode.message()而不是socketMode.event(message)。如果换了版本接口名不一样旧写法可能不触发。建议我在实际项目里直接跑通最小示例先试试socketMode.message(({ event }) console.log(event))能不能打印出消息再往下接。5.3 Claude Code 进程卡死或超时最典型的情况服务收到消息后给用户回了“收到”然后就没有下文了。过了一会用户又收到“执行出错Claude 进程异常”。这里分两种原因。第一种是prompt 里带了导致 CLI 挂起的字符。比如中文双引号、反斜杠、换行符。我在runClaude里做的转义是处理英文双引号的但如果你传的参数包含换行符最好先把换行符替换成空格const sanitized prompt.replace(/[\r\n]/g, );第二种是Claude Code 在非交互模式下卡在确认步骤。比如它在执行过程中准备修改文件但又没有--accept-editor之类的参数于是进程等待用户输入最终超时。用只读模式可以规避大部分确认卡顿claude -p 你的提示 --permission-mode read-only如果还是要让它执行写操作那我建议不要用exec这种一次性命令而是切换到spawn用标准输入去喂确认输入。5.4 输出太长导致消息格式错乱一个非常常见的体验问题Claude Code 返回了几万字的分析服务端直接 post 到 Slack结果消息在客户端里变成一坨翻都翻不完。我的做法是分块发送。Slack 消息有 40000 字符的单条上限但聊天体验极度依赖排版。我自己习惯按 3000 到 4000 字符为一段超过就拆成多条消息依次发出。function chunkText(text, size 3500) { const chunks []; let current text; while (current.length size) { chunks.push(current.slice(0, size)); current current.slice(size); } chunks.push(current); return chunks; }还有一种思路是把输出写到一个外部服务或文件里Slack 只发一个摘要和一个链接。如果你有内部的日志平台这招更好用属于团队内部工具的标准做法。5.5 403 / invalid_auth 错误这个错误一看就是 Token 问题。但具体是哪一环节的 Token需要分开查。如果是事件连接建立时报invalid_auth那就是SLACK_APP_TOKEN错误或权限不足回到 2.2 节检查 App-Level Token 的connections:write是否添加。如果是发送消息时报invalid_auth那就是SLACK_BOT_TOKEN的问题。有可能是选错了 Token 类型也可能是应用安装后又被重新安装旧的 Bot Token 失效了。重新安装应用后在OAuth Permissions页面复制最新 Token替换到.env里重启服务。一个我遇到过的隐蔽情况同样的.env文件在本机能跑换到别的机器上就报403。排查了半天发现是.env文件没被新机器加载环境变量缺失。别笑这种低级错误真的能浪费一小时。推荐在代码入口处加一段启动自检if (!process.env.SLACK_APP_TOKEN || !process.env.SLACK_BOT_TOKEN) { console.error(缺少必要的环境变量请检查 .env 文件); process.exit(1); }趁早暴露问题比跑到一半才报错好得多。6. 后续还能怎么玩这部分我必须说点自己踩过坑之后的体会。最初我做这套桥接只是为了让同事不装环境也能让 AI 帮忙看代码。后来发现它做“代码搜索问答”特别稳就把功能扩展到了“自动写周报”“每日站会前自动扫一遍代码提交记录”。原理都一样就是把不同 prompt 塞进/cc指令再让 Claude Code 按照固定模板输出。这个思路我建议所有想团队落地 AI 编程工具的读者参考先固定两三个高频场景跑通再扩。一个特别值得一试的方向是把 Slack 消息连上 MCP。Claude Code 的 MCP 客户端能力可以从文件系统、数据库、API 网关拉数据。如果你在桥接服务的 prompt 里注入 MCP 相关配置那 Slack 里的同事就能直接问“昨天订单表的退款失败率是多少”这种数据问题。不需要搭复杂的数据分析平台Claude Code 会通过 MCP 工具去查。最后提醒一句所有把命令行工具暴露成团队服务的人都必须先想清楚安全边界。我的原则是“默认只读按需放开保持审计”。哪怕团队内部再信任也别让机器人拥有无授权的改文件和执行命令能力。你可以在服务端把所有指令记录下来出了问题好回溯。这一条谁用谁知道。