Skip to content

feat: read-only MCP server for external AI clients - #109

Merged
FabioHAraujo merged 2 commits into
mainfrom
feat/mcp-server
Oct 29, 2026
Merged

FabioHAraujo merged 2 commits into
mainfrom
feat/mcp-server

Conversation

@FabioHAraujo

Copy link
Copy Markdown
Contributor

An MCP server inside the LowNotes process, so external AI clients (Claude Desktop, Claude Code, Cursor) can read the open vault while the window sits in the tray. Read-only, off by default.

What it does

  • SDK: rmcp 3.8.0 (Apache-2.0), pinned exactly, with only server, macros and transport-async-rw.
  • Endpoint, per user:
    • Windows: a named pipe with a DACL that grants access to the current user's SID only. reject_remote_clients is on, and first_pipe_instance refuses a name that another process already holds.
    • macOS/Linux: a Unix socket in its own 0700 folder, with the socket itself at 0600.
  • Authentication: before any MCP traffic the client sends LOWNOTES-MCP/1 <token>\n and gets OK or DENIED back. The comparison is constant-time.
    • The token is generated on first start and stored in the OS credential store (slot mcp:token).
    • for_frontend() and settings exports blank it.
  • Tools (all readOnlyHint): list_notes (optional folder), read_note, search_notes (the current keyword search), get_links, get_backlinks.
  • Resource template: lownotes://note/{path}.
  • Responses: JSON that carries the vault_id. The server instructions tell the client that note text is data, not instructions.
  • Scope: only the active vault, and only when its id is in mcp.vault_ids.
    • Paths must be Markdown and outside .lownotes/; safe_join still rejects .. and hidden folders.
    • Notes are cut at 512 KiB, responses are capped at 2 MiB, and incoming lines at 1 MiB.
    • At most 8 clients connect at once.
  • Threads: tool handlers run on the blocking pool. vault_service reads now take the vault the caller resolved, so a vault switch between the scope check and the read cannot serve another vault.
  • Logs: tool name, duration, response size and error key only. No note content, queries or token.

Not in this PR

Checks

  • cargo fmt --check, cargo clippy --lib --tests -- -D warnings (Windows)
  • cargo test --locked --lib: 194 passed, 10 ignored. The new tests run over a real named pipe with the restricted DACL on Windows; CI covers the Unix socket path.
  • cargo deny check bans sources: ok. advisories reports RUSTSEC-2026-0414 in pulldown-cmark 0.13.4. That was already on main, where references.rs enables ENABLE_WIKILINKS, so the Security check on this PR fails until a separate dependency PR moves to 0.13.5.

Closes #101

LowNotes serves the open vault to MCP clients over a per-user named pipe
(Windows) or Unix socket, after a token handshake. The token lives in the OS
credential store and never reaches the webview. Tools list, read and search
notes and read links and backlinks; a resource template exposes notes by path.
Off by default, and only vaults listed in the MCP settings are served.

Closes #101
@FabioHAraujo FabioHAraujo added enhancement New feature or request priority: P2 Important: performance, architecture or release quality size: L Large: a week or more, likely several PRs area: backend Rust side (src-tauri) security Security or privacy impact rust Pull requests that update rust code labels Oct 29, 2026
@FabioHAraujo
FabioHAraujo merged commit 10d2e62 into main Oct 29, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: backend Rust side (src-tauri) enhancement New feature or request priority: P2 Important: performance, architecture or release quality rust Pull requests that update rust code security Security or privacy impact size: L Large: a week or more, likely several PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP server inside the app with read-only tools over a local pipe or socket

1 participant