mcp · model context protocol

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.

start the server
$ outl --workspace ~/notes mcp serve

Same binary as the CLI. JSON-RPC over stdio. Protocol revision 2024-11-05.

why this matters

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.

local-first

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.

host-agnostic

MCP is an open protocol. Today: Claude, Cursor, Zed, ChatGPT. Tomorrow: whatever wins. You don't migrate your second brain when you switch models.

same code path

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.

setup

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.

01
anthropic · macOS, Windows

Claude Desktop

config path
~/Library/Application Support/Claude/claude_desktop_config.json
claude desktop config
{
  "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.

02
anthropic · CLI / editors

Claude Code

config path
~/.claude.json (or per-project .mcp.json)
claude code config
{
  "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.

03
ide · macOS, Windows, Linux

Cursor

config path
Settings → MCP → Add Server
cursor config
{
  "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.

04
ide · macOS, Linux

Zed

config path
~/.config/zed/settings.json → context_servers
zed config
{
  "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.

05
openai · macOS, Windows

ChatGPT (desktop)

config path
Settings → Connectors → Add MCP server
chatgpt (desktop) config
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.

06
stdio JSON-RPC

Any other MCP host

config path
host-specific
any other mcp host config
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.

smoke test

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
what the LLM sees

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

41 of them, each a 1:1 mirror of a CLI subcommand. Destructive tools (outl_page_delete, outl_block_delete) require confirm: true — the server fails closed.

what a reply costs you

Every successful call used to send its payload twice — once as a structured envelope, once as pretty-printed JSON in the text content — and every outline node carried a pre-tokenized inline AST that restates text the model already has. It is invisible on one call and it is most of your context window by the tenth.

A success reply is content-only and compact now, with default-valued fields dropped. Measured against a 2,862-page workspace:

outl_daily_today
12.2k → 5.9k
outl_page_get
22k → 5.9k
outl_page_list
726k → 303k

Characters, not tokens — the ratio is what matters. Two things are deliberately not trimmed: the journal reads keep their JSON, because the outline is the only place a block id appears and you need one to write back; and errors keep the structured envelope, because it is the only machine-readable copy of the error data. If you wrote a client that read structuredContent on success, that is the one thing here that breaks.

tool name what it does
pages
outl_page_get page meta + outline tree
outl_page_create new page, idempotent on slug · takes a content forest so a page lands with its whole outline in one call
outl_page_update patch title/icon
outl_page_rename rename a slug · does not rewrite [[old]] refs, it reports them in affected_refs
outl_page_delete move the page root to trash · needs confirm: true
outl_page_list list pages, filter by tag:foo or kind:journal|page
outl_page_render projected .md (clean, no sidecar)
blocks
outl_block_get a single block by id
outl_block_append append a child to a page or block
outl_block_append_tree append a whole subtree in one call — prefer it over a chain of appends
outl_block_insert insert a sibling right after another
outl_block_update replace a block's text
outl_block_move reparent · cycle-safe
outl_block_delete move a block to trash · needs confirm: true
outl_block_toggle_todo None → TODO → DOING → DONE → None
outl_block_tree the block plus its descendants
journal
outl_daily_today today's journal (creates on first read)
outl_daily_get a journal by ISO date, today/yesterday/tomorrow, or April 22nd, 2026
outl_daily_append append to a journal, today by default
outl_daily_range every journal between two inclusive dates
finding things
outl_search full-text across blocks and page titles
outl_query structured filter over pages — tag, property, date range, kind
outl_backlinks pages and blocks that mention [[slug]]
outl_block_refs blocks that cite ((blk-XXXXXX))
outl_block_embed resolve !((…)) recursively — source block and children
outl_tag_list every tag with usage counts
outl_tag_pages pages mentioning #tag anywhere in their subtree
properties
outl_page_prop_set set a page-level key:: value
outl_page_prop_get read a page property by key
outl_page_prop_list every property on a page
templates
outl_template_list pages carrying a non-empty template:: property
outl_template_apply instantiate a structural template under a target block
outl_template_resolve a callable template's language, source and declared params
outl_template_run run a callable template and write the result subtree under the anchor
export & files
outl_export_md the page as clean markdown
outl_export_json the page's AST and sidecar as JSON
outl_export_hugo the page as a Hugo markdown file
outl_asset_add import a file into assets/ and link it from a new block · takes a path, because MCP is stdio
workspace
outl_batch a sequence of writes in one session · stops at the first error and reports failed_at + applied
outl_workspace_info path, actor, counts, ops
outl_workspace_doctor integrity checks over the op log, sidecars, block refs and lock

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.
daily-driver patterns

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.

01

morning summary

prompt

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.

02

ask your notes

prompt

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.

03

block → blog draft

prompt

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.

04

task triage

prompt

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.

05

append from chat

prompt

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.

privacy & security

the honest data flow.

what outl does
  • ✓ 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.
what outl never does
  • ✗ 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.
be honest about the host

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.

frequently asked

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 — and when you sync across devices, that's peer-to-peer over iroh, end-to-end encrypted, with no third-party server holding your notes either.

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?

+ −

41 tools' worth. Read any page or block, search full-text, run structured queries on properties and tags, list backlinks and block references, append to today's journal or any date, create pages with a whole outline in one call, rename pages, cycle tasks through TODO/DOING/DONE, move blocks, set page properties, run templates, export to markdown, JSON or Hugo, import a file into assets, and apply a batch of writes in one session. Destructive operations (page delete, block delete) require `confirm: true` in the call — without it the server returns a recoverable error and nothing is touched.

How many tokens does an outl MCP call cost?

+ −

Far fewer than it used to. Every successful call once sent its payload twice — as a structured envelope and again as pretty-printed JSON — and every outline node carried a pre-tokenized inline AST that restated text the model already had. A success reply is content-only and compact now, with default-valued fields dropped. Measured on a 2,862-page workspace: reading today's journal fell from about 12.2k to 5.9k characters, reading a journal page from about 22k to 5.9k, and listing every page from about 726k to 303k. Two things are deliberately not trimmed: journal reads keep their JSON, because the outline is the only place a block id appears and you need one to write back, and errors keep the structured envelope because it is the only machine-readable copy of the error data.

Can an agent make several edits without a round-trip per block?

+ −

Yes, and it should. `outl_block_append_tree` appends a block and its whole recursive subtree in one call, `outl_page_create` accepts a content forest so a new page lands fully populated, and `outl_batch` applies a sequence of writes in a single workspace session. A batch stops at the first error and reports `failed_at` plus what it `applied`, so the caller can resume the suffix that never ran instead of guessing what landed.

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/outlmd/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.