mcp-lab-01-notes-server
# mcp-lab-01-notes-server
Nível 01 da trilha **MCP Lab**: primeiro servidor MCP construído com o **SDK oficial** (`mcp` no PyPI). Domínio: um gerenciador de notas e tarefas guardadas como arquivos markdown locais.
Termos técnicos seguem o glossário em [`../CONTEXT.md`](../CONTEXT.md).
## O que este Server faz
5 Tools sobre uma pasta de arquivos `.md`:
| Tool | O que faz |
|---|---|
| `create_note(title, content="", is_task=False)` | Cria uma nota; com `is_task=True` ela nasce como tarefa (`status: pending`) |
| `list_notes()` | Lista todas as notas com título e status |
| `read_note(slug)` | Devolve o corpo completo de uma nota |
| `mark_done(slug)` | Marca uma tarefa como concluída |
| `delete_note(slug)` | Apaga uma nota |
```mermaid
flowchart LR
subgraph server.py [camada de protocolo]
T[Tools do MCPServer]
end
subgraph note.py [domínio]
N[Note, slugify, parse, render]
end
subgraph storage.py [armazenamento]
S[NotesStorage: ler/escrever/apagar .md]
end
T -->|delega| N
T -->|delega| S
N -.->|não conhece| S
S -.->|não conhece| N
```
`server.py` é a única camada que conhece as duas outras. `note.py` (regra de negócio) e `storage.py` (arquivos) não se conhecem entre si — isso é proposital: dá pra trocar onde as notas são guardadas (um banco de dados, por exemplo) sem tocar no que é uma Nota.
## Como rodar
```bash
uv sync
uv run pytest -q # 33 testes
uv run ruff check .
uv run mypy
```
Por padrão as notas vão para `~/.mcp-lab/notes`. Pra usar outra pasta:
```bash
NOTES_SERVER_DIR=/caminho/qualquer uv run notes-server
```
### Usando de verdade no Claude Code
```bash
claude mcp add notes-server -- uv run --directory "$(pwd)" notes-server
```
Depois disso, numa conversa do Claude Code, peça pra ele criar uma nota ou listar suas tarefas — o modelo vai decidir sozinho quando chamar cada Tool.
## Estrutura do código
```
src/notes_server/
├── storage.py → só sabe ler/escrever/apagar arquivos; barra path traversal
├── note.py → o que é uma Note; slugify; frontmatter -> Note e Note -> frontmatter
└── server.py → liga os Tools do MCP ao domínio; ToolError pros erros esperados
```
Um `.md` por camada em [`docs/`](./docs/), explicando o pra-quê/o-quê/como de cada uma:
- [`docs/01-armazenamento.md`](./docs/01-armazenamento.md)
- [`docs/02-dominio-nota.md`](./docs/02-dominio-nota.md)
- [`docs/03-tool-error-e-saida-estruturada.md`](./docs/03-tool-error-e-saida-estruturada.md)
- [`docs/04-injecao-de-dependencia.md`](./docs/04-injecao-de-dependencia.md)
## O que este nível deliberadamente NÃO faz
- Não expõe as notas como **Resources** nem tem **Prompts** prontos — isso é o Nível `02-resources-prompts`.
- Não valida concorrência (dois processos escrevendo a mesma nota ao mesmo tempo) — fora de escopo pra um projeto local de estudo.
- O frontmatter é um parser feito à mão, de propósito (ver `docs/02-dominio-nota.md`) — não é um formato geral de YAML.
## Referência oficial
[modelcontextprotocol.io](https://modelcontextprotocol.io) para a spec do protocolo. [github.com/modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk) para o código-fonte do SDK — o pacote `mcp` mudou de `FastMCP` para `MCPServer` na versão 2.x; se você ler isto muito depois, confira se a API ainda bate com o que está aqui.
TDQS
Scored across 5 tools
Each tool has a clear primary action, and the CRUD operations are distinct. However, create_note doubles as a task creator via is_task, and mark_done applies only to tasks, which could cause slight ambiguity about whether notes also support status changes.
Most tools follow a clear verb_noun pattern (create_note, list_notes, read_note, delete_note), but mark_done breaks the pattern by using verb_adjective instead of verb_noun. This is a minor inconsistency that still leaves the intent understandable.
Five tools is appropriately scoped for a simple notes/tasks server. The set covers the essential operations without unnecessary bloat, making it easy for an agent to navigate.
The set lacks an update_note/edit_note operation, which is a notable gap for a notes domain. It also has no way to unmark a task or filter tasks from notes, limiting full lifecycle management for tasks.