开源 MCP 代码分析服务加强读取边界
Sentinel-IR-MCP 0.4 系列增加文件替换防护、大小与耗时限制,以及 Agent 和项目均不能修改的读取排除配置。项目还报告目录结果缩小约 64%,并提高原生代码风险等级、改善长期运行稳定性。
AI 深度解读
AI 编程助手读仓库时,不一定需要先吞下全部源码。sentinel-ir-mcp 的做法是先给文件生成结构摘要,让 agent 根据风险、操作类型和代码位置决定接下来读哪里。0.4.0 到 0.4.3 的更新,围绕这个入口补上了文件读取校验、资源限制、精简输出和用户排除规则。
它值得关注的地方,是把「少读一点」落实成了可检查的工程行为。不过,摘要能帮助筛选,不能证明代码安全;服务器上的读取限制,也管不到客户端的其他工具。
先给结构摘要,能省下多少上下文
sentinel-ir-mcp 是一个 MCP server,支持 JavaScript、TypeScript 和 Python,可供 Cursor、Windsurf、Claude Desktop、VS Code、Claude Code 等客户端使用。它把源码转换成简短报告,例如文件会写磁盘、在哪一行调用网络、风险等级是什么,再让 agent 决定是否查看完整内容。
0.4.2 把目录分析的默认结果改成 detail: "summary",每个文件保留路径、风险、分析状态、语言、行数、操作类型等信息。riskSignals 最多列出 3 条决定风险等级的 kind:code@line 信号;需要全部事实时,再用 detail: "full"。
作者用已发布的 0.4.3,分析 MCP SDK 1.32.2 的 dist/cjs 目录,共 174 个文件:完整结果为 79,974 字符,摘要结果为 29,059 字符,缩小约 64%。工具说明则增加约 1,200 字符,每个会话加载一次。
这里测量的是输出字符数,并没有测 token 账单、任务耗时或修改正确率。可以确认返回文本明显减少,实际节省多少费用,还取决于模型和后续读取行为。
风险等级也补了语义说明:后端写文件、调用 API,出现 medium 很正常,agent 应优先查看 critical。0.4.0 将分析器依赖升级到 sentinel-ir ^0.4.2,把 ctypes、cffi、process.dlopen、*.node 和 FFI 包等原生代码入口判为 critical,并在 forbidden 中标记 native_code。
这是审查优先级,不是恶意代码结论。
列目录时安全,不代表打开时仍然安全
0.4.1 处理的是检查与使用之间的竞态:服务器列完文件,真正读取前,目录里有写权限的进程可能把文件替换成指向外部文件的符号链接。
作者让另一进程持续替换目录项,执行 300 次 analyze_directory 扫描。旧版有 9 次读到了目录外文件,新版测试结果为 0 次。核心读取顺序如下,它解决的是「实际打开的文件,还是刚才允许读取的那个吗」:
open(path, O_RDONLY | O_NOFOLLOW | O_NONBLOCK)
fstat(handle) must be a regular file
realpath(path) must be inside an allowed folder
dev/inode of handle == dev/inode of realpath
nlink == 1 on the handle and on the path
read at most maxFileBytes from the handle
重点看 dev/inode 和 nlink 两行:前者核对打开的句柄与路径是否指向同一文件,后者同时检查两处的硬链接数量。
硬链接让同一个文件拥有多个名字,系统只报告链接数,不告诉服务器其他名字在哪里。因此,这个实现拒绝链接数大于 1 的文件。作者最初只检查句柄上的 nlink,仍泄露了 155 次;把路径侧也纳入检查后,测试中的泄露才归零。
这些限制有兼容性代价:合法硬链接文件也会被拒绝。它选择了更保守的读取边界。
资源限制同样具体。--max-file-bytes 默认 1 MiB,上限 2 MiB;--time-budget-ms 默认 30 秒。预算耗尽后不再开始新文件,返回已有结果,设置 timedOut: true,其余文件进入 notAnalyzed。这个设计考虑了原文提到的客户端 60 秒超时,但不能理解为每次扫描必定在第 30 秒结束。
0.4.0 还修复了解析树未释放的问题。旧服务累计处理约 87 MB 代码后开始退化,从第 4,918 次调用起静默返回不完整的文本回退结果。对常驻进程而言,这比一次明显报错更难察觉。
谁来决定 agent 不能读哪些文件
0.4.3 增加了用户排除规则。关键设计是:规则放在客户端配置的服务器启动参数里,由用户决定,agent 和项目都不能修改。
作者没有采用项目内的 .sentinelignore,理由很直接:如果仓库可以定义自己的排除规则,恶意仓库也能把危险文件藏出检查范围。项目自带规则方便团队共享,但不适合独自承担这条安全边界。
下面这组命令覆盖配置、检查和规则文本生成,适合先接到一个现有项目里验证:
cd my-project
npx -y sentinel-ir-mcp@0.4 setup # asks which client and what to exclude
npx -y sentinel-ir-mcp@0.4 setup --client cursor --exclude secrets --exclude "*.min.js"
npx -y sentinel-ir-mcp@0.4 doctor --client cursor # after restarting the client
npx -y sentinel-ir-mcp@0.4 guide # text for AGENTS.md / client rules
重点看重复使用的 --exclude,以及重启客户端后执行的 doctor。它会列出模式、当前匹配数量,并提醒没有匹配结果的规则,帮助发现拼写错误。
setup 展示变更并在确认后写入,--yes 跳过确认,--dry-run 不写文件;旧配置备份为 <file>.bak-<timestamp>,只改 sentinel-ir 条目。对 VS Code 和 Claude Code,它输出对应的 code --add-mcp、claude mcp add 命令。配置写入 sentinel-ir-mcp@0.4,后续补丁版本无需重新执行 setup。
再次配置时,按 Enter 保留排除项,输入 - 或使用 --no-exclude 清空。规则不支持 ! 反向匹配,拒绝绝对路径、.. 和指定 shell 特殊字符,防止破坏 Windows 的 cmd /c npx 命令行。
排除检查覆盖请求路径、真实路径和读取时刻。Windows、macOS 上忽略大小写,也考虑 Windows 短文件名。analyze_file 在确认文件是否存在之前就拒绝排除路径;analyze_directory 只报告排除数量,不暴露名称,排除项也不占 max_files。
这些排除规则只约束这个 MCP server,不能替代客户端整体的文件访问控制。
我的判断
我认可它把摘要压缩和读取边界一起处理。减少源码直接进入上下文,可以减少接触源码内指令文本的机会;但原文没有给出提示注入成功率的对照实验,不能据此判断防御效果。
证据最扎实的是具体回归测试。300 次替换测试、硬链接首次修复仍泄露 155 次,以及 Windows 路径写法差异导致误拒绝,都解释了检查为何需要做到这一步。原文还报告 npm test 共 94 项测试:Linux 为 93 通过、1 项 Windows 专用测试跳过;Windows CI 为 92 通过、2 项跳过,覆盖 Node 20、22、24。每次发布还从 npm 安装并通过 stdio 调用验证。
这些是作者报告的测试结果,不是完整安全审计。原文也没给出大型 JavaScript 文件从「分钟」降到「秒」的具体测量表,更没有证明所有团队都能获得相同收益。
与直接读源码相比,摘要适合先筛选,再围绕关键位置展开。跨文件业务语义、鉴权是否正确、数据流是否完整,仍需要源码和上下文。把原生调用排为 critical 有助于安排审查顺序,但合法依赖也可能命中。
low 不是安全保证,未分析文件更不能当作已经通过检查。
能拿来做什么
- 给现有 AI 编程流程增加一次目录摘要扫描,让 agent 先处理
critical和相关风险位置,再读取完成任务所需的源码。 - 分析 Node 项目时,把
process.dlopen、.node插件和 FFI 依赖作为重点审查入口,继续核对调用目的和依赖来源。 - 用用户侧
--exclude排除secrets、生产配置或*.min.js,再通过doctor检查匹配情况,同时核对客户端其他读取工具的权限。 - 批量扫描时显式处理
timedOut、notAnalyzed和跳过信息;需要追踪请求时,可启用--audit-log,记录路径、哈希、状态和风险,日志不包含代码。