CCM
/MCP
SkillsMCPMarketplacesDigestToolsAdvertise

This week in Claude

Every Monday: Claude Code, Agent SDK, MCP, and the Anthropic platform moves worth your time.

Skills by Category
Frontend DevelopmentBackend & APIsTesting & QASecurityDevOps & CI/CDGit & Pull RequestsDocumentationCode Review & QualityAI & Agent BuildingSkill Development
MCP Servers by Category
Sales & MarketingWeb & Browser AutomationDatabasesAI & LLM ToolsCloud & InfrastructureCommunication & MessagingDeveloper ToolsDesign & CreativeDocuments & KnowledgeSearch & Web Crawling
Marketplaces by Category
AI Agents & OrchestrationLLM IntegrationDevelopment ToolsFrontend & UIBackend & APIsDatabasesTesting & Code QualityDevOps & CloudSecurity & ComplianceGit & Version Control

Claude Code Marketplaces

Discover Claude Code plugins, extensions, and tools. Automatically updated directory of Anthropic Claude AI marketplaces with development tools, productivity plugins, and integrations.

Resources

  • Browse Skills
  • Browse MCP Servers
  • Browse Marketplaces
  • Skill index
  • MCP index
  • Marketplace index
  • Plugins Reference

Community

  • About
  • Tools
  • Feedback
  • Privacy Policy
  • Advertise

Built for the Claude Code community with Claude Code by mertbuilds.com

Independent project, not affiliated with Anthropic
cyanheads avatar

Obsidian Mcp Server

cyanheads/obsidian-mcp-server
58212 toolsauthSTDIO, HTTPregistry active
Summary

The Obsidian MCP Server provides AI agents and LLMs with comprehensive access to Obsidian vaults through the Model Context Protocol, enabling read, write, search, and management operations on notes and files via the Obsidian Local REST API plugin. It exposes specialized tools including obsidian_read_note, obsidian_update_note, obsidian_search_replace, and obsidian_global_search, with capabilities such as flexible content formatting, regex-based search-and-replace, case-insensitive path fallback, and metadata retrieval. The server solves the problem of integrating AI assistants and development tools with Obsidian knowledge bases by providing a standardized, secure interface for programmatic vault interaction.

Install to Claude Code

verified
claude mcp add obsidian --env OBSIDIAN_API_KEY=YOUR_OBSIDIAN_API_KEY --env OBSIDIAN_BASE_URL=http://127.0.0.1:27123 --env OBSIDIAN_VERIFY_SSL=false --env OBSIDIAN_REQUEST_TIMEOUT_MS=30000 --env OBSIDIAN_ENABLE_COMMANDS=false --env OBSIDIAN_READ_PATHS=YOUR_OBSIDIAN_READ_PATHS --env OBSIDIAN_WRITE_PATHS=YOUR_OBSIDIAN_WRITE_PATHS --env OBSIDIAN_READ_ONLY=false --env MCP_LOG_LEVEL=info -- node -y obsidian-mcp-server run start:stdio

Run in your terminal. Replace YOUR_* placeholders with real values; add --scope user to install for every project.

Review the command, arguments, and environment values before installing — MCP servers run with your local permissions.

CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
ego lite browserego lite browser
ego lite browser
Fastest browser for AI agents to run web automation tasks, always free.
Download Free life-time →
Give your AI the whole web as clean markdownGive your AI the whole web as clean markdown
Give your AI the whole web as clean markdown
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Agent, connect blockchain
Agent, connect blockchain
Connect your Claude agent to live crypto prices and trading routes via 1inch
Get the MCP →
inference shell
inference shell
create and run specialised agents in minutes
build now →
CodeHealth MCP ServerCodeHealth MCP Server
CodeHealth MCP Server
Protect your code quality, stop the AI slop.
Try For Free →
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
ego lite browserego lite browser
ego lite browser
Fastest browser for AI agents to run web automation tasks, always free.
Download Free life-time →
Give your AI the whole web as clean markdownGive your AI the whole web as clean markdown
Give your AI the whole web as clean markdown
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Agent, connect blockchain
Agent, connect blockchain
Connect your Claude agent to live crypto prices and trading routes via 1inch
Get the MCP →
inference shell
inference shell
create and run specialised agents in minutes
build now →
CodeHealth MCP ServerCodeHealth MCP Server
CodeHealth MCP Server
Protect your code quality, stop the AI slop.
Try For Free →

Tools

Verified live against the running server on Jun 10, 2026.

verified live12 tools
obsidian_get_noteRead a note from the vault — by path, the active file, or a periodic note. Choose a `format` projection: raw body, full object, structural document map, or a single section.4 params

Read a note from the vault — by path, the active file, or a periodic note. Choose a `format` projection: raw body, full object, structural document map, or a single section.

Parameters* required
format*string
Which projection to return. `content` — raw markdown body. `full` — content plus parsed frontmatter, tags, and file metadata. `document-map` — catalog of headings, block IDs, and frontmatter field...one of content · full · document-map · section
includeLinksboolean
When true with `format: "full"`, parses outgoing wiki and markdown link references from the note body. Skipped for other formats.default: false
sectionobject
Required when `format` is `"section"`. Identifies the heading/block/frontmatter to extract.
target*value
Where the note lives.
obsidian_list_notesList notes and subdirectories at a vault path. Defaults to the vault root when `path` is omitted. Tune recursion with `depth`, or filter the walk with `extension` / `nameRegex`. Capped at 1000 entries per call — when reached, walking stops and `excluded` is set; narrow `path`...4 params

List notes and subdirectories at a vault path. Defaults to the vault root when `path` is omitted. Tune recursion with `depth`, or filter the walk with `extension` / `nameRegex`. Capped at 1000 entries per call — when reached, walking stops and `excluded` is set; narrow `path`...

Parameters* required
depthinteger
How many directory levels to walk. `1` = target directory only (no recursion); `2` = target plus its immediate children — a structural overview; bump higher to drill in. Prefer narrowing `path` to...default: 2
extensionstring
Only include files matching this extension, with or without leading dot. Applies to files only — directories are returned regardless.
nameRegexstring
Optional ECMAScript regex (no flags) applied to entry names. Matches both files and directories; directories that fail the regex are skipped without recursing into them.
pathstring
Vault-relative directory path. Omit for the vault root.
obsidian_list_tagsList every tag found across the vault, with usage counts. Includes hierarchical parents — `work/tasks` contributes to both `work` and `work/tasks`. Filter to a subset with the optional `nameRegex`. To find notes by tag, use `obsidian_search_notes` in jsonlogic mode (e.g. `{"in...1 params

List every tag found across the vault, with usage counts. Includes hierarchical parents — `work/tasks` contributes to both `work` and `work/tasks`. Filter to a subset with the optional `nameRegex`. To find notes by tag, use `obsidian_search_notes` in jsonlogic mode (e.g. `{"in...

Parameters* required
nameRegexstring
Optional ECMAScript regex (no flags, ≤256 chars, no nested quantifiers like `(a+)+`) matched against the bare tag name (no leading `#`). Hierarchical tags like `work/tasks` are matched as the full...
obsidian_open_in_uiOpen a file in the Obsidian app UI. By default fails when the path does not exist; the `failIfMissing` flag controls the open-or-create behavior.3 params

Open a file in the Obsidian app UI. By default fails when the path does not exist; the `failIfMissing` flag controls the open-or-create behavior.

Parameters* required
failIfMissingboolean
When true (default), fails if the file does not exist. When false, allows Obsidian to create the file on open.default: true
newLeafboolean
Open in a new leaf (split pane) instead of the active one.default: false
path*string
Vault-relative path of the file to open.
obsidian_search_notesSearch the vault by text substring or JSONLogic predicate. Pick the mode that matches the query shape. Results paginate via opaque cursors: omit `cursor` for the first page, then pass `nextCursor` from the prior response. Text-mode hits additionally clip per file at `maxMatche...7 params

Search the vault by text substring or JSONLogic predicate. Pick the mode that matches the query shape. Results paginate via opaque cursors: omit `cursor` for the first page, then pass `nextCursor` from the prior response. Text-mode hits additionally clip per file at `maxMatche...

Parameters* required
contextLengthinteger
Characters of context on each side of the match (text mode only).default: 100
cursorstring
Opaque cursor from a prior response. Omit for the first page. Page size is server-determined; do not assume a fixed value.
logicobject
JSONLogic tree. Required for `jsonlogic` mode; ignored in `text` and `omnisearch` modes (use `query` instead — passing a string here will fail Zod validation since this field must be an object).
maxMatchesPerHitinteger
Cap on match contexts returned per file in text mode. When clipped, the hit carries `truncated: true` and `totalMatches`.default: 10
mode*string
Which search algorithm to run. `text` matches a substring case-insensitively across filenames and note bodies, returning surrounding context windows. `jsonlogic` evaluates a JSONLogic tree against...one of text · jsonlogic
pathPrefixstring
Filter returned filenames by prefix (text mode only, applied client-side).
querystring
The query string. Required for `text` and `omnisearch` modes; ignored in `jsonlogic` mode (use `logic` instead — passing a JSONLogic tree here will fail Zod validation since this field must be a st...
obsidian_write_noteCreate or overwrite a note. With `section`, replaces just that heading/block/frontmatter section in place; nested headings need `Parent::Child` syntax — use `obsidian_get_note` with `format: "document-map"` to discover available targets. Whole-file writes fail with `file_exist...5 params

Create or overwrite a note. With `section`, replaces just that heading/block/frontmatter section in place; nested headings need `Parent::Child` syntax — use `obsidian_get_note` with `format: "document-map"` to discover available targets. Whole-file writes fail with `file_exist...

Parameters* required
content*string
Body to write. For heading sections, the new section body — do not repeat the heading line (it stays in place). Markdown unless `contentType` is `json`.
contentTypestring
Content body format. Use "json" for typed frontmatter values or block-targeted table rows. JSON values must be valid JSON literals — strings need quoting (`"\"draft\""`, not `"draft"`), and numbers...one of markdown · jsondefault: markdown
overwriteboolean
Whole-file mode only (ignored when `section` is set). When `false` (default), the call fails with `file_exists` if the target note already exists — read it first and use `obsidian_patch_note` / `ob...default: false
sectionobject
Optional sub-document target. When set, only this section is replaced; rest of the note is untouched.
target*value
Where the note lives.
obsidian_append_to_noteAppend content to a note. **Without `section`: appends to the end of the file, or creates the file if it does not exist (your content becomes the full file).** With `section`: appends to the end of that heading/block/frontmatter; nested headings need `Parent::Child` syntax — u...5 params

Append content to a note. **Without `section`: appends to the end of the file, or creates the file if it does not exist (your content becomes the full file).** With `section`: appends to the end of that heading/block/frontmatter; nested headings need `Parent::Child` syntax — u...

Parameters* required
content*string
Body to append. Markdown unless `contentType` is `json`.
contentTypestring
Content body format. Use "json" for typed frontmatter values or block-targeted table rows. JSON values must be valid JSON literals — strings need quoting (`"\"draft\""`, not `"draft"`), and numbers...one of markdown · jsondefault: markdown
createTargetIfMissingboolean
When `section` is provided, create the section if it does not already exist (otherwise the call fails when the section is missing).default: false
sectionobject
Optional sub-document target. When set, content is appended to that section instead of the file.
target*value
Where the note lives.
obsidian_patch_noteEdit a heading, block reference, or frontmatter field in place — append to, prepend to, or replace the target's body. Use `obsidian_get_note` with `format: "document-map"` to discover available targets first; nested headings need `Parent::Child` syntax.6 params

Edit a heading, block reference, or frontmatter field in place — append to, prepend to, or replace the target's body. Use `obsidian_get_note` with `format: "document-map"` to discover available targets first; nested headings need `Parent::Child` syntax.

Parameters* required
content*string
Body to insert/replace. Markdown unless `contentType` is `json`.
contentTypestring
Content body format. Use "json" for typed frontmatter values or block-targeted table rows. JSON values must be valid JSON literals — strings need quoting (`"\"draft\""`, not `"draft"`), and numbers...one of markdown · jsondefault: markdown
operation*string
How to apply `content` relative to the targeted section. `append` — at the end of the target's body (for headings, before the next sibling/parent heading; for frontmatter array fields, as a new arr...one of append · prepend · replace
patchOptionsobject
Optional flags: createTargetIfMissing, applyIfContentPreexists, trimTargetWhitespace.
section*object
Which heading/block/frontmatter field to edit.
target*value
Where the note lives.
obsidian_replace_in_noteSearch and replace inside a single note, literally or by regex. Replacements run in array order, each over the previous one's output. Use for edits that don't fit `obsidian_patch_note`'s structural targets — e.g., body-wide find-and-replace.2 params

Search and replace inside a single note, literally or by regex. Replacements run in array order, each over the previous one's output. Use for edits that don't fit `obsidian_patch_note`'s structural targets — e.g., body-wide find-and-replace.

Parameters* required
replacements*array
Replacements to apply in array order over the evolving content.
target*value
Where the note lives.
obsidian_manage_frontmatterGet, set, or delete a single frontmatter key on a note, atomically. `set` requires a JSON-typed `value` (string, number, boolean, array, or object).4 params

Get, set, or delete a single frontmatter key on a note, atomically. `set` requires a JSON-typed `value` (string, number, boolean, array, or object).

Parameters* required
key*string
Frontmatter field name.
operation*string
Operation to perform on `key`. `get` — read the current value. `set` — write `value`, creating the key if absent. `delete` — remove the key from frontmatter.one of get · set · delete
target*value
Where the note lives.
valuevalue
Required when `operation` is `"set"`. JSON-typed value to write — strings, numbers, booleans, arrays, and objects all accepted.
obsidian_manage_tagsAdd, remove, or list a note's tags. Defaults to the frontmatter `tags:` array — set `location` to `inline` or `both` to mutate the note body. `add` ensures the tag is present in the requested location(s); `remove` strips it; `both` reconciles across both representations. Inlin...4 params

Add, remove, or list a note's tags. Defaults to the frontmatter `tags:` array — set `location` to `inline` or `both` to mutate the note body. `add` ensures the tag is present in the requested location(s); `remove` strips it; `both` reconciles across both representations. Inlin...

Parameters* required
locationstring
Where to apply the change. Defaults to `frontmatter` (the canonical Obsidian tag location, leaves the body untouched). `inline` mutates the note body — `add` appends `#tag` at end-of-file. `both` i...one of frontmatter · inline · bothdefault: frontmatter
operation*string
`add` and `remove` mutate the note; `list` reads the current tag set.one of add · remove · list
tagsarray
Tags to add or remove. Omit the leading `#`. Required for add/remove.
target*value
Where the note lives.
obsidian_delete_notePermanently delete a note from the vault. Confirms with the user before deleting when the client supports interactive confirmation. Recovery requires the local trash in Obsidian — there is no API-level undo.1 params

Permanently delete a note from the vault. Confirms with the user before deleting when the client supports interactive confirmation. Recovery requires the local trash in Obsidian — there is no API-level undo.

Parameters* required
target*value
Which note to delete.

obsidian-mcp-server

Read, write, search, and surgically edit Obsidian vault notes, tags, and frontmatter via MCP. STDIO or Streamable HTTP.

14 Tools • 3 Resources

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework


Overview

Read, write, search, and surgically edit Obsidian vault notes — sections, frontmatter, tags — over the Local REST API plugin, with folder-scoped read/write permissions built in. Runs as a stdio process or a local Streamable HTTP server.

Tools

ToolDescription
obsidian_get_noteRead a note as raw content, full structured form (content + frontmatter + tags + stat, with optional outgoing links), structural document map, or a single section.
obsidian_list_notesList notes and subdirectories under a vault path. Recursive walk (default depth 2, max depth 20; 1000-entry cap) with optional extension and nameRegex filters.
obsidian_list_tagsList vault tags with usage counts, including hierarchical parents. Ordered by count descending and capped at limit (default 200, max 10000), with the withheld remainder disclosed. Optional nameRegex and minCount narrow the set first.
obsidian_list_commandsList Obsidian command-palette commands, optionally filtered by nameRegex on display name. Opt-in via OBSIDIAN_ENABLE_COMMANDS=true (paired with obsidian_execute_command).
obsidian_search_notesSearch the vault by text, JSONLogic, or BM25-ranked Omnisearch (when the plugin is reachable). Results paginate via opaque cursors.
obsidian_write_noteCreate a note, replace a single section in place, or — with overwrite: true — clobber an existing file. Refuses whole-file writes against an existing path by default.
obsidian_append_to_noteAppend content to a note. Without section, creates the file if missing. With section, appends to a specific heading, block, or frontmatter field (file must exist).
obsidian_patch_noteSurgical append / prepend / replace against a heading, block reference, or frontmatter field.
obsidian_replace_in_noteSearch-replace inside a single note, scoped to the body by default. Literal or regex matching with whole-word, whitespace-flexible, and case-sensitivity options; supports capture-group replacement.
obsidian_manage_frontmatterAtomic get / set / delete on a single frontmatter key.
obsidian_manage_tagsAdd, remove, or list tags. Defaults to the frontmatter tags: array; location: 'inline' or 'both' opts into mutating the note body.
obsidian_delete_notePermanently delete a note. Always asks the user to confirm first — the call is answered with a confirmation request and retried with the answer.
obsidian_open_in_uiOpen a file in the Obsidian app UI, with failIfMissing and newLeaf toggles.
obsidian_execute_commandExecute an Obsidian command-palette command by ID. Opt-in via OBSIDIAN_ENABLE_COMMANDS=true.

Resources

ResourceDescription
obsidian://vault/{+path}A note in the vault — content, frontmatter, tags, and file metadata.
obsidian://tagsAll tags found across the vault, with usage counts (full snapshot).
obsidian://statusServer reachability, auth status, plugin/Obsidian version info, and registered API extensions.

Vault-note and tag data are also reachable via tools — obsidian_get_note for obsidian://vault/{+path}, obsidian_list_tags for obsidian://tags (count-ranked and capped, unlike the resource's raw snapshot). obsidian://status has no tool equivalent. Resources exist for clients that prefer attaching a note or vault snapshot to a conversation.

Capability reference

obsidian_get_note tool

  • format: "content" | "full" | "document-map" | "section" selects the projection; full accepts includeLinks: true for outgoing wiki/markdown links (vault-internal only — external URLs are filtered)
  • Addressed by vault path, the active file, or a periodic note (daily / weekly / monthly / quarterly / yearly)
  • Heading sections use Parent::Child syntax; a bare leaf name matching several headings returns the first match and lists every colliding path in candidates
  • Forgiving path resolution: a case-mismatched path retries against the canonical filename, an ambiguous case match fails with Conflict, and a NotFound carries Did you mean: …? suggestions when near-matches exist
  • Typed errors include note_missing, path_forbidden, no_active_file, periodic_unsupported / periodic_disabled, and path_traversal

obsidian_list_notes tool

  • Recursive walk from path (default vault root); depth 1–20 (default 2 = target plus immediate children)
  • Optional extension and nameRegex (≤256 chars, no nested quantifiers) filters; a directory failing nameRegex is skipped without recursing into it
  • Hard cap of 1000 entries per call — excluded.reason: "entry_cap" signals a truncated walk; narrow path or the filters to see the rest
  • Per-directory truncated: true marks entries cut off by the depth limit or by path policy

obsidian_list_tags tool

  • Vault-wide tag counts, including hierarchical parents (work/tasks contributes to both work and work/tasks)
  • Ordered by count descending, capped at limit (default 200, max 10000); optional nameRegex and minCount narrow the candidate set before ranking
  • Reports truncated / shown / cap when the limit withheld results
  • Not narrowed by OBSIDIAN_READ_PATHS — tag names (never note contents) can surface from outside the read scope

obsidian_list_commands tool

  • Lists Obsidian command-palette IDs and display names; optional nameRegex filters on display name
  • Opt-in via OBSIDIAN_ENABLE_COMMANDS=true — absent from tools/list when unset
  • Discovery partner for obsidian_execute_command

obsidian_search_notes tool

  • mode: "text" | "jsonlogic" always; "omnisearch" is added to the schema only when the Omnisearch plugin's HTTP server is reachable at startup (restart to re-probe)
  • text — substring match with contextLength-sized context windows (default 100) and an optional pathPrefix; jsonlogic — a JSONLogic tree with var paths into path / content / frontmatter.<key> / tags / stat.{ctime,mtime,size}, plus glob / regexp operators taking [PATTERN, VALUE]; omnisearch — BM25-ranked, quoted phrases, -exclusion, path: / ext: filters, typo tolerance, PDF/OCR via Text Extractor, hard-capped at 50 upstream hits (truncated: true when likely hit)
  • Cursor pagination — omit cursor for page one, pass nextCursor from the prior response; text-mode hits additionally clip to maxMatchesPerHit (default 10), flagged with truncated / totalMatches
  • No dedicated backlinks tool — express "what links here" via jsonlogic: {"regexp": ["\\[\\[Target Note(\\||#|\\]\\])", {"var": "content"}]}

obsidian_write_note tool

  • Without section — full-file write; refuses to clobber an existing note unless overwrite: true (file_exists conflict otherwise, naming the surgical-edit tools as the alternative)
  • With section — PATCH-with-replace against a heading/block/frontmatter target, leaving the rest of the file untouched (overwrite is ignored); a bare heading leaf shared by several headings fails with ambiguous_section
  • Output reports created, plus previousSizeInBytes / currentSizeInBytes on every call to spot an accidental clobber or a mistyped path

obsidian_append_to_note tool

  • Without section — appends to an existing file, or creates it with the given content as the whole body (created: true flags the second case)
  • With section — appends to a heading/block/frontmatter target; the file must already exist, and createTargetIfMissing: true brings the section itself into existence
  • Block-reference targets concatenate with no separator — include a leading newline in content for one
  • previousSizeInBytes / currentSizeInBytes bracket every call for drift detection

obsidian_patch_note tool

  • operation: "append" | "prepend" | "replace" against one heading, block reference, or frontmatter field per call
  • Heading targets accept the full Parent::Child path or an unambiguous bare leaf name; a leaf matching several headings fails with ambiguous_section and lists the candidates
  • patchOptions: createTargetIfMissing, applyIfContentPreexists (idempotency guard — otherwise content_preexists), trimTargetWhitespace

obsidian_replace_in_note tool

  • One or more replacements, applied in array order, each over the previous one's output
  • scope: "body" (default, frontmatter left byte-identical) | "frontmatter" | "both"; frontmatter/both re-parse the rewritten YAML afterward and write nothing if it breaks (frontmatter_invalid)
  • Per-replacement options: useRegex (≤1024 chars, no nested quantifiers), caseSensitive, wholeWord (\b…\b in both modes), flexibleWhitespace (literal mode only), replaceAll (default true)
  • perReplacement[] reports bodyCount / frontmatterCount per entry; totalReplacements sums them

obsidian_manage_frontmatter tool

  • operation: "get" | "set" | "delete" on a single frontmatter key; set requires a JSON-typed value (string, number, boolean, array, or object)
  • get needs read access; set / delete need the path inside OBSIDIAN_WRITE_PATHS with OBSIDIAN_READ_ONLY=false
  • set / delete return the full frontmatter after the change plus previousSizeInBytes / currentSizeInBytes

obsidian_manage_tags tool

  • operation: "add" | "remove" | "list"; location: "frontmatter" (default, canonical tags: array) | "inline" (body #tag, add appends at end-of-file) | "both" (reconciles both)
  • Inline detection skips fenced/inline code spans, link spans ([[...]], [text](...), [text][ref]), and \#-escaped hashes, so a heading anchor or wikilink alias is never mistaken for a tag
  • add / remove report applied vs. skipped tags plus the full tags set after the change; list ignores the input tags array

obsidian_delete_note tool

  • Always asks for confirmation first — the initial call returns an elicitation request naming the file's byte size, and is retried with the answer; declining fails with cancelled and issues no DELETE
  • No API-level undo — recovery requires Obsidian's local trash
  • Requires an MCP client that can serve an elicitation round-trip; every other tool works without one

obsidian_open_in_ui tool

  • failIfMissing (default true) controls open-vs-create: opening an existing file needs read access, opening a missing one (with failIfMissing: false) creates it and needs write access
  • newLeaf opens in a split pane instead of the active one
  • Same forgiving path resolution as obsidian_get_note (case fallback, Did you mean suggestions); obsidian_delete_note deliberately doesn't get it — a destructive op never silently rewrites its target
  • Output reports createdIfMissing so the caller can tell which branch ran

obsidian_execute_command tool

  • Dispatches an Obsidian command-palette command by commandId (discover via obsidian_list_commands); runs with the same authority as a keyboard invocation
  • Opt-in via OBSIDIAN_ENABLE_COMMANDS=true — absent from tools/list when unset
  • Behavior is command-dependent — some are destructive (delete file, close vault), some open UI

obsidian://vault/{+path} resource

  • The {+path} segment captures everything after /vault/, including slashes
  • Returns the same shape as obsidian_get_note with format: "full" — content, frontmatter, tags, stat
  • Gated by OBSIDIAN_READ_PATHS / OBSIDIAN_WRITE_PATHS like the tool equivalent

obsidian://tags resource

  • Full snapshot of the upstream /tags/ payload — unsorted, uncapped, includes hierarchical parents
  • Not a mirror of obsidian_list_tags: no count-descending order, no limit / nameRegex / minCount

obsidian://status resource

  • Reachability, plugin version, authenticated (whether the configured OBSIDIAN_API_KEY was accepted), and plugin manifest info
  • apiExtensions[] lists registered plugin extensions — check for local-rest-api-periodic-notes before relying on periodic targets on plugin v5.0.2 and later
  • Still reports reachability when the API key is misconfigured; only authenticated reflects the key's validity

Path policy (folder-scoped permissions)

Three optional env vars gate which vault paths each tool can target. Default unset = full vault for both reads and writes — backwards compatible.

GoalConfig
Default (current behavior)all unset
Read everywhere, write only in projects/ and scratch/OBSIDIAN_WRITE_PATHS=projects/,scratch/
Read only public/, write only public/inbox/OBSIDIAN_READ_PATHS=public/, OBSIDIAN_WRITE_PATHS=public/inbox/
Read-only deployment — no writes anywhereOBSIDIAN_READ_ONLY=true

Matching is prefix-based with implicit recursion, case-insensitive, with trailing slashes normalized. projects/ matches projects/a.md, projects/sub/b.md, etc.

Write paths are implicitly readable — you can't sanely edit what you can't see. So a read passes when the target matches READ_PATHS or WRITE_PATHS.

OBSIDIAN_READ_ONLY=true short-circuits before the path checks — every write tool and the command-palette pair are wrapped with disabledTool() at startup (absent from tools/list), and any write that still reaches the service is denied at runtime regardless of WRITE_PATHS.

Denies are typed path_forbidden (JSON-RPC code Forbidden) with the active scope echoed back in data.recovery.hint and data.activeScope, so the LLM can self-correct without inspecting server logs. Search results from obsidian_search_notes are filtered against READ_PATHS silently — surfacing a "we hid N hits" indicator would defeat the gate.

Tag listing is vault-wide. obsidian_list_tags and the obsidian://tags resource aggregate tag names across the whole vault and are not narrowed by OBSIDIAN_READ_PATHS — they take no path to gate, so tag names (never note contents) from outside the read scope can surface.

The startup banner logs the active scope so operators can verify their config at boot.

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

Obsidian-specific:

  • Wraps the Obsidian Local REST API plugin — typed client, deterministic error mapping
  • Section-aware editing across headings, block references, and frontmatter fields via PATCH-with-target operations
  • Search across three modes — text, JSONLogic, and (when reachable) BM25-ranked Omnisearch — cursor-paginated per the MCP 2025-11-25 spec
  • Tag reconciliation across both representations: frontmatter tags: array and inline #tag syntax
  • Folder-scoped read/write permissions via OBSIDIAN_READ_PATHS / OBSIDIAN_WRITE_PATHS and a global OBSIDIAN_READ_ONLY kill switch; opt-in command-palette pair gated by OBSIDIAN_ENABLE_COMMANDS
  • Server-level instructions on initialize report the active deployment — path policy, read-only mode, command-palette toggle

Agent-friendly output:

  • Recovery-guided errors — every declared failure carries a reason, a JSON-RPC code, and a recovery.hint written for that case, so a rejection names what to do next instead of only what broke
  • Size-delta self-correction — every mutating tool returns previousSizeInBytes / currentSizeInBytes, so a caller can spot an accidental clobber or unexpected upstream behavior without a follow-up read
  • Ambiguity surfaced structurally — a heading leaf name shared by several headings returns candidates instead of silently picking one; tag operations report applied vs. skipped so a caller sees exactly what changed
  • Discriminated output contracts — format on obsidian_get_note, operation on obsidian_manage_frontmatter and obsidian_manage_tags, mode on obsidian_search_notes — callers branch on typed fields instead of parsing text

Getting started

Add the following to your MCP client configuration file. The Obsidian Local REST API plugin must be installed and enabled in your vault — see Prerequisites.

{
  "mcpServers": {
    "obsidian-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["obsidian-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info",
        "OBSIDIAN_API_KEY": "your-local-rest-api-key"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "obsidian-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "obsidian-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info",
        "OBSIDIAN_API_KEY": "your-local-rest-api-key"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "obsidian-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MCP_TRANSPORT_TYPE=stdio",
        "-e", "MCP_LOG_LEVEL=info",
        "-e", "OBSIDIAN_API_KEY=your-local-rest-api-key",
        "ghcr.io/cyanheads/obsidian-mcp-server:latest"
      ]
    }
  }
}

The default OBSIDIAN_BASE_URL (http://127.0.0.1:27123) points at the container's own loopback, not your host — add -e OBSIDIAN_BASE_URL=http://host.docker.internal:27123 (Docker Desktop) or run with --network host (Linux) so the container can reach the plugin.

For Streamable HTTP, set the transport and start the server. Inline env vars work for one-off runs; for repeated use, copy values into .env (see .env.example) and run bun run start:http.

MCP_TRANSPORT_TYPE=http OBSIDIAN_API_KEY=... bun run start:http
# Server listens at http://127.0.0.1:3010/mcp by default

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • The Obsidian Local REST API plugin, v4.0.0 through v5.x, installed and enabled in your vault. Generate an API key in Settings → Community Plugins → Local REST API and copy it into OBSIDIAN_API_KEY. Plugin v6.0 removes the markdown-patch 1.x wire format this server pins for section-targeted writes and the document map.
  • Periodic-note targets (target: { "type": "periodic" }) work across that whole range: natively on plugin v5.0.1 and earlier, and on v5.0.2 and later — which moved the /periodic/ routes out of the plugin — once the companion periodic-notes API extension is installed. Without that extension on v5.0.2+, periodic targets fail with a periodic_unsupported error naming it; obsidian://status lists the registered extensions if you want to check first. Every other target type is unaffected.
  • An MCP client that can answer an input request (elicitation). obsidian_delete_note always asks for confirmation before deleting, so a client without that support can read and write notes but cannot delete one.
  • This server defaults to http://127.0.0.1:27123 for simplicity. Enable "Non-encrypted (HTTP) Server" in the plugin settings to use it. To use the always-on HTTPS port instead, set OBSIDIAN_BASE_URL=https://127.0.0.1:27124; the plugin's self-signed cert is handled by OBSIDIAN_VERIFY_SSL=false (the default).

Installation

  1. Clone the repository:

    git clone https://github.com/cyanheads/obsidian-mcp-server.git
    
  2. Navigate into the directory:

    cd obsidian-mcp-server
    
  3. Install dependencies:

    bun install
    
  4. Configure environment:

    cp .env.example .env
    # edit .env and set OBSIDIAN_API_KEY
    

Configuration

VariableDescriptionDefault
OBSIDIAN_API_KEYRequired. Bearer token for the Obsidian Local REST API plugin.—
OBSIDIAN_BASE_URLBase URL of the Local REST API plugin. Use https://127.0.0.1:27124 for the always-on HTTPS port (self-signed cert).http://127.0.0.1:27123
OBSIDIAN_VERIFY_SSLVerify the TLS certificate. Default false because the plugin uses a self-signed cert. On Node, the dispatcher's rejectUnauthorized option handles this without any process-wide change. On Bun, the runtime ignores that option, so the service additionally sets NODE_TLS_REJECT_UNAUTHORIZED=0 — that fallback is scoped to Bun only.false
OBSIDIAN_REQUEST_TIMEOUT_MSPer-request timeout in milliseconds.30000
OBSIDIAN_ENABLE_COMMANDSOpt-in flag for the command-palette pair (obsidian_list_commands + obsidian_execute_command). Off by default — Obsidian commands are opaque and can be destructive.false
OBSIDIAN_READ_PATHSComma-separated vault-relative folder allowlist for read operations. Prefix-based with implicit recursion; case-insensitive; trailing slashes normalized. Unset = full vault. Write paths are implicitly readable.unset
OBSIDIAN_WRITE_PATHSComma-separated vault-relative folder allowlist for write operations. Same syntax as OBSIDIAN_READ_PATHS. Unset = full vault.unset
OBSIDIAN_READ_ONLYGlobal kill switch. When true, denies every write regardless of OBSIDIAN_WRITE_PATHS, and suppresses the OBSIDIAN_ENABLE_COMMANDS pair (commands can mutate).false
OBSIDIAN_OMNISEARCH_URLOverride URL for the Omnisearch plugin's HTTP server. When unset, derives from OBSIDIAN_BASE_URL host with port 51361 (falling back to http://localhost:51361). Probed once at startup — if reachable, the omnisearch mode is added to obsidian_search_notes; otherwise it's omitted from the tool schema. Restart the server to re-probe.derived
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_HOSTHost for the HTTP server.127.0.0.1
MCP_HTTP_PORTPort for the HTTP server.3010
MCP_HTTP_ENDPOINT_PATHEndpoint path for the JSON-RPC handler./mcp
MCP_SESSION_MODESession handling for the HTTP transport: stateless, stateful, or auto. Pinned to stateful — obsidian_delete_note confirms via an elicitation round, which stateless disables.stateful
MCP_PUBLIC_URLPublic origin override for TLS-terminating reverse-proxy deployments (landing page, Server Card, RFC 9728 metadata).unset
MCP_AUTH_MODEAuth mode: none, jwt, or oauth.none
MCP_AUTH_SECRET_KEYRequired when MCP_AUTH_MODE=jwt. ≥32-char shared secret used to verify incoming JWTs.—
MCP_AUTH_DISABLE_SCOPE_CHECKSWhen true, bypasses per-tool scope enforcement after the auth-context presence check. Token signature, audience, issuer, and expiry validation remain intact. Use only when a custom claim can't be injected and combine with OBSIDIAN_READ_PATHS / OBSIDIAN_WRITE_PATHS / OBSIDIAN_READ_ONLY for access control. A WARNING is logged at startup whenever the bypass is active.false
MCP_LOG_LEVELLog level (RFC 5424).info
LOGS_DIRDirectory for log files (Node.js only).<project-root>/logs
OTEL_ENABLEDEnable OpenTelemetry instrumentation (spans, metrics, completion logs).false

See .env.example for the full list of optional overrides.

Running the server

Local development

  • Build and run the production version:

    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:stdio
    # or
    bun run start:http
    
  • Run checks and tests:

    bun run devcheck   # Lint, format, typecheck, security, changelog sync
    bun run test       # Vitest test suite
    bun run lint:mcp   # Validate MCP definitions against spec
    

Docker

docker build -t obsidian-mcp-server .
docker run --rm -e OBSIDIAN_API_KEY=your-key -p 3010:3010 obsidian-mcp-server

The Dockerfile defaults to HTTP transport, stateful session mode (required for the obsidian_delete_note confirmation round), and logs to /var/log/obsidian-mcp-server. Point OBSIDIAN_BASE_URL at http://host.docker.internal:27123 (Docker Desktop) or run with --network host (Linux) so the container reaches the plugin on your host. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

The image binds to 0.0.0.0 inside the container (required for Docker port mapping). For any deployment reachable beyond your own machine, set MCP_AUTH_MODE=jwt (with MCP_AUTH_SECRET_KEY) or oauth — otherwise the listener forwards your OBSIDIAN_API_KEY to the vault on behalf of every caller.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point — registers tools/resources and inits the Obsidian service.
src/configServer-specific environment variable parsing (OBSIDIAN_*) with Zod.
src/services/obsidianLocal REST API client, frontmatter operations, section extractor, domain types.
src/mcp-server/toolsTool definitions (*.tool.ts) and shared input schemas.
src/mcp-server/resourcesResource definitions (*.resource.ts).
src/mcp-server/promptsPrompt definitions (currently empty — CRUD/search shape doesn't benefit from a structured template).
tests/Vitest tests mirroring src/.
docs/Upstream OpenAPI spec for the Local REST API plugin and the generated tree.md.
changelog/Per-version release notes; CHANGELOG.md is the regenerated rollup.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools and resources via the barrels in src/mcp-server/*/definitions/index.ts
  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Contributing

Bugs, feature requests, and documentation gaps belong in an issue — see CONTRIBUTING.md for what makes one actionable, and CODE_OF_CONDUCT.md for how we work together. Security reports go through SECURITY.md, never a public issue.

Run checks and tests before submitting:

bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.

Featured
CodeRabbit
CodeRabbit
AI writes the code. CodeRabbit catches the slop.
Try For Free →
ego lite browserego lite browser
ego lite browser
Fastest browser for AI agents to run web automation tasks, always free.
Download Free life-time →
Give your AI the whole web as clean markdownGive your AI the whole web as clean markdown
Give your AI the whole web as clean markdown
Integrate web data into your AI product. One API to scrape website & brand data.
Get API Key Now →
belt - the only tool your agent needs
belt - the only tool your agent needs
belt cli automatically finds the best tools and skills for your agent. image, video, music, tts...
one prompt install →
AppSignal
AppSignal
Monitor with ease. Code with confidence.
Start Free Trial →
Agent, connect blockchain
Agent, connect blockchain
Connect your Claude agent to live crypto prices and trading routes via 1inch
Get the MCP →
inference shell
inference shell
create and run specialised agents in minutes
build now →
CodeHealth MCP ServerCodeHealth MCP Server
CodeHealth MCP Server
Protect your code quality, stop the AI slop.
Try For Free →

Configuration

OBSIDIAN_API_KEY*

Bearer token for the Obsidian Local REST API plugin (Settings → Community Plugins → Local REST API).

OBSIDIAN_BASE_URLdefault: http://127.0.0.1:27123

Base URL of the Obsidian Local REST API. Default: http://127.0.0.1:27123 (enable "Non-encrypted (HTTP) Server" in plugin settings). Use https://127.0.0.1:27124 for the always-on HTTPS port (self-signed cert; pair with OBSIDIAN_VERIFY_SSL=false).

OBSIDIAN_VERIFY_SSLdefault: false

Whether to verify the TLS certificate on the Obsidian endpoint. Default false because the plugin uses a self-signed cert.

OBSIDIAN_REQUEST_TIMEOUT_MSdefault: 30000

Per-request timeout in milliseconds.

OBSIDIAN_ENABLE_COMMANDSdefault: false

Opt-in flag for the command-palette pair (obsidian_list_commands + obsidian_execute_command). Off by default — Obsidian commands are opaque and can be destructive.

OBSIDIAN_READ_PATHS

Optional comma-separated vault-relative folder allowlist for reads. Prefix-based with implicit recursion; case-insensitive; trailing slashes normalized. Unset = full vault. Write paths are implicitly readable. Example: 'public/,projects/'.

OBSIDIAN_WRITE_PATHS

Optional comma-separated vault-relative folder allowlist for writes. Same syntax as OBSIDIAN_READ_PATHS. Unset = full vault. Example: 'projects/,scratch/'.

OBSIDIAN_READ_ONLYdefault: false

Global read-only kill switch. When true, every write is denied regardless of OBSIDIAN_WRITE_PATHS, and the command-palette pair is suppressed (commands can mutate). Useful for shared or public-facing deployments.

MCP_LOG_LEVELdefault: info

Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').

MCP_HTTP_HOSTdefault: 127.0.0.1

The hostname for the HTTP server.

MCP_HTTP_PORTdefault: 3010

The port to run the HTTP server on.

MCP_HTTP_ENDPOINT_PATHdefault: /mcp

The endpoint path for the MCP server.

MCP_AUTH_MODEdefault: none

Authentication mode to use: 'none', 'jwt', or 'oauth'.

Categories
Documents & KnowledgeSearch & Web Crawling
Registryactive
Packageobsidian-mcp-server
TransportSTDIO, HTTP
AuthRequired
Resources2
Tools verifiedJun 10, 2026
UpdatedJun 2, 2026
View on GitHub

More from cyanheads

  • Hn Mcp Server3
  • Git Mcp Server221
  • Secedgar Mcp Server5
  • Pubchem Mcp Server9
  • Cdc Health Mcp Server3
  • Met Museum Mcp Server3
  • Mcp Ts Core145
  • Courtlistener Mcp Server2
  • Open Meteo Mcp Server2
  • Treasury Fiscaldata Mcp Server2
  • Usaspending Mcp Server2
  • Worldbank Mcp Server2
  • Bls Labor Mcp Server1
  • Cpsc Recalls Mcp Server1
  • Devops Status Mcp Server1
  • Earthquake Mcp Server1
  • Eia Energy Mcp Server1
  • Eurostat Mcp Server1
  • Fcc Broadband Mcp Server1
  • Gdelt Mcp Server1
  • Onebusaway Mcp Server1
  • Openfda Mcp Server1
  • Openfec Mcp Server1
  • Openfoodfacts Mcp Server1

Related Documents & Knowledge MCP Servers

View all →
domdomegg avatar
Airtable

domdomegg/airtable-mcp-server

Read and write access to Airtable database schemas, tables, and records.
447
newtype-01 avatar
Obsidian

newtype-01/obsidian-mcp

Obsidian MCP (Model Context Protocol) Server
305
iansinnott avatar
Obsidian Claude Code

iansinnott/obsidian-claude-code-mcp

Provides a dual-transport MCP server for Claude Code and Claude Desktop to read and write your Obsidian vault via MCP.
287
ergut avatar
MCP LogSeq Server

ergut/mcp-logseq

MCP server to interact with LogSeq via its Local HTTP API - enabling AI assistants like Claude to seamlessly read, write, and manage your LogSeq graph.
267
danhilse avatar
Notion MCP Integration

danhilse/notion_mcp

A simple MCP integration that allows Claude to read and manage a personal Notion todo list
207
kaliaboi avatar
Zotero

kaliaboi/mcp-zotero

A connector for Claude Desktop to work with collection and sources on your Zotero Cloud.
159