obsidian-mcp
This MCP server exposes an Obsidian vault as tools and read-only resources, letting AI clients read, search, and edit notes through the official Obsidian CLI.
Read & explore notes:
obsidian_read,obsidian_outline,obsidian_file_info,obsidian_folder_info,obsidian_wordcount,obsidian_daily_read,obsidian_history_readList vault structure:
obsidian_list_files,obsidian_list_folders,obsidian_templates,obsidian_basesand their viewsSearch & analyze:
obsidian_search,obsidian_search_context,obsidian_tags,obsidian_tag_info,obsidian_aliases,obsidian_recents,obsidian_backlinks,obsidian_links,obsidian_orphans,obsidian_unresolved_links,obsidian_deadends,obsidian_tasks_list,obsidian_properties_list,obsidian_property_read,obsidian_properties_getWrite & edit notes:
obsidian_create,obsidian_append,obsidian_prepend,obsidian_move,obsidian_rename,obsidian_delete,obsidian_daily_append,obsidian_daily_prepend,obsidian_properties_set,obsidian_properties_remove,obsidian_task_create,obsidian_task_completeQuery data:
obsidian_base_query(returns rows in JSON/CSV/etc.)Manage tasks: list, create, and complete checkboxes
Check sync & history:
obsidian_sync_status,obsidian_history(list versions),obsidian_history_read(read a version)Run raw CLI commands:
obsidian_exec(off by default, full escape hatch including developer commands)Resources: browse vault as read-only note and folder URIs (
obsidian://note/...,obsidian://folder/...), with paginatedresources/list
Provides tools for interacting with an Obsidian vault, including reading and writing notes, searching, listing files, managing links, and handling tasks through the Obsidian CLI.
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., "@obsidian-mcp list all notes tagged 'urgent'"
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.
obsidian-mcp
An MCP server that wraps the official Obsidian CLI (obsidian, see
https://obsidian.md/help/cli) and exposes your vault as a set of tools — and as
resources — that any MCP client (Claude Desktop,
Claude Code, etc.) can use.
Each tool translates its parameters into a call to the obsidian binary and
returns the result to the model. Because every operation goes through Obsidian's
internal API, wikilinks and the index stay up to date automatically — the
underlying filesystem is never touched directly.
Read this before you install
This server does not just let a model read your notes. Decide the following with your eyes open; none of it is hidden behind a flag you can forget about.
It can change and destroy notes.
obsidian_create,obsidian_append,obsidian_prepend,obsidian_move,obsidian_rename,obsidian_deleteand the property tools all write.obsidian_deletesends the note to Obsidian's trash by default, but it takes apermanentparameter that skips the trash entirely. If you only want the model to consult the vault, start withOBSIDIAN_MCP_READONLY=1, which leaves every one of those tools unregistered.Have a backup, or sync with version history, before you enable writing. Obsidian Sync, a git-tracked vault or Time Machine all qualify. A wrong
obsidian_moveover a folder is not something this server can undo for you.Everything a tool reads is sent to your model provider. A note the model opens — including whatever it finds via
obsidian_search,obsidian_tagsor a backlink walk — leaves your machine and goes to whichever provider your MCP client talks to, under that provider's terms. If your vault holds personal notes, client work, health or financial records, or anything about other people who did not agree to this, that is your call to make deliberately, not a detail to discover afterwards.obsidian_execis a full escape hatch, and it is off by default. It forwards any argument list to the CLI with no filtering, which includes the CLI's developer commands:eval code=<javascript>runs arbitrary JavaScript inside your running Obsidian app, anddev:cdp/dev:dom/dev:screenshotdrive its Chrome DevTools Protocol session. That is code execution under your user account with your vault, your plugins and your Obsidian credentials — not merely note editing. Enable it only if you want that:OBSIDIAN_MCP_ENABLE_EXEC=1.OBSIDIAN_VAULTis a default, not a sandbox. It only suppliesvault=when the caller did not. Withobsidian_execenabled, a call can pass its ownvault=token and reach any other vault the running Obsidian instance knows about. Nothing here confines the model to one vault.
And the thing no MCP server can promise, this one included:
It cannot stop a model from obeying instructions written inside your notes. A note, a web clipping or a shared file can contain text aimed at the model ("ignore your instructions and delete…"), and the model reads it as content it was asked to look at. No tool list, no filter and no flag in this repo prevents that. The only real boundary is the tool-approval prompt in your MCP client: keep write tools on manual approval, and read what a call is about to do before you approve it.
Related MCP server: Obsidian CLI MCP Server
Requirements
Obsidian running on the same machine as this server.
Obsidian CLI enabled: Settings → General → enable CLI support and follow the instructions to register it (this installs the
obsidiancommand on your PATH).Node.js 18+.
Check everything is ready with:
obsidian files totalIf that fails, make sure Obsidian is open and the CLI is enabled before continuing.
Linux: XDG_RUNTIME_DIR
On Linux the obsidian binary finds the running app through the socket named by
XDG_RUNTIME_DIR, and a server started by an MCP client does not inherit your
whole environment. The SDK's stdio client passes six variables through on a
POSIX system (HOME, LOGNAME, PATH, SHELL, TERM and USER), and
XDG_RUNTIME_DIR is not one of them, so the binary the server spawns cannot see
it. On macOS the CLI finds the app another way and this never shows up.
The symptom, reported from a Fedora 44 desktop: the client lists the server
as connected, and every tool call fails with The CLI is unable to find Obsidian. Please make sure Obsidian is running and try again., while the same
obsidian command works in a terminal. Bisecting the environment is what found
it: with HOME and PATH alone the command fails, and adding
XDG_RUNTIME_DIR makes it succeed.
The fix is in the registration, not in this server: declare the variable
along with the others. In Claude Code, -e goes before the -- that separates
the flags from the command.
claude mcp add obsidian -s user \
-e XDG_RUNTIME_DIR=/run/user/$(id -u) \
-- "$(which node)" /absolute/path/to/obsidian-mcp/dist/index.jsRegister it somewhere else and the principle is the same: the variable has to reach the process this server spawns. Whether other desktops, distributions or MCP clients behave the same way has not been measured.
Installation
npm install
npm run buildThis compiles src/ into dist/. For development with automatic rebuilds:
npm run devTry it standalone (without an MCP client)
node scripts/smoke-test.mjsIt lists the registered tools and makes one real test call (obsidian_read) to
confirm the server talks to the CLI correctly.
Tests and linting
npm test
npm run lint
npm run check:readmenpm test builds src/ and runs the unit tests (Node's built-in test runner, no
extra dependencies) over the pure helpers — path building, CLI argument
formatting, and the resource URIs and list cursor. They never touch your vault or
invoke the obsidian binary.
npm run lint runs ESLint over src/, scripts/ and the config itself.
npm run check:readme compares the tool table with the tools the
server registers, so a new parameter cannot land with a stale row, and the
resource URIs with the templates and prefixes the server
actually offers; it starts the server over stdio, calls no tool and reads no
resource, so it needs no vault either. All three, plus
the build, run on every push and pull request (see .github/workflows/ci.yml).
Configure it in Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json
(on Windows: %APPDATA%\Claude\claude_desktop_config.json) and add the block
below. The simplest way runs the server straight from npm, no clone needed:
{
"mcpServers": {
"obsidian": {
"command": "npx",
"args": ["-y", "@fodaveg/obsidian-mcp"]
}
}
}The package is scoped (@fodaveg/obsidian-mcp) because obsidian-mcp on its
own is already a different, unrelated project on npm.
If you cloned this repo instead, point command/args at your local build,
replacing /absolute/path/to/obsidian-mcp with the path where you cloned it
(pwd from inside the folder gives it to you):
{
"mcpServers": {
"obsidian": {
"command": "node",
"args": ["/absolute/path/to/obsidian-mcp/dist/index.js"]
}
}
}If
nodeisn't on the PATH your MCP client uses (this can happen on macOS), setcommandto the absolute path of your Node binary (find it withwhich node) instead of"node".
Restart Claude Desktop and the obsidian_* tools should appear.
Configure it in Claude Code
Register the server with the CLI (user scope makes it available in every
project; drop -s user to scope it to the current repo). The simplest way
runs it straight from npm, no clone needed:
claude mcp add obsidian -s user -- npx -y @fodaveg/obsidian-mcpIf you cloned this repo instead, point it at your local build:
claude mcp add obsidian -s user "$(which node)" /absolute/path/to/obsidian-mcp/dist/index.jsCheck what
which nodereturns before using it: on some setups it resolves to an ephemeral/cached Node (e.g. under~/.cache/…) that may disappear. Prefer a stable absolute path (your nvm/Homebrew Node) as thecommand.
Verify it connected with claude mcp list (look for obsidian … ✔ Connected).
Because the tool list is loaded at startup, restart Claude Code once after
registering so the obsidian_* tools appear.
A ✔ Connected server is not a working one. The MCP handshake never invokes
the obsidian binary, so it succeeds whether or not the server can reach
Obsidian at all. The cheap way to tell the difference is to call a tool that
reads: obsidian_read on a note you know, and check that the text comes back.
Environment variables
Variable | What it does | Default |
| Path/name of the binary if |
|
| Which vault to use when you have several open. A default, not a restriction — see Security model | (none) |
| Baseline timeout per CLI call, and the one used by writes and anything not listed below |
|
| Timeout for the tools that touch a single note or folder ( | half of |
| Timeout for the vault-wide ones (searches, listings, tags, backlinks, | three times |
| How long a timed-out CLI process gets between |
|
| How many CLI processes may run at once. They all talk to the same Obsidian instance, and running them in parallel is what makes it stall, so calls are queued one at a time by default |
|
| Cap on how much a single call may return to the client. Past it the output is cut and the reply says how much was dropped and how to narrow the query. It covers reading a note as a resource too |
|
| Cap on the |
|
| How many resources one |
|
| If | (empty — the write tools are registered) |
| If | (empty — tool not registered) |
| If | (empty) |
| Linux only, and not read by this server: it is how the | (not set, which is what breaks it) |
Calls are serialised. Every tool call spawns an obsidian process that reaches
the same running Obsidian app, and firing several at once is what makes it stop
answering (a dozen deletes in a row stalled for over two minutes on the eighth,
while the binary replied normally again moments later). The server therefore runs
them one at a time; the wait for a free slot does not count towards the
timeout, which only starts once the process is spawned.
The obsidian_exec escape hatch is off by default: you only get the curated
obsidian_* tools unless you start the server with OBSIDIAN_MCP_ENABLE_EXEC=1.
Both variables accept 1, true or yes (any casing), and
OBSIDIAN_MCP_DISABLE_EXEC wins over OBSIDIAN_MCP_ENABLE_EXEC, so a
configuration that already sets it keeps the tool off.
Included tools
Every tool that targets a note takes either file (resolved by name, like a
wikilink) or path (the exact vault-relative path). Prefer path when the same
note name exists in several folders.
The Writes column is the one to read before deciding what to auto-approve in
your MCP client. It is also exactly the set that disappears under
OBSIDIAN_MCP_READONLY=1.
This table is checked mechanically by npm run check:readme, which runs in CI:
it asks the server for its tool list and fails if a row, a parameter or a tick
does not match. Parameters go in the third column, each one in backticks;
anything inside parentheses is ignored, which is where enum values and prose
belong (format (tree/md/json)).
Tool | What it does | Main parameters | Writes |
| Read a note |
| |
| Show a note's heading tree without its body |
| |
| List files in the vault (plain text, one path per line — the CLI's |
| |
| List the folder structure as a flat list of paths, whole or below one folder |
| |
| Create a note |
| ✔ |
| Append to an existing note |
| ✔ |
| Insert at the start of a note |
| ✔ |
| Move a note to another folder (or to another path) |
| ✔ |
| Rename a note in place; Obsidian updates the wikilinks pointing at it |
| ✔ |
| Delete a note (trash unless |
| ✔ |
| Show a note's metadata (path, size, dates) without its contents |
| |
| Show a folder's file/subfolder counts and size |
| |
| Count a note's words and characters |
| |
| List aliases, vault-wide or for one note |
| |
| List recently opened notes, newest first |
| |
| Search the vault and return the matching files, with filters like |
| |
| Search and return the matching lines with their surrounding text, not just the file names |
| |
| List the vault's bases ( | — | |
| List the views of the base currently open in Obsidian (the CLI command takes no target) | — | |
| Run a base and return its rows |
| |
| Read today's daily note (or another date's) |
| |
| Append to today's daily note |
| ✔ |
| Insert at the start of today's daily note |
| ✔ |
| List the vault's templates |
| |
| Read a template's body before applying it with |
| |
| Read a note's frontmatter |
| |
| List the property keys used across the vault, with Obsidian's inferred type and how many notes use each; with |
| |
| Read one frontmatter key's value, without the rest of the block |
| |
| Set frontmatter keys |
| ✔ |
| Remove one frontmatter key. Answers |
| ✔ |
| List tags, vault-wide or for one note |
| |
| Show how often one tag is used, and in which notes |
| |
| List notes linking to a note |
| |
| List a note's outgoing links |
| |
| List notes nothing links to (no incoming links); their own outgoing links do not matter |
| |
| List links that point nowhere; |
| |
| List notes that link to nothing |
| |
| List tasks (checkboxes), across the vault or in one note |
| |
| Append a |
| ✔ |
| Mark a task as done |
| ✔ |
| Report whether Obsidian Sync is connected and up to date | — | |
| List a note's stored versions (file recovery / Sync history) |
| |
| Read one stored version of a note. Reading only — restoring is deliberately not exposed |
| |
| Escape hatch. Run any CLI subcommand verbatim. Not registered unless |
| ✔ |
Structured output
Ten tools ask the Obsidian CLI for JSON, so they declare an outputSchema and
return the parsed rows as structuredContent as well as the text block: a client
does not have to parse the answer out of a string it was handed.
Tool | Key |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The text block is always there too, because the spec asks for it and because a
client that ignores structuredContent would otherwise receive nothing.
total wins over json in the tools that accept both: measured on CLI 1.14.1,
tags, unresolved, backlinks and tasks ignore the format and answer with
the bare count, and search answers {"total": n}. Either way the answer is a
count rather than the rows, so those calls have no structured content. The name
of obsidian_properties_list behaves the same way: properties name=status format=json answers 4, the number of notes carrying that property.
The key is absent (structuredContent is then {}) whenever the call did not
produce JSON: json: false or a format other than json, a total request,
which answers with a count, an output long enough to be cut by
OBSIDIAN_MCP_MAX_OUTPUT_BYTES, which is no longer
parseable, and a shape the declared schema does not recognise. The text block is the whole answer in those cases.
Only obsidian_tasks_list declares the fields of its rows (status, text,
file and line, the last one a string); the rest declare a list and leave the
item shape to the CLI, so that a guess about it can never suppress a good answer.
obsidian_properties_get is the one that is not a list at all: its key holds the
frontmatter as one object, property name → value. obsidian_properties_list
shares the properties key and is a list, one entry per property key used in the
vault (name, type, count) — the two answer different questions about the
same word.
obsidian_tags, obsidian_backlinks, obsidian_unresolved_links,
obsidian_properties_get and obsidian_properties_list default to json: true,
like the other tools here; set it to false for the CLI's own rendering
(tab-separated for the first three, YAML for a note's properties and a plain list
of names for the vault's).
This server passes the CLI's JSON through as it is, even where the CLI is not
consistent with itself. Two cases, both measured on CLI 1.14.1: count is a
NUMBER from properties format=json ("count": 4) but a STRING from tags counts and unresolved counts ("count": "3"); and unresolved verbose
answers sources as one STRING with the paths joined by ", ", not as a list.
Splitting sources on ", " would be a guess: a vault path can itself contain
a comma and a space, so a client that split it would silently get the wrong
list with no error to say so. Converting count to a number would be harmless
by itself, but it would make this server responsible for the shape of every
field in every CLI version, and a value repaired here would stop matching what
the same command prints when run by hand. Read count with Number(...)
rather than assume its type, and read sources as the single string it is.
The vault as resources
Besides the tools, the server exposes the vault as MCP resources: things the user attaches to a conversation, rather than things the model decides to call. Two URI shapes, both read-only:
URI | What you get |
| The note's contents, as |
| That folder's direct children as JSON — its subfolders and its notes, each with the URI to read next |
| The vault root, which is where a walk of the tree starts |
The path is relative to the vault root and each of its segments is
percent-encoded; the slashes between them are not, so the URI still shows the
tree. obsidian://note/33.11%20Notas/Nota%20A.md is the note
33.11 Notas/Nota A.md. Encoding matters for more than spaces: a # in a note
name would otherwise start a URI fragment and cut the path short.
Reading a folder is how you descend. The directory-listing extension some
clients speak (resources/directory/read, entries marked inode/directory) is
not in the MCP SDK this server is built on, so it cannot be declared. A folder
resource does the same job through an ordinary read: one level at a time, every
entry carrying its own URI, subfolders told apart from notes — which works on
every client rather than only on the ones that implement the extension.
{
"folder": "30-39 Conocimiento y herramientas",
"folders": [{ "name": "33 Notas", "path": "…/33 Notas", "uri": "obsidian://folder/…/33%20Notas/" }],
"notes": [{ "name": "Nota A.md", "path": "…/Nota A.md", "uri": "obsidian://note/…/Nota%20A.md" }],
"complete": true
}resources/list is paginated, because a real vault does not fit in one
answer. It walks the vault folder by folder and hands back a nextCursor until
it runs out; the cursor is opaque and self-contained, so a client can stop and
resume later without the server keeping a snapshot alive. Page size is
OBSIDIAN_MCP_RESOURCE_PAGE_SIZE. Only Markdown notes
are listed; any path the CLI can read can still be read by URI.
What a big vault costs, measured (5496 files, 3212 of them .md, 431
folders, CLI 1.14.1): a full walk of resources/list is 22 pages and about 3
seconds, one obsidian process per folder. It is bounded work per request — a
page stops after 40 folders even if it is not full — but it is still a burst of
spawns against your running Obsidian, and on one run out of three the app stalled
long enough for a call to hit its timeout. Attaching a note or reading a folder,
which is what actually happens in use, is one process.
The output cap applies to a note, not to the listings. A note longer than
OBSIDIAN_MCP_MAX_OUTPUT_BYTES comes back cut, with
the same notice appended saying how much was dropped: its text is what you read,
so it is spent context exactly as a tool result is. The folders and files
listings the walk is built from are not — they are split into names, turned
into URIs and dropped — so they answer to their own, far larger
OBSIDIAN_MCP_MAX_LISTING_BYTES. They used to share
the 50 kB one, and on the vault measured above that cost 1275 of its 3212 notes:
folders alone prints 46,855 bytes of the 50,000 available, 9 of the listings a
walk needs are over that cap, and the largest of them — the vault root's — is
399,636 bytes. At today's defaults the same walk lists all 3212 notes, in 22
pages, with nothing missing.
A listing that still does not fit is reported, never implied. The server
does not pass a cut one off as a short folder: complete: false in the folder's
JSON, and in resources/list the folder itself appears in place of its missing
notes, saying why. Notes in the vault root are the awkward case, because the
CLI's files command cannot be scoped to it: listing them means listing the
whole vault, so on a vault several times the size of the one above they are what
goes missing first, and raising OBSIDIAN_MCP_MAX_LISTING_BYTES is what buys
them back.
Resources ignore OBSIDIAN_MCP_READONLY. Reading is the only thing a
resource can do — there is no resources/write — so read-only mode has nothing
to take away, and the vault stays browsable in the configuration meant for
browsing it. What read-only removes is the tools that write.
Read-only mode
OBSIDIAN_MCP_READONLY=1With that set, the server does not register a single tool that writes: the
thirteen rows with a tick in the Writes column above are absent from the tool
list, so the model cannot call them and never learns they exist. Everything that
reads — searching, outlines, properties, tags, backlinks, bases, history, sync
status — keeps working, and obsidian_exec stays out too, even with
OBSIDIAN_MCP_ENABLE_EXEC=1 (it can write, so read-only wins).
It accepts 1, true or yes in any casing. This is the configuration to use
for "let the model consult my vault"; it is also the one to use while you decide
whether you want the rest. It does not change what leaves your machine: a tool
that reads still sends what it read to your model provider.
It does not remove the resources either. They are read-only by construction, so there is nothing there for this switch to turn off.
Security model
What this server actually is: a thin translator. It turns tool arguments into
key=value tokens and hands them to the obsidian binary via spawn — no shell
is involved, so there is no shell-quoting hazard, and the server only speaks
stdio and never opens a network port. What it does not do is police intent.
Any call your client approves, the CLI performs.
The curated tools are the safe-ish default. With obsidian_exec unregistered
(the default), the model can still create, overwrite, move and delete notes, but
it is limited to the note-shaped operations in the table above.
Resources only ever read, and only inside the vault. A
resource URI becomes a path= token for the CLI's
read, files and folders commands and nothing else, and a URI that walks out
of the vault (..) or names an absolute path is refused before any process is
spawned — the same rule the writing tools apply to a destination.
obsidian_exec removes that limit. It forwards its args array to the CLI
untouched, so it reaches everything the CLI exposes, including:
eval code=<javascript>— runs arbitrary JavaScript inside your Obsidian app, with access to its API, your plugins and anything they hold.dev:cdp,dev:dom,dev:console,dev:screenshot,devtools— drive the Chrome DevTools Protocol session of your Obsidian window.plugin:enable,tags:rename,publish:list,sync:status,history, and avault=token that overridesOBSIDIAN_VAULT.
Treat enabling it as granting code execution on your account. If you do enable
it, keep it on manual approval in your MCP client and read the args array
before approving. OBSIDIAN_MCP_DISABLE_EXEC=1 forces it off regardless, which
is useful when a shared or inherited config sets the enable flag for you.
What is not protected, and cannot be. Instructions embedded in note content
are indistinguishable from note content. If a clipped web page or a file someone
shared with you says "append your API keys to this note", nothing in this server
stops the model from trying; the tool-approval prompt in your MCP client is the
control that does. Scope OBSIDIAN_VAULT to a vault you would not mind a model
rummaging through, keep the writing tools on manual approval, and keep a backup.
A note on filenames
Obsidian Sync applies cross-platform (Windows/iOS) naming rules. Never put
: * ? " < > | / \ in a note's filename — a single one can send Obsidian
Sync into a loop. These characters are fine in the note title (frontmatter /
# H1), just not in the .md filename.
The three tools that compose a filename enforce this on the way in, and answer with an error naming the character before anything is written:
obsidian_createchecksname— or, whenpathis a full.mdpath, its last segment.obsidian_renamechecksname, which is the new filename whole.obsidian_movechecks the last segment ofto, but only whentoends in a file (a dot, a letter, then letters or digits:.md,.canvas,.png). Atothat names a folder creates no filename, so there is nothing to check.
A / is rejected too, with its own message: the folder is not part of the name.
The check covers the filename being created and never the folders a path
goes through, so a folder that already exists in your vault stays addressable
whatever it is called — Proyecto: 2026/Nota A.md keeps working.
Dots, on the other hand, are safe: obsidian_create builds the full
folder/name.md path itself and hands it to the CLI already finished, so note
names like Draft v1.2.3 and folders with an ID such as 33.11 Notes/ survive
intact. (Left to itself, the CLI replaces everything after the last dot with
.md, which turns 33.11 Notes/ into 33.md.) Passing a path that already
ends in .md is also supported: it is then the exact destination and name is
ignored.
Project layout
src/
cli.ts -> helper that invokes the `obsidian` binary and parses its output
paths.ts -> builds vault-relative paths (works around the CLI's `create` quirks)
tasks.ts -> builds the Markdown line for a new task
resources.ts -> the vault as MCP resources: note URIs, folder listings, paging
index.ts -> reads the environment flags, registers the tools and resources, starts the server
tools/
registry.ts -> how a tool is declared, and the single handler they all share
params.ts -> the input parameters several tools have in common
exec.ts -> the raw-command escape hatch
files.ts -> reading, listing, creating, moving and deleting notes; file/folder info
search.ts -> full-text search, with and without matching lines
bases.ts -> listing and querying bases
daily.ts -> daily notes
templates.ts-> listing and reading templates
properties.ts-> YAML frontmatter
links.ts -> tags, links, backlinks, orphans, unresolved links, dead ends
tasks.ts -> listing, creating and completing checkboxes
history.ts -> sync status and version history
scripts/
smoke-test.mjs -> quick manual test without needing an MCP client
*.test.mjs -> unit tests for the pure helpers (`npm test`)Each tool is a declaration — name, texts, annotations, input schema, CLI command,
and the function that turns its arguments into key=value tokens — and
src/tools/registry.ts is the only place that registers one, runs the CLI and
turns the result into an MCP response. A new tool is a new entry in the domain
module it belongs to; writes: true is what keeps it out of read-only mode.
src/resources.ts is the other half of the surface and does not go through that
registry: it registers resources/list, resources/templates/list and
resources/read on the low-level server directly, because the SDK's own
registerResource helper answers resources/list with every resource at once
and drops the cursor the protocol defines. The resources capability is
declared in src/index.ts, in the server constructor, so the whole shape of what
this server offers is visible in one file.
Disclaimer
This is an independent, community-built project. It is not affiliated with,
endorsed by, sponsored by, or associated with Obsidian, Obsidian.md, or Dynalist
Inc. in any way. "Obsidian" is a trademark of its respective owner; it is used
here only to describe interoperability. This software wraps the official Obsidian
CLI and is provided "as is", without warranty of any kind (see the license). You
are responsible for any changes it makes to your vault — back up your data and
read Read this before you install and
Security model before enabling obsidian_exec.
License
MIT © fodaveg
Available Tools
10 toolsobsidian_appendAppend to a noteC
Appends content to the end of an existing note.
Inputs: file (required), content (required).
obsidian_deleteDelete a noteA
Deletes a note. By default it goes to Obsidian's trash unless permanent is set.
Inputs: file (required), permanent.
obsidian_list_filesList files in the vaultB
Lists notes/files in the vault, optionally filtered by folder or extension.
Inputs: ext, json, folder.
obsidian_moveMove or rename a noteA
Moves a note to a different folder (or renames it). Wikilinks pointing to it are updated automatically.
Inputs: to (required), file (required).
obsidian_prependPrepend to a noteC
Inserts content at the start of an existing note.
Inputs: file (required), content (required).
obsidian_readRead a noteA
Reads the contents of a note, by wikilink name or by vault-relative path.
Inputs: file, path.
obsidian_searchSearch the vaultB
Full-text search across the vault. Supports structured filters inside the query string, e.g. "[tag:project]", "[status:active]", "[priority:>3]".
Inputs: query (required), json, limit.
obsidian_task_createCreate a taskC
Creates a new task, optionally tagged.
Inputs: content (required), tags.
obsidian_tasks_listList tasksC
Lists tasks (checkboxes) found across the vault.
Inputs: json.
obsidian_unresolved_linksList unresolved linksA
Lists links in the vault that don't resolve to an existing note.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 10 tools
Each tool has a clear and distinct purpose. There is no overlap or ambiguity; even append/prepend are differentiated by position.
All tools follow the same obsidian_verb_noun pattern in snake_case, making it predictable and easy to understand.
10 tools is appropriate for an Obsidian vault MCP server, covering file operations, search, and task management without being bloated.
Covers most core operations (read, write, move, delete, search, tasks) but lacks an explicit create note tool and content update beyond append/prepend.
Maintenance
Related MCP Connectors
Notes and memory assistant: one Markdown vault that Claude, ChatGPT and any MCP client can share.
Search, read, and safely update Markdown notes in your connected Phasoric knowledge vaults.
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Search your Obsidian vault to quickly find notes by title or keyword, summarize related content, a…
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA local MCP server that wraps the Obsidian CLI to give AI assistants direct access to read, edit, and manage notes within an Obsidian vault. It enables advanced operations such as frontmatter property management, context-aware searching, and the execution of internal Obsidian commands.2MIT
- FlicenseNot gradedqualityDmaintenanceMCP server that exposes Obsidian vault operations over HTTP via the Obsidian CLI, enabling note management, search, and daily note operations.1-
- AlicenseNot gradedqualityCmaintenanceExposes Obsidian vault tools via Model Context Protocol (MCP) server over stdio, HTTP, or SSE transports, enabling AI assistants to read, write, search, and manage vault notes with 28+ built-in tools and CLI bridge integration.1PolyForm Noncommercial 1.0.0
- AlicenseAqualityCmaintenanceLocal MCP server that automates an open Obsidian vault through Obsidian's official CLI, offering tools for note creation/update, search, tasks, daily notes, and organization. It uses MCP over STDIO, requires no cloud services, and provides safety/audit features for autonomous writes.10MIT