Skip to main content
Glama
withsivram

GoodNotes MCP Server

by withsivram
README.md
# GoodNotes MCP Server

An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that reads handwritten notes from [GoodNotes](https://www.goodnotes.com/) on macOS. It exposes your handwritten notebooks as structured data that AI assistants like Claude can read, search, and process.

## How It Works

GoodNotes stores all data in local SQLite databases on macOS:

- **`projection.sqlite`** — document metadata (names, folders, page ordering)
- **`fts.sqlite`** — full-text search index with OCR'd handwriting (multiple recognition candidates per word)

This MCP server reads those databases (read-only) and exposes 6 tools:

| Tool | Description |
|------|-------------|
| `list_notebooks` | List all notebooks with IDs, page counts, dates |
| `read_notebook` | Read OCR text from a notebook (supports page ranges) |
| `read_page` | Read OCR text from a single page |
| `search_notes` | Full-text search across all handwritten notes |
| `get_unprocessed` | Find new/changed pages since last processing |
| `mark_processed` | Mark pages as processed (for pipeline workflows) |

### OCR Candidate Format

GoodNotes OCR produces multiple word candidates. The server returns them separated by `|`:

```
Temple|Tomple|temple Voice|Voica recognition
```

Your AI assistant picks the best word using semantic context — much more accurate than taking the top candidate alone.

## Requirements

- macOS (GoodNotes stores its databases locally)
- [GoodNotes](https://www.goodnotes.com/) installed and synced
- Python 3.11+
- [uv](https://docs.astral.sh/uv/) (recommended) or pip

## Installation

```bash
git clone https://github.com/withsivram/goodnotes-mcp.git
cd goodnotes-mcp
uv venv && uv pip install -e .
```

## Configuration

### Claude Code

Add to your Claude Code MCP settings:

```json
{
  "mcpServers": {
    "goodnotes": {
      "type": "stdio",
      "command": "/path/to/goodnotes-mcp/.venv/bin/python",
      "args": ["/path/to/goodnotes-mcp/server.py"]
    }
  }
}
```

### Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "goodnotes": {
      "command": "/path/to/goodnotes-mcp/.venv/bin/python",
      "args": ["/path/to/goodnotes-mcp/server.py"]
    }
  }
}
```

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `GOODNOTES_DB_DIR` | `~/Library/Containers/com.goodnotesapp.x/Data/Library/Databases` | Path to GoodNotes SQLite databases |
| `GOODNOTES_TRACKING_FILE` | `~/.goodnotes-mcp/processed.json` | Path to processing state file |

## Usage

Once configured, your AI assistant can:

1. **List your notebooks**: "What notebooks do I have in GoodNotes?"
2. **Read notes**: "Read my latest notebook"
3. **Search**: "Search my handwritten notes for 'meeting action items'"
4. **Process pipeline**: Use `get_unprocessed` + `mark_processed` to build automated workflows (e.g., handwriting → structured Obsidian notes)

### Example: Processing Notes into Obsidian

The server is designed as a minimal data pipe — all intelligence (OCR resolution, categorization, structuring) happens in the AI assistant. A typical workflow:

1. `get_unprocessed` → find new pages
2. `read_notebook` → get raw OCR with word candidates
3. AI resolves `Temple|Tomple|temple` → "Temple" using context
4. AI categorizes and structures into markdown
5. Write to Obsidian (or any markdown-based system)
6. `mark_processed` → track what's been handled

## Architecture

```
iPad (GoodNotes) → iCloud Sync → macOS SQLite DBs → MCP Server → AI Assistant
```

The server is intentionally minimal (~390 lines, zero external dependencies beyond `mcp`). All intelligence lives in the AI layer, making the server easy to maintain and extend.

## License

MIT License — see [LICENSE](LICENSE).

TDQS

A4/5.0

Scored across 6 tools

Disambiguation3/5

read_notebook and read_page overlap significantly, as read_notebook can read a single page using page_start/page_end, causing potential misselection. The other tools (list, get_unprocessed, search, mark_processed) are clearly distinct.

Naming Consistency4/5

Tool names follow a snake_case verb_noun pattern, but verbs vary (list, read, get, search, mark) and 'get_unprocessed' and 'mark_processed' use adjectives rather than clear nouns, creating minor inconsistency.

Tool Count5/5

With 6 tools, the set is well-scoped for the server's purpose of reading, searching, and tracking GoodNotes content. It is neither too sparse nor overly heavy.

Completeness4/5

The core workflow of listing notebooks, reading pages, searching, retrieving unprocessed pages, and marking them processed is covered. Minor gaps include redundancy between read_notebook/read_page and no explicit tool for listing page IDs independently.

Maintenance

ActivityInactive
ResponsivenessNo issues