Ocarina 完全指南:用 Rondo 为 MCP 服务器编写确定性自动化测试
MCP(Model Context Protocol)生态正在快速膨胀——如今已有数千个 MCP 服务器,涵盖数据库、GitHub、文件系统、浏览器、3D 渲染等各类场景。每个服务器都暴露一组工具(get_issues、query_database、create_file),供 AI 编码 Agent 调用。
但这里有一个核心痛点:你如何知道 MCP 服务器正常工作? 当你在 CI 流水线中依赖某个 MCP 服务器时,怎么确保它下次返回的结果依然正确?
现有方案要么引入另一个 LLM 来做”智能验证”(成本高、结果不稳定),要么根本不验证。Ocarina 选择了第三条路——用 YAML 编写确定性测试,无需 LLM 参与,每次运行结果完全可复现。
Ocarina 是什么?
Ocarina 是一个 MCP 服务器的自动化框架,核心思想很简单:用 YAML 文件(称为 Rondo)描述你要调用的工具、传入的参数、期望的输出,然后让 Ocarina 执行它。 不调语言模型,不消耗 Token,每次运行结果一致。
它的作者 msradam 从 Ansible 获得了灵感——正如 Ansible Playbook 让基础设施变成代码,Ocarina Rondo 让 MCP 测试变成代码。
server:
command: uvx
args: [mcp-server-time]
rondo:
- name: what time is it in Tokyo
tool: get_current_time
args:
timezone: Asia/Tokyo
expect:
contains: datetime
这个 Rondo 做了三件事:启动一个 MCP 服务器(mcp-server-time),调用它的 get_current_time 工具,验证输出中包含 datetime。全部是确定性的——同样的 YAML,同样的结果。
安装
Ocarina 是一个 Go 单二进制文件,安装非常简单:
go install github.com/msradam/ocarina@latest
如果没有 Go 环境,可以从 GitHub Releases 页面下载预编译的二进制文件。需要 Go 1.26+(从源码构建时)。
核心命令
Ocarina 提供了 9 个命令,覆盖了 MCP 服务器自动化的全生命周期:
| 命令 | 功能 |
|---|---|
play | 执行 Rondo 中的每一步,验证断言 |
validate | 检查 Rondo 格式、工具名、参数类型,不实际调用 |
serve | 把 Rondo 暴露为复合 MCP 工具 |
diff | 对比 Rondo 中的工具与当前服务器 Schema 的差异 |
lock | 快照当前工具 Schema,--check 模式下检测漂移 |
load | 对 Rondo 执行并发负载测试 |
record | 代理客户端会话,把实际调用录制成 Rondo |
docs | 生成 MCP 服务器的 Markdown 文档 |
hum | 单次调用一个工具并打印结果 |
深入 Rondo 格式
Rondo 的威力远超简单的”调用-验证”。它支持变量插值、多步骤流水线、条件执行、循环、错误处理,以及可复用的片段(Motif)。
变量与数据流
Rondo 支持通过 keys 定义顶层变量,用 echo 在步骤间传递数据,用 grab 从 JSON 输出中提取特定字段:
keys:
owner: pytorch
repo: pytorch
server:
command: npx
args: [-y, "@modelcontextprotocol/server-github"]
rondo:
- name: recent commits
tool: list_commits
args:
owner: "{{owner}}"
repo: "{{repo}}"
grab: ".0.sha" # 取第一个 commit 的 SHA
echo: latest_sha # 保存为变量
- name: commit detail
tool: get_commit
args:
sha: "{{latest_sha}}"
expect:
contains: "feat"
{{key}} 语法用于变量插值,支持从 keys、echo 输出和 {{env.NAME}} 环境变量读取数据。
断言系统
断言是 Rondo 的”测试断言”。Ocarina 支持四种断言方式:
- name: check output
tool: some_tool
args: {input: "hello"}
expect:
contains: "expected substring" # 子串匹配
matches: "^[A-Z].*" # 正则匹配
equals: "exact output" # 精确匹配
is_error: false # 是否期望错误
rule: "output.total == 2" # CEL 表达式
max_duration: "500ms" # 超时门控
rule 使用 CEL(Common Expression Language)编写表达式,当工具返回结构化 JSON 时,可以直接操作对象字段。
错误处理:Block / Rescue / Always
Ocarina 借鉴了 Ansible 的错误处理模式——block 执行一组步骤,任一失败时跳转到 rescue,always 块无论如何都会执行:
rondo:
- name: provision with rollback
block:
- tool: create_directory
args: {path: "{{dir}}"}
- tool: write_file
args: {path: "{{dir}}/config", content: "data"}
rescue:
- tool: write_file
args: {path: "/logs/failure", content: "failed"}
always:
- tool: list_directory
args: {path: "{{dir}}"}
这种模式非常适合测试需要事后清理的场景——测试失败时自动回收资源。
可复用片段:Motif
Motif 是 Rondo 的可复用片段,相当于 Ansible 的 Role 或 pytest 的 Fixture。定义一个 Motif 文件,然后在多个 Rondo 中引用:
keys:
zone: UTC
rondo:
- tool: get_current_time
args: {timezone: "{{zone}}"}
expect: {contains: datetime}
引用 Motif 并覆盖参数:
rondo:
- name: default zone
motif: motifs/time-probe.yaml
- name: tokyo
motif: motifs/time-probe.yaml
with:
zone: Asia/Tokyo
CI 集成:让 MCP 服务器测试进入流水线
Ocarina 最实用的场景之一是 CI 集成。play 命令在所有断言通过时返回 0,任何断言失败返回非零:
- name: MCP smoke test run: ocarina play tests/mcp-smoke.yaml
Ocarina 还提供了 GitHub Action:
- uses: msradam/ocarina@v1
with:
rondo: tests/mcp-smoke.yaml
快照测试
Ocarina 的快照测试工作流与 Jest 类似:
ocarina record session.yaml uvx mcp-server-fetch ocarina play session.yaml --snapshot ocarina play session.yaml --update
这在 CI 中特别有用——--snapshot 确保任何意外的输出变化都会阻断流水线,而 --update 只在开发者主动修改后才重新生成基准。
多种输出格式
Ocarina 支持 JUnit XML 和 JSON 输出格式,方便集成到各种 CI 报告系统:
ocarina play tests/mcp-smoke.yaml --output junit ocarina play tests/mcp-smoke.yaml --output json
此外还支持 OpenTelemetry 追踪——设置 OTEL_EXPORTER_OTLP_ENDPOINT 后,每个步骤的调用都会作为 span 导出。
高级功能
复合 MCP 工具(Serve)
ocarina serve 可以把一个 Rondo 暴露为单个 MCP 工具。这意味着你可以把多步操作封装成一个原子工具,Agent 只需要调用一次:
name: provision_workspace
description: Create a workspace and seed it with config files
params:
- name: dir
type: string
required: true
return: listing
server:
command: npx
args: [-y, "@modelcontextprotocol/server-filesystem", /tmp]
rondo:
- tool: create_directory
args: {path: "{{dir}}"}
- tool: write_file
loop: '["alpha.conf", "beta.conf"]'
args: {path: "{{dir}}/{{item}}", content: "managed by ocarina"}
- tool: list_directory
args: {path: "{{dir}}"}
echo: listing
启动复合工具:
ocarina serve provision.yaml # stdio 模式 ocarina serve provision.yaml --http :8080 # HTTP 模式
安全模式
play --safe 和 serve --safe 模式会拒绝调用任何非只读工具(根据 MCP 的 readOnlyHint 标注)。这让你可以放心地在不信任的服务器上执行只读检查:
ocarina play audit.yaml --safe
多服务器支持
一个 Rondo 可以驱动多个 MCP 服务器:
servers:
time: {command: uvx, args: [mcp-server-time]}
fetch: {command: uvx, args: [-y, "@modelcontextprotocol/server-fetch"]}
rondo:
- server: time
tool: get_current_time
args: {timezone: UTC}
- server: fetch
tool: fetch
args: {url: "https://example.com"}
数据驱动测试
从 CSV 或 JSON 文件注入多行数据,每行作为一次独立运行:
ocarina play zone-check.yaml --data timezones.csv
每行 CSV 的列名作为 {{key}} 注入 Rondo 的变量。
远程服务器支持
Ocarina 支持通过 Streamable HTTP 协议连接远程 MCP 服务器:
server:
url: https://api.githubcopilot.com/mcp/
headers:
Authorization: "Bearer {{env.GITHUB_TOKEN}}"
rondo:
- tool: get_me
expect: {contains: login}
实际应用场景
场景一:CI 中验证 MCP 服务器不出现回归
在每次部署前,运行一组 Rondo 检查所有关键 MCP 工具是否按预期工作:
server:
command: uvx
args: [-y, "@modelcontextprotocol/server-github"]
rondo:
- tool: list_issues
args: {owner: myorg, repo: myrepo, state: open}
expect: {contains: title}
- tool: get_repository
args: {owner: myorg, repo: myrepo}
expect: {contains: myrepo}
场景二:生成 MCP 服务器文档
ocarina docs uvx mcp-server-time > mcp-time-docs.md
场景三:代理录制真实会话用于故障排查
ocarina record session.yaml uvx mcp-server-github
然后重放录制的 Rondo,对比输出是否与录制时一致。
总结
Ocarina 填补了 MCP 生态中的一个关键空白——确定性测试和自动化。当 AI 编码 Agent 的可靠度越来越依赖底层 MCP 服务器的正确性时,一个能反复验证、不依赖 LLM、可嵌入 CI 的测试框架就变得不可或缺。
它的 Rondo 语法借鉴了 Ansible 的设计哲学,降低了学习曲线;快照测试和 CI 集成让它适合生产环境;Serve 功能更是把自动化测试的概念扩展到了复合工具层面。
如果你在使用或开发 MCP 服务器,Ocarina 值得一试——它可能是你工具箱里最不起眼但最实用的小工具。
相关链接: