your notes are
your LLM's second brain.
outl ships an MCP server. Claude Desktop, Claude Code, Cursor, Zed, ChatGPT — anything that speaks the Model Context Protocol — reads and writes your local markdown the same way you do. No upload. No cloud. No copy-paste.
outl --workspace ~/notes mcp serve
Same binary as the CLI. JSON-RPC over stdio. Protocol revision
2024-11-05.
a second brain that stays yours.
Notion AI, Roam AI, Mem — all of them solve the second-brain problem by shipping your notes to their servers and renting the answer back to you. The "AI second brain" market got "second" right and "yours" backwards.
The .md files stay on your disk. outl is a binary that reads them. The LLM host runs locally too. Nothing is uploaded by outl.
MCP is an open protocol. Today: Claude, Cursor, Zed, ChatGPT. Tomorrow: whatever wins. You don't migrate your second brain when you switch models.
Every MCP tool maps 1:1 to a CLI subcommand. Same handlers, same JSON envelope, same exit codes. No parallel logic to drift.
The flow: you write markdown in your editor. outl runs an MCP server in the background. Your LLM client (Claude Desktop, Cursor, …) connects to it. The model can now read any page, search the graph, append to today's journal, move a block — the same set of operations the CLI exposes. The contract is the same one documented at /docs/cli.
wire it into your
LLM host.
The shape is the same everywhere: register a server whose
command is outl and whose args point at the
workspace. Restart the host. Done.
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json {
"mcpServers": {
"outl": {
"command": "outl",
"args": ["--workspace", "/Users/you/notes", "mcp", "serve"]
}
}
} Restart Claude Desktop. The outl tools show up under the server name. Pin outl://daily/today as a resource and Claude reads today's journal without a tool call.
Claude Code
~/.claude.json (or per-project .mcp.json) {
"mcpServers": {
"outl": {
"command": "outl",
"args": ["--workspace", "/Users/you/notes", "mcp", "serve"]
}
}
} Same JSON shape as Claude Desktop. Inside Claude Code, your notes become tools (outl_page_get, outl_search, etc.) and resources you can @-mention.
Cursor
Settings → MCP → Add Server {
"mcpServers": {
"outl": {
"command": "outl",
"args": ["--workspace", "/Users/you/notes", "mcp", "serve"]
}
}
} Cursor reads MCP servers from the same JSON shape. Once enabled, the composer can grep your notes, append to today's journal, and turn a block into a draft — all without leaving the editor.
Zed
~/.config/zed/settings.json → context_servers {
"context_servers": {
"outl": {
"command": {
"path": "outl",
"args": ["--workspace", "/Users/you/notes", "mcp", "serve"]
},
"settings": {}
}
}
} Zed exposes MCP as context servers. The Assistant panel can pull outl://page/<slug> straight into the conversation.
ChatGPT (desktop)
Settings → Connectors → Add MCP server command: outl
args: ["--workspace", "/Users/you/notes", "mcp", "serve"]
transport: stdio ChatGPT desktop's MCP support is rolling out per region. The transport is stdio, same binary. Once the connector is enabled, ChatGPT can call outl_search and read outl://daily/today.
Any other MCP host
host-specific command: outl
args: ["--workspace", "<absolute path>", "mcp", "serve"]
transport: stdio (JSON-RPC 2.0)
protocol: MCP 2024-11-05 If the host speaks MCP, it speaks outl. Zero special-casing — the server is the same binary, the same handlers, the same workspace lock as the CLI.
verify before you wire it
Pipe two JSON-RPC frames into outl mcp serve. If both come back with valid responses, the server is
healthy.
printf '%s\n%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| outl --workspace ~/notes mcp serve
tools, resources,
prompts.
MCP surfaces three things to the host. Tools the model can call. Resources it can attach as context without a call. Prompts you can pick from a slash menu. outl exposes all three.
tools
Every tool is a 1:1 mirror of a CLI subcommand. Destructive
tools (outl_page_delete, outl_block_delete) require confirm: true — the
server fails closed.
| tool name | what it does |
|---|---|
| outl_page_get | page meta + outline tree |
| outl_page_create | new page with title/icon/tags |
| outl_page_update | patch title/icon |
| outl_page_list | list pages, filter by tag |
| outl_page_render | projected .md (clean, no sidecar) |
| outl_block_get | block content + children |
| outl_block_append | append child to a page or block |
| outl_block_insert | insert after a sibling |
| outl_block_update | edit block text |
| outl_block_move | reparent · cycle-safe |
| outl_block_toggle_todo | None → TODO → DONE → None |
| outl_block_tree | subtree as JSON |
| outl_daily_today | today's journal (creates on first read) |
| outl_daily_get | journal by ISO date |
| outl_daily_append | append to a daily |
| outl_search | full-text across pages + blocks |
| outl_query | structured query on properties/tags |
| outl_tag_list | all tags + counts |
| outl_backlinks | what references this page/block |
| outl_prop_get | read a block property |
| outl_prop_set | write a block property |
| outl_export_md | render a slice of the graph as .md |
resources
URIs the host can attach as context — no explicit tool call.
Pin outl://daily/today in
Claude Desktop and the model sees the day's context every
turn.
| uri | type | body |
|---|---|---|
| outl://workspace/info | application/json | Workspace path, actor id, counts (pages, blocks, ops). |
| outl://daily/today | text/markdown | Today's journal projection. Lazily creates the file the first time it's read — same semantics as outl daily today in the CLI. |
| outl://page/{slug} | text/markdown | Projected markdown for any page by slug. Use it to attach a specific page as context without a tool call. |
prompts
Slash shortcuts the host renders in the prompt picker. Optional sugar over tools.
| prompt | arguments | does |
|---|---|---|
| outl-summarize-day | date? (ISO) | pulls the daily journal, asks the model for a summary. |
| outl-blog-from-block | block_id | expands a single block into a blog draft. |
what you'll actually do with it.
The point isn't "now the LLM knows about your notes". The point is the everyday glue: stop pasting context manually.
morning summary
you're my second brain. Summarize what's open from yesterday and what's on the journal for today. Pull outl://daily/today.
outcome: 1-paragraph status, links to the blocks Claude touched, ready to paste anywhere.
ask your notes
what did I write about the tree-CRDT trade-offs? call outl_search with q: "tree-crdt trade-offs" and quote the blocks.
outcome: search returns matching blocks with their parent page; the LLM cites them inline with stable block IDs.
block → blog draft
expand blk-9f12 into an 800-word blog post in my voice. tone: direct, terminal-flavored.
outcome: outl_block_get pulls the block + children; the model drafts; you keep the source unchanged.
task triage
list everything with status:: todo across the last 14 daily notes and group by tag.
outcome: outl_query filters by property + date range; the model formats the table; no agent loop needed.
append from chat
add this paragraph to today as a child of blk-3a1d.
outcome: outl_block_append writes one op. The CRDT log records it, the .md updates, your editor sees it on next open.
the honest data flow.
- ✓
Reads
.md+ sidecars on your disk. - ✓ Talks JSON-RPC over stdio to whatever process spawned it.
- ✓ Holds an exclusive workspace lock — no race with the TUI.
- ✓ Records every op in the local CRDT log.
- ✗ Open a network socket. No HTTP, no WebSocket.
- ✗ Upload your notes anywhere. There is no outl cloud.
- ✗ Send telemetry. The binary doesn't phone home.
- ✗ Need an account. You install it and use it.
The LLM host (Claude Desktop, Cursor, ChatGPT, …) is the party that does ship your prompt — including any block content the model loaded — to its model provider. That's how the model gets the context. outl's privacy story doesn't override the host's privacy story. Pick a host whose terms you're comfortable with. If you need air-gap, point an MCP-speaking local model (Ollama via an MCP bridge, etc.) at the same server.
mcp, claude, second brain —
straight answers.
What is MCP and why does outl need one?
MCP (Model Context Protocol) is the open standard from Anthropic for connecting LLMs to local tools and data. outl ships an MCP server (`outl mcp serve`) so Claude, Cursor, Zed, ChatGPT and anything else that speaks MCP can read and write your notes through a stable contract — no copy-paste, no upload, no parsing of human output.
Is outl as MCP server the same as Notion AI or Roam AI?
No. Notion AI and Roam AI send your workspace to a remote model on their servers. outl is local-first: your notes never leave your machine. The LLM client (Claude Desktop, Cursor, etc.) runs locally and talks to `outl mcp serve` on stdio. The model itself runs wherever you chose to run it. There is no outl cloud.
How do I configure Claude Desktop to use outl as a second brain?
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` and add an `mcpServers.outl` entry with `command: "outl"` and `args: ["--workspace", "/absolute/path/to/notes", "mcp", "serve"]`. Restart Claude Desktop. The outl tools appear under the server name in the tools panel.
Does this work with ChatGPT or only Claude?
It works with anything that speaks MCP. Claude Desktop, Claude Code, Cursor, Zed all support MCP today. ChatGPT desktop is rolling out MCP connectors. Any new host that adopts the protocol picks outl up for free — the binary doesn't change.
What can the LLM actually do with my notes?
Read any page or block, search full-text, run structured queries on properties and tags, list backlinks, append to today's journal, create new pages, toggle TODOs, move blocks. Destructive operations (page delete, block delete) require `confirm: true` in the call — without it the server returns a recoverable error and nothing is touched.
Does outl upload my notes to Anthropic or OpenAI?
outl does not upload anything. outl is a local binary that reads and writes .md files on your disk. What the LLM host does with the data you feed it is governed by that host's privacy policy (e.g. Claude Desktop's terms with Anthropic). Pick a host you trust.
Can I run outl as an MCP server without the TUI?
Yes. `outl mcp serve` is its own subcommand and doesn't need the TUI to be open. It holds an exclusive lock on the workspace — you can't have `outl serve` and `outl mcp serve` writing to the same workspace at the same time. Two readers are fine.
What's the difference between the CLI and the MCP server?
Same logic, two surfaces. Every CLI subcommand (`outl page get`, `outl search`, `outl block append`, …) maps 1:1 to an MCP tool (`outl_page_get`, `outl_search`, `outl_block_append`). The MCP server holds the workspace in-memory between calls and caches the index, so it's faster than re-invoking the CLI. Both speak the same JSON envelope.
Is the MCP server free and open source?
Yes. The MCP server is a subcommand of the same MIT-licensed Rust binary as the rest of outl. No separate license, no paid tier, no telemetry. Source: github.com/avelino/outl.
Where do I find the full tools and resources reference?
On this page (tables below) and in the project docs at /docs/mcp. The CLI counterpart contract — same handlers, same error codes — lives at /docs/cli.
give your LLM
a second brain.
One binary. One config block. Same notes you already have.