Skip to main content
Glama

mcp-server-example — servidor MCP para una base de notas en Markdown

Un servidor MCP (Model Context Protocol) de ejemplo, funcional y probado, que le da a un asistente acceso a un second brain: un directorio local de notas en Markdown que puede crear, leer, actualizar, listar, buscar y medir.

El foco aquí no es la cantidad de funciones, sino mostrar un servidor MCP honesto: esquemas generados a partir de los type hints, sanitización de verdad contra path traversal, y una suite de pruebas que llama a las herramientas de verdad en lugar de simular la llamada.


Qué es MCP

El Model Context Protocol es un protocolo abierto que estandariza cómo un asistente conversa con sistemas externos. En lugar de que cada aplicación invente su propio formato de plugin, el servidor MCP declara tres cosas — tools (acciones que el modelo puede ejecutar), resources (datos que puede leer, direccionados por URI) y prompts (plantillas de conversación que el usuario puede invocar) — y cualquier cliente compatible las descubre y las usa solo. La comunicación es JSON-RPC, normalmente sobre stdio: el cliente levanta el servidor como un subproceso e intercambia mensajes por la entrada y salida estándar.


Related MCP server: Notes MCP Server

Qué hay aquí

Archivo

Qué hace

mcp_notas/server.py

Define el servidor FastMCP: tools, resources, prompts y los modelos Pydantic de salida.

mcp_notas/storage.py

Todo el I/O en disco y la sanitización de identificadores. Único punto que construye rutas.

mcp_notas/search.py

Búsqueda textual con ranking por campo (título > tags > cuerpo), insensible a acentos.

mcp_notas/__main__.py

Punto de entrada de python3 -m mcp_notas.

tests/test_server.py

45 pruebas que ejercitan el servidor de verdad, incluida una sesión MCP completa.

requirements.txt

Dependencias de runtime y de prueba.

pytest.ini

Configuración de pytest-asyncio.

Cada nota es un archivo .md con un front matter mínimo:

---
title: Teste env
tags: []
created: 2026-08-25T00:20:24+00:00
updated: 2026-08-25T00:20:24+00:00
---

Qué expone el servidor

Tools

Tool

Argumentos

Devuelve

criar_nota

titulo (obligatorio), corpo, tags, slug

La nota creada, con fechas completadas.

ler_nota

slug

La nota completa (cuerpo, tags, fechas).

atualizar_nota

slug, corpo, titulo, tags, anexar

La nota ya actualizada.

apagar_nota

slug

Confirmación en texto.

listar_notas

tag (opcional)

Total y resumen de cada nota, sin el cuerpo.

buscar_notas

consulta, limite

Resultados ordenados por relevancia, con fragmento.

estatisticas_base

Conteos, tags más usadas, nota más larga.

Resources

URI

Tipo

Contenido

notas://index

application/json

Índice de toda la base: slug, título, tags y URI de cada nota.

notas://{slug}

text/markdown

Markdown íntegro de una nota, con front matter.

Prompts

Prompt

Argumentos

Qué arma

resumir_nota

slug, tamanho (curto/longo)

Una petición de resumen con el contenido de la nota ya incrustado.

sugerir_conexoes

slug, quantidade

Cuatro mensajes: instrucción, nota de partida, catálogo de las demás notas y la apertura del asistente.


Instalación

git clone <url-do-repositorio> mcp-server-example
cd mcp-server-example
pip install -r requirements.txt

Requiere Python 3.11+ y mcp >= 1.27.0.


Cómo ejecutarlo

El transporte predeterminado es stdio — así es como un cliente MCP levanta el servidor:

cd mcp-server-example
python3 -m mcp_notas

El proceso permanece en silencio esperando mensajes JSON-RPC en la entrada estándar; eso es el comportamiento correcto, no un bloqueo.

El directorio de la base es configurable mediante la variable de entorno MCP_NOTAS_DIR (predeterminado: ./notas, creado automáticamente):

MCP_NOTAS_DIR=~/meu-second-brain python3 -m mcp_notas

Configuración en el cliente

Bloque listo para pegar en la configuración de un cliente MCP:

{
  "mcpServers": {
    "notas": {
      "command": "python3",
      "args": ["-m", "mcp_notas"],
      "cwd": "/caminho/absoluto/para/mcp-server-example",
      "env": {
        "MCP_NOTAS_DIR": "/caminho/absoluto/para/suas-notas"
      }
    }
  }
}

⚠️ Este bloque no se ha probado contra un cliente MCP real en este entorno. Lo que se verificó aquí es el equivalente programático: el servidor se levantó como subproceso con python3 -m mcp_notas y un ClientSession del propio SDK completó el handshake por stdio, listó las tools y ejecutó llamadas (ver "Estado de verificación"). La traducción de ese handshake al formato de configuración de un cliente específico no se ha ejercitado.


Ejemplo de uso

Salidas reales, capturadas ejecutando el servidor in-process (criar_servidor() + call_tool). El campo diretorio se reemplazó por una ruta genérica; el resto es literal.

>>> criar_nota
{
  "slug": "protocolo-mcp",
  "titulo": "Protocolo MCP",
  "tags": [
    "mcp",
    "protocolo"
  ],
  "corpo": "O Model Context Protocol padroniza como um assistente acessa ferramentas e dados externos.",
  "criada_em": "2026-08-25T00:20:03+00:00",
  "atualizada_em": "2026-08-25T00:20:03+00:00"
}

>>> listar_notas(tag='mcp')
{
  "total": 1,
  "filtro_tag": "mcp",
  "notas": [
    {
      "slug": "protocolo-mcp",
      "titulo": "Protocolo MCP",
      "tags": [
        "mcp",
        "protocolo"
      ],
      "atualizada_em": "2026-08-25T00:20:03+00:00",
      "resumo": "O Model Context Protocol padroniza como um assistente acessa ferramentas e dados externos.",
      "tamanho": 90
    }
  ]
}

>>> buscar_notas(consulta='protocolo')
{
  "consulta": "protocolo",
  "total": 2,
  "resultados": [
    {
      "slug": "protocolo-mcp",
      "titulo": "Protocolo MCP",
      "tags": [
        "mcp",
        "protocolo"
      ],
      "pontuacao": 8.0,
      "trecho": "O Model Context Protocol padroniza como um assistente acessa ferramentas e dados externos."
    },
    {
      "slug": "memoria-de-longo-prazo",
      "titulo": "Memória de longo prazo",
      "tags": [
        "produtividade"
      ],
      "pontuacao": 1.0,
      "trecho": "Anotações sobre second brain. Cita o protocolo de revisão semanal."
    }
  ]
}

Observa el ranking: la palabra "protocolo" está en el título y las tags de la primera nota (puntuación 8.0) y solo en el cuerpo de la segunda (puntuación 1.0).

>>> estatisticas_base()
{
  "total_de_notas": 2,
  "total_de_caracteres": 156,
  "total_de_palavras": 23,
  "media_de_caracteres": 78.0,
  "total_de_tags": 3,
  "tags_mais_usadas": {
    "mcp": 1,
    "produtividade": 1,
    "protocolo": 1
  },
  "nota_mais_longa": "protocolo-mcp",
  "ultima_atualizacao": "2026-08-25T00:20:03+00:00",
  "diretorio": "/caminho/para/notas"
}

>>> read_resource('notas://index')
{
  "diretorio": "/caminho/para/notas",
  "total": 2,
  "notas": [
    {
      "slug": "memoria-de-longo-prazo",
      "titulo": "Memória de longo prazo",
      "tags": [
        "produtividade"
      ],
      "uri": "notas://memoria-de-longo-prazo"
    },
    {
      "slug": "protocolo-mcp",
      "titulo": "Protocolo MCP",
      "tags": [
        "mcp",
        "protocolo"
      ],
      "uri": "notas://protocolo-mcp"
    }
  ]
}

>>> get_prompt('resumir_nota', {'slug': 'protocolo-mcp'})
Resuma em no máximo 3 bullets.
Não invente informação que não esteja na nota.

# Protocolo MCP
Tags: mcp, protocolo

O Model Context Protocol padroniza como um assistente acessa ferramentas e dados externos.

Y el handshake real por stdio, con el servidor ejecutándose como subproceso y un ClientSession del SDK del otro lado (salida literal, sin los logs INFO del servidor):

serverInfo: mcp-notas 1.27.0
instructions[:60]: Servidor de uma base local de notas em Markdown. Use 'listar
tools: ['apagar_nota', 'atualizar_nota', 'buscar_notas', 'criar_nota', 'estatisticas_base', 'ler_nota', 'listar_notas']
criar_nota isError: False slug: handshake-stdio
estatisticas: 1 nota(s)
traversal isError: True
traversal msg: Error executing tool ler_nota: Identificador inválido '../../etc/passwd': separadores de caminho não são permitidos. Use

Seguridad

El error clásico de un servidor MCP que manipula archivos es aceptar un identificador proveniente del modelo y concatenarlo directo en la ruta: Path(base) / slug. Con slug = "../../etc/passwd", eso entrega el disco entero a quien controle el prompt.

Aquí la defensa está en mcp_notas/storage.py y tiene dos capas.

1. sanitizar_slug() — validación por lista de permisos. Un identificador solo pasa si coincide con ^[a-z0-9][a-z0-9._-]{0,79}$, después de rechazar explícitamente separadores de ruta (/, \), byte nulo, letras de unidad de Windows (C:) y cualquier aparición de ... Exigir que comience con letra o dígito también descarta nombres ocultos como .ssh.

2. BaseDeNotas.caminho() — verificación de la ruta resuelta. Después de sanitizar, la ruta se resuelve con Path.resolve() y el código comprueba que su padre es exactamente el directorio de la base. Esa comprobación es redundante por construcción — y ese es el punto: si algún día la primera capa tiene un agujero, la fuga igualmente no ocurre.

El ataque canónico, ejecutado de verdad contra la tool:

>>> call_tool('ler_nota', {'slug': '../../etc/passwd'})
ToolError: Error executing tool ler_nota: Identificador inválido '../../etc/passwd': separadores de caminho não são permitidos. Use apenas o slug da nota, sem diretórios.

El resource notas://{slug} tiene la misma protección, y por dos vías diferentes: la URI cruda notas://../../etc/passwd ni siquiera coincide con la plantilla (Unknown resource), mientras que la forma percent-encoded notas://..%2F..%2Fetc%2Fpasswd coincide, llega a la sanitización y es bloqueada allí — es ese segundo caso, el peligroso, el que cubre la prueba.

Una prueba también demuestra en el sistema de archivos que el objetivo del ataque no llega a crearse: después de un intento de criar_nota con slug="../vazamento", el directorio de la base sigue vacío y el archivo fuera de él no existe.

Además: ninguna clave de API, ningún acceso de red, y el servidor nunca lee ni escribe fuera del directorio configurado.


Pruebas

$ python3 -m pytest tests/ -q
.............................................                            [100%]
45 passed in 1.48s

Solo las pruebas de path traversal:

$ python3 -m pytest tests/ -q -k traversal
.................                                                        [100%]
17 passed, 28 deselected in 0.67s

La suite cubre, en orden:

  1. Sanitización — 13 entradas maliciosas parametrizadas (../../etc/passwd, /etc/passwd, ..\\..\\windows\\system32\\config\\sam, C:\Windows\win.ini, nota\x00.md, cadena vacía…), más la prueba en disco de que nada se crea fuera de la base.

  2. Superficie MCPlist_tools devuelve exactamente las siete tools, y los esquemas (required, type, default, outputSchema) son los generados a partir de los type hints y docstrings.

  3. Llamada real de cada tool — creación con persistencia verificada en disco, duplicado, lectura, lectura de inexistente, actualización, actualización con anexar, listado con y sin filtro de tag, búsqueda con ranking y con límite, estadísticas y eliminación.

  4. Resourceslist_resources, list_resource_templates, lectura del índice JSON, lectura de una nota individual y las dos formas de traversal.

  5. Promptslist_prompts, get_prompt de los dos prompts, comprobando que el contenido de la nota realmente se incrusta y que la nota de partida no aparece en el catálogo de las demás.

  6. Sesión de extremo a extremocreate_connected_server_and_client_session levanta un cliente y un servidor MCP conectados en memoria; la prueba lista tools, crea nota, lista, lee resource, obtiene prompt y confirma isError: True en el intento de traversal.

  7. Almacenamiento aislado — round-trip del front matter y archivos que no son notas siendo ignorados en el listado.


Estado de verificación

Todo lo siguiente se ejecutó en este entorno, con mcp 1.27.0, pytest 9.1.1 y pytest-asyncio 1.4.0 bajo Python 3.11.

Verificado

  • python3 -m pytest tests/ -q45 passed.

  • Las siete tools llamadas de verdad mediante FastMCP.call_tool, con los resultados comprobados.

  • Los dos resources leídos mediante FastMCP.read_resource; los dos prompts mediante FastMCP.get_prompt.

  • Sesión MCP completa cliente↔servidor en memoria con mcp.shared.memory.create_connected_server_and_client_session.

  • Handshake stdio real: servidor levantado como subproceso (python3 -m mcp_notas) y un ClientSession del SDK ejecutando initialize, list_tools y call_tool a través de él.

  • Path traversal rechazado en sanitizar_slug, en la tool, en el resource y en el sistema de archivos.

  • MCP_NOTAS_DIR respetado: la nota creada apareció en el directorio señalado por la variable.

  • Todas las salidas mostradas en este README se copiaron de ejecuciones reales.

⚠️ No probado

  • El bloque mcpServers no se ha probado contra un cliente MCP real (Claude Desktop, editores, etc.). No hay ningún cliente instalado en este entorno; lo que sustituye esa verificación es el handshake stdio programático descrito arriba.

  • Los transportes sse y streamable-http existen en FastMCP.run, pero este proyecto solo ejercita stdio.

  • Sin pruebas de concurrencia: las escrituras simultáneas en la misma nota no se coordinan con lock.

  • Sin pruebas en Windows o macOS — solo Linux.


Licencia

MIT

A
license - permissive license
Not graded
quality - not tested
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

  • F
    license
    Not graded
    quality
    D
    maintenance
    Manages markdown notes in a specified directory, allowing users to create, read, update, and list notes through the Model Context Protocol.
    1
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to search, read, create, update, and remove personal markdown notes stored locally, providing persistent memory across sessions.
    13
    2
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to interact with a local folder of Markdown notes, supporting listing, reading, searching, creating, and appending to notes with strict security boundaries.
    5
    MIT

View all related MCP servers

Related MCP Connectors

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

  • Read and write your Fresh Jots notes from Claude, Cursor, and any MCP client.

  • Create, validate, edit, export (markdown/svg/png/mermaid), and search JSON Canvas files.

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/herickbrandao483-jpg/mcp-server-example'

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