把文件、数据库或命令封装成 MCP 工具后,模型获得的不只是更多上下文,也可能获得修改系统状态的入口。风险并不取决于工具名称是否友好:名为 read_file 的工具可能接受任意路径,名为 run_test 的工具也可能把参数直接拼进 Shell。接入前应把每个工具当作外部调用接口审查,明确输入边界、运行身份、可观察副作用和失败策略。本文给出一份不依赖具体 SDK 版本的检查方法,适合本地或远程 MCP Server 的设计评审。
从一张工具清单开始
对每个 Server 列出实际暴露的能力:
| 字段 | 需要记录的内容 |
|---|---|
| 工具名 | 客户端可见的稳定标识 |
| 输入 | 字段、类型、长度和允许值 |
| 读取范围 | 文件、数据库、网络或应用对象 |
| 写入范围 | 会创建、修改或删除什么 |
| 运行身份 | 使用哪个系统账号或服务凭据 |
| 外部影响 | 是否发送消息、执行命令或改变远端状态 |
| 审批要求 | 哪些参数组合必须人工确认 |
| 审计字段 | 如何关联用户、任务和结果 |
只写“文件工具”或“Git 工具”不足以评估风险。读取仓库状态和推送远端分支属于完全不同的权限等级,应拆成不同工具。
文件工具必须先归一化路径
仅检查字符串是否以允许目录开头并不可靠。相对路径、.. 和符号链接都可能越过预期边界。实现层应先把用户输入解析为规范路径,再确认它位于明确的根目录内。
下面的 Python 示例只演示路径边界检查:
调用方仍需分别处理文件是否存在、符号链接策略、允许扩展名和单文件大小。写入工具还应默认拒绝覆盖,并使用独立输出目录。
读和写不要共用一个万能工具
把能力拆成最小动作:
拆分后,客户端可以只启用当前任务需要的集合。若一个 file_tool 同时支持读取任意文件、覆盖和删除,就无法针对高风险动作设置独立审批。
命令工具使用参数数组和白名单
不要提供接受任意 Shell 字符串的 run_command。优先为具体任务定义工具,例如运行目标测试或读取版本信息,并将可选参数限制为枚举或经过验证的路径。
实现命令调用时使用参数数组,不通过 Shell 解释用户输入:
超时时间、输出上限和子进程环境需要按实际服务设置。示例不能替代操作系统级隔离,也不表示所有仓库都应允许模型执行测试。
数据库工具优先只读和参数化查询
如果任务只需分析数据,使用只读账号连接只读副本,并将查询范围限制在允许的表或预定义操作。不要让模型提交任意 SQL 后再依靠提示词禁止写入。
确需修改数据时,把读取、生成变更计划和执行变更拆成不同阶段。执行前展示目标记录、预期差异和回滚标识,由业务权限系统批准。
远程 Server 需要独立身份
远程部署时,客户端身份、Server 进程身份和后端资源凭据应分开。一个请求至少需要能够关联:
日志不应默认记录完整文件内容、数据库结果、提示词和认证凭据。身份验证成功也不代表有权调用所有工具,授权应在 Server 侧按工具和资源继续判断。
防止错误重试扩大副作用
只读操作可以在有限条件下重试,写入操作则要有幂等标识和状态查询。若客户端超时,Server 应能判断同一操作是否已经完成,而不是再次执行。
对删除、推送、发送和审批等动作,默认要求人工确认。确认信息必须包含具体目标和参数摘要,不能只显示“允许工具调用”。
用负面测试验证边界
接入测试不能只验证正常请求。至少覆盖:
../和绝对路径能否越过工作区;- 不允许的扩展名、超大输入和空参数是否被拒绝;
- 命令参数中包含 Shell 控制字符时是否仍按普通参数处理;
- 只读身份能否执行写操作;
- 未审批的外部写入是否失败;
- 超时重试是否产生重复记录;
- 日志中是否出现凭据或敏感正文。
每个拒绝结果都应有稳定的错误类别,客户端才能停止或请求人工处理,而不是盲目换一种参数继续尝试。
结论与限制
MCP 工具的安全边界来自 Server 实现和运行环境,而不是工具描述或系统提示词。先建立工具清单,再落实规范路径、读写分离、参数白名单、最小身份和负面测试,可以减少模型调用越过任务范围的机会。
本文不覆盖某一 MCP SDK 的具体接口、传输认证或部署配置。协议与 SDK 会变化,实施时应依据所用版本的官方规范核对消息格式和能力;高风险 Server 仍需要代码审计、依赖检查与操作系统级隔离。




