Overview
Shared Markdown notes for MCP-compatible AI tools.

NoteMCP stores your notes in one place. The web app is for browsing and editing; MCP clients connect to /mcp with an API key so assistants can read, search, and update the same notes. Generate a key and per-tool config snippets on /mcp-config.

Examples
What it looks like when an AI assistant talks to NoteMCP. Notes are only read or written when you explicitly mention NoteMCP.
Capture

Save a debugging session as a note

in Cursor

User says

Save this debugging session to NoteMCP. Title it 'Webhook retry logic — exponential backoff fix'.

Assistant calls

  1. 01save_note

    The assistant writes a self-explanatory title and the full Markdown body.

    input

    {
      "title": "Webhook retry logic — exponential backoff fix",
      "content": "# Problem\n\nRetries hammered the upstream during transient 5xx.\n\n# Fix\n\nSwitched to exponential backoff with jitter; capped at 5 attempts."
    }

    content

    Saved note 'Webhook retry logic — exponential backoff fix' (8f3a…b1)
    
    ## Result
    - id: 8f3a…b1
    - title: Webhook retry logic — exponential backoff fix
    - created_at: 2026-05-28T10:14:02Z
    - updated_at: 2026-05-28T10:14:02Z

Outcome

The note is now stored once and reachable from every AI tool you use.

Recall across tools

Resume work in a different AI assistant

in Claude Code (later, on a different machine)

User says

What did I save in NoteMCP about webhook retries?

Assistant calls

  1. 01search_notes

    Typo-tolerant search — the user's phrasing is passed verbatim.

    input

    { "query": "webhook retries" }

    content

    Found 1 note(s).
    
    ## Matches (1)
    
    ### Match 1
    - id: 8f3a…b1
    - title: Webhook retry logic — exponential backoff fix
    - snippet: <b>Retries</b> hammered the upstream during transient 5xx…
    - created_at: 2026-05-28T10:14:02Z
    - updated_at: 2026-05-28T10:14:02Z
  2. 02get_note

    Assistant fetches the full Markdown body to ground its response.

    input

    { "id": "8f3a…b1" }

Outcome

Your future self gets the same context your past self had — across tools, sessions, and devices.

Iterate

Append today's learnings to an existing note

in Codex CLI

User says

Update my NoteMCP note on webhook retries with today's incident learnings.

Assistant calls

  1. 01search_notes

    Resolve the note's id from a natural-language description.

    input

    { "query": "webhook retries" }
  2. 02get_note

    Fetch the current body so the assistant can extend it without losing anything.

    input

    { "id": "8f3a…b1" }
  3. 03update_note

    Replaces the body in-place. update_note is destructive but idempotent — clients only auto-confirm safe tools.

    input

    {
      "id": "8f3a…b1",
      "content": "# Problem\n…\n\n# 2026-05-28 follow-up\n\nObserved tail latency on retry #3. Adding circuit breaker."
    }

    content

    Updated note 'Webhook retry logic — exponential backoff fix' (8f3a…b1)
    
    ## Result
    - id: 8f3a…b1
    - title: Webhook retry logic — exponential backoff fix
    - updated_at: 2026-05-28T17:42:11Z

Outcome

Your evolving knowledge stays in one place instead of fragmenting across chats.

Browse

Surface recently touched notes

in Any MCP client

User says

What were my 5 most recently updated NoteMCP notes?

Assistant calls

  1. 01list_notes

    Notes are returned ordered by most-recently-updated first.

    input

    { "limit": 5 }

    content

    2 note(s), most recent first.
    
    ## Notes (2)
    
    ### Note 1
    - id: 8f3a…b1
    - title: Webhook retry logic — exponential backoff fix
    - created_at: 2026-05-28T10:14:02Z
    - updated_at: 2026-05-28T17:42:11Z
    
    ### Note 2
    - id: 21de…a4
    - title: Q2 OKR notes
    - created_at: 2026-05-01T13:00:00Z
    - updated_at: 2026-05-27T08:09:00Z

Outcome

Great for catching up at the start of a work session.

Clean up

Permanently delete a note

in Cursor

User says

Delete my NoteMCP note about that one-off webhook test.

Assistant calls

  1. 01search_notes

    Resolve the id by description before deleting.

    input

    { "query": "one-off webhook test" }
  2. 02delete_note

    delete_note carries the destructive hint, so MCP clients typically prompt for explicit user confirmation before calling it.

    input

    { "id": "44ee…0c" }

    content

    Deleted note 44ee…0c
    
    ## Result
    - id: 44ee…0c
    - deleted: true

Outcome

Hard delete — there's no undo, so the assistant is trained to confirm first.

MCP tools
Six tools on the /mcp endpoint. All require a valid Bearer token. Descriptions, inputs, and outputs match the live MCP server.
save_noteSave note
no read-onlyno destructiveno idempotentno open-world

Description

Only use when the user explicitly asks to save to NoteMCP. Create a new Markdown note and return id/title/timestamps. Title is required: write a specific, descriptive title so the note's subject is clear from the title alone (not generic labels like 'Note', 'Meeting', or 'Saved'). Returns the new note id so the caller can immediately update or delete it.

Input

title: string (required) — Required. A specific, descriptive title that summarizes the note so readers understand the content from the title alone (e.g. 'Q2 sprint retro: auth latency fix and deploy checklist', not 'Retro' or 'Note').
content: string (required) — Markdown body. Min length 1, max length 100000 characters.

Output (success)

content (markdown/text)

Saved note '{title}' ({id})

## Result
- id: <uuid>
- title: <string>
- created_at: <ISO-8601>
- updated_at: <ISO-8601>

Output (error)

{ "isError": true, "content": [{ "type": "text", "text": "<error message>" }] }
get_noteGet note
read-onlyno destructiveidempotentno open-world

Description

Only use when the user explicitly asks to read from NoteMCP. Returns the full Markdown body of the note. If id is not known, call search_notes first. IMPORTANT: Always present the complete note content to the user exactly as returned — do not summarize, truncate, or omit any part of the note.

Input

id: string (UUID) (required) — The note's UUID. Obtain from list_notes or search_notes.

Output (success)

content (markdown/text)

## Note
- id: <uuid>
- title: <string>
- created_at: <ISO-8601>
- updated_at: <ISO-8601>

### Content
<full note body (Markdown)>

Output (error)

{ "isError": true, "content": [{ "type": "text", "text": "Note not found" }] } — or another error message
list_notesList notes
read-onlyno destructiveidempotentno open-world

Description

Only use when the user explicitly asks to list NoteMCP notes. Returns the user's notes ordered by most-recently-updated first. Use search_notes to find a specific note by content.

Input

limit: number (integer) (optional, default 10) — 1–50. Defaults to 10 when omitted.

Output (success)

content (markdown/text)

{n} note(s), most recent first.

## Notes ({n})

### Note 1
- id: <uuid>
- title: <string>
- created_at: <ISO-8601>
- updated_at: <ISO-8601>

Output (error)

{ "isError": true, "content": [{ "type": "text", "text": "<error message>" }] }
search_notesSearch notes
read-onlyno destructiveidempotentno open-world

Description

Only use when the user explicitly asks to search or resolve a NoteMCP note. Pass the user query verbatim (including typos) — search is typo-tolerant. Matches both title and body. Each result includes a highlighted snippet. Query must be at least 3 characters; limit defaults to 10.

Input

query: string (required) — Min length 3. Pass the user's query verbatim (including typos).
limit: number (integer) (optional, default 10) — 1–50.

Output (success)

content (markdown/text)

Found {n} note(s).

## Matches ({n})

### Match 1
- id: <uuid>
- title: <string>
- snippet: <string>
- created_at: <ISO-8601>
- updated_at: <ISO-8601>

Output (error)

{ "isError": true, "content": [{ "type": "text", "text": "<error message>" }] }
update_noteUpdate note
no read-onlydestructiveidempotentno open-world

Description

Only use when the user explicitly asks to update a NoteMCP note. Replaces title and/or body in place — provide only the field(s) you want to change. To append text, fetch with get_note first and re-send the combined body. Requires id; call search_notes first if unknown. At least one of title or content must be provided.

Input

id: string (UUID) (required) — The note's UUID. Obtain from list_notes or search_notes.
title: string (optional) — New title (trimmed, min length 1).
content: string (optional) — New Markdown body to replace the existing body. Max length 100000 characters.

Output (success)

content (markdown/text)

Updated note '{title}' ({id})

## Result
- id: <uuid>
- title: <string>
- updated_at: <ISO-8601>

Output (error)

{ "isError": true, "content": [{ "type": "text", "text": "Provide title or content." }] } — or another error message
delete_noteDelete note
no read-onlydestructiveidempotentno open-world

Description

Only use when the user explicitly asks to delete from NoteMCP. Permanently deletes the note — there is no undo. Confirm with the user before calling. Requires resolved id and explicit user intent.

Input

id: string (UUID) (required) — The note's UUID. Obtain from list_notes or search_notes.

Output (success)

content (markdown/text)

Deleted note {id}

## Result
- id: <uuid>
- deleted: true

Output (error)

{ "isError": true, "content": [{ "type": "text", "text": "Note not found" }] } — or another error message
share_noteShare note
no read-onlyno destructiveidempotentopen-world

Description

Only use when the user explicitly asks to share a NoteMCP note by email. Grants read-only access to the given email addresses; recipients with a NoteMCP account under those emails will see the note alongside their own and receive a notification email. Re-sharing the same email is a no-op. Public link sharing is web-only and not available here. Requires a resolved note id and explicit user intent.

Input

id: string (UUID) (required) — The note's UUID. Obtain from list_notes or search_notes.
emails: string[] (email) (required) — 1–50 email addresses to grant read-only access.

Output (success)

content (markdown/text)

Shared note '{title}' with {n} recipient(s)

## Result
- id: <uuid>
- shared_with: <comma-separated emails>

Output (error)

{ "isError": true, "content": [{ "type": "text", "text": "<error message>" }] }