site logo

Marico's space

用免费的 MCP server 为你的 AI coding agent 配备 API 测试工具箱

AI技术与应用 2026-11-05 20:55:50 3

最近折腾 AI(人工智能)编程代理的 API(应用程序接口)测试,踩了几个坑:这活儿明明有确定答案,模型却总想自己“理解”一遍。于是把规则校验、响应比对、类型生成这些任务全塞给模型,结果它说得头头是道,真正出错时连个报错都指不出来。

后来我换了个思路:有些 API 工作根本不需要判断,只需要严格执行规则。比如按照 JSON(JavaScript 对象表示法)Schema 校验响应、比较两份 JSON、执行 JSONPath 查询,或者检查 OpenAPI 文件里的 operationId 是否重复,这些任务都只有一个正确答案,交给代码执行比让模型猜靠谱得多。

MCP(模型上下文协议)的价值就在这里:让编程代理直接调用确定的工具,而不是凭空判断。下面用一个免费、公开的 MCP Server(模型上下文协议服务)连接编辑器,并实际跑一遍它提供的 API 测试工具。文中所有输出都来自对线上服务的真实调用。

这个 Server 能做什么

服务地址是 https://testingapis.com/mcp。它无状态、只读,不需要注册,也不需要 API Key(密钥),通信协议为 Streamable HTTP(可流式传输的 HTTP)。它与 testingapis.com 上对应的浏览器工具运行同一套代码,每个结果末尾还会附上对应页面链接,方便你手工复核。

工具 用途
validate_openapi 找出会导致 OpenAPI 3.0/3.1 和 Swagger 2.0 工具链出错的问题
validate_json_schema 按照 Schema 校验 JSON,支持 draft 4 到 2020-12,并给出每个错误的 JSONPath
generate_json_schema 根据示例响应生成 draft 2020-12 Schema
json_to_types 把 JSON 转换为 TypeScript、Go、Python(Pydantic)、Java、C#、Kotlin 或 Rust 类型
compare_json 按数据比较两份 JSON,忽略对象键顺序,并可选是否忽略数组顺序
query_jsonpath 执行符合 RFC(请求评论)9535 的 JSONPath 查询,支持过滤器
curl_to_code 把 cURL 命令转换为 Python、fetch、axios、Go、Java、C# 或 PHP 代码
convert_api_spec 在 OpenAPI、Swagger、Postman、cURL、HAR(HTTP 归档)和 .http 之间转换
explain_cron 用自然语言解释 cron 表达式,并列出接下来几次运行时间
get_test_data 从公开 REST(表述性状态转移)API 获取商品、文章或待办事项等真实感测试数据

除 get_test_data 外,所有工具都只处理传入的文本。get_test_data 会向公开 API 发起一次 GET(读取)请求。

接入编辑器

Claude Code

claude mcp add --transport http testingapis https://testingapis.com/mcp
Enter fullscreen mode Exit fullscreen mode

Cursor:将下面配置加入 ~/.cursor/mcp.json,也可以放在项目的 .cursor/mcp.json 中。

{ "mcpServers": { "testingapis": { "url": "https://testingapis.com/mcp" } }
}
Enter fullscreen mode Exit fullscreen mode

VS Code 的 Copilot Agent Mode:将下面配置加入 .vscode/mcp.json,也可以在命令面板中运行 MCP: Add Server。

{ "servers": { "testingapis": { "type": "http", "url": "https://testingapis.com/mcp" } }
}
Enter fullscreen mode Exit fullscreen mode

Claude Desktop 和 claude.ai:进入 Settings → Connectors → Add custom connector,然后粘贴服务地址。

ChatGPT、Windsurf 以及其他支持远程 MCP Server 的客户端,填入同一个地址即可,不需要身份认证。

工具会返回什么

这些工具不需要你手动调用。直接用自然语言提问,比如“检查这个响应是否符合 Schema”“为什么我的接口规范无法导入”,代理会自行选择工具。真正值得关注的是返回结果,所以下面直接展示每次调用的实际输出。

测试数据与类型定义

要求获取一条示例商品数据,会调用 get_test_data:

GET https://api.testingapis.com/api/products/5 { "id": 5, "title": "7-Port USB-C Hub", "price": 34.99, "description": "Aluminium USB-C hub with HDMI, SD card reader, and 100W passthrough.", "category": "electronics", "image": "https://picsum.photos/seed/prod5/400/400", "rating": { "rate": 4.2, "count": 141 }, "inStock": true, "createdAt": "2025-06-20T16:20:00Z"
}
Enter fullscreen mode Exit fullscreen mode

接着调用 json_to_types,传入 language: "typescript" 和 rootName: "Product":

export interface Product { id: number; title: string; price: number; description: string; category: string; image: string; rating: Rating; inStock: boolean; createdAt: string;
} export interface Rating { rate: number; count: number;
}
Enter fullscreen mode Exit fullscreen mode

嵌套对象会获得独立命名的类型。如果传入数组,工具会读取每一项,因此某些元素中缺失的字段会被标记为可选,而不是必填。

发现响应结构漂移

这正是工具比模型靠谱的地方。先用 generate_json_schema 并设置 strict: true,把上面的商品响应转换成同时拒绝未知属性的 Schema。然后我故意把数据改坏三处:价格从数字变成字符串、删除 category,再增加一个 discount 字段,最后交给 validate_json_schema:

3 errors:
- $ (required): Missing the required property "category".
- $.price (type): Expected number, got string.
- $.discount (additionalProperties): "discount" isn't one of the allowed properties.
Enter fullscreen mode Exit fullscreen mode

"34.99" 代替 34.99,正是模型阅读两份 JSON 时容易含糊带过的地方,校验器不会。

检查 OpenAPI 文件

下面是一份小型接口规范,包含两个在复制、粘贴接口操作时很容易犯的错误:

openapi: 3.0.3
info: title: Orders version: "1.0"
paths: /orders/{orderId}: get: operationId: getOrder responses: "200": description: The order /orders: get: operationId: getOrder responses: "200": description: All orders
Enter fullscreen mode Exit fullscreen mode

validate_openapi 返回:

Orders (OpenAPI 3.0.3, 2 operations): 2 errors, 1 warnings.
- ERROR at /paths/~1orders~1{orderId}/get: The path has {orderId}, but GET doesn't declare a path parameter "orderId".
- ERROR at /paths/~1orders/get/operationId: operationId "getOrder" is also used by GET /orders/{orderId}; it must be unique.
- WARNING at /: No servers: tools will send requests to the host the spec was loaded from.
Enter fullscreen mode Exit fullscreen mode

每个问题都带有 JSON Pointer,代理可以直接定位到对应位置进行修改。OpenAPI 规范要求 operationId 唯一,而那些按照 operationId 命名的代码生成器,也无法生成两个都叫 getOrder 的方法。

比较两份响应

使用 compare_json 并设置 ignoreArrayOrder: true,比较订单更新前后的数据。两份数据的键顺序不同,标签数组也反了:

{ "id": 42, "status": "paid", "tags": ["gift", "express"], "total": 59.99 }
Enter fullscreen mode Exit fullscreen mode
{ "total": 59.99, "status": "shipped", "tags": ["express", "gift"], "id": 42, "trackingNo": "1Z999" }
Enter fullscreen mode Exit fullscreen mode
2 differences:
- changed $.status: "paid" → "shipped"
- added $.trackingNo: "1Z999"
Enter fullscreen mode Exit fullscreen mode