mcp-notes-server
by nadimhoss
README.md
# mcp-notes-server
An [MCP](https://modelcontextprotocol.io) server that gives an AI agent a searchable
markdown notes vault: six tools over a flat directory of `.md` files with YAML-style
frontmatter.
Notes stay plain markdown on disk. Nothing is locked in a database, so the same files
work with your editor, `grep`, and git.
```
~/notes/
├── pad-thai.md
├── sourdough-starter.md
└── weekly-review.md
```
## Tools
| Tool | Description |
| --- | --- |
| `create_note` | Create a note. Returns the generated slug. |
| `read_note` | Read one note in full, by slug. |
| `list_notes` | Summaries (no bodies), newest first, optionally filtered by tag. |
| `search_notes` | Ranked full-text search with snippets. |
| `update_note` | Patch title / body / tags. The slug never changes. |
| `delete_note` | Delete a note by slug. |
## Install
```bash
npm install
npm run build
```
## Use it with an MCP client
Add it to your client's server config — for Claude Desktop, `claude_desktop_config.json`:
```json
{
"mcpServers": {
"notes": {
"command": "node",
"args": ["/absolute/path/to/mcp-notes-server/dist/src/index.js", "--vault", "/absolute/path/to/notes"]
}
}
}
```
The vault directory is resolved in this order: `--vault <dir>`, then `$NOTES_VAULT`,
then `~/notes`. It is created on startup if it does not exist.
## Design notes
**Slugs are the identity, and they are validated.** A note lives at `<vault>/<slug>.md`,
and every slug is checked against `/^[a-z0-9]+(?:-[a-z0-9]+)*$/` before it reaches the
filesystem. That is what makes `read_note({slug: "../../.ssh/id_rsa"})` impossible rather
than merely unlikely — an allowlist, not an escaping pass. Slugs are also stable across
updates: renaming the file when the title changes would invalidate any slug the model is
still holding from an earlier tool call.
**Tool errors are data, not exceptions.** "No note with slug X" comes back as a normal
tool result with `isError: true`, so the model reads it and corrects itself. If it were
thrown, the client would see a protocol error, which the model cannot recover from.
Genuine bugs (anything that is not a `VaultError`) are still thrown, so they stay loud.
**Search is weighted term frequency.** A term in the title counts triple and a term in a
tag counts double, so searching `sourdough` ranks a note *about* sourdough above one that
mentions it in passing. It is deliberately simple — no index to keep in sync, and a vault
of a few thousand notes scans in milliseconds.
**The vault knows nothing about MCP.** `src/vault.ts` is plain filesystem code, `src/server.ts`
is the MCP binding, and `src/index.ts` is the stdio entry point. That split is why the
vault can be tested directly and the server can be tested through a real MCP client over
an in-memory transport, with no subprocess and no mocks.
## Development
```bash
npm test # 46 tests, vitest
npm run typecheck # tsc --noEmit
npm run build # emit to dist/
```
Tests cover the vault directly (`test/vault.test.ts`) and the server end to end through a
real MCP `Client` over `InMemoryTransport` (`test/server.test.ts`), so tool schemas,
argument validation, and result shapes are all exercised — not just the logic behind them.
## License
MIT
TDQS
A4.4/5.0
Scored across 6 tools
Disambiguation5/5
Each tool has a distinct purpose: create, read, list, search, update, and delete notes. No two tools overlap in functionality, and the descriptions clearly differentiate them.
Naming Consistency5/5
All tool names follow a consistent verb_noun pattern (create_note, read_note, list_notes, search_notes, update_note, delete_note), making the API predictable and easy to navigate.
Tool Count5/5
With 6 tools covering the full CRUD lifecycle plus listing and search, the set is well-scoped for a notes server. No redundant or missing tools; each earns its place.
Completeness5/5
The tool surface provides complete coverage for note management: create, read, list, search, update, and delete. There are no obvious dead ends or missing operations for the intended domain.
Maintenance
ActivityMaintained
ResponsivenessSyncing