突破 Claude Code 权限封锁:Kanban × MCP 自动化集成实战
突破 Claude Code 权限封锁:Kanban × MCP 自动化集成实战
背景:一个被"安全模型"卡住的需求
我们用 Hermes Agent 做了一套多 Agent 协作的看板系统——任务自动分配给不同角色(编码师、审核员、规划师),由 AI 自主执行。为了让编码任务的执行质量更高,我们想把 Claude Code CLI 作为实际的编码引擎接入看板:Hermes 负责调度,Claude Code 负责写代码。
设计思路很清晰:Claude Code 通过 MCP(Model Context Protocol)连接看板服务,自主读取任务、写代码、标记完成。听起来很美好,直到我们撞上了 Claude Code 的安全模型。
问题:-p 模式的权限铁幕
Claude Code 有一个 -p(prompt)模式,可以非交互式地执行任务。我们的 Hermes worker 就是通过 terminal("claude -p '...'") 来调用它的。
但问题来了:在 -p 模式下,Claude Code 禁止自动批准任何"有副作用"的工具调用——包括 MCP 工具和 Bash 命令。这是硬编码行为,不是配置能解决的。
我们尝试了所有能想到的绕过方式:
--dangerously-skip-permissions:root 用户禁止使用
--permission-mode acceptEdits:Write 能用,但 MCP 和 Bash 仍被拦截
~/.claude/settings.json 的 allow list:MCP 工具无视此配置
非 root 用户:同样被拦截
核心原因:Claude Code 的安全模型把 MCP 工具调用和 Bash 命令执行视为同一风险等级,-p 模式下需要交互式批准。对于自动化流水线来说,这等于判了死刑。
突破口:一个关键发现
在几乎要放弃的时候,我们发现了一个被忽略的组合:
非 root 用户 + --dangerously-skip-permissions
这个 flag 文档明确说"root 禁止"(因为 root 权限太大,跳过审批太危险),但对普通用户是放行的。之前测试时,我们用 root 跑会报错,用非 root 跑时又忘了加这个 flag。
验证结果:
✅ 非 root + --dangerously-skip-permissions + MCP → 工具自动批准
✅ 单轮、多轮 MCP 调用全部通过
✅ MCP + 文件读写组合正常
唯一的限制:Bash 仍然被拦截。但 MCP 工具通了,这就够了——状态更新走 MCP,代码编写走 Read/Write/Edit。
架构设计
确定技术可行后,我们设计了完整的集成方案:
核心组件
1. kanban-mcp-server:一个 Python 实现的 MCP stdio 服务,直接读写 kanban.db(SQLite),暴露四个工具——kanban_show(查看任务)、kanban_complete(标记完成)、kanban_block(标记阻塞)、kanban_comment(添加评论)。每个实例绑定一个 task_id,启动时通过命令行参数传入。
2. claude-worker 用户:专用的非 root 用户,加入 kanban 组以获得数据库写权限。通过 sudoers 配置免密执行 kanban-mcp-server(因为 MCP server 需要读写 root 目录下的数据库)。
3. claude-worker-wrapper:Python 脚本,负责切换到 claude-worker 用户、配置 DeepSeek API 代理、启动 Claude Code CLI。
4. kanban-worker-lite skill:薄 Hermes worker 的行为定义,告诉 worker 如何生成 MCP 配置、调用 wrapper、检查结果、处理异常。
5. 路由配置:kanban_routing.yaml 定义哪些 profile 走 Claude Code 路径。删除配置行即回滚,零代码改动。
数据流
看板 Dispatcher 发现新任务 → Hermes worker 启动(加载 kanban-worker-lite skill)→ 读取任务详情 → 生成 MCP 配置 JSON → 调用 claude-worker-wrapper → Claude Code 以 claude-worker 身份运行,通过 MCP 读写看板,通过 Read/Write/Edit 操作代码 → 完成后调用 kanban_complete → Hermes worker 验证状态更新 → 退出
关键实现细节
权限配置
让非 root 用户访问 root 拥有的数据库是个挑战。我们用了三层机制:
1. /root 目录设为 755(可遍历)
2. kanban.db 设为 kanban 组可写(664)
3. sudoers 免密规则:claude-worker 可以免密执行 kanban-mcp-server
注意:.hermes 目录会被 Hermes 自动重置为 700(安全机制)。如果遇到权限问题,需要手动 chmod 755 /root/.hermes。
MCP 配置生成
每个任务启动时,动态生成 MCP 配置文件:
{"mcpServers": {"kanban-tools": {"command": "sudo", "args": ["-n", "/usr/local/bin/kanban-mcp-server", "--task-id", "xxx", "--db", "/root/.hermes/kanban.db"]}}}
关键点:MCP server 通过 sudo -n 以 root 身份运行(读写数据库),而 Claude Code 本身以 claude-worker 身份运行(受限权限)。
Dispatcher 路由
修改了 Hermes 的 kanban_db.py 中的 _default_spawn() 函数,加入路由逻辑:读取 kanban_routing.yaml,如果当前 profile 在路由表中,就使用 kanban-worker-lite skill + Claude Code 路径;否则走原来的 Hermes 原生路径。
回滚方式:编辑 YAML 文件,注释掉对应 profile 行。零代码改动,即时生效。
踩坑记录
坑 1:Skill 找不到
每个 Hermes profile 有独立的 skills 目录。创建 skill 后需要复制到各 profile 的 skills 目录下,否则 worker 进程加载时会报 "Unknown skill"。虽然这个错误是非致命的(worker 会降级到标准路径),但 Claude Code 委托就不会发生了。
坑 2:.hermes 目录权限被重置
Hermes 启动时会把 .hermes 目录重置为 700。如果 claude-worker 需要访问这个目录下的文件(比如 kanban.db),需要定期检查权限或在 wrapper 中加入权限修复逻辑。
坑 3:wrapper 的 shell 引号地狱
最初用 bash 脚本做 wrapper,结果 prompt 中的单引号、双引号、特殊字符导致各种诡异错误。最终改用 Python 脚本,通过写临时文件传递 prompt,彻底解决了引号问题。
坑 4:DeepSeek 代理需要单独启动
wrapper 依赖本地 DeepSeek 代理(localhost:8765),但代理服务默认不自启。需要确保 claude-ds-proxy 服务在运行。
灰度策略
安全第一,分三周灰度:
Week 1:只切 coder2(GLM-5.1),观察稳定性
Week 2:加入 coder(DeepSeek V4 Pro)
Week 3:加入 reviewer 和 planner(Opus 4.6)
任何时候出问题,编辑 YAML 注释掉对应行即可回滚。
性能数据
端到端测试结果:一个简单的"创建 Python 脚本并运行"任务,从调度到完成耗时约 2 分钟。其中 Claude Code 执行占 40 秒,其余是进程启动、MCP 握手、状态更新的开销。对于中等复杂度的编码任务,预期 5-15 分钟。
经验总结
1. 不要和安全模型硬刚
Claude Code 的权限设计是有道理的。与其想办法绕过,不如找到架构上的出路——把需要权限的操作和不需要权限的操作分开。我们最终的方案是:Claude Code 只负责读写文件(低风险),Hermes 负责状态更新(通过原生 MCP)。
2. 非 root 用户是自动化的朋友
--dangerously-skip-permissions 对非 root 放行,这个设计很合理——普通用户的破坏力有限。我们的方案让 Claude Code 以受限用户运行,MCP server 通过 sudo 获取必要的数据库权限,实现了最小权限原则。
3. 路由配置 > 代码修改
把"哪些 profile 走 Claude Code 路径"这个决策放在 YAML 配置里,而不是硬编码在 Python 中。好处是回滚零成本——注释一行配置比改代码、测试、部署快得多。
4. 渐进式集成
先验证单个 MCP 工具调用,再验证多轮调用,再验证和文件操作的组合,最后才跑端到端。每一步都有明确的通过/失败标准。这种渐进式验证比"写完所有代码再测试"效率高得多。