Notes MCP
MCP Eval Demo
Рабочий пример применения оценочных проверок, чтобы убедиться, что LLM-агент действительно умеет пользоваться MCP-сервером, — а не только того, что код сервера корректен.
Модульные тесты отвечают на вопрос «удаляет ли delete_note заметку?» Но они не могут ответить на вопросы, которые решают, хорош ли MCP-сервер на практике:
Находит ли агент нужную заметку, когда пользователь описывает её словами, а не идентификатором?
Замечает ли он, что превью из списка было обрезано, или отвечает по половине заметки?
Понимает ли, что
update_noteперезаписывает содержимое, или молча уничтожает пользовательские данные, когда его просят «добавить строку в мой список покупок»?Приходит ли он в себя после сообщения об ошибке или сдаётся?
Это свойства поверхности инструментов — имён, описаний, схем, форм результатов, текста ошибок — и единственный способ их проверить — запустить реального агента против сервера и оценить, что он сделал. Именно для этого предназначен этот репозиторий.
Статус
MCP-сервер, его инфраструктура и стенд для оценочных проверок — всё на месте.
Related MCP server: MCP Notepad Server
Сервер под тестом: Notes MCP
Записная книжка в памяти. Состояние живёт в процессе сервера и отбрасывается при выходе, поэтому каждый оценочный прогон начинается с одного и того же известного корпуса (см. seed.py).
Инструмент | Подсказка о поведении | Что делает |
| запись | Создаёт заметку; заголовки должны быть уникальными без учёта регистра. |
| только чтение | Возвращает полное содержимое одной заметки по id. |
| чтение | Список заметок сначала те, что обновлены позже, в виде усечённых превью, с опциональной подстрокой |
| разрушающее | Перезаписывает заголовок и/или содержимое заметки. |
| разрушающее | Окончательно удаляет заметку. |
Несколько проектных решений приняты специально, чтобы оценочным проверкам было что ловить:
Идентификаторы, а не заголовки. Каждый изменяющий инструмент принимает
note_id, поэтому агент, которому нужно изменить «мой список покупок», должен сначала найти идентификатор. Именно здесь агенты обычно пытаются угадать.Усечённые превью.
list_notesвозвращает только первые 120 символов каждой заметки, помечая результатcontent_truncatedиcontent_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, а не в инлайн-докстрингах. Формулировки описаний — это основное, что вы настраиваете в ответ на падение оценочной проверки, и хранение их в одном месте делает эти дифы читабельными.
Начало работы
Требуется 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, и им можно управлять вручную.
Чтобы увидеть поверхность инструментов, которую видит агент — то, о чём на самом деле эти оценочные проверки, — вообще не запуская агента:
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О версии
mcp: сервер построен на отдельной библиотеке FastMCP, а не на той копии, которая раньше поставлялась внутри SDKmcpкакmcp.server.fastmcp, — модуль mcp 2.0 был удалён. FastMCP сам управляет версиейmcp, которая ему нужна (3.x резолвит mcp 1.x), поэтому в pyproject.toml нет прописанной вручную привязки кmcp. Оценочный стенд приходит к той же библиотеке с другой стороны: MCP-клиентpydantic-aiпостроен на FastMCP-овомClient. Обе части репозитория поэтому согласуются по версии конструктивно, а не за счёт версии, которую кому-то придётся поддержать вручную. FastMCP 4 — это шаг, который переводит обе части на mcp 2.x, поэтому зависимость я могу до границы ниже этого.
Подход к тестированию
Два уровня pytest, которые запускаются через scripts/test.sh:
test_store.pyпокрывает семантику хранения — уникальность, порядок сортировки, лимиты, временные метки. Быстро, полно, без участия протокола.test_server.pyгоняет сервер через внутрипроцессной сессии MCP-клиента (fastmcp.Clientна транспорт в памяти FastMCP), поэтому проверяется то, что агент реально получает: список инструментов, JSON-схемы, аннотации поведения, структурированные результаты и текст ошибок. Протокол настоящий; только подпроцесс и сокет — нет.
Асинхронные тесты используют плагин anyio для pytest, а не pytest-asyncio, потому что MCP-клиент держит открытым cancel scope на время жизни сессии, а anyio выполняет настройку и закупание фикстур в одной задаче.
Третий вид проверки — оценочные тесты поведения агента — вызывает живую модель и стоит денег, поэтому он вообще не входит в набор pytest; у него есть собственный запускатор и скрипт, описанный дальше.
Оценочный стенд
evals/ собирает минимального агента на pydantic-ai — общий системный промпт в одну строку, никаких примеров с few-shot, никаких особых инструкций — и подключает его единственные инструменты к серверу Notes MCP через pydantic_ai.mcp.MCPToolset (agent.py). Системный промпт специально оставлен голым: эти проверки существуют, чтобы выяснить, достаточно ли собственных имён, описаний и схем инструментов сервера для правильного поведения, а не может ли инжиниринг промптов прикрыть слабого агента.
evals/ лежит на верхнем уровне, а не в src/: это инструменты для разработки этого репозитория, а не часть устанавливаемого пакета notes-mcp.
pydantic_evals гоняет этого агента на Dataset из Case, каждый из которых нацелен на одно из четырёх поведений из начала этого файла.:
Case | Что проверяется |
| При просьбе удалить «заметку мой список покупок» агент вызывает |
| Вопрос, ответ на который выходит за пределы превью |
| «Добавить крекеры в мой список покупок» требует сначала прочитать полную заметку; проверяется, что в вызове |
| При создании заметки с уже существующим заголовком нельзя молча терять новое содержимое или заявлять, что дубликат создан. |
| Запрос на удаление несуществующей заметки не должен приводить к вызову |
| Простый «счастливый путь» для контрольной проверки. |
Кейсы живут в cases.yaml, а не в Python — они являются данными, поэтому добавление кейса или изменение критериев оценки не трогает код. cases.py — только загрузчик: он передаёт пользовательские оценщики в Dataset.from_file (YAML-файл может назвать только тот оценщик, который загрузчик регистрирует) и задаёт модель судьи. Заголововок yaml-language-server в YAML указывает на cases_schema.json, чтобы редактор мог дополнять и проверять имена оценщиков и их аргументы; повторно создавайте его после добавления или изменения пользовательского оценщика:
uv run python -c "from evals.cases import write_json_schema; print(write_json_schema())"Оценщики сочетают встроенные элементы pydantic-evals (ToolCorrectness, Contains, MaxToolCalls, LLMJudge для тех двух кейсов, где есть больше одного допустимого сценария) с двумя небольшими собственными в evaluators.py: ToolNotCalled (проверяет, что инструмент никогда не был вызван — встроенной негативной проверки нет) и ArgumentContains (проверка подстроки в аргументе инструмента, для случаев, где «старый текст должен сохраниться», а точную формулировку LLM нельзя зажать сравнением на равенство или по подмножеству словаря). Оба, как и встроенные, читают срезы вызовов инструментов-трейсов, которые собирают Agent.instrument_all() и локальный (send_to_logfire=False) logfire.configure() — см. configure_instrumentation() в agent.py.
__main__.py прогоняет набор данных, печатает полный отчёт и выходит с ненулевым кодом, если что-то не удалось — ошибка задачи, упавший оценщик или проваленный assert. Запуск:
uv run scripts/evals.shНастройка провайдера
NOTES_MCP_EVAL_MODEL задаёт и провайдера, и модель как строку provider:model из pydantic-ai и по умолчанию равенanthropic:claude-haiku-4-5-20251001. Скопируйте .env.example в .env и заполните раздел для того из трёх провайдеров, который вы используете — scripts/evals.sh загружает .env автоматически (через python-dotenv; он никогда не переопределяет уже заданную переменную окружения в вашей оболочке), а .env добавлен в .gitignore:
Anthropic API (по умолчанию) — нужен
ANTHROPIC_API_KEY.OpenAI —
NOTES_MCP_EVAL_MODEL=openai:gpt-5иOPENAI_API_KEY.Amazon Bedrock —
NOTES_MCP_EVAL_MODEL=bedrock:<bedrock-model-id>. Аутентификация идёт через обычную цепочку учётных данных boto3, поэтому ничего специфичного для оценочных проверок настраивать не нужно — используйте стандартные переменные AWS SDKAWS_PROFILEдля именованного профиля (и тожеAWS_DEFAULT_REGION, если в профиле ещё не задан регион — обратите внимание, что именноAWS_DEFAULT_REGION, неAWS_REGION, которую резолв региона в boto3 не проверяет), либо оставьте их неуказанными, чтобы использовать профиль/регион по умолчанию.
Код нигде не зависит от провайдера — строка анализа eval_model() передаётся напрямую и агенту, и LLMJudge, а infer_model из pydantic-ai выбирает нужный клиент и учётные данные для любого префикса провайдера, который видит.
Maintenance
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
- -licenseNot gradedqualityNot gradedmaintenanceA 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.
- FlicenseBqualityDmaintenanceA 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
- FlicenseNot gradedqualityDmaintenanceProvides MCP tools to create and retrieve notes stored in memory.
- FlicenseAqualityDmaintenanceA 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.31
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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