Skip to main content
Glama
Vontk

notes-mcp

by Vontk
README.md
# notes-mcp

A remote MCP server that captures notes into a Git-backed Markdown vault (Obsidian-friendly). Deploy your own instance to Cloudflare Workers in one click.

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)

[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/Vontk/notes-mcp)

## What & why

`notes-mcp` lets any MCP client — Claude, Claude Desktop, Cursor, and others — append and create Markdown notes directly in a GitHub repository. It's designed for an [Obsidian](https://obsidian.md/) vault synced via git ([obsidian-git](https://github.com/Vinzent03/obsidian-git)): capture a thought from a chat with Claude, and it shows up in your vault the next time Obsidian pulls.

Each deployment is single-tenant — you run your own instance, pointed at your own notes repo, protected by your own auth token. Nothing is hardcoded; everything is configuration.

## Features

- **Five focused tools**: append to a note, create/overwrite a note, append to today's daily note, read a note, list a directory.
- **Single-file edge deploy** — one Cloudflare Worker, no servers to manage.
- **Runs on Cloudflare's free tier.**
- **Works with any Git-backed Markdown vault**, not just Obsidian.
- **Token-guarded** — every request to `/mcp` requires a bearer token you control.

## How it works

```
MCP client  →  Worker (/mcp)  →  GitHub Contents API  →  your notes repo  →  Obsidian Git pulls it
```

Your MCP client calls a tool (e.g. `append_note`). The Worker authenticates the request, then reads/writes the target file through the GitHub Contents API, which creates a commit. The note appears on your other devices the next time Obsidian Git pulls — tune its auto-pull / pull-on-startup settings for how fresh you want that to be.

## Prerequisites

- A [Cloudflare](https://dash.cloudflare.com/sign-up) account (free tier is enough).
- A GitHub repository to hold your notes (can be the same repo as your Obsidian vault).
- A fine-grained GitHub Personal Access Token scoped to that repo.

## Create the PAT

1. Go to [github.com/settings/personal-access-tokens](https://github.com/settings/personal-access-tokens).
2. Click **Fine-grained tokens** → **Generate new token**.
3. Set **Resource owner** to yourself (or the org that owns your notes repo).
4. Under **Repository access**, choose **Only select repositories** and pick your notes repo.
5. Under **Repository permissions**, set **Contents** to **Read and write**. Leave everything else as **No access**.
6. Generate the token and copy it — you'll only see it once.

## Deploy

### A. One-click deploy

1. Click the **Deploy to Cloudflare** button above.
2. Cloudflare will fork/clone this repo and prompt you for the two secrets below (`GITHUB_PAT`, `MCP_AUTH_TOKEN`) during setup.
3. After deploying, open your Worker in the Cloudflare dashboard and set the `vars` (`GITHUB_OWNER`, `GITHUB_REPO`, etc.) under **Settings → Variables**.

### B. CLI

```bash
git clone https://github.com/Vontk/notes-mcp.git
cd notes-mcp
npm install

# Edit the "vars" block in wrangler.jsonc for your repo/timezone/etc.

npx wrangler secret put GITHUB_PAT
npx wrangler secret put MCP_AUTH_TOKEN   # e.g. generate with: openssl rand -hex 32

npm run deploy
```

## Configuration

| Name | Kind | Required | Description | Example |
|---|---|---|---|---|
| `GITHUB_OWNER` | var | yes | GitHub username or org that owns the notes repo. | `Vontk` |
| `GITHUB_REPO` | var | yes | Name of the notes repo. | `my-vault` |
| `GITHUB_BRANCH` | var | no | Branch to commit to. Empty string uses the repo's default branch. | `main` |
| `DEFAULT_NOTE_PATH` | var | no | File used by `append_note` when no `path` is given. Defaults to `inbox.md`. | `inbox.md` |
| `TIMEZONE` | var | no | IANA timezone used to compute daily note dates. Defaults to `UTC`. | `America/Argentina/Buenos_Aires` |
| `GITHUB_PAT` | secret | yes | Fine-grained PAT with **Contents: Read and write** on the notes repo only. | *(set via `wrangler secret put`)* |
| `MCP_AUTH_TOKEN` | secret | yes | Shared secret required on every request to `/mcp`. | *(set via `wrangler secret put`)* |

## Connect to Claude

**Claude.ai (custom connector):**

1. Go to [claude.ai/customize/connectors](https://claude.ai/customize/connectors).
2. Click **+ Add custom connector**.
3. URL: `https://<your-worker>.<your-subdomain>.workers.dev/mcp`
4. Pass your token either as an `Authorization: Bearer <MCP_AUTH_TOKEN>` header, or by appending `?token=<MCP_AUTH_TOKEN>` to the URL.

**Claude Desktop / other clients (via `mcp-remote`):**

```json
{
  "mcpServers": {
    "notes-mcp": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://<your-worker>.<your-subdomain>.workers.dev/mcp?token=<MCP_AUTH_TOKEN>"
      ]
    }
  }
}
```

## Tools

| Tool | Params | What it does | Example |
|---|---|---|---|
| `append_note` | `path?`, `text` | Appends a line to a note, creating it if needed. `path` defaults to `DEFAULT_NOTE_PATH`. | Append "buy milk" to `inbox.md`. |
| `create_note` | `path`, `content` | Creates a note, or overwrites it if it already exists. | Write a new `Projects/notes-mcp.md`. |
| `append_daily` | `text`, `folder?` | Appends a line to today's daily note (`<folder><YYYY-MM-DD>.md`). | Append "stand-up notes" to `Daily/2026-08-25.md`. |
| `read_note` | `path` | Returns the contents of a note. | Read `inbox.md`. |
| `list_notes` | `path?` | Lists files and folders in a directory (repo root if omitted). | List `Daily/`. |

## Security

- Each deployment is single-tenant: one Worker, one notes repo, one token.
- Keep `MCP_AUTH_TOKEN` secret — anyone with it can read and write to your notes repo through this Worker.
- Scope `GITHUB_PAT` to exactly one repository with **Contents: Read and write** only.
- Never commit secrets. `.dev.vars` and `.wrangler` are gitignored; use `.dev.vars.example` as a template.
- To rotate a credential, generate a new value and run `wrangler secret put <NAME>` again — it overwrites the existing secret immediately.

## Local dev

```bash
npm start
```

Test it with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):

```bash
npx @modelcontextprotocol/inspector
```

Point it at `http://localhost:8787/mcp` with your `MCP_AUTH_TOKEN` from `.dev.vars`.

## Testing / CI

```bash
npm run typecheck
npm run lint
npm test
```

`ci.yml` runs all three on every pull request and push. `deploy.yml` deploys to Cloudflare on every push to `main`, once those checks pass — it needs two repo secrets under **Settings → Secrets and variables → Actions**:

- `CLOUDFLARE_API_TOKEN`
- `CLOUDFLARE_ACCOUNT_ID`

## Contributing

Issues and pull requests are welcome. Please run `npm run lint`, `npm run typecheck`, and `npm test` before submitting.

## License

[MIT](./LICENSE)