MCP 面试高频考点:如何为 stdio 传输的 MCP 服务编写可重复执行的 Prompts 集成测试

MCP 面试高频考点:如何为 stdio 传输的 MCP 服务编写可重复执行的 Prompts 集成测试

面试场景

面试官:我们正在做一个本地运行的 MCP 服务,通过 stdio 与 Host 通信,对外暴露 Tools、Resources 和 Prompts。现在团队发现单测只能覆盖函数逻辑,一旦接入真实 MCP 会话,经常出现协议格式、提示模板拼接、传输边界等问题。如果让你设计一套可重复执行的集成测试,你会怎么入手?

候选人:我的结论是,集成测试不要走真实 AI Host,也不要依赖远程网络,而是直接在测试进程里启动 MCP Server 子进程,使用 stdio 作为唯一传输通道,再用一个轻量 MCP Client 按 JSON-RPC 协议发请求、收响应;Prompts 相关能力要单独覆盖"模板是否能被列出、参数是否按 schema 校验、返回消息结构是否符合预期、与 Tool/Resource 的协作链路是否稳定"。这样做的核心原因是,stdio 传输适合 Host 在本机启动子进程的场景,Server 从 stdin 读 JSON-RPC,往 stdout 写协议消息,stderr 用于日志,测试可以完全复刻这个真实边界,同时避免网络波动和外部依赖,保证测试可重复执行资料1资料2。

基础问题:为什么先选 stdio 做集成测试

面试官:为什么你优先选 stdio,而不是在测试里直接调用 Python 或 Java 对象方法?

候选人:直接调用内部方法只能验证业务函数,覆盖不到 MCP 的协议层和传输层。MCP 是客户端---服务端架构,通信消息使用 JSON-RPC;在 stdio 模式下,每条消息是一个 JSON-RPC request、notification 或 response,按换行分隔,且不能包含嵌入换行,stdout 只能输出合法 MCP 消息,调试日志必须写到 stderr资料1资料2。如果我们只测函数,很容易漏掉这些问题:比如开发人员不小心把 print 日志打到 stdout,真实 Host 就会解析失败;或者消息里混入换行,导致客户端按行切包时报错。

所以集成测试至少要覆盖三层:第一,子进程启动与生命周期管理;第二,stdio 上的 JSON-RPC 消息收发;第三,Prompts、Tools、Resources 的能力暴露与协作。Prompts 是用户可显式选择的模板化消息或工作流,适合验证"用户选择一个提示模板后,服务是否返回正确的消息片段、是否引导后续 Tool 调用或 Resource 读取",不应把只读提示逻辑硬塞进有副作用的 Tool资料1。

追问一:Prompts 在测试里怎么测,才不是"拼字符串单测"

面试官:你提到 Prompts,具体怎么设计测试用例?如果只是断言返回字符串包含某个词,价值似乎不大。

候选人:Prompts 测试要围绕 MCP 语义,而不是只测字符串包含关系。我的测试分四类。

第一类是能力发现:客户端启动会话后,先请求列出 Prompts,断言服务暴露的提示名称、描述、参数 schema 符合预期。这能防止有人误改 prompt 名称或删除必填参数,导致 Host UI 中找不到入口。

第二类是参数校验:Prompts 通常有输入参数,测试要分别覆盖合法参数、缺失参数、类型错误、超长或非法字符输入。这里要注意,schema 只是结构约束,不能代替服务端校验;服务端必须把模型或客户端传入的文本视为不可信输入资料1。例如一个"总结日志文件"的 Prompt,参数里如果带路径,测试就要验证服务是否拒绝越权路径,而不是只看 JSON 格式是否合法。

第三类是返回结构:Prompt 返回的不是普通字符串,而是模板化消息或工作流。测试要断言消息角色、内容、附带的资源引用、建议调用的 Tool 名称是否正确。比如一个"排查订单问题"的 Prompt,返回内容可以包含让模型先读取某个 Resource,再调用某个查询 Tool;测试要验证这些协作关系没有被破坏。

第四类是幂等与可重复性:Prompts 一般不应产生副作用,所以同一个 Prompt 用同一组固定参数多次调用,返回结构应稳定。测试可以连续调用多次,并固定随机种子、时间注入和样本数据,避免因为时间、随机排序或外部状态导致结果漂移。

下面给一个版本无关的测试伪代码,用来表达结构,不绑定具体 SDK 内部 API:

text 复制代码
testcase: 列出并调用 order_diagnosis Prompt
1. 以 stdio 方式启动 server 子进程
2. 建立 MCP Client,连接子进程 stdin/stdout
3. 发送 prompts/list 请求
4. 断言返回中包含 order_diagnosis,且参数 schema 要求 order_id
5. 发送 prompts/get,参数为 {order_id: "ORD-1001"}
6. 断言响应消息包含:
   - 引导用户提供订单背景的说明
   - 建议读取 resource://orders/ORD-1001
   - 建议调用 query_shipping_status Tool
7. 重复执行步骤 5-6,断言结果结构一致
8. 关闭子进程,断言 stderr 中无协议级错误

面试官点评:这一部分的考察点是候选人是否理解 Prompts 与普通函数返回值的区别。合格回答应覆盖发现、参数校验、消息结构和幂等性;加分项是能明确提出 Prompts 不应承担副作用,并把参数不可信作为测试输入设计依据。

追问二:stdio 传输有哪些容易踩坑的异常场景

面试官:如果测试中出现子进程挂起、stderr 输出、消息截断或偶发失败,你会怎么处理?

候选人:这正是 stdio 集成测试最容易踩坑的地方,我会按传输协议边界分别处理。

第一,stdout/stderr 分流必须严格。Server 绝不能把非 MCP 消息写到 stdout,日志必须走 stderr;客户端可以捕获 stderr,但不能把 stderr 当成错误信号,因为规范允许服务端往 stderr 输出 info、debug、error 等日志资料2。测试里要单独收集 stderr,用它诊断问题,但断言协议响应时只读 stdout。很多本地调试失败,就是因为代码里把"启动成功"之类的 print 打到 stdout,破坏了 JSON-RPC 流。

第二,消息边界要按换行处理。stdio 消息以换行分隔,且不能包含嵌入换行资料2。测试客户端不能按固定长度读,也不能假设一次 read 就能拿到完整消息;应持续读取 stdout 流,按换行切分,再对每一段做 JSON-RPC 解析。如果服务端返回的 JSON 被美化输出或包含换行,测试会立刻暴露问题。

第三,子进程生命周期要可控。可重复测试不能依赖已经运行的本地服务,也不能留下僵尸进程。每个测试用例或测试类启动独立子进程,设置超时,测试结束后关闭 stdin、等待进程退出、校验退出码。若进程异常退出,测试应把最后一段 stdout 和 stderr 一起输出,否则排查成本很高。

第四,异常路径要显式构造。比如发送非法 JSON、发送不存在的 Prompt 名称、发送缺少参数的 prompts/get、在子进程退出后继续写 stdin。这些测试不是为了"攻击服务",而是确认服务在异常输入下不会把错误日志泄漏到 stdout,不会卡死,也不会返回格式错乱的消息。

第五,避免外部依赖导致不稳定。即使 Prompts 本身无副作用,若模板里拼接了当前时间、主机名、临时路径或调用了外部网络,测试就会变脆。集成测试应使用固定测试数据和可控的资源内容;如果 Prompt 推荐 Tool 调用,测试只验证推荐关系,不真实调用外部系统,除非进入更高等级的端到端测试。

面试官:那你会把 Tool 真实调用也放进这套集成测试吗?

候选人:要分层。基础集成测试只验证 MCP 会话、Prompt 返回结构和能力发现,不触发真实外部副作用,执行快、稳定性高;少量高价值链路可以做"近真实"测试,例如用内存数据库或本地临时文件模拟 Resource 和 Tool,但必须在测试结束后清理。这里的关键取舍是:覆盖越真实,测试越慢、越脆弱;覆盖越浅,越容易漏掉协议和接线错误。我的建议是,stdio 集成测试套件以毫秒级到秒级用例为主,专门守协议、传输和 Prompt/Tool/Resource 协作边界;涉及网络、数据库、第三方 API 的用例放到更少量的契约或环境测试中。

面试官点评:考察点是候选人是否真正理解 stdio 传输的边界条件,而不是只会写"happy path"。合格回答应提到 stdout/stderr 分离、换行分隔、子进程管理和异常输入;加分项是能说明测试分层与稳定性取舍。

方案设计:一套可落地的集成测试结构

面试官:如果让你把方案落成工程目录和执行流程,你会怎么组织?

候选人:我会把测试拆成四个模块。

一是测试夹具:负责以 stdio 启动 MCP Server 子进程,连接 stdin/stdout,收集 stderr,并在测试结束后销毁进程。这里不要硬编码 SDK 私有方法,应使用官方 SDK 提供的客户端与 stdio 传输能力。Python SDK 支持 stdio 等标准传输,安装 mcp[cli] 后可通过命令行运行服务;Java SDK 也提供 mcp-test 用于集成测试资料3资料4。具体 API 以所选 SDK 版本为准,测试代码只封装稳定的"启动、发送请求、接收响应、关闭"能力。

二是协议断言:封装通用断言,例如"返回是合法 JSON-RPC 响应""响应 ID 与请求匹配""错误对象包含 code/message""Prompts 列表包含指定名称和 schema"。这样每个业务用例只关心业务语义。

三是 Prompt 用例集:按 Prompt 名称组织,覆盖合法调用、参数缺失、参数非法、重复调用稳定性、与 Tool/Resource 的引用关系。对于涉及不可信输入的 Prompt,必须加入路径穿越、URL 注入、特殊字符等反例,因为服务端不能因为请求来自 MCP 客户端就默认可信资料1。

四是可观测与排错:测试失败时输出请求消息、最后一段 stdout、stderr 和进程退出状态;但不能把密钥、令牌、用户隐私打印到日志中。凭据不应出现在日志、Tool 返回值或模型上下文中,审计相关字段需要脱敏资料1。

适用边界上,这套方案最适合本地子进程模式的 MCP Server,尤其是开发阶段快速回归;如果是 Streamable HTTP 远程服务,还需要额外考虑认证、授权、会话管理、限流和超时,不能直接照搬 stdio 测试方式资料1。一个很容易踩坑的细节是:很多人在本地调试时往 stdout 打印"connected""listening"之类日志,单测不会发现,但一接到 MCP Inspector 或真实 Host 就解析失败;stdio 集成测试必须把这类问题挡在提交前。

总结

面试官:最后用一句话总结你的核心思路。

候选人:用测试进程直接启动 stdio 子进程,把 MCP Client 当成真实 Host 的最小替身,重点测协议、传输、Prompts 模板及其与 Tools/Resources 的协作边界,同时通过严格的流处理、异常输入和生命周期管理保证测试可重复执行。

面试官点评:整体回答从业务问题出发,先给方案结论,再解释 MCP 客户端---服务端、JSON-RPC、stdio 流边界和 Prompts 语义,能够覆盖基础原理、异常处理、工程分层和安全边界。合格的工程实现不应只停留在"能跑通一次",而要让测试在本地、CI、不同机器上稳定重复;进一步的加分方向是把 Prompt 变更纳入契约测试,防止提示模板、参数 schema 和 Tool/Resource 引用在迭代中被无意破坏。

参考资料

相关推荐
jyOverQ10 小时前
JSON-RPC 2.0、Codex、MCP Client 和 MCP Server 是什么关系?以调用 GitHub 工具为例
ai·mcp
xrlfreedom13 小时前
MCP 面试高频考点:用 Streamable HTTP 控制返回大小并降低上下文消耗
mcp·mcp client·typescript mcp sdk·streamable http 传输
EatFan13 小时前
2026 后端 AI 工程化:Spring AI 2.0、MCP 协议与 Agent 内嵌如何收进 Java 生产系统
java·人工智能·spring·agent·spring ai·spring boot 3·mcp
Sayai14 小时前
MCP Server 开发实战:Spring AI 把后端日志查询暴露给 AI Agent(7 条设计原则与踩坑实录)
java·人工智能·ai agent·spring ai·mcp
一缕82年的清风21 小时前
别把MCP只当插件!当所有AI都在接入USB-C接口,架构师该如何设计智能体底座?
mcp
EatFan21 小时前
AI Agent Harness 元年:从框架混战到运行时收敛,2026 年 Agent 基础设施分层重构解读
大数据·人工智能·重构·ai agent·mcp·a2a·agent harness
sarasuki1 天前
MCP 为什么是一个协议而不是一个框架呢?
设计模式·agent·mcp
全栈Agent 小李1 天前
【无标题】
前端·后端·agent·ai编程·全栈·cursor·mcp
xrlfreedom1 天前
MCP 面试高频考点:把知识库 RAG 封装成 Tool,JSON Schema 与 Prompts 怎么分工才不踩坑
json schema·prompts·mcp