mcp-notas
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 |
| Define el servidor |
| Todo el I/O en disco y la sanitización de identificadores. Único punto que construye rutas. |
| Búsqueda textual con ranking por campo (título > tags > cuerpo), insensible a acentos. |
| Punto de entrada de |
| 45 pruebas que ejercitan el servidor de verdad, incluida una sesión MCP completa. |
| Dependencias de runtime y de prueba. |
| Configuración de |
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 |
|
| La nota creada, con fechas completadas. |
|
| La nota completa (cuerpo, tags, fechas). |
|
| La nota ya actualizada. |
|
| Confirmación en texto. |
|
| Total y resumen de cada nota, sin el cuerpo. |
|
| Resultados ordenados por relevancia, con fragmento. |
| — | Conteos, tags más usadas, nota más larga. |
Resources
URI | Tipo | Contenido |
|
| Índice de toda la base: slug, título, tags y URI de cada nota. |
|
| Markdown íntegro de una nota, con front matter. |
Prompts
Prompt | Argumentos | Qué arma |
|
| Una petición de resumen con el contenido de la nota ya incrustado. |
|
| 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.txtRequiere 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_notasEl 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_notasConfiguració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 sí se verificó aquí es el equivalente programático: el servidor se levantó como subproceso con
python3 -m mcp_notasy unClientSessiondel 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. UseSeguridad
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.48sSolo las pruebas de path traversal:
$ python3 -m pytest tests/ -q -k traversal
................. [100%]
17 passed, 28 deselected in 0.67sLa suite cubre, en orden:
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.Superficie MCP —
list_toolsdevuelve exactamente las siete tools, y los esquemas (required,type,default,outputSchema) son los generados a partir de los type hints y docstrings.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.Resources —
list_resources,list_resource_templates, lectura del índice JSON, lectura de una nota individual y las dos formas de traversal.Prompts —
list_prompts,get_promptde 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.Sesión de extremo a extremo —
create_connected_server_and_client_sessionlevanta un cliente y un servidor MCP conectados en memoria; la prueba lista tools, crea nota, lista, lee resource, obtiene prompt y confirmaisError: Trueen el intento de traversal.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/ -q→ 45 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 medianteFastMCP.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 unClientSessiondel SDK ejecutandoinitialize,list_toolsycall_toola través de él.Path traversal rechazado en
sanitizar_slug, en la tool, en el resource y en el sistema de archivos.MCP_NOTAS_DIRrespetado: 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
mcpServersno 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
sseystreamable-httpexisten enFastMCP.run, pero este proyecto solo ejercitastdio.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
This server cannot be installed
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
- FlicenseNot gradedqualityDmaintenanceManages markdown notes in a specified directory, allowing users to create, read, update, and list notes through the Model Context Protocol.1
- AlicenseAqualityDmaintenanceEnables creating, managing, and searching Markdown notes with support for tags, timestamps, and full-text search. Includes AI prompts for analyzing and summarizing notes.61MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI assistants to search, read, create, update, and remove personal markdown notes stored locally, providing persistent memory across sessions.132MIT
- AlicenseAqualityCmaintenanceEnables AI assistants to interact with a local folder of Markdown notes, supporting listing, reading, searching, creating, and appending to notes with strict security boundaries.5MIT
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.
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/herickbrandao483-jpg/mcp-server-example'
If you have feedback or need assistance with the MCP directory API, please join our Discord server