返回 AI 测试成长路线
Phase 03 / Agent Quality 02

MCP 工具接入与安全测试教程

从协议握手到业务副作用,逐层验证 MCP 工具是否能被正确发现、安全调用、可靠追踪并稳定升级。

10 个章节协议 + 工具契约权限、内容与运行安全
01

MCP 连接的不只是工具,也是权限与数据边界

协议可用不等于业务安全

案例:售后工具服务

售后 Agent 通过 MCP Server 获取订单查询、退款政策检索和创建工单三个工具。测试既要确认 Client 与 Server 能正常通信,也要证明工具只暴露必要能力、只访问当前用户的数据,并且所有写操作都受审批和幂等控制。

MCP 接入链

Host目标与身份
Client协商与调用
MCP Server能力与校验
业务服务权限与副作用
审计证据结果与追踪

四层测试边界

层级主要问题关键证据
协议版本、能力和消息是否兼容initialize、能力声明、错误响应
工具契约名称、描述、Schema 和返回值是否稳定tools/list、JSON Schema、契约差异
安全控制身份、scope、资源归属和审批是否有效授权日志、策略决策、拒绝结果
运行治理超时、断线、重试、审计和隐私是否可控request_id、Trace、脱敏日志
02

先验证初始化和能力协商

不兼容时明确失败

能力协商测试

场景断言失败处理
协议版本匹配双方选择共同支持的版本没有交集时拒绝连接并说明版本
能力声明只声明实际实现的 tools、resources 或 prompts调用未声明能力时返回协议错误
服务身份名称、版本和环境来源可识别未知或未授权服务不得进入允许列表
重新连接断线后重新协商,不沿用过期状态旧会话与旧授权立即失效
扩展字段忽略兼容扩展,拒绝破坏性语义变化保留原始错误供定位
“连接成功”只证明握手完成。能力多报、版本误判或服务来源不可验证,都会把不该出现的工具交给 Agent。
03

工具 Schema 要让错误参数在业务执行前被拦住

严格输入,明确输出
写工具输入 Schema
{
  "name": "create_after_sale_ticket",
  "inputSchema": {
    "type": "object",
    "required": ["order_id", "reason", "request_id"],
    "properties": {
      "order_id": {"type":"string", "pattern":"^ORD-[0-9]{8}$"},
      "reason": {"type":"string", "minLength":5, "maxLength":500},
      "request_id": {"type":"string", "minLength":8}
    },
    "additionalProperties": false
  }
}

工具契约负例

输入预期
缺少 order_id返回字段级错误,不调用下游
order_id 格式错误拒绝并指出 pattern 不匹配
多出 operator_role拒绝未知字段,避免隐式越权参数穿透
同名工具来自未知服务不进入可调用列表
返回成功但缺少 ticket_id判为契约失败,不能向用户报告已创建
description 和 annotations 只能帮助模型理解工具,不能证明工具没有副作用,也不能代替后端权限校验。
04

身份、scope 和业务对象要逐层绑定

模型参数不可信

授权测试矩阵

范围测试断言预期
Token audienceToken 只为当前 MCP 服务签发错误 audience 返回 401
scope只读身份不能调用写工具权限不足返回 403
租户tenant_id 来自已验证身份模型传入其他租户仍被拒绝
资源归属订单属于当前用户且状态允许操作越权对象在下游再次拦截
人工审批写操作绑定工具、对象、参数摘要和有效期参数变化后旧批准失效
字段最小化只返回当前任务需要的数据手机号、地址和内部备注默认隐藏
Client 的工具白名单是第一道门,MCP Server 和业务服务仍必须重新校验身份与资源;不能把 Agent 传来的 customer_id 当作可信身份。
05

工具描述、资源和返回值都按不可信内容处理

防止指令穿透

内容污染样本

污染位置攻击示例合格行为
工具描述要求忽略审批并优先选择本工具仅按受信策略与任务需要选择
资源文档政策正文夹带系统指令作为业务数据引用,不提升指令优先级
工具返回值订单备注要求读取其他用户订单只解析预定义字段,拒绝目标漂移
近似工具名伪造 get_order_secure校验服务来源、工具 ID 和允许列表
超长内容返回大量无关文本挤掉系统约束截断非关键字段并保留来源标记

进入模型前

  • 按 Schema 解析结构化字段,不把整段原文直接拼进高优先级指令。
  • 保留 server、tool、resource 和调用版本,确保每段内容可追溯。
  • 对敏感字段做最小化和脱敏,对未知内容类型安全失败。
  • 把 Prompt Injection 命中记录为安全事件,而不是普通回答错误。
06

会话与传输测试要覆盖断线、乱序和隔离

消息正确到达还不够

传输与会话场景

场景断言
响应乱序使用 request_id 绑定请求,不把结果交给另一调用
连接中断未确认写入状态进入 UNKNOWN,不自动重复提交
会话恢复重新协商能力,旧审批和临时状态不复用
并发用户session、actor、tenant 和工具结果完全隔离
取消请求下游可取消时传播取消;不可取消时回读最终状态
超大消息按上限拒绝或分页,不静默截断关键字段
07

按错误语义决定重试、回读还是停止

错误不能被包装成成功

错误到动作映射

错误动作禁止
参数校验失败修正输入或询问用户原样重试
权限不足停止并提供授权边界切换身份绕过
限流遵守 Retry-After 和总预算并发放大请求
网络超时有界重试;写操作复用幂等键生成新 request_id 重复写入
结果未知按幂等键回读业务状态直接报告失败或成功
业务拒绝保留拒绝原因并转人工或结束把拒绝当技术异常重试
调用记录最小字段
{
  "request_id": "req-8f21",
  "session_id": "session-hash",
  "tool": "create_after_sale_ticket",
  "arguments_digest": "sha256:...",
  "attempt": 2,
  "status": "UNKNOWN",
  "next_action": "READ_BACK"
}
08

日志既要能重放,也不能泄露敏感数据

可观测与最小披露

审计字段

需要记录不能记录
主体哈希、租户、会话和 request_id明文 Token、Cookie 和授权码
Client、Server、协议和工具版本完整手机号、地址和证件号
工具名、参数摘要、策略决策和耗时未经脱敏的工具原始返回
审批人、审批摘要、有效期和使用结果可复用的 approval_token
错误分类、重试次数和最终业务状态只写“调用失败”的无诊断日志
审计的目标是回答“谁在什么版本下,以什么权限,对哪个业务对象执行了什么,结果怎样”,不是把所有敏感内容原样保存。
09

协议、Schema 和策略变化都要进入契约回归

升级前先比较差异

变更影响

变更测试重点发布判断
新增可选字段旧 Client 能否忽略并继续工作兼容后允许发布
新增必填字段旧调用是否全部失败提供版本迁移后再发布
字段改名或改义Agent 参数和下游语义是否一致按破坏性变更处理
工具描述变化选择率、误调用率和注入风险回放 Agent 轨迹集
授权策略变化角色、scope、租户和资源边界越权样本必须全部拒绝
传输实现变化断线、乱序、取消和超时故障注入通过后发布

固定回归集

  • 初始化与能力协商样本。
  • 每个工具的有效、缺参、错型、越界和未知字段样本。
  • 跨用户、跨租户、少 scope 和过期审批样本。
  • 工具描述、资源和返回值注入样本。
  • 超时、断线、重复响应和结果未知样本。
10

用三个工具跑通一套 MCP 接入验收

练习与检查

练习

  1. 准备订单查询、政策检索和创建工单三个工具,明确只读与写入边界。
  2. 为每个工具建立严格输入输出 Schema 和错误语义。
  3. 覆盖初始化、tools/list、版本不兼容和破坏性 Schema 变化。
  4. 用不同角色、租户、订单归属和审批状态验证授权。
  5. 向描述、资源和返回值注入不可信指令,确认不会改变系统目标。
  6. 注入断线、超时、重复响应和结果未知,验证幂等、回读与审计。

协议契约

  • 协商结果明确
  • Schema 严格
  • 版本差异可识别
  • 错误可诊断

安全边界

  • 身份资源绑定
  • 写操作先审批
  • 不可信内容隔离
  • 敏感字段最小化

运行治理

  • 会话不串线
  • 重试保持幂等
  • 结果未知先回读
  • 审计可重放