notes-mcp

notes-mcp

An MCP server that stores notes as Markdown files on your machine, enabling you to save, search, and manage notes through natural language with Claude Code or Claude Desktop.

Category
Visit Server

README

notes-mcp: Virtual Sticky Notes

for Mac

CI

An MCP server that keeps notes on your own machine, stored as plain Markdown files. Connect it to Claude Code or Claude Desktop and you can say "save a note about this" or "what did I write down about the migration?" in the middle of a conversation.

What it exposes

MCP servers offer three kinds of things to a client. This one uses all three, because they do different jobs:

  • Tools are verbs. They are actions the model decides to take. Writing and deleting notes belongs here.
  • Resources are nouns. They are content the client can pull into context, addressed by URI. The model does not have to "decide" to call them; you or the client can attach them directly.
  • Prompts are reusable message templates the user invokes deliberately, usually from a menu.

Tools

Tool Arguments Returns
add_note title, content, tags The new note's id
list_notes tag (optional) Every note's id, title, and tags
search_notes query, tags, match_all Matching notes with a short snippet
get_note note_id One note in full, including its body
delete_note note_id Confirmation

search_notes matches query against note titles and bodies, case-insensitively, and ANDs that with the tag filter. match_all decides whether a note needs every tag listed or just one of them.

Resources

URI Contents
notes://all An index of every note: id, title, tags
notes://tags Every tag in use, with how many notes carry it
note://{note_id} One note's full Markdown source

note://{note_id} is a resource template — the client fills in {note_id} to read a specific note, rather than the server listing all of them up front.

Prompt

summarize_notes(tag) gathers your notes, groups them by theme, and pulls out anything that looks like a task or an open question. Pass a tag to narrow it, or leave it empty for everything.

Setup

Requires uv and Python 3.10+.

git clone https://github.com/pdegner/notes-mcp.git
cd notes-mcp
uv sync

Claude Code

claude mcp add notes -- uv --directory /full/path/to/notes-mcp run notes-mcp

Claude Desktop

Add this to claude_desktop_config.json:

{
  "mcpServers": {
    "notes": {
      "command": "uv",
      "args": ["--directory", "/full/path/to/notes-mcp", "run", "notes-mcp"]
    }
  }
}

Where your notes live

In ~/.notes-mcp/notes/, one Markdown file per note. Set NOTES_MCP_DIR to put them elsewhere.

---
id: 20260730-171920-dentist-appointment
title: Dentist appointment
tags:
- errands
- health
created: '2026-07-30T17:19:20Z'
updated: '2026-07-30T17:19:20Z'
---

Tuesday 3pm, Dr. Okafor.

Because a note is just a Markdown file, you can read, edit, grep, or back it up without this server involved. Hand-edited files are read back fine, including a tags: work, urgent shorthand instead of a YAML list.

Your notes stay on this machine. They live outside this repository, so git never sees them and they are never committed or pushed — the repo holds code only. The notes directory is created 0700 and each note file 0600, so other accounts on the machine cannot read them.

They are stored as plain text, not encrypted. That protects against other local users and against accidentally publishing them; it does not protect against anyone who has your login, admin access to the machine, or a backup that copies your home directory. Treat them like sticky notes on your desk, and don't put passwords in them.

Design notes

A few decisions worth explaining:

List and search return metadata, not bodies. Only get_note returns note text. If list_notes returned full bodies, a directory of a few hundred notes would flood the model's context on a single call. Search returns a short snippet around the match instead, which is enough for the model to pick the right note and then ask for it.

Note ids are validated before they touch the filesystem. Ids arrive as tool arguments, which means they come from a model and are untrusted input. Every id is checked against ^[a-z0-9][a-z0-9-]*$ before it is turned into a path, so ../../etc/passwd is rejected rather than resolved. Writes go to a temp file and are then atomically renamed, so an interrupted write can't truncate an existing note.

Nothing prints to stdout. On the stdio transport, file descriptor 1 carries the JSON-RPC message stream, so anything else written there corrupts the framing. The Python SDK does defend against this — mcp.server.stdio claims fd 1 for the transport and repoints the process's own stdout at stderr — but that only happens when the SDK opens the stream itself, and other MCP implementations don't all do it. Treating stdout as reserved and sending logs to stderr is the portable discipline rather than a workaround for one library's behavior.

Development

uv run pytest          # 59 tests
uv run ruff check .    # lint
uv run ruff format .   # format

License

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured