Skip to main content
Glama
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.