工具调用与 MCP:协议之外,还要有执行边界
定义参数、结果、错误和权限契约,用离线工具分发器示例防止模型输出直接执行。
设计一个可以校验、授权和观察的工具接口,理解 MCP 的职责范围。
工具调用的可靠性来自契约,而不来自模型输出看起来有多合理。模型提出工具名称与参数,应用决定这些请求能否执行。
MCP 提供工具发现与调用等标准化接口,但协议互通不会自动赋予访问权限,也不会替你处理业务幂等。
定义四类契约
参数契约说明字段、类型、范围与必填项。结果契约说明成功时返回什么。错误契约区分参数错误、无权限、暂时失败和结果不确定。权限契约则限制谁能操作哪些资源。
名称也应表达清楚的行为。把“查询状态”和“修改状态”合在一个含糊工具里,会增加模型误选和权限判断的难度。
对于有副作用的工具,还要定义操作 ID、重试条件与必要确认。一个正确的 JSON schema,只解决参数结构问题。
执行器必须拥有最终决定权
模型输出只能进入已注册的分发器。禁止把工具名称直接当函数名执行,也不要把输出交给 eval 或通用 shell。
服务端重新验证参数和权限,不能只相信客户端或模型已经检查。返回的信息也应限制大小、结构与敏感字段。
离线练习工具分发
这个 Python 示例只实现一个本地单词计数工具。它不是 MCP 服务端,重点是允许清单与参数校验。授权工具集合由应用提供。
def dispatch(call, allowed_tools):
if not isinstance(call, dict) or set(call) != {"name", "arguments"}:
raise ValueError("invalid call")
name = call["name"]
if name != "word_count" or name not in allowed_tools:
raise PermissionError("tool not allowed")
args = call["arguments"]
if not isinstance(args, dict) or set(args) != {"text"}:
raise ValueError("invalid arguments")
text = args["text"]
if not isinstance(text, str) or len(text) > 2000:
raise ValueError("invalid text")
return {"count": len(text.split())}
result = dispatch({
"name": "word_count", "arguments": {"text": "small useful tools"}
}, {"word_count"})
assert result == {"count": 3}
try:
dispatch({"name": "run_shell", "arguments": {}}, {"word_count"})
except PermissionError:
print("Unregistered tool rejected")
else:
raise AssertionError("unsafe tool accepted")
真实的计数规则需要定义语言与分词方法。这里的空格分词仅适合示例,不能据此宣称支持所有语言的准确统计。
MCP 解决互通,应用解决授权
接入时核对当前协议版本、能力声明、工具列表和输入输出 schema。不要把教程里的片段当成完整协议消息;真实请求还需要满足该版本的消息元数据要求。
工具返回的文本也可能携带不可信指令。它是外部结果,不应直接升级为系统规则。标准化连接越容易,资源边界就越需要清楚。
下一步
从一个只读工具开始,写出参数、结果与错误类型。增加未知字段、超长输入和未授权工具三个测试用例。
之后再加入有副作用的操作,为它设计操作 ID、确认与恢复流程。下一篇讨论进程中断后,任务如何继续而不重复执行。
资料来源与延伸阅读
资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。