GoodNotes MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@GoodNotes MCP Serverlist my notebooks"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
GoodNotes MCP Server
An MCP (Model Context Protocol) server that reads handwritten notes from GoodNotes 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 all notebooks with IDs, page counts, dates |
| Read OCR text from a notebook (supports page ranges) |
| Read OCR text from a single page |
| Full-text search across all handwritten notes |
| Find new/changed pages since last processing |
| 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 recognitionYour AI assistant picks the best word using semantic context — much more accurate than taking the top candidate alone.
Related MCP server: Apple Notes MCP
Requirements
macOS (GoodNotes stores its databases locally)
GoodNotes installed and synced
Python 3.11+
uv (recommended) or pip
Installation
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:
{
"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:
{
"mcpServers": {
"goodnotes": {
"command": "/path/to/goodnotes-mcp/.venv/bin/python",
"args": ["/path/to/goodnotes-mcp/server.py"]
}
}
}Environment Variables
Variable | Default | Description |
|
| Path to GoodNotes SQLite databases |
|
| Path to processing state file |
Usage
Once configured, your AI assistant can:
List your notebooks: "What notebooks do I have in GoodNotes?"
Read notes: "Read my latest notebook"
Search: "Search my handwritten notes for 'meeting action items'"
Process pipeline: Use
get_unprocessed+mark_processedto 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:
get_unprocessed→ find new pagesread_notebook→ get raw OCR with word candidatesAI resolves
Temple|Tomple|temple→ "Temple" using contextAI categorizes and structures into markdown
Write to Obsidian (or any markdown-based system)
mark_processed→ track what's been handled
Architecture
iPad (GoodNotes) → iCloud Sync → macOS SQLite DBs → MCP Server → AI AssistantThe 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.
Available Tools
6 toolsget_unprocessedA
Get notebooks and pages that haven't been processed yet or changed since last processing.
Compares the tracking file against current FTS index to find new/changed content.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It explains the non-obvious mechanism (comparing a tracking file against the FTS index), which gives meaningful insight into how results are determined. It does not explicitly mention side effects, but the described behavior is read-only in nature.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no filler. The first sentence states the purpose, the second explains the underlying mechanism. Every word earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a simple zero-parameter retrieval tool, and the description fully explains what it returns and how it works. An output schema is present, so return value details are not required. No critical context is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the schema provides complete coverage. The description still adds value by clarifying what 'unprocessed' means in terms of change detection, which is relevant context even though no parameter details are needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Get') with a clear resource ('notebooks and pages') and a distinct scope ('haven't been processed yet or changed since last processing'). This cleanly distinguishes it from sibling tools like list_notebooks, read_notebook, search_notes, and mark_processed.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly conveys when this tool is appropriate: when you need items that are new or changed since last processing. It implies a workflow with mark_processed, though it does not explicitly name alternatives or state exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_notebooksA
List all GoodNotes notebooks with their names, IDs, page counts, and last modified dates.
Filters out templates (documents with epoch-zero timestamps). Returns a formatted list of user notebooks sorted by last modified date.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses two key behaviors: filtering out templates (epoch-zero timestamps) and returning a formatted list sorted by last modified date. The verb 'List' implies a read-only operation, but it does not explicitly mention side effects or authentication requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the core purpose, followed by valuable behavioral details. Every word earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description doesn't need to explain return values. It adds contextual information about filtering and sorting that the schema cannot convey, making the tool's behavior fully understandable for a list operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so the baseline of 4 applies. The description correctly omits parameter details, and the schema already covers all (none) parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action ('List all GoodNotes notebooks') with the exact resource and return fields (names, IDs, page counts, last modified dates). It clearly distinguishes from siblings like read_notebook and read_page by focusing on metadata listing rather than content access.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context for when to use: to get a full list of user notebooks. Mentions filtering out templates and sorting, which clarifies expected behavior. However, it does not explicitly name alternatives or state when not to use, even though the purpose makes it fairly obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
mark_processedA
Mark pages as processed after they've been written to Obsidian.
Args: notebook_id: The document UUID page_ids: Comma-separated page UUIDs to mark as processed
| Name | Required | Description | Default |
|---|---|---|---|
| page_ids | Yes | ||
| notebook_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden for behavioral transparency. It only states the action and does not disclose side effects, idempotency, reversibility, or prerequisites (other than the 'written to Obsidian' context). For a mutation tool, this is a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded, with a one-sentence purpose followed by a clear args list. Every sentence earns its place; there is no fluff or repetition of structured data.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple 2-parameter tool, the description covers the essential purpose and args, and an output schema exists to handle return values. However, it lacks context about side effects, idempotency, or what 'processed' means in the system, which would be expected given no annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds meaningful parameter semantics: it explains that notebook_id is 'the document UUID' and page_ids are 'Comma-separated page UUIDs', which goes beyond the schema's bare titles. This compensates for the 0% schema description coverage, providing crucial format and type information.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action: 'Mark pages as processed after they've been written to Obsidian.' This is a specific verb+resource, and it distinguishes from sibling tools that are all read/list operations. The purpose is unambiguous and contextually relevant.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use the tool ('after they've been written to Obsidian'), giving some contextual guidance. However, it does not explicitly mention alternatives or exclusions, nor does it explain what to do if pages are already processed. It is implied usage rather than explicit guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_notebookA
Read OCR text from a GoodNotes notebook.
Returns the handwriting-recognized text for each page, with multiple word candidates separated by | so Claude can pick the best word from context.
Args: notebook_id: The document UUID from list_notebooks page_start: First page to read (1-indexed, default: 1) page_end: Last page to read (0 = all pages, default: 0)
| Name | Required | Description | Default |
|---|---|---|---|
| page_end | No | ||
| page_start | No | ||
| notebook_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description discloses the key behavior: it returns OCR text for each page, including multiple word candidates separated by '|' to aid context selection. This clarifies output format and implies a read-only operation, though it doesn't cover error handling or edge cases like missing notebook IDs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise: one sentence for purpose, one for return format, and a clean argument list with defaults. No redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, output format, and parameter semantics effectively. Given the presence of an output schema and the simplicity of the read operation, it is complete enough for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, but the description fully compensates by explaining each parameter: notebook_id sourced from list_notebooks, page_start with 1-indexing and default, page_end with special value 0 meaning all pages. This adds meaning beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Read OCR text from a GoodNotes notebook' with a specific verb and resource. It further distinguishes itself by noting it returns text 'for each page' with candidate separators, which is distinct from sibling tools like read_page or list_notebooks.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage via page range parameters and defaults (0 = all pages), but it does not explicitly state when to choose this over read_page or other siblings. No exclusions or alternative tool names are mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_pageA
Read OCR text from a single page of a GoodNotes notebook.
Args: notebook_id: The document UUID page_number: Page number (1-indexed)
| Name | Required | Description | Default |
|---|---|---|---|
| notebook_id | Yes | ||
| page_number | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It indicates a read-only operation via the verb 'Read', but does not explicitly state that it is non-destructive or address edge cases like invalid page numbers or missing notebooks. Some context is given (1-indexed pages), but more detail would improve transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is remarkably concise, consisting of a single sentence plus an Args block. Every sentence earns its place, and the structure is front-loaded with the primary purpose first. No redundant or extraneous information is present.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter read tool, the description is almost complete. It explains both parameters and the core functionality, and an output schema exists to define return values. However, it lacks explicit usage guidance and does not describe error handling or behavioral nuances, which prevents a perfect score.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Since the input schema has no property descriptions (0% coverage), the description must compensate. It does so by explaining notebook_id as 'The document UUID' and page_number as 'Page number (1-indexed)', adding semantic meaning beyond the raw types. This is helpful, though not exhaustive in describing formats or constraints.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Read OCR text from a single page of a GoodNotes notebook.' It specifies the verb (read), the resource (OCR text), and the scope (single page), which distinguishes it from siblings like read_notebook. The purpose is immediately understandable and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool versus alternatives such as read_notebook or search_notes. It does not mention any exclusions or conditions, leaving the agent to infer usage solely from the tool name and basic description.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_notesA
Search across all handwritten notes using case-insensitive text search.
Splits multi-word queries so each word is matched independently — necessary because GoodNotes OCR stores words in separate groups delimited by special chars.
Args: query: Search term (each word matched independently, case-insensitive)
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description discloses key behaviors: case-insensitive matching and splitting multi-word queries, including the reason (GoodNotes OCR word grouping). This adds useful transparency, but it omits potential limitations like lack of exact-phrase search or result ordering.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded with purpose, followed by a necessary behavioral explanation. The Args section is structured and avoids redundancy. Every sentence contributes value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple one-parameter input and presence of an output schema, the description covers the essential behavioral aspects. It could mention result ordering or limits, but these are likely provided by the output schema. It is complete enough for an agent to use the tool effectively.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides no description for 'query' (0% coverage). The description compensates clearly, explaining that each word is matched independently and case-insensitively. This is precise and adds full semantics beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states 'Search across all handwritten notes' with a specific verb and resource, clearly distinguishing it from sibling tools like list_notebooks or read_notebook. This is a clear, non-tautological statement of purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The context is clear: this tool is for searching, not browsing or processing. However, it lacks explicit exclusions or alternatives, such as 'use list_notebooks to browse'. The clear context earns a 4, but the absence of explicit when-not-to-use keeps it from a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
6 tool updates
v0.1.0- First observed
get_unprocessed - First observed
list_notebooks - First observed
mark_processed - First observed
read_notebook - First observed
read_page - First observed
search_notes
TDQS
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.
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.
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.
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
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Search, read, and write your Apple Notes from ChatGPT/Claude via a local Mac agent + MCP relay.
Create, search, and update notes in an xNotepad AI notebook, with semantic search and AI Q&A.
Search your Obsidian vault to quickly find notes by title or keyword, summarize related content, a…
Search your Glasp web and Kindle highlights, notes, and AI memories from any MCP client. Read-only.
Related MCP Servers
- AlicenseDqualityBmaintenanceEnables interaction with Apple Notes via natural language, supporting note creation, search, and retrieval with iCloud integration for seamless note management.31724MIT
- AlicenseAqualityDmaintenanceEnables AI clients to search, read, create, update, move, and delete Apple Notes on macOS via AppleScript automation.104332MIT
- AlicenseAqualityBmaintenanceEnables AI assistants to read, search, and traverse your reMarkable tablet's library, including handwritten notes via OCR.12MIT
- AlicenseAqualityAmaintenanceEnables AI assistants to read, search, and traverse your entire reMarkable library, including handwritten notes via OCR.14219MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/withsivram/goodnotes-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server