notes-mcp
README.md
# notes-mcp
A note-taking MCP server. Notes are dated, and the same process also exposes
a plain REST API and an HTML view — so it can run locally as a stdio MCP
server, or be hosted as a normal web service.
## Storage
SQLite by default (`NOTES_DB_PATH`, defaults to `./data/notes.db`) — fine for
local/stdio use and for hosts with a persistent disk.
Set `DATABASE_URL` to a Postgres connection string instead, and the server
switches to Postgres automatically — needed on platforms with no persistent
disk for the app container itself (e.g. Render's free web services, see
below). Same schema, same behavior, just a different backend.
## MCP tools
- `add_note(content, date?)` — add a note; `date` defaults to today (`YYYY-MM-DD`)
- `delete_note(note_id)` — delete a note by id
- `get_notes_for_date(date?)` — notes for a given date, defaults to today
- `list_all_notes()` — every note, most recent date first
- `view_notes_html(date?)` — returns an HTML page of all notes, grouped by
date, with the given/today's date highlighted
## Auth (HTTP mode)
When running with `MCP_TRANSPORT=http`, every request — the REST API and the
`/mcp` endpoint alike — requires `Authorization: Bearer <NOTES_API_TOKEN>`.
`/healthz` is the only unauthenticated route (for container health checks).
The server refuses to start over HTTP if `NOTES_API_TOKEN` isn't set.
Generate a token and keep it secret, e.g.:
```bash
python -c "import secrets; print(secrets.token_urlsafe(32))"
```
Stdio mode (local Claude Desktop/Code) has no auth — the client already
trusts it by spawning the process directly, and there's no network exposure.
## REST API
Available when running with `MCP_TRANSPORT=http` (always on in the Docker image).
All requests below need the `Authorization: Bearer <token>` header.
| Method | Path | Body / Query | Description |
|--------|-------------------|-------------------------|----------------------------------|
| POST | `/api/notes` | `{"content", "date"?}` | Create a note |
| GET | `/api/notes` | `?date=YYYY-MM-DD` | List all notes, or by date |
| GET | `/api/notes/today` | — | Today's notes |
| GET | `/api/notes/{id}` | — | Get one note |
| DELETE | `/api/notes/{id}` | — | Delete a note |
| GET | `/notes` | `?date=YYYY-MM-DD` | HTML view (highlights that date) |
| GET | `/healthz` | — | Health check |
The MCP endpoint (streamable-http) is mounted at `/mcp` on the same port.
## Local setup (stdio, for Claude Desktop/Code)
```bash
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
.venv\Scripts\python server.py
```
### Register with Claude Code
```bash
claude mcp add notes -- "C:\Users\srjnm\git\notes-mcp\.venv\Scripts\python.exe" "C:\Users\srjnm\git\notes-mcp\server.py"
```
### Register with Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"notes": {
"command": "C:\\Users\\srjnm\\git\\notes-mcp\\.venv\\Scripts\\python.exe",
"args": ["C:\\Users\\srjnm\\git\\notes-mcp\\server.py"]
}
}
}
```
## Run locally over HTTP (MCP + REST, no Docker)
```bash
NOTES_API_TOKEN=devtoken MCP_TRANSPORT=http PORT=8000 .venv\Scripts\python server.py
```
## Docker (hosted deployment)
```bash
docker build -t notes-mcp .
docker run -d --name notes-mcp -p 8000:8000 \
-e NOTES_API_TOKEN=<your-token> \
-v notes-data:/data \
notes-mcp
```
The `notes-data` named volume keeps `notes.db` across container restarts and
upgrades. Point an MCP client at `http://<host>:8000/mcp` (streamable-http)
with an `Authorization: Bearer <your-token>` header, or call the REST API
directly at `http://<host>:8000/api/notes` with the same header.
Claude Code, as a remote MCP client:
```bash
claude mcp add --transport http notes http://<host>:8000/mcp \
--header "Authorization: Bearer <your-token>"
```
## Deploying to Render
Render's free web services have no persistent disk, so this repo talks to
Postgres there instead of the local SQLite file.
1. **Push this repo to GitHub** (Render deploys from a git repo).
2. **Create a Postgres database** in the Render dashboard (New → PostgreSQL).
Free instances expire after 30 days — fine to start with, upgrade to a
paid instance whenever you want the data to stick around longer.
3. **Create a Web Service** from the same repo (New → Web Service → pick this
repo). Render detects the `Dockerfile` automatically.
4. **Set env vars** on the web service (Environment tab):
- `DATABASE_URL` → copy the Postgres instance's *Internal Database URL*
from its dashboard page and paste it here.
- `NOTES_API_TOKEN` → your generated token.
- Leave `PORT` unset — Render injects its own and the app already reads
`PORT` from the environment.
5. Deploy. Render terminates HTTPS for you at `https://<your-service>.onrender.com`,
so there's no Caddy/reverse-proxy step needed (unlike a bare VPS).
Then point clients at `https://<your-service>.onrender.com/mcp` (MCP) or
`.../api/notes` (REST), both with the `Authorization: Bearer <token>` header.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues