margins-mcp
Allows Claude to read and write notes in an Obsidian vault, with staging for proposals and tools for searching, linking, and compiling raw files into structured wiki pages.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@margins-mcp summarize my recent notes from this week"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
margins-mcp
Use your Claude Pro/Max subscription on your Obsidian vault. No API key. No per-token costs. No embedding pipelines. Claude reads your notes and proposes updates; your subscription pays for inference; your files stay on your disk.
margins-mcp is a small Node program that sits between Claude (Desktop or Code) and your Obsidian vault. When you chat with Claude, it reads your notes through margins-mcp. Your files stay local, Claude does the thinking, your existing subscription pays for it.
Which Margins are you?
Margins works for six common starting points. margins_start auto-detects your state on the first call, so you don't have to classify yourself — but knowing which row you're on helps set expectations.
You... | Persona | First conversation |
Obsidian, vault organized with wikilinks | A1 | Q&A on your notes. |
Obsidian, vault is empty or new | A2 | Ask Claude to scaffold daily-note + meeting templates. |
Obsidian, many files but few wikilinks | A3 | Run |
No Obsidian, organized markdown | B1 | Same as A1. Install Obsidian for the proposal-review UX. |
No Obsidian, empty | B2 | Use |
No Obsidian, messy folder | B3 | Run |
Related MCP server: Obsidian MCP Server
Requirements
An Obsidian vault, or any folder with notes, PDFs, Office/OpenDocument files, email exports, EPUBs, or plain text
A Claude subscription: Pro ($20/mo), Max, or Claude Code
Install
Two commands:
npm install -g margins-mcp
margins-mcp installThe installer prompts for your vault path, detects Claude Desktop and Claude Code, writes the right config files, scaffolds raw/ + proposed/ + .margins/ inside your vault if missing, and runs a verification probe. Restart Claude Desktop (Cmd-Q on macOS, not just close the window), or in Claude Code run /mcp to see Margins listed.
Don't have a vault yet?
Scaffold a Margins-shaped one:
margins-mcp install --starter-vault ~/notesFinding your Obsidian vault path
If you already use Obsidian and don't know your vault's absolute path:
In Obsidian: right-click the vault name in the file tree → "Reveal in Finder" (macOS) or "Show in Explorer" (Windows). The path is in the title bar.
macOS common paths:
~/Documents/<VaultName>, or~/Library/CloudStorage/iCloudDrive/Obsidian/<VaultName>if iCloud-synced.Linux common paths:
~/Documents/<VaultName>, or~/.local/share/Obsidian/<VaultName>.Windows common paths:
%USERPROFILE%\Documents\<VaultName>.
Pass it to the installer via --vault /absolute/path or answer the prompt.
Try-without-installing
npx margins-mcp install --vault /path/to/vaultYou'll see a warning that npm may garbage-collect the npx cache and your configs would break weeks later. For real use, prefer npm install -g above. The npx path is fine for kicking the tires.
Manual install
If you'd rather edit config yourself, add this to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS; see Anthropic docs for Windows/Linux paths):
{
"mcpServers": {
"margins": {
"command": "node",
"args": ["/absolute/path/to/margins-mcp/bin/margins-mcp.js"],
"env": { "MARGINS_VAULT": "/absolute/path/to/your/vault" }
}
}
}Try it
In a new Claude conversation, ask:
Use margins to give me a summary of my recent notes.
Or:
What pages support the claim that my project deadline is May 30th? Use margins to find them.
Or:
I just dropped a spreadsheet into
raw/. Compile it into a structured source page in my wiki.
The model can read your vault, propose new pages, propose edits to existing ones, and pull raw sources into structured wiki pages. propose_compile_from_raw accepts Markdown/plain text, PDFs, Word docs, spreadsheets, decks, email exports, EPUBs, OpenDocument files, RTF, HTML, CSV/TSV, JSON, YAML, and common text-ish formats. Every write stages to proposed/ first; nothing lands without you accepting.
Tools
Context (call once per conversation)
Tool | Purpose |
| Vault stats + pending proposals + uningested raw files + recent preferences + the vault's |
| Read durable user preferences from |
Read
Tool | Purpose |
| Full-text + filename search across the vault. |
| Read one page by relative path. |
| Most recently modified pages. |
| Pages that wikilink to a target slug. |
| ChatGPT Deep Research compatibility pair. |
Propose writes (staged — nothing lands until accepted)
Tool | Purpose |
| Stage a new page at |
| Stage a string-replacement edit. |
| Stage an append. Creates the page if missing; stacks on pending proposals. |
| Turn a raw transcript, note, PDF, Word doc, spreadsheet, deck, email, EPUB, or other supported document in |
| List pending proposals + overwrite-risk flag per entry. |
|
|
Suggest (for A3 / B3 — vaults with many files but few links)
Tool | Purpose |
| Scan a page for entity-shaped phrases and propose wikilinks to other vault pages that share the same slug. The model then chains |
Learn
Tool | Purpose |
| Append a durable rule to |
How the proposal flow works
Every write tool stages to proposed/<path> inside your vault. Nothing touches the live tree until you (or an MCP client acting on your behalf) call resolve_proposal with action: "accept". You can also inspect staged content (ls proposed/) and accept by moving files yourself.
Sequential edits stack: a second propose_edit on the same path reads from the pending proposal, not the vault.
Configuration
Env var | Default | Purpose |
| (required) | Absolute path to your Obsidian vault or Markdown folder. |
| auto-detected | Comma-separated subfolders to index. Auto-detection: |
| (consent file) | Override telemetry: |
What Margins is NOT
To keep scope crisp:
Not an inference layer. Your Claude subscription pays for that. Margins makes zero LLM calls.
Not a web app. The chat surface lives in Claude Desktop / Claude Code / claude.ai. Margins is plumbing.
Not a cloud sync. Local-first. Your files stay on your disk.
Not a CRM integration. Different product.
Not API-key-based. Subscription-passthrough is the whole point.
Privacy
Vault content never leaves your machine. Margins is a Node process that reads/writes files locally and exposes structured tools over stdio.
Anonymous telemetry (opt-in at install time) reports tool-call counts to help me prioritize what to build next. Sample event payload:
GET https://margins.goatcounter.com/count?p=/tool/search_vault. No vault content, no file paths, no user identifier beyond the standard 24-hour rolling session token GoatCounter assigns. Disable per-session withMARGINS_TELEMETRY=off. Decision stored at~/.margins/consent.json.
Develop
git clone https://github.com/cflorczyk9/Margins.git
cd Margins
npm install
npm test
MARGINS_VAULT=/path/to/test/vault npm startThe compiler (src/compiler/) was originally vendored from an earlier Margins web app. The web app and its landing page live on the legacy-webapp branch in this repo. Re-vendor with scripts/vendor-compiler.sh if you ever need to pull updates back from there.
How Margins gets smarter over time
Two things compound:
Your vault's
CLAUDE.mdis auto-loaded bymargins_start. Drop vault-specific rules in there — filing conventions, voice, naming patterns — and Claude obeys them in every conversation. No copy-paste..margins/preferences.mdis a Margins-maintained file inside your vault. When you correct Claude ("no, that should be in projects, not personal"), Claude callsrecord_preferenceto remember the rule. Next conversation it reads them viarecall_preferencesbefore proposing writes. The file is plain Markdown — you can audit it, hand-edit it, delete sections that no longer apply.
Both files live in the vault, so they travel with it. Switch machines, switch hosts (Claude Desktop → Claude Code → ChatGPT once that lands), and your conventions follow.
Roadmap
v0.6: web onboarding at marginsmcp.com — pick a folder via File System Access, scaffold a vault, get the install command. Closes B2/B3 personas without requiring CLI fluency.
v0.6:
get_citations(semantic embedding search, opt-in dep).v0.6: OCR/image, legacy Office binary, and audio/video ingestion for
propose_compile_from_raw.v0.7: HTTP / Streamable transport for claude.ai web and ChatGPT custom connectors.
v0.7+: Obsidian community plugin alongside MCP, if signal supports it.
v0.8+: file watcher / auto-scaffold on drop into
raw/.
License
MIT
Available Tools
23 toolsappend_toA
Append content to the end of a page. If the page doesn't exist, it's created. If a pending proposal exists for the path, the append stacks on top of it. Result is staged at proposed/. If the user asks for final proposed content, read proposed/ after appending instead of inferring.
Inputs: path (required), content (required).
fetchBRead-only
ChatGPT Deep Research fetch. Returns {id, title, text, url, metadata} for the given vault path.
Inputs: id (required).
get_backlinksARead-only
Find vault pages that link to a target slug or filename. Matches [[wikilinks]] and relative .md links.
Inputs: target (required), limit.
get_vault_contextARead-only
Read the vault's wikilinking context: entity slugs (people, organizations, places, tools, projects), active project/decision slugs (priority=active or recently updated), and the in-use semantic tag taxonomy. Call this BEFORE propose_compile_from_raw so the page you stage uses real entity slugs in [[wikilinks]] (not invented names) and existing tags (not new variants). Region/X and vibrance/X tags are excluded from the response — those are auto-computed by scripts/wiki_regions.py and must not be model-generated. Cached on a vault-mtime key; safe to call repeatedly.
Inputs: refresh.
list_proposalsARead-only
List pending proposals with optional filtering. Each entry has proposal path, destination path, whether accepting overwrites an existing vault file, size, and (for overwrites under default settings) a small first-diff preview. Use the pattern filter to scope to a folder or file shape when the queue is large.
Inputs: limit, sortBy, pattern, includeDelta.
list_recentARead-only
List the most recently modified vault files. Use this to answer 'what did I just ingest / update'.
Inputs: limit.
list_unprocessedARead-only
List vault files that have not yet been compiled into a wiki source page. Files can live anywhere in the vault (raw/ is conventional but not required) — detection works on raw_file: frontmatter, not folder placement. Use this when the user asks 'what haven't I filed yet?' or before a compile pass. Each item is a vault-relative path you can pass directly to propose_compile_from_raw.
Inputs: limit.
margins_doctorARead-only
Diagnose the vault's health. Returns a structured report of issues: orphan source pages (raw_file points to a missing file), tracker drift (source pages without tracker rows, or tracker rows for missing sources), files with malformed frontmatter, and large raw files. Read-only — never modifies the vault. Use when the user asks 'is anything broken?', 'check my vault', or before major operations.
Inputs: no named parameters.
margins_reset_proposalsADestructive
Clear all pending proposals from proposed/. Use when proposals have accumulated from failed Claude sessions, or when the user wants a clean slate. Returns the number of files that would be deleted. Requires confirm=true to actually delete — without confirm, returns a dry-run list. Vault files are never touched; only files under proposed/.
Inputs: confirm.
margins_startARead-only
Conversation-start primer. Always call this once at the start of every vault-relevant conversation. The response includes a 'mode' field — read it and follow the 'guidance' field tailored to that mode: (1) mode='pile' for unstructured many-file vaults — returns a time-stratified sample of vault files, filename patterns, and (importantly) a rawScan with a priorityQueue of raw/ files to compile FIRST via propose_compile_from_raw. If priorityQueue is non-empty, your best opening move is to compile those files in parallel in a single turn (read_page each to get readable text, then propose_compile_from_raw with a structured summary) — the user dropped source documents and wants wiki source pages within ~90 seconds. (2) mode='empty' for near-empty vaults — ask what the user wants and offer to scaffold. (3) mode='synthesis' for organized linked vaults — returns folder stats, pending proposals, uningested files, recent user preferences, and the vault's CLAUDE.md. Ground answers in this structure and cite file paths. All modes also return pendingProposals, uningestedRaw, recentPreferences, and vaultManual.
Inputs: no named parameters.
propose_compile_from_rawA
Compile a supported source file (.adoc, .asciidoc, .conf, .csv, .docx, .eml, .epub, .htm, .html, .ini, .json, .log, .markdown, .md, .mdx, .odp, .ods, .odt, .org, .ott, .pdf, .pptx, .rst, .rtf, .srt, .tex, .text, .toml, .tsv, .txt, .vtt, .xlsm, .xlsx, .xml, .yaml, .yml) into a wiki source page proposal staged at proposed/.
IMPORTANT — produce a knowledge artifact, not a topic recap. The page you stage is the substrate the user (and future Claude sessions) will retrieve from. Thin pages = thin retrieval.
Before calling, call get_vault_context to know which entity slugs and active project slugs to wikilink, and which semantic tags are already in use.
For a meeting/call/transcript: pick content-specific H2 headings that reflect what was actually discussed (e.g. 'Practice Overview', 'Service Philosophy', 'Succession Status' — NOT 'Summary' / 'Bullets' / 'Takeaways'). Quote verbatim where the exact phrasing matters. Wikilink every entity using slugs from get_vault_context. Include a relevanceCallout naming the active projects/decisions this source bears on. Surface 2-4 concrete applications.
For a synthesis page (pageType='synthesis'): pass the source URLs as sources, structure body sections around the argument, include a relevanceCallout.
For a concept page (pageType='concept'): structure as snapshot + context + source log.
Legacy callers can still pass summary + summaryBullets + takeaways and skip sections — Margins will fall back to the simple template. But the rich path is what produces a knowledge artifact.
Inputs: rawPath (required), tags, force, quiet, +22 more.
propose_editA
Propose an edit to an existing page via exact string replacement. 'before' must appear exactly once in the current file (or in the pending proposal if one exists); add surrounding context if it doesn't. The edit is staged at proposed/.
Inputs: path (required), after (required), before (required).
propose_entity_stubsA
Stage stub entity pages for a list of candidate slugs (typically from scan_entity_candidates). Each stub becomes a proposed page at wiki//.md (default bucket 'entities') with frontmatter type:entity + a from_scan marker, an auto-built '## Mentioned in' block linking source files when snippets/files are provided, and a '## Next' checklist. Skips slugs whose destination page already exists in the vault — edit those directly instead of overwriting them. Closes the cold-start loop: scan finds names with no page; this stages the pages; resolve_proposal accepts the ones the user wants. Reject events on a stub (single or bulk) automatically append the slug to .margins/entity-rejections.md so it doesn't re-surface in the next scan.
Inputs: candidates (required), bucket.
propose_pageA
Propose a new page in the vault. Body is the full markdown (frontmatter optional). The page is staged at proposed/ until the user accepts it via resolve_proposal. Errors if a page already exists at this path in the vault — use propose_edit or append_to in that case.
Inputs: body (required), path (required).
propose_wikilinksA
Scan vault pages for entity-shaped phrases and propose wikilinks to other vault pages that share the same slug. Two modes:
(1) single-page — pass path, get a ranked list of {phrase, wikilink, occurrences} for that one page.
(2) scope — pass scope (glob/folder), scan every matching page using one shared slug index (much faster than calling repeatedly), get aggregated suggestions across pages.
With apply: true in scope mode, Margins stages one rewritten page per scanned page (a propose_page proposal that replaces every candidate phrase with its wikilink). Apply mode preserves the proposal-review contract — nothing lands until resolve_proposal accepts.
Useful for A3/B3 personas (many files, few wikilinks): scope across a folder finds entity references that already have target pages.
Inputs: path, apply, scope, maxPages, +1 more.
read_pageARead-only
Read a single vault file by relative path (e.g. 'wiki/career/career.md' or 'raw/report.pdf'). Extracts readable text from supported document formats before returning it.
Inputs: path (required).
recall_preferencesARead-only
Read the user's vault-scoped preferences file (.margins/preferences.md). Returns durable rules the user has stated or that you've recorded via record_preference. Call this before any propose_* tool so your proposals follow the user's filing conventions, naming patterns, and prior corrections.
Inputs: no named parameters.
record_preferenceA
Append a durable user preference, convention, or correction to the vault's preferences file. Call this when the user corrects a proposal in a way that should apply next time (filing path, naming pattern, summary length, link style, etc.). Do NOT record one-off disagreements or transient feedback. Aim for one-line rules.
Inputs: observation (required), category.
record_telemetry_consentA
Record the user's choice on anonymous telemetry. Call this exactly once, only when the margins_start response had telemetryConsentNeeded=true AND the user has answered the in-chat opt-in question. Pass enabled=true if they said yes, enabled=false if they said no. The choice persists in ~/.margins/consent.json and applies to all future Margins sessions.
Inputs: enabled (required).
resolve_proposalADestructive
Accept or reject pending proposals. Two modes: (1) single — pass path to apply the action to exactly one proposal; (2) bulk — pass pattern (glob) to apply the action to every matching proposal. Exactly one of path/pattern is required. Accept moves proposal to its destination atomically (overwriting any existing file at the destination); reject deletes the proposal without touching the vault. Per-destination lock guarantees concurrent edits on the same path serialize. Use dryRun: true with a pattern to preview which proposals would be touched without applying anything.
Inputs: action (required), path, dryRun, pattern, +1 more.
scan_entity_candidatesARead-only
Find capitalized phrases that recur across vault pages but have no matching slug — i.e. entities the user keeps mentioning without having a page for them. This is the inverse query of propose_wikilinks: instead of 'where should I add a wikilink to an existing page?', it answers 'what page should exist that doesn't yet?'. Returns candidates ranked by file-spread × mention-count, with snippets and the list of files where each appears. Read-only — never stages. The companion tool propose_entity_stubs takes the slugs you choose from this list and stages stub entity pages. Layered filtering: a global English/structural stoplist, an optional domain pack ('med', 'realestate', 'law', 'generic'), and an optional excludeUserRejections list (slugs the user already rejected). Existing slugs are excluded automatically — the shared vault-slug index is the same one propose_wikilinks uses. Best used right after a fresh import or split-mode compile, when the corpus has many entity references but few entity pages.
Inputs: limit, scope, domain, minMentions, +3 more.
searchBRead-only
ChatGPT Deep Research search. Returns a list of {id, title, url} where id is the vault path. Pair with fetch.
Inputs: query (required).
search_vaultARead-only
Full-text + filename search across the Margins vault. Returns top hits with path and snippet.
Inputs: query (required), limit.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 23 tools
Most tools are clearly separated by verb_noun intent, but there are confusing pairs: search_vault vs. search, and read_page vs. fetch, which both return text for a vault path. The detailed descriptions help, but an agent could easily misselect between these overlapping read/search tools.
The dominant pattern is verb_noun, with list_, get_, propose_, and record_ tools carrying most of the surface. However, the mix is inconsistent: three tools use a margins_ prefix, two bare tools are just search and fetch, and recall_preferences breaks the get/list convention.
23 tools is at the heavy end for a single MCP server and feels like several subsystems (vault context, raw ingestion, proposal management, research, diagnostics) bundled together. Each tool has a real use in the workflow, but the set would be easier to reason about if split or trimmed.
The core lifecycle is well covered: ingest raw files, propose compilations, edit or append to pages, list/resolve proposals, and read/search the vault. Obvious gaps include no explicit delete/remove tool for vault pages and no rename/move operation, but these are workarable rather than workflow-breaking.
Maintenance
Related MCP Connectors
Give Claude only the Google Drive files you choose. Every action logged.
Notes and memory assistant: one Markdown vault that Claude, ChatGPT and any MCP client can share.
A second brain for Claude: remembers your work, plans your day, encrypted under a key only you hold.
Connect Claude to your Platform7n workspaces: chat, links, work and decks. One-click OAuth.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceConnects Claude.ai to your local Obsidian vault for full CRUD access, search, and daily note creation via the Model Context Protocol.67 npm14MIT
- AlicenseNot gradedqualityDmaintenanceConnects Claude Desktop to your Obsidian vault, enabling reading, writing, searching, and organizing notes locally.1MIT
- AlicenseAqualityDmaintenanceConnects Claude Desktop to an Obsidian vault, enabling reading, searching, capturing ideas, and managing notes through natural language.213 npmMIT
- AlicenseNot gradedqualityCmaintenanceConnects Claude to a local Obsidian vault, enabling listing, reading, searching, and optionally editing notes via natural language, with all operations running locally.3,512 npmMIT