Skip to main content
Glama

MCP Eval Demo

Un ejemplo práctico de cómo usar evaluaciones para verificar que un agente de LLM realmente puede usar de forma eficaz un servidor MCP — no solo que el código del servidor es correcto.

Las pruebas unitarias responden a «¿delete_note borra una nota?». No pueden responder a las preguntas que deciden si un servidor MCP es bueno en la práctica:

  • ¿Encuentra el agente la nota correcta cuando el usuario la describe con palabras en lugar de por id?

  • ¿Se da cuenta de que una vista previa del listado estaba truncada, o responde a partir de media nota?

  • ¿Se da cuenta de que update_note sobrescribe, o destruye en silencio el contenido del usuario cuando le pide “añade una línea a mi lista de la compra”?

  • ¿Se recupera de un mensaje de error o se rinde?

Esas son propiedades de la superficie de las herramientas — nombres, descripciones, esquemas, formas de los resultados, texto de error — y la única manera de comprobarlas es ejecutar un agente real contra el servidor y evaluar lo que ha hecho. Para eso está este repositorio.

Estado

El servidor MCP, su infraestructura y el harness de evaluación ya están en su sitio.

Related MCP server: MCP Notepad Server

El servidor bajo prueba: Notes MCP

Un cuaderno en memoria. El estado reside en el proceso del servidor y se descarta al salir, por lo que cada ejecución de evaluación parte del mismo corpus conocido (consulta seed.py).

Herramienta

Comportamiento

Qué hace

create_note

escritura

Crea una nota; los títulos deben ser únicos sin distinguir entre mayúsculas y minúsculas.

get_note

solo lectura

Devuelve el contenido completo de una nota, por id.

list_notes

solo lectura

Lista las notas de la más reciente a la más antigua, como vistas previas truncadas, con un query opcional.

update_note

destructivo

Sobrescribe el título y/o el contenido de una nota.

delete_note

destructivo

Elimina una nota de forma permanente.

Hay varias decisiones de diseño pensadas expresamente para que las evaluaciones tengan algo que detectar:

  • Ids, no títulos. Toda herramienta de mutación toma un note_id, así que un agente al que se le pide cambiar “mi lista de la compra” debe buscar el id antes. Ahí es donde los agentes suelen tomar la solución.

  • Vistas previas truncadas. list_notes devuelve solo los primeros 120 caracteres de cada nota, marcadas con content_truncated y content_length. Un agente que responde una pregunta de contenido directamente desde un listado falla; un buen agente llama a get_note.

  • Reemplaza, no añade. update_note sobrescribe. “Añade huevos a mi lista de la compra” es, por tanto, una operación de lectura-modificación-escritura, y un agente que se salta la lectura destruye datos.

  • Errores que enseñan. Cada fallo nombra el valor problemático y señala la herramienta que lo resolvería, de modo que el agente tiene una salida y no un punto muerto.

Estructura

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)

Las descripciones de herramientas son constantes a nivel de módulo en server.py, no docstrings en línea. La redacción de las descripciones es lo que principalmente ajustas cuando una evaluación falla, y tenerla en un solo lugar hace que esos cambios sean legibles.

Primeros pasos

Requiere uv y Python 3.12 (fijado en .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

Ejecutar el servidor

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 registra el servidor stdio para este proyecto, por lo que un anfitrión MCP que se cualйтесь desde este directorio — Claude Code, por ejemplo — reconoce automáticamente el servidor notes y puedes trabajarlo a mano.

Para leer la superficie de herramientas que vería un agente — que es de lo que realmente va estas evaluaciones — sin iniciar un agente:

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

Acerca de la versión de mcp: el servidor está construido sobre la biblioteca independiente FastMCP y no con la copia que antes se incluía dentro del SDK de mcp como mcp.server.fastmcp» — mcp 2.0 eliminó ese módulo. FastMCP decide qué versión de mcpnecesita (3.x resuelve mcp 1.x), por lo que [pyproject.toml](pyproject.toml) no tiene una dependenciamcpescrita a mano. El harness de evaluación llega a la misma biblioteca desde el otro lado: el cliente MCP depydantic-aise basa enClient` de FastMCP. Así, las dos mitades de este repositorio concuerdan en la versión por construcción y no por un pin que alguien tenga que mantener. FastMCP 4 es el paso que mueve ambas a mcp 2.x, lo que explica por qué la dependencia está limitada por debajo.

Enfoque de pruebas

Dos capas de pruebas, ejecutadas por scripts/test.sh:

  • test_store.py cubre la semántica del almacenamiento: singularidad, orden de clasificación, límites y marcas de tiempo. Rápidas, exhaustivas, sin protocolo.

  • test_server.py acciona el servidor en una sesión de cliente MCP entre procesos (fastmcp.Client sobre el transporte de memoria de FastMCP), por lo que verifica lo que el agente realmente recibe: la lista de herramientas, esquemas JSON, anotaciones de comportamiento, resultados estructurados y texto de error. El protocolo es real; solo lo son el subproceso y el socket.

Las pruebas asíncronas usan el plugin anyio en lugar de pytest-asyncio, porque el cliente del MCP mantiene un cancel scope abierto durante toda vida de la sesión y anyio ejecute la configuración y desmontaje de los fixtures en la misma tarea.

Un tercer tipo de verificación — las evaluaciones del comportamiento del agente — invoca un modelo real y cuesta dinero, así que no forma parte de la suite de pruebas en absoluto; tiene su propio ejecutor y su propio script, descrito a continuación.

El harness de evaluación

evals/ crea un agente pydantic-ai mínimo — un prompt de sistema genérico de una solo línea, agentes few-shot, sin instrucciones tratadas especialmente — y conecta sus únicas herramientas a través de un servidor Notes MCP en proceso mediante pydantic_ai.mcp.MCPToolset (agent.py). El prompt del sistema es deliberadamente escueto: estas evaluaciones existen para comprobar si los propios nombres, descripciones y esquemas del servidor son suficientes para guiar el comportamiento correcto, y no si la ingeniería de prompts puede cubrir una herramienta débil.

evals/ está en el nivel superior y no bajo src/: es una herramienta de desarrollo para este repositorio, no parte del paquete notes-mcp que cualquiera instalaría.

pydantic_evals ejecuta ese agente sobre un Dataset de Case, cada uno dirigido a uno de los cuatro comportamientos el comienzo de este documento:

Caso

Qué comprueba

delete_by_description_looks_up_the_id_first

Al pedirle que elimine “mi nota de la lista de la compra”, el agente llama a list_notes antes de delete_note y elimina el id correcto.

answers_past_the_list_notes_preview_cutoff

Una pregunta cuya respuesta se ha truncado en la vista previa de list_notes solo se responde correctamente si el agente llama a get_note

appending_to_a_note_preserves_its_truncated_tail

“Añade crackers a mi lista de la compra” debe leer primero la nota completa; la llamada a update_note se comprueba que solo el que existe pasado ese límite de vista previa.

title_conflict_on_create_is_not_silently_lost

Al crear una nota con un título que ya existe, no debe desaparecer en silencio el contenido de la nueva nota ni afirmar que se ha creado un duplicado.

deleting_a_nonexistent_note_does_not_fabricate_success

Una petición para eliminar una nota inexistente no debe dar lugar a una llamada delete_note con un id adivinado ni a una respuesta que diga que se ha eliminado.

simple_lookup_answers_from_the_right_note

Un camino feliz para comprobación.

Los casos están en cases.yaml, non en Python — son datos, por lo que añadir un caso o reformular una rúbrica no toca el código. cases.py es solo el cargador: pasa los evaluadores personalizados a Dataset.from_file (un archivo YAML solo puede nombrar un evaluador que el cargador registro) y establece el modelo juez. La cabecera yaml-language-server de el YAML apunta a cases_schema.json, para que un editor pueda completar y validar los nombres de los evaluadores y sus argumentos; hazlo notar sim registro después de añadir o cambiar un evaluador personalizado:

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

Los evaluadores combinan las utilidades incorporadas de pydantic-evals (ToolCorrectness, Contains, MaxToolCalls, LLMJudge para los dos casos con más de una recuperación válida) con dos pequeñas personalizadas en evaluators.py: ToolNotCalled (comprueba que una herramienta no se ha invocado más nunca — no existe ToolNotCalled root check) y ArgumentContains (comprobaciones de subcadenas sobre un argumento de herramienta, para los casos de “el contenido antiguo debe conservarse” donde no puede fijarse la redacción exacta de un LLM con un matching de igualdad ni de subdiccionario).sim Contains. Ambos, como los incorporados, leen las pistas de las llamadas a herramientas que captura la combinación de Agent.instrument_all()y una configuración local delogfire.configure() (send_to_logfire=False); consulta configure_instrumentation()` en agent.py.

__main__.py ejecuta el conjunto de datos, imprime un informe completo y sale non-zero si algo fall y ha sido un error de tarea, un evaluador roto o una aserción fallida. La ejecución así:

uv run scripts/evals.sh

Configurar un proveedor

NOTES_MCP_EVAL_MODEL selecciona a la vez el proveedor y el modelo, como cadena números:modelo, y su valor predeterminado es anthropic:claude-haiku-4-5-20251001. Copia example a .env y rellena la sección de cualquiera de estos tres que vayas a usar; scripts/evals.sh carga .env automáticamente (a través de python-dotenv; nunca modifica una variable ya definida en tu shell) y .env está en el .gitignore.

  • Anthropic API (por defecto) — necesita ANTHROPIC_API_KEY.

  • OpenAINOTES_MCP_EVAL_MODEL=openai:gpt-5 y OPENAI_API_KEY.

  • Amazon BedrockNOTES_MCP_EVAL_MODEL=bedrock:<bedrock-model-id>. La autenticación se realiza por la cadena normal de credenciales de boto3, así que no hay nada específicambre específica que configurar más allá de las variables estándar del SDK de AWS: establece AWS_PROFILE para usarlo perfil concretado (AWS_DEFAULT_REGION también si este perfil no define una región; nota que debe ser AWS_AWS_DEFAULT_REGION, no no AWS_REGION, que la resolución de región de boto3 no comprueba), o déjalos si está definidos en tu valores/región por defecto.

. Igualmente, no hay código el mismo modelo del proveedor: la cadena de eval_model() se pasa directamente al agente y al LLMJudge, y el infer_model de pydantic-ai resuelve el cliente y las credenciales en función del prefijo de uso que vea.

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