Skip to main content
Glama
JackieNonSense

Notes MCP Server

README.md
# inktrace-mcp-template

A minimal remote MCP (Model Context Protocol) server you can run in ten minutes: Hono, the official MCP SDK over streamable HTTP, token auth with a constant-time comparison, Zod-validated tools, and Postgres via Drizzle. The domain is a toy `notes` table, but the structure is the one I run in production for [InkTrace](https://www.inktrace.app) — this repo is a clean-room extraction of that pattern, written for reading. The full design write-up is in the [inktrace-showcase](https://github.com/JackieNonSense/inktrace-showcase) repo.

## What it demonstrates

- **Stateless streamable HTTP.** A fresh `McpServer` + transport per request, so the same code runs on a laptop, a container, or a serverless function with nothing to desynchronize. See `src/index.ts` — the whole transport dance is five lines.
- **Token auth done carefully.** SHA-256 both sides, then `timingSafeEqual` (`src/auth.ts`). Hashing first equalizes buffer lengths so a wrong-length guess can't throw. The secret is accepted as a Bearer header (preferred) or a URL path segment for connector UIs that can't send headers — with a comment explaining why the header variant is preferred (URLs end up in logs).
- **Policy in the protocol.** Write tools carry a "requires prior explicit user approval" prefix and the server `instructions` state the rules of engagement, so a well-behaved client asks the user before mutating anything (`src/mcp/server.ts`, `src/mcp/tools.ts`).
- **Snapshot before destructive writes.** `write_note` versions the current content before overwriting it and prunes history to the newest 20 (`snapshotBeforeEdit` in `src/mcp/tools.ts`). Undo is a data-model feature, not a support ticket.
- **Zod on every tool input**, and a `safe()` wrapper that turns thrown exceptions into MCP tool errors instead of protocol crashes.
- **No delete tool.** The worst realistic failure of a confused model is an unwanted addition — visible and reversible — not silent destruction.

## Quickstart

You need Node 20+ and a Postgres database.

```bash
npm install
cp .env.example .env        # fill in DATABASE_URL and MCP_SECRET
npm run db:push             # create the notes / note_versions tables
npm run dev                 # http://localhost:3001/mcp
```

Connect from Claude Code:

```bash
claude mcp add --transport http notes http://localhost:3001/mcp \
  --header "Authorization: Bearer <your MCP_SECRET>"
```

Then ask Claude to `list_notes`, have it draft something, approve it, and watch `write_note` create the row — and snapshot before any edit.

## Layout

```
src/
├── index.ts        # Hono app, health check, MCP mount, stateless transport
├── auth.ts         # timing-safe token middleware (Bearer or path segment)
├── db.ts           # postgres.js + Drizzle, serverless-friendly pool size
├── schema.ts       # notes + note_versions
└── mcp/
    ├── server.ts   # server identity + usage-policy instructions
    └── tools.ts    # 3 read tools + 1 write tool with pre-write snapshot
```

## What the production version adds

Since this template is deliberately single-file-per-concern, it's worth naming what the real InkTrace server layers on top: per-tool project-ownership checks, Postgres row-level security pinned per request as a second authorization layer, HTML sanitization on rich-text input, batch caps on bulk operations, and rate limiting inherited from the surrounding API. The auth gate here is single-tenant by design; the multi-user path is OAuth, which replaces `tokenAuth` without touching any tool.

## License

MIT — take the structure, replace the notes.