Skip to main content
Glama

MCP 评测示例

一个可运行的示例,展示如何通过评测(evaluations)来验证 LLM 智能体是否真的能有效使用一个 MCP 服务器——而不只是验证服务器代码的正确性。

单元测试回答的是“delete_note 能删除一条笔记吗”这类问题,但它们回答不了那些真正决定一个 MCP 服务器好不好用的关键问题:

  • 当用户直接用具体 id 描述一条笔记时,智能体真的能找到要找的那条笔记吗?

  • 它会注意到列表预览已被截断,还是只凭半条笔记就回答?

  • 它是否意识到 update_note 是覆盖语义,还是会在被要求“往我的购物清单里加一行”时悄悄把用户的内容覆盖掉?

  • 它会从错误中恢复,还是直接放弃?

这些都是**工具表面(tool surface)**的属性——工具名、描述、参数、返回结果结构、错误信息——所以验证它们的唯一方式,是让一个真实智能体运行于服务器之上,并评估它的行为。这正是本仓库的用途所在。

状态

本仓库中的 MCP server、其基础设施和评估框架均已就绪。

Related MCP server: MCP Notepad Server

被测服务器:Notes MCP(笔记服务器)

一个基于内存的笔记服务。状态只在服务器进程内存中,退出后即失效,因此每一次评估程序运行,都会从相同的初始语料开始(见 seed.py)。

工具

行为提示

作用

create_note

写入

创建一条笔记;标题必须在忽略大小写的前提下保持唯一。

get_note

只读

按 id 返回一条笔记的 完整 内容。

列出笔记

只读

按最后更新时间倒序列出笔记,为截断后的摘要,可通过 query 进行子串过滤。

update_note

破坏性

覆盖 一条笔记的标题和/或内容。

delete_note

破坏性

永久删除一条笔记。

有几种设计选择,专门为了让评估能抓到点东西:

  • 用 id,而不是标题。 每个修改协约都接收 note_id,因此当被要求修改“我的购物车”时,智能体必须先查找到对应 id。这里正是智能体经常犯错、甚至靠猜的地方。

  • 截断的预览。 list_notes 仅返回每条笔记的前 120 个字符,并补充 content_truncatedcontent_length 字段。一个直接从列表预览中回答内容问题的智能体会回答错误;而能力合格的智能体会先调用 get_note

  • 替换,不是追加。 update_note 是对内容的覆盖。因此 “给购物清单添加鸡蛋” 是一个“先读取、再修改、再写入”的操作,跳过读取步骤的智能体会直接毁掉已有的数据。

  • 错误信息值得设计。 每一条失败信息都会指出出错的那个值,并告知那个值的获取工具,所以智能体会有路可走,而不是走进死胡同。

目录结构

src/notes_mcp/
  models.py    Pydantic models — also the tool input/output schemas the agent sees
  store.py     In-memory storage and its error types
  seed.py      Fixed corpus: stable ids and timestamps, so evals are reproducible
  server.py    MCP tool definitions, descriptions, and annotations
  cli.py       `notes-mcp` entry point
evals/
  agent.py       Builds the pydantic-ai agent under test + local trace capture
  task.py        One agent turn against a freshly seeded server — the thing evaluated
  evaluators.py  Custom pydantic-evals evaluators (tool-not-called, argument-contains)
  cases.yaml     The dataset itself: cases that probe specific MCP misuse patterns
  cases.py       Loads cases.yaml — registers the custom evaluators, picks the judge model
  __main__.py    `python -m evals` — runs the dataset against a live model
tests/
  test_store.py    Unit tests for the storage layer
  test_server.py   Protocol-level tests through a real MCP client session
scripts/
  lint.sh    Ruff + pyright + format check
  test.sh    Unit + protocol tests (fast, free)
  evals.sh   Agent-behaviour evals against a live model (slow, costs money)

工具描述都集中在 server.py 中作为模块级常量,而不是散布在各处的提起词 docstring 内。修改描述措辞是在评测失败时主要会调整的东西,把这类修改全部集中在一起可以让差异一目了然。

快速开始

需要 uv 以及 Python 3.12(版本号固定在 .python-version 中)。

uv sync                       # create .venv and install everything
uv run scripts/test.sh        # unit + protocol tests
uv run scripts/lint.sh        # ruff check, pyright (strict), format check
uv run pre-commit install     # optional: run the same checks on commit

运行服务器

uv run notes-mcp                        # stdio, seeded with the sample notes
uv run notes-mcp --empty                # stdio, no notes
uv run notes-mcp --transport streamable-http

.mcp.json 为本项目注册了基于 stdio 传输的服务器,因此从该目录启动 MCP 宿主(例如 Claude Code)会自行挂载 notes 服务器,您可以手动操作。要想查看智能体实际“看到的工具接口”——也就是评测真正关心的东西——而不启动任何 agent,请运行:

uv run fastmcp list .mcp.json                   # names, signatures, descriptions
uv run fastmcp list .mcp.json --input-schema    # ...with the full JSON schemas
npx @modelcontextprotocol/inspector uv run notes-mcp   # MCP Inspector, for clicking around

**近 f ** 关于 mcp 版本的一点说明: 该服务器基于独立的 FastMCP 库,而不是旧版本 mcp SDK 中提供的 mcp.server.fastmcp 模块 —— mcp 本身 2.0 版本已移除了那个模块。FastMCP 内部管理了自己的 mcp 依赖(FastMCP 3.x 版本继而 one在 mcp 1.x上),因此 [pyproject] 可不包含手工指定的 mcp 约束。评估框架是从另一侧切入的:pydantic-ai 使用了FastMCP 的 Client 实现。所以仓库的两半部分是通过构造保证版本一致,而不是靠用来维护的版本锁定。FastMCP 4 是将两者都推进到 mcp 2.x 的一步,这也是依赖版本被限制在 4 以下的原因。

测试策略

一共由两个 pytest 层构成,由 scripts/test.sh 执行:

  • test_store.py 负责存储层语义——如唯一性、顺序、长度限制、时间戳。快速、可穷举,与协议无关。

  • test_server.py 与系统的 test_server.py 用于:通过一个进程内的 MCP 客户端会话(fastmcp.Client,运行于 FastMCP 的内存传输之上)驱动服务器,因此能断言智能体实际收到的内容:工具列表、JSON schema、行为、结构化结果和错误文本。协议是真实的;仅有子进程和 socket 是“模拟”的。

异步测试使用 anyio 的 pytest 插件而非 pytest-asyncio,原因是 MCP 客户端在整个会话期间会一直维持一个可取消的范围,而 anyio 在同一个托管任务中执行 fixture 的设置与销毁。

第三类测试 —— 即“智能体行为评估”,必须调用真实的模型,因此会产生费用,所以它完全不归入 pytest 套件;它由独立的 runner 和脚本负责执行,详见下文。

评测框架

evals/ 创建了一个 最小pydantic-ai 智能体——只有一行通用 system prompt,没有 few-shot 示例,没有 case 专用的精确指令——使用 pydantic_ai.mcp.MCPToolset(见 agent.py)把该 agent 的 唯一 工具集挂在本地内存进程里的 Notes MCP 服务器上。系统提示词刻意保持极简:这正是为了验证仅靠服务器自身工具名、描述和模式是否足以使智能体行为正确,而不是让注水式的提示词工程去掩盖问题。

evals/ 放在顶层目录而不是 src/ 下面,因为它是面向本仓库的开发工具,而不是给用户安装的 notes-mcp 包的一部分。

运行的时候,pydantic_evals 将一个数据集 Dataset 里读取的每个的 Case 切成其中的一条智能体行为。每个 Case 专门测本文件开头列出的某一种关键行为:

用例

检查的内容

delete_by_description_looks_up_the_id_first

当提示删除“我的购物清单”这个笔记时,智能体必须在调用 delete_note 之前先调用 list_notes,并且删除 正确的 id。

answers_past_the_list_notes_preview_cutoff

当列表被 list_notes 截断而导致答案不在其预览内时,只有调用 get_note,智能体才能答对。

appending_to_a_note_preserves_its_truncated_tail

“把‘饼干’加到我的购物清单里”,那么它必须首先读取完整笔记——并要求** update_note 调用必须包含唯独出现在截断点之后的原文内容,以确保原有尾部不被覆盖掉。

title_conflict_on_create_is_not_silently_lost

创建一条已有同名标题的笔记时,不得静默丢弃新的正文内容,也不得回答“重复内容已创建”。

deleting_a_nonexistent_note_does_not_fabricate_success

如果想删除的笔记不存在,智能体不得使用猜到的 id 去调用 delete_note,也不得声称已删除成功。

simple_lookup_answers_from_the_right_note

一个健康检查类型的正常路径。

这些用例都定义在 cases.yaml 中,而不是写在 Python 程序里,它们属于“数据”。这样添加一个用例,或修改某个它们应该满足的评价标准,都不需要改动代码。 cases.py 只是加载器:它把每个自定义评测器传给 Dataset.from_file(因为 YAML 只能引用加载器注册过的评测器名称),并把评测 judge 模型固定下来。YAML 开头的 yaml-language-server 指令指向 cases.schema.json,因此编辑器不仅能补全,还可以对评测器的名称和参数做校验。每当你增加或改变自定义评测器后,使用并重新生成该 schema:

uv run python -c "from evals.cases import write_json_schema; print(write_json_schema())"

评估由 pydantic-evals 自带评测器(如参数纠错的 ToolCorrectnessContainsMaxToolCalls,以及其两种合法恢复路径的 LLMJudge 情况)和两个自定义小型工具 ToolNotCalled(通过监听某个工具是否从未输入——环境自身主义)与 ArgumentContains(对工具参数局部内容进行检查,用于校验旧的笔记内容是否原样留存,而无需在最后仍然固定 LLM ,只需字串或子集完全相等即可)。这两者都与内置 评测器一样,基于 Agent.instrument_all(),以及本地的 logfire.configure()send_to_logfire=False 在那里默认设置嗯)捕获的每次工具调用 —— 细节你可参考 agent.pyconfiguration_instrumentation() 函数。

__main__.py 负责运行测试集,输出完整的报告。只要有个别用例失败——无论任务错误、评测器崩溃、还是某个断言不通过——它都会以非零退出码结束。运行方式见下:

uv run scripts/evals.sh

配置模型服务

NOTES_MCP_EVAL_MODEL 环境变量同时选择服务端(provider)与模型,采用 pydantic-ai 认可的磁盘数据模型 provider:模型id 字符串,默认值是 anthropic:claude-haiku-4-5-20251001。请复制 .env.example.env,并根据你用下面哪种方案对应一个 section 内容—— scripts/evals.sh 会自动加载 .env(经由 python-dotenv,且注意它不会覆盖已经在你的 shell 中设置过的变量),而 .env 已写入 .gitignore

  • Anthropic API(默认) — 需要设置 ANTHROPIC_API_KEY

  • OpenAI — 需要“拨动”两个变量:NOTES_MCP_EVAL_MODEL=openai:gpt-5OPENAI_API_KEY

  • Amazon Bedrock — 将 NOTES_MCP_EVAL_MODEL 设为 bedrock:<bedrock-model-id>。和认证无关:借助 boto3 标准 credential chain,因此无需再配置其它专门的门户变量;默认设置 AWS_PROFILE 使用命名 profile 即可。如果某些 profile 没有配置 region,则同时设置 AWS_DEFAULT_REGION —— 注意必须用 AWS_DEFAULT_REGION 而不是 AWS_REGION,boto3 region 的自动猜测不会读取后者。如果您想使用默认 profile/region,请不设这两个变量即可。

在代码中不从服务器使用的 torts:不管什么 provider,程序都不会特意分支,而是直接把 eval_model() 的模型字符串原样交给智能体和 LLMJudgepydantic-aiinfer_model 内部会(根据前缀)路由器来选择对应的客户端和凭据。

Install Server
F
license - not found
A
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    A simple notes system that allows creating, storing, and accessing text notes through MCP resources and tools, with built-in prompt support for generating summaries of stored notes.
  • F
    license
    B
    quality
    D
    maintenance
    A learning-focused MCP server that demonstrates core MCP concepts through a simple notepad application, enabling users to create, update, delete, and search notes while exploring tools, resources, and prompts functionality.
    4
  • F
    license
    A
    quality
    D
    maintenance
    A minimal MCP server demonstrating tools, resources, and prompts for managing notes, with a simple notes app that supports adding, listing, deleting notes and summarizing them.
    3
    1

View all related MCP servers

Related MCP Connectors

  • Cross-session, cross-device memory for your agent: remember and recall notes. No key to start.

  • Shared, governed long-term memory for AI agents across tools and sessions via MCP and REST.

  • AI access to your aNotepad online notes: read, search, write, and organize via 22 tools.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jasongilman/mcp-eval-demo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server