本文内容为通用技术分享,不涉及任何公司、项目或保密信息。文中观点仅代表个人技术理解。
在完成 MCP(Model Context Protocol)Server的测试设计与测试执行之后,我回头整理整个过程时发现,这类应用的测试策略并不是传统 Web 或 API 项目的简单延伸,而是一个围绕 LLM(Large Language Model大语言模型)、工具调用与外部系统状态变化的协作型系统问题。
在这种架构中,系统行为不再是单一函数式输入输出,而是由 LLM 决策、工具编排以及外部执行结果共同构成的链路式过程,其正确性只能通过对整个执行链路的观测与拆解来验证。
这篇文章记录我在制定测试策略过程中遇到的关键问题、解决方法,以及最终形成的一套相对稳定的测试思路。
1. MCP 的基本理解(理解前提)
在开始做测试之前,我其实对 MCP(Model Context Protocol)并没有现成经验。
所以第一步不是写测试,而是先搞清楚一件事: MCP Server 到底是什么,它是怎么工作的?
MCP(Model Context Protocol)是一种让 AI 客户端(如 Claude Desktop、Cursor)能够访问外部系统的协议。它的作用是把模型连接到真实世界的数据和工具,比如文件系统、数据库、API 等。 从结果上看,它让 LLM (大语言模型) 不再只是“生成文本的模型”,而是可以通过标准化方式调用外部能力。 在本文中,我们只关注它作为“LLM + 工具系统”的执行基础,而不是协议本身的设计细节。
2. MCP Server 测试范围与分层设计
2.1 定义测试范围
在开始设计 MCP Server 的测试策略之前,我们首先需要明确一个前提:我们测试的对象并不是一个传统的单体系统,而是由 LLM、MCP 协议层以及多个外部工具共同组成的行为链路。
在这个链路中,用户输入首先由模型进行理解与决策,再通过 MCP 协议调用外部工具(如 API、数据库或文件系统),工具执行结果再返回给模型进行最终整合与输出。因此,整个系统的正确性不再取决于单一组件,而取决于多个环节之间的协同行为。
我们可以把这个“系统”拆成三个责任域
基于这一结构,我可以定义测试的范围,明确我们测什么、不测什么。
我们不关心 LLM 的内部推理机制,但我们关心它的 tool 调用行为是否正确;我们不仅验证 MCP 协议是否符合规范,同时也会对工具实现本身进行独立的正确性验证,并在真实执行环境中验证其稳定性与行为一致性。
在此基础上,测试被拆解为四个关注层面:MCP 协议层的契约正确性、工具执行层的可靠性、LLM 决策层的工具调用行为合理性,以及最终输出的语义正确性。
这种拆解方式并不是传统意义上的分层架构设计,而是基于系统失败模式的一种责任划分,用来帮助定位不同类型的问题来源。
通过这样的范围定义与分层方式,我的测试策略从“验证输出是否正确”,转向了“验证系统行为链路是否正确完成任务”。
2.2 测试分层
在 MCP Server 中,LLM 不作为“思考对象”,而作为“行为生成器”被测试。
(1)MCP 协议层(Protocol Layer)
这一层是整个流程最开始的地方,用户给LLM输入prompts(提示词)后,LLM需要将用户的输入进行“翻译”,变成MCPserver可以理解的“语言”。这个“翻译”就是需要依靠协议,协议层主要验证 LLM 与 MCP server 之间的结构化通信是否符合协议规范。
其核心目标是确保双方在“形式层面”能够正确理解彼此的请求与响应,而不涉及工具是否正确执行或模型是否做出合理决策。
| 测试维度 | 验证内容 | 测试工具/方法 | 触发时机 | 负责人 |
|---|---|---|---|---|
| Schema correctness | JSON schema validation / contract test | JSON Schema Validator / pytest / 自研 MCP test harness | 每次 MCP protocol 更新;tool schema 变更;CI pipeline (pre-merge) | Backend Dev |
| Tool call format correctness | request/response diff test | (同上) | (同上) | (同上) |
| Error format correctness | error injection + assertion | (同上) | (同上) | (同上) |
- Schema validation:
- request / response schema 是否符合 MCP 定义
- Call execution formatting
- MCP request → tool call 的格式转换是否正确
- 参数是否正确序列化 / 映射
- Error contract correctness
- error structure 是否标准化
- error code / message 是否符合协议
(2)工具调用行为层(Tool Invocation Layer)
上一层结束后,LLM可以和MCPserver互相理解,发送请求,那么下一步,就是根据请求,LLM来决定如何使用 MCP server的Tools(工具)。这一层验证的核心转向“LLM 有没有做对选择”。
这里仍然不关心 LLM 内部推理过程,只关心可观测行为(observable behavior)。
这一层的本质是:LLM 在给定输入下,是否生成了正确的 tool 使用行为序列
Trace 在 MCP 系统中应被视为基础观测能力而非测试附属品,通过分级存储与按需采样,可以在控制成本的同时支撑测试与生产可观测性。
| 测试维度 | 验证内容 | 测试工具/方法 | 触发时机 | 负责人 |
|---|---|---|---|---|
| tool selection correctness | trace comparison/validation | Trace system | prompt 变化时;model upgrade 时;tool schema 变化时;agent logic 变化时;CI regression test | Backend Dev / QA |
| tool call sequence correctness | (同上) | (同上) | (同上) | (同上) |
| missing tool calls | (同上) | (同上) | (同上) | (同上) |
| extra tool calls | (同上) | (同上) | (同上) | (同上) |
| parameter reasoning correctness | (同上) | (同上) | (同上) | (同上) |
(3)工具执行层(Tool Execution Layer)
这一层开始,测试的重心发生一个非常重要的变化:从“LLM和MCPserver通信是否正确” → 转向“MCPserver内部的Tool(工具)本身是否做对了事”。
这一层关注的是:tool 作为一个可执行单元,它的输入→输出→副作用是否符合预期。
| 测试维度 | 验证内容 | 测试工具/方法 | 触发时机 | 负责人 |
|---|---|---|---|---|
| tool input/output contract correctness | unit test + schema validation | pytest / jest / junit; mock server(WireMock / MSW); MCPjam | Tools变化驱动 | Backend Dev / QA |
| business logic correctness | Integration Tests | (同上) | (同上) | (同上) |
| side effect correctness | state diff test (before/after) | (同上) | (同上) | (同上) |
| failure handling | chaos/failure simulation | (同上) | (同上) | (同上) |
- Tool contract correctness:
- tool input/output,参数类型,必填字段, output schema
- Tool business logic correctness
- tool 是否实现了它声明的功能
- 逻辑计算是否正确
- DB query 是否正确
- Side effect correctness
- 是否正确写入 DB
- 是否正确修改文件
- 是否产生预期外副作用
- Failure handling correctness
- API fail 是否处理
- timeout 是否处理
- retry / fallback 是否正确
(4)语义输出层(Semantic Layer)
在LLM 和MCP server进行正常通信然后正确的调用了Tool(s),并且MCP server的工具正确的执行也得到了正确的执行结果。
此时LLM得到了工具执行结果之后,需要给出用户Final Response(最终输出)。 从LLM拿到工具执行结果,到输出final response,这部分就是语义输出层。
这一层也是最容易“看起来简单、但其实最难定义清楚”的一层,因为它已经脱离了 MCP / tool / trace,进入:
“这个系统到底有没有帮用户解决问题”
所以这一层关注的是:最终输出是否满足用户真实意图(user intent)。 语义层不是对输出质量的语言分解,而是基于故障模式对系统输出是否有用、正确和自洽的评估。
| 测试维度 | 验证内容 | 测试工具/方法 | 触发时机 | 负责人 |
|---|---|---|---|---|
| Intent Fulfillment(意图完成度) | rubric scoring | evaluation framework | prompt 变化时;model upgrade 时 | Backend Dev / QA |
| Factual Correctness(事实正确性) | DB / API verification + judge | backend system + verifier | (同上) | (同上) |
| Consistency(系统一致性) | rule engine / graph validation | rule engine / graph tools | (同上) | (同上) |
3. 总结与局限
上面提出的四层测试模型,从协议层、工具执行层、LLM 调用行为层到语义输出层,提供了一种从系统失败模式出发的结构化拆解方式。它的核心价值在于:将 MCP Server 的整体正确性分解为不同层级的可观测问题,从而避免将所有错误归因于最终输出。
从方法论角度来看,这种分层模型能够较清晰地覆盖 MCP 系统中的主要风险点,包括协议错误、工具实现缺陷、调用路径错误以及语义层面的意图偏差。
然而,在实际工程落地过程中,这套分层测试体系会逐渐暴露出一个关键问题:
并不是所有层都具备同等的测试成本效益。
我们可以发现:
-
底层具备较强的确定性与可测试性
协议层与工具执行层在工程上是相对确定性的:
- schema 可以验证
- tool logic 可以单测
- side effects 可以通过 integration test 覆盖
这些层的测试虽然工程成本存在,但具有较高的确定性和可自动化程度。
-
上层测试逐渐转向高成本与高不确定性问题
从 LLM tool invocation layer 开始,问题开始变化:
- tool selection 依赖模型行为
- call sequence 具有不确定性
- trace 依赖运行时观测
-
而在 semantic layer,这种不确定性进一步放大:
- “是否真正解决问题”难以严格定义
- evaluation 变成近似判断而非确定性断言
- 自动化测试成本显著上升
因此,一个现实问题是:
虽然四层模型在理论上完整覆盖 MCP 系统,但在当前工程阶段,完整实现这一套测试体系的成本并不一定具有良好的 ROI。
尤其是 semantic layer 的测试,如果完全依赖结构化 evaluation 或 trace-based validation,会显著增加系统复杂度,而收益在早期阶段可能有限。
基于这一点,在实际工程中我们更倾向于采用一种折中策略:
- lower layers(protocol + tool execution)保持严格测试
- upper layers(invocation + semantic)采用轻量化 E2E 方式覆盖
也就是说,从“全分层验证”转向“关键路径 E2E 验证 + 选择性深入分析”
4. 本系列的下一部分
因此,这篇文章更偏向于一个方法论层面的建模尝试,而不是完整的工程落地方案。
在下一篇文章中,我会尝试讨论一个更实际的问题:如何在降低 MCP 测试成本的同时,仍然保持对系统行为的信心。
我们也会进一步探索是否存在一种更轻量的测试方式,例如基于 E2E 的 MCP testing,用于替代部分复杂的分层测试体系。
本文首发于作者个人公众号「小圆在路上Go」,原文链接:[mp.weixin.qq.com/s/0gqRWBF21…]