mcp-notas
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-notassearch my notes for 'MCP' and give me a summary"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-server-example — MCP server for a Markdown notes base
A functional, tested example MCP (Model Context Protocol) server that gives an assistant access to a second brain: a local directory of Markdown notes it can create, read, update, list, search, and measure.
The focus here is not the number of features, but rather showing an honest MCP server: schemas generated from type hints, real sanitization against path traversal, and a test suite that actually calls the tools instead of mocking the call.
What MCP is
The Model Context Protocol is an open protocol that standardizes how an assistant talks to external systems. Instead of each application inventing its own plugin format, the MCP server declares three things — tools (actions the model can execute), resources (data it can read, addressed by URI), and prompts (conversation templates the user can invoke) — and any compatible client discovers and uses all of this on its own. Communication is JSON-RPC, usually over stdio: the client starts the server as a subprocess and exchanges messages through standard input and output.
Related MCP server: Notes MCP Server
What's here
File | What it does |
| Defines the |
| All disk I/O and identifier sanitization. The only place that builds paths. |
| Text search with per-field ranking (title > tags > body), accent-insensitive. |
| Entry point for |
| 45 tests that exercise the real server, including a full MCP session. |
| Runtime and test dependencies. |
|
|
Each note is a .md file with minimal front matter:
---
title: Teste env
tags: []
created: 2026-08-25T00:20:24+00:00
updated: 2026-08-25T00:20:24+00:00
---What the server exposes
Tools
Tool | Arguments | Returns |
|
| The created note, with dates filled in. |
|
| The full note (body, tags, dates). |
|
| The already-updated note. |
|
| Text confirmation. |
|
| Total and summary of each note, without the body. |
|
| Results ordered by relevance, with excerpt. |
| — | Counts, most-used tags, longest note. |
Resources
URI | Type | Content |
|
| Index of the whole base: slug, title, tags, and URI of each note. |
|
| Full Markdown of a note, with front matter. |
Prompts
Prompt | Arguments | What it builds |
|
| A summary request with the note's content already embedded. |
|
| Four messages: instruction, starting note, catalog of the other notes, and the assistant's opening. |
Installation
git clone <url-do-repositorio> mcp-server-example
cd mcp-server-example
pip install -r requirements.txtRequires Python 3.11+ and mcp >= 1.27.0.
How to run
The default transport is stdio — that's how an MCP client starts the server:
cd mcp-server-example
python3 -m mcp_notasThe process stays silent waiting for JSON-RPC messages on standard input; that's the correct behavior, not a hang.
The base directory is configurable via the MCP_NOTAS_DIR environment variable (default: ./notas, created automatically):
MCP_NOTAS_DIR=~/meu-second-brain python3 -m mcp_notasClient configuration
Ready-to-paste block for an MCP client configuration:
{
"mcpServers": {
"notas": {
"command": "python3",
"args": ["-m", "mcp_notas"],
"cwd": "/caminho/absoluto/para/mcp-server-example",
"env": {
"MCP_NOTAS_DIR": "/caminho/absoluto/para/suas-notas"
}
}
}
}⚠️ This block has not been tested against a real MCP client in this environment. What was verified here is the programmatic equivalent: the server was started as a subprocess with
python3 -m mcp_notasand aClientSessionfrom the SDK itself completed the stdio handshake, listed the tools, and executed calls (see "Verification status"). The translation of that handshake into a specific client's configuration format has not been exercised.
Usage example
Real outputs, captured by running the server in-process (criar_servidor() + call_tool). The diretorio field was replaced with a generic path; the rest is 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."
}
]
}Notice the ranking: the word "protocolo" is in the title and tags of the first note (score 8.0) and only in the body of the second (score 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.And the real stdio handshake, with the server running as a subprocess and a ClientSession from the SDK on the other side (literal output, without the server's INFO logs):
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. UseSecurity
The classic bug in an MCP server that touches files is accepting an identifier from the model and concatenating it directly into the path: Path(base) / slug. With slug = "../../etc/passwd", that hands the entire disk to whoever controls the prompt.
Here the defense lives in mcp_notas/storage.py and has two layers.
1. sanitizar_slug() — allowlist validation. An identifier only passes if it matches ^[a-z0-9][a-z0-9._-]{0,79}$, after explicitly rejecting path separators (/, \), null bytes, Windows drive letters (C:), and any occurrence of ... Requiring it to start with a letter or digit also blocks hidden names like .ssh.
2. BaseDeNotas.caminho() — resolved-path check. After sanitizing, the path is resolved with Path.resolve() and the code verifies that its parent is exactly the base directory. This check is redundant by construction — and that's the point: if the first layer ever has a hole, the leak still doesn't happen.
The canonical attack, actually executed against the 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.The notas://{slug} resource has the same protection, through two different paths: the raw URI notas://../../etc/passwd doesn't even match the template (Unknown resource), while the percent-encoded form notas://..%2F..%2Fetc%2Fpasswd matches, reaches sanitization, and is blocked there — it's that second, dangerous case that the test covers.
A test also proves on the filesystem that the attack target is never created: after a criar_nota attempt with slug="../vazamento", the base directory remains empty and the file outside it doesn't exist.
Additionally: no API keys, no network access, and the server never reads or writes outside the configured directory.
Tests
$ python3 -m pytest tests/ -q
............................................. [100%]
45 passed in 1.48sJust the path traversal tests:
$ python3 -m pytest tests/ -q -k traversal
................. [100%]
17 passed, 28 deselected in 0.67sThe suite covers, in order:
Sanitization — 13 parameterized malicious inputs (
../../etc/passwd,/etc/passwd,..\\..\\windows\\system32\\config\\sam,C:\Windows\win.ini,nota\x00.md, empty string…), plus the on-disk proof that nothing is created outside the base.MCP surface —
list_toolsreturns exactly the seven tools, and the schemas (required,type,default,outputSchema) are the ones generated from the type hints and docstrings.Real call of each tool — creation with persistence verified on disk, duplicate, read, read of nonexistent, update, update with
anexar, listing with and without tag filter, search with ranking and with limit, statistics, and removal.Resources —
list_resources,list_resource_templates, reading the JSON index, reading an individual note, and the two traversal forms.Prompts —
list_prompts,get_promptfor both prompts, checking that the note's content is actually embedded and that the starting note doesn't appear in the catalog of the others.End-to-end session —
create_connected_server_and_client_sessionstarts a connected MCP client and server in memory; the test lists tools, creates a note, lists, reads a resource, gets a prompt, and confirmsisError: Trueon the traversal attempt.Isolated storage — front matter round-trip and non-note files being ignored in listing.
Verification status
Everything below was run in this environment, with mcp 1.27.0, pytest 9.1.1, and pytest-asyncio 1.4.0 under Python 3.11.
✅ Verified
python3 -m pytest tests/ -q→ 45 passed.The seven tools actually called via
FastMCP.call_tool, with results checked.Both resources read via
FastMCP.read_resource; both prompts viaFastMCP.get_prompt.Full in-memory client↔server MCP session with
mcp.shared.memory.create_connected_server_and_client_session.Real stdio handshake: server started as a subprocess (
python3 -m mcp_notas) and aClientSessionfrom the SDK runninginitialize,list_tools, andcall_toolthrough it.Path traversal rejected in
sanitizar_slug, in the tool, in the resource, and on the filesystem.MCP_NOTAS_DIRhonored: the created note appeared in the directory pointed to by the variable.All outputs shown in this README were copied from real runs.
⚠️ Not tested
The
mcpServersblock has not been tested against a real MCP client (Claude Desktop, editors, etc.). No client is installed in this environment; what substitutes for that verification is the programmatic stdio handshake described above.The
sseandstreamable-httptransports exist inFastMCP.run, but this project only exercisesstdio.No concurrency tests: simultaneous writes to the same note are not coordinated by a lock.
No tests on Windows or macOS — Linux only.
License
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