Skip to main content
Glama
nadimhoss

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