Notepatra Notepatra MCP · v0.1.129 · 49 tools

Your editor, readable by your AI

From v0.1.118, Notepatra ships notepatra-mcp — a Model Context Protocol server that lets Claude Desktop, Claude Code, OpenAI Codex, the OpenAI Agents SDK, and any spec-compliant MCP client see what you have open, search your workspace, run read-only SQL over your data files, read your Git status and Noter notes, and — only with your explicit per-action approval — edit, save, and create notes. Everything runs over stdio and a local socket. Nothing leaves your machine.

Release status. MCP first shipped in v0.1.118; the current release is v0.1.129. The server provides 49 tools (Git, read-only SQL, Noter write verbs, diagram export) and supports Windows named-pipe transport, and v0.1.120 adds a prebuilt, cosign-signed Windows sidecar (notepatra-mcp-windows-x64.zip) plus a one-click notepatra-mcp.mcpb Claude Desktop bundle as release assets. Everything on this page describes v0.1.129.

v0.1.125, v0.1.126 and v0.1.127 were all MCP-only releases, driven by testing this server against a real editor rather than by new features. v0.1.125 fixed a credential leak in search_project, a goto_line that reported success from the wrong line, and a sidecar that hung on every mistyped argument. A retest then found that the credential fix covered only one of four read paths — see what changed in v0.1.127 below.

What it is

MCP (the Model Context Protocol) is the open standard AI assistants use to talk to tools. Notepatra's implementation is a small standalone binary, notepatra-mcp, that sits between your AI client and the running editor:

AI client (Claude Desktop, Claude Code, Codex, ...)
        │  stdio · JSON-RPC 2.0 (MCP)
        ▼
  notepatra-mcp  ──local socket──▶  Notepatra (the running editor)

In practice: ask your assistant "what do I have open?", "find where the config is parsed", "diff these two tabs", "format this JSON and put it in a new tab" — and it drives the actual editor on your screen.

The tool surface — 49 tools, three tiers

notepatra-mcp exposes 49 tools, grouped in three tiers by what they can touch. Read tools observe. Act tools drive visible, non-destructive editor actions. Write tools change buffer contents or disk — and every one of those is gated behind a human approval card.

Tier 1 — Read (24 tools, no approval needed)

ToolWhat it does
list_open_tabsList the open tabs with id, index, title, file path, and modified state. The id is stable; the index is not.
read_tabRead the text of one open tab, selected by tab_id, index, or title. Takes max_bytes and reports truncated + total_chars, so a large buffer can be paged instead of swallowing your whole context.
get_selectionGet the currently selected text, with the tab it belongs to.
get_statusEditor state: active tab, file path, language, encoding, cursor position.
app_infoApplication info: name, version, edition (Lite/Full), platform.
list_recent_filesList recently opened file paths.
find_in_tabFind text inside one open tab (default: the active tab), selected by tab_id, index, or title — a literal substring, or a regular expression with {regex: true}.
search_projectSearch all open tabs plus files under the workspace folder, case-insensitive (capped at 200 matches) — a literal substring, or a regular expression with {regex: true}. Reports workspace_searched and scope, so a tabs-only search can never pass for a project-wide one.
list_notesList Noter notes with title, file name, and last-modified time.
read_noteRead one Noter note by its file path.
list_remindersList your Noter reminders with note title, due time, and bucket (overdue / today / upcoming).
git_statusRead-only git status for the workspace repository.
git_diffRead-only git diff, optionally scoped to one path.
git_logRead-only git log (recent commits; default 20, capped at 100).
git_showRead-only git show for a commit or ref.
git_branchRead-only list of branches in the workspace repository.
validate_npdValidate a Notepatra diagram (.npd) — a tab or supplied source; returns parse errors with line numbers.
run_sqlRun a read-only SELECT over a CSV / Parquet / JSON file (or the active tab) and return columns + rows. SELECT-only and sandboxed — see the note below.
list_languagesList the canonical language names set_language accepts (as shown in the Language menu).
get_capabilitiesEditor capability profile: edition, platform, version, tool count and tiers, and feature flags (DuckDB, WebEngine, Noter).
get_diagram_sourceRead the .npd source of an open Diagram tab.
list_connectionsList your saved database connections (name, driver, database) — never passwords.
run_queryRun a read-only SELECT against a saved named connection (PostgreSQL / MySQL / SQL Server / SQLite / DuckDB) — what run_sql cannot reach; mutations rejected. Full edition (DuckDB) for DuckDB-driver connections.
list_tablesList the user tables available over a saved named connection.
How run_sql stays safe. Every query is checked by the SQL classifier and rejected unless it is a pure SELECT — no INSERT, UPDATE, ATTACH, COPY, or DDL ever runs. On the Full (DuckDB) edition there is a second wall: the target CSV / Parquet / JSON is first materialized into an in-memory table, then DuckDB's external filesystem access is turned off (SET enable_external_access=false) before the untrusted SQL executes — so the query cannot reach host files even through read_text, read_csv_auto, glob, or a replacement scan. The csv_path argument is confined to your open workspace. Results are row- and cell-capped so a single answer can't exfiltrate a whole file.

Tier 2 — Act (14 tools, visible and non-destructive)

ToolWhat it does
open_fileOpen a file in a new tab, or focus the tab that already has it.
new_tabCreate a new untitled tab, optionally pre-filled with text.
goto_lineMove the cursor to a line in a tab — for pointing you at a location.
select_rangeSelect a range of text in a tab by line/column or character offset, so replace_selection has a selection to act on.
set_languageSet a tab's syntax-highlighting language. Accepts friendly aliases (python, js, cpp, go) and resolves them to the canonical name.
compare_tabsOpen Notepatra's side-by-side Compare view for two open tabs.
format_jsonFormat JSON text and return the result (fails on invalid JSON).
format_sqlFormat SQL text and return the result.
format_htmlFormat HTML text and return the result.
open_noteOpen one Noter note in the editor by its file path.
create_diagramCreate a new Diagram (.npd) tab — optionally pre-filled — and focus it; reports parse validity.
open_noterOpen or focus the Noter panel tab.
open_data_analystReveal and focus the AI dock in Data Analyst mode.
render_chartRender a chart inline in the Data Analyst transcript (Vega-Lite or the simplified {type,x,y,data} form). Full edition (WebEngine) only.

Tier 3 — Write, with approval (11 tools, human-gated)

ToolWhat it does
insert_textInsert text into an open tab at the cursor, or at an explicit line/column.
replace_selectionReplace the currently selected text with new text.
apply_editLiteral find-and-replace in one open tab — first occurrence, or all of them.
save_tabSave an open tab to its file on disk.
create_noteCreate a new Noter note with a title and body.
append_noteAppend text to an existing Noter note.
set_reminderSet a due-time reminder on a Noter note.
export_diagramExport a diagram tab to a PNG or PDF file on disk.
set_diagram_sourceReplace the entire .npd source of a Diagram tab; the canvas re-renders.
export_query_resultsRun a read-only SELECT against a saved connection and write the results to a CSV or JSON file.
export_chartRender a chart off-screen and export it to a PNG / SVG / HTML / spec file. Full edition (WebEngine) only.

What changed in v0.1.127

Retesting v0.1.126 found a sixth credential door, and the interesting part is not the door — it is why there kept being another one.

Five doors refused a private key; the sixth handed it over in chunks. select_range accepted any range inside a credential file and get_selection returned it as text. get_selection had been exempted on the reasoning that a selection is human-made, so reading it back implies consent — but select_range lets the client choose the range, which makes that reasoning false.

apply_edit was a quieter form of the same thing: it reads the buffer as a match oracle, so "no match" versus an approval card naming the match reports what a file contains, one probe at a time, without ever returning a byte of text.

Why the fix is structural rather than a sixth guard

Three consecutive releases shipped this same shape. v0.1.125 unified two hand-maintained copies of the deny list. v0.1.126 added the check to four verbs. The retest then found a fifth and a sixth.

Every one of those fixes was correct and none of them converged, because what kept going stale was not the list — it was the enumeration. Every new verb had to remember to ask, and the point of a security guard is that it does not depend on remembering.

Buffer text now leaves the editor through exactly one accessor, which refuses before it returns. select_range refuses to stage a selection over a credential file at all, so no future verb can inherit a leak that was already set up for it. And a test lints the bridge source and fails the build on any direct buffer read that bypasses the accessor — so a verb added next year cannot reintroduce the class without turning a test red.

Two smaller corrections

What changed in v0.1.126

v0.1.125 fixed a credential leak in search_project. A retest of that release found the fix covered one read path out of four — and eight other defects alongside it. Everything below is now the shipped behaviour.

The credential deny-list guarded one door. v0.1.125 taught search_project's filesystem walk to skip credential files, and that was the only place the check ran. So the leak survived in the plainest possible form — stop searching and just ask:

open_file {"path": ".../.ssh/id_rsa"} opened it with isError:false, and read_tab returned the entire private key. find_in_tab returned the matching key lines. And search_project's open-tab leg read the same file straight out of the buffer while its disk leg refused it.

All four now check the same list, and open_file refuses before the tab exists — opening a key and then declining to read it would still put it on your screen.

Tools that reported success while returning the wrong answer

Tabs have a stable id

Every write verb used to address its target by tab_index. That index is positional: close a tab to its left and it silently re-points at a different document, underneath a caller with no way to notice. Out-of-range was the lucky case — you get an error. A shifted-but-still-valid index writes silently, with the approval card the only thing in the way.

list_open_tabs now publishes an id per tab that survives reordering, save-as, and its neighbours closing, and every verb that names a tab accepts tab_id. Asking for an id that is gone tells you it was closed, rather than reporting a bad index you never supplied. tab_index keeps working everywhere it did.

Smaller corrections

The write-approval model

The eleven write tools never execute on arrival. Each incoming write request shows a non-modal approval card inside the Notepatra window — it does not steal focus or block your typing — describing what the assistant wants to do, with a preview of the text involved and two buttons: Approve and Deny.

The gate lives in the editor process itself (the C++ bridge), not in the MCP sidecar — so a modified or malicious MCP client still cannot write without a human clicking Approve inside Notepatra. Read and act tools don't prompt: they can't change buffer contents or touch disk.

Setup — pick your client

The server binary is notepatra-mcp. All snippets below pass --socket, which connects it to the running editor; make sure notepatra-mcp is on your PATH (or use its absolute path), and have Notepatra running when the assistant calls a tool.

Get notepatra-mcp

From v0.1.118, every GitHub release ships prebuilt notepatra-mcp binaries — cosign-signed like every other release artifact:

Extract, chmod +x notepatra-mcp, and put it somewhere on your PATH:

tar xzf notepatra-mcp-linux-x64.tar.gz
chmod +x notepatra-mcp
sudo mv notepatra-mcp /usr/local/bin/

Or build it from source — the sidecar lives in notepatra-mcp/ in the repo and needs only stable Rust, no other dependencies:

cd notepatra-mcp
cargo build --release   # binary at target/release/notepatra-mcp

Windows named-pipe transport arrived in v0.1.119 but did not actually work: it deadlocked on the first verb after the greeting, so no tool call ever completed. It is fixed in v0.1.120 — the sidecar connects to the running editor over a named pipe (\\.\pipe\…) and --socket works there for real. As of v0.1.120 the signed release bundle provides a prebuilt Windows binary (notepatra-mcp-windows-x64.zip, cosign-signed like the others) and a one-click Claude Desktop bundle (notepatra-mcp.mcpb — install it from Claude Desktop's Settings → Extensions), so Windows users no longer need to build from source. cargo install notepatra-mcp remains a fully supported alternative on every platform.

ClientVendor ecosystemHow it connectsWorks with Notepatra?
Claude DesktopAnthropicstdio, via claude_desktop_config.json — or one-click via the notepatra-mcp.mcpb bundle from the next releaseYes
Claude CodeAnthropicstdio, via claude mcp addYes
Codex CLIOpenAIstdio, via config.tomlYes
OpenAI Agents SDKOpenAIstdio, via MCPServerStdioYes
Cursor, Windsurf, VS Code, othersVariousstdio, via each client's MCP configYes — any spec-compliant stdio MCP client
ChatGPT connectorsOpenAIRemote URL onlyNo — cannot reach a desktop editor (see limitations)
claude.ai web connectorsAnthropicRemote URL onlyNo — cannot reach a desktop editor (see limitations)

Claude Desktop

Add Notepatra to claude_desktop_config.json (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "notepatra": {
      "command": "notepatra-mcp",
      "args": ["--socket"]
    }
  }
}

Restart Claude Desktop; the Notepatra tools appear in the tools menu.

Claude Code

One command:

claude mcp add notepatra -- notepatra-mcp --socket

Then /mcp inside Claude Code shows the server and its 49 tools.

OpenAI Codex CLI

Add a block to ~/.codex/config.toml:

[mcp_servers.notepatra]
command = "notepatra-mcp"
args = ["--socket"]

OpenAI Agents SDK

Use the SDK's stdio server class (Python shown):

from agents import Agent, Runner
from agents.mcp import MCPServerStdio

async with MCPServerStdio(
    params={"command": "notepatra-mcp", "args": ["--socket"]},
) as notepatra:
    agent = Agent(
        name="Editor assistant",
        instructions="Help the user work with what they have open in Notepatra.",
        mcp_servers=[notepatra],
    )
    result = await Runner.run(agent, "What tabs do I have open?")

Any other MCP client

Anything that can launch a stdio MCP server works — Cursor, Windsurf, VS Code, or your own client. Configure it to run the command notepatra-mcp with the argument --socket. The server identifies itself as notepatra-mcp and advertises the tools, resources, and prompts capabilities.

Tip: running notepatra-mcp without --socket starts it against a built-in mock editor — handy for testing a client integration without Notepatra running.

Resources and prompts

Beyond tools, the server exposes two other MCP capabilities:

Resources

Clients that support MCP resources (Claude Desktop's attachment picker, for example) can pull a tab or note straight into the conversation without a tool call.

Prompts

PromptWhat it does
review-current-fileCode-review the file open in the active tab — embeds its live contents and language.
explain-selectionExplain the text currently selected in the editor.
summarize-notesSummarize your Noter notes (embeds up to the 3 most recent).

Limitations — the honest list

FAQ

Is my data sent anywhere?

Not by Notepatra. The notepatra-mcp binary you install makes no network connections: it talks to your AI client over stdio (pipes between two processes on your machine) and to the editor over a local per-user socket. Nothing leaves the machine through this path — and the shipped binary is not merely configured that way, it is compiled without networking at all. Every CI job runs the released binary and fails the build unless it reports built without remote support. One honest caveat that applies to every MCP server: whatever your assistant reads through these tools becomes part of its conversation, and goes wherever that client sends its conversations — a local model stays local, a cloud model does not. Choose the client and model accordingly.

What is the remote gateway in v0.1.120, and does it weaken any of this?

v0.1.120 adds an opt-in remote gateway, and it is off unless you deliberately build it: the code sits behind a remote Cargo feature that is not in the default feature set, so cargo install notepatra-mcp and every signed release binary contain no gateway and no networking or crypto dependencies at all. If you do build it (cargo build --features remote), serve binds loopback only127.0.0.1, never a LAN or public interface — and a client must complete an 8-digit one-time HMAC pairing to receive a 256-bit bearer token, of which only the SHA-256 is stored, mode 0600. Scopes are fail-closed: a client is read-only unless it paired for more, calls above its scope are rejected at dispatch, and tools/list is filtered to the tiers it is allowed. A missing or invalid token never elevates.

The approval gate is unchanged, and there is no remote approval. A remote write request is only forwarded to the editor, which raises the same Approve / Deny card on your screen. Nobody can approve a write from the far end of the connection, and there is no headless bypass. The editor itself never listens on the network — only the opt-in sidecar gateway binds a socket. Until LAN support and TLS land, use an SSH tunnel rather than exposing the port.

Can the AI edit my files without me noticing?

No. The only tools that can change a buffer or touch disk are the eleven write tools, and each call requires you to click Approve on a card inside the editor within 120 seconds. No response means denied.

Is this the same as Notepatra's built-in AI Assistant?

No — they're complementary. The AI Assistant dock (Ctrl+Shift+A) is Notepatra talking to a model you configure. MCP is the reverse direction: an external assistant you already use (Claude, Codex, ...) talking to Notepatra. Neither requires the other.

Does it work with the Lite build?

Yes. The MCP bridge is part of the editor from v0.1.118 on both editions, and app_info reports which edition is running.

Which tab is index 0?

Tab indices are zero-based, exactly as shown in the tab bar — including the Welcome tab when it's open. Use list_open_tabs first rather than guessing indices. Better still, use the id it returns: an index is positional and moves the moment a tab closes, while an id belongs to the document for as long as it is open.

What happened to the mock server?

Running notepatra-mcp without --socket still starts an in-memory demo editor, so any MCP client can exercise the protocol with nothing installed. As of v0.1.127 it says so on the wire: every result carries a [MOCK DATA] block and _meta.mock, and initialize reports serverInfo.mock. Before that, a config file that had lost its --socket served fabricated tabs an assistant could not tell from your real ones.