Obsidian Readonly MCP
This server is a read-only MCP bridge to a running Obsidian vault, letting agents query notes and vault state without filesystem or shell access.
Search notes (
search,search_context,tags,tag)Read notes and daily/random notes, including bounded section/line reads (
read,daily_read,daily_path,random_read)Explore note graph:
backlinks,links,outline,aliases,unresolved,deadends,orphansInspect metadata and files:
properties,property_read,wordcount,file,files,folder,foldersWork with structured features:
bases,base_query,base_views,bookmarks,tasks,templates,template_readRead history and diffs:
history,history_list,history_read,diffInspect configuration:
hotkey/hotkeys,plugins,plugin,plugins_enabled,theme,themes,snippets,snippets_enabled,tabs,workspace,workspaces,commandsGet vault/version/help info:
vault,vaults,version,helpInspect dev data:
dev_console,dev_errors,dev_css,dev_domhealthchecks server/vault connectivity (documented in README)All operations are read-only, with path validation, output caps, timeouts, and no shell interpolation.
Provides tools for querying a running Obsidian vault, including search, read, backlinks, links, outline, tags, properties, and other read-only operations.
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 Readonly MCP search for 'agent harness memory' in my vault"
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 Readonly MCP
Read-only MCP tools for querying a running Obsidian vault from Codex or another agentic runtime.
Why this exists
The Obsidian CLI is not just a filesystem reader; it is connected to your main Obsidian app. Commands such as backlinks, links, outline, and indexed search need to talk to the running Obsidian app. In agentic runtimes, the agent's filesystem sandbox can block or destabilize that app/IPC boundary. For example, running obsidian help inside an agent sandbox can cause the Obsidian app to crash.
Giving the whole agent unsandboxed shell access fixes the IPC problem, but it gives the agent too much permission. This MCP server is the narrower solution:
sandboxed agent
-> read-only MCP tool call
-> host-side MCP server
-> obsidian CLI
-> running Obsidian appThe agent can stay sandboxed. The host-side MCP server gets permission to reach Obsidian, but it exposes only a fixed allowlist of read-only Obsidian commands.
Related MCP server: obsidian-local-mcp-server
Safety model
This server:
exposes only read-only Obsidian operations
rejects unknown tools
validates vault-relative paths and rejects
..path traversalbuilds
obsidianargv directly without shell interpolationcaps output size
times out long calls
It does not expose:
note creation or editing
delete, move, rename, append, prepend
property mutation
plugin/theme install, enable, disable, reload, uninstall
obsidian commandobsidian evalapp reload/restart/open/tab/workspace mutation
browser/web/open actions
Feel free to modify it to add these features if you need them.
Tools
Core vault reads:
searchsearch_contextreaddaily_readdaily_pathbacklinkslinksoutlinealiasestagstagunresolvedpropertiesproperty_readhelp
Filesystem/index reads through Obsidian:
filefilesfolderfolderswordcountdeadendsorphansrecentsrandom_readvaultvaultsversion
Structured features:
basesbase_querybase_viewsbookmarkstaskstemplatestemplate_readsnippetssnippets_enabled
History/config metadata reads:
diffhistoryhistory_listhistory_readhotkeyhotkeyspluginspluginplugins_enabledthemethemestabsworkspaceworkspacescommands
Developer read-only inspection:
dev_consoledev_errorsdev_cssdev_dom
Install With uvx
You can run the MCP server directly from GitHub without cloning the repo:
[mcp_servers.obsidian_readonly]
command = "uvx"
args = [
"--from",
"git+https://github.com/eothL/obsidian-readonly-mcp",
"obsidian-readonly-mcp"
]
[mcp_servers.obsidian_readonly.env]
OBSIDIAN_READONLY_VAULT = "Your Vault Name"Use the Obsidian vault name, not the filesystem path. It should be the same value you would pass to obsidian vault="Your Vault Name" ....
If this package is later published to PyPI, the config can become:
[mcp_servers.obsidian_readonly]
command = "uvx"
args = ["obsidian-readonly-mcp"]
[mcp_servers.obsidian_readonly.env]
OBSIDIAN_READONLY_VAULT = "Your Vault Name"Restart Codex or start a new thread so the MCP server is discovered.
Install From A Local Clone
Clone this repo somewhere stable, then add the MCP server to your Codex config:
[mcp_servers.obsidian_readonly]
command = "python3"
args = ["/absolute/path/to/obsidian-readonly-mcp/src/obsidian_readonly_mcp.py"]
[mcp_servers.obsidian_readonly.env]
OBSIDIAN_READONLY_VAULT = "Your Vault Name"Restart Codex or start a new thread so the MCP server is discovered.
Codex Plugin
This repository also includes Codex plugin metadata:
.codex-plugin/plugin.json.mcp.jsonskills/obsidian-readonly-mcp/SKILL.md
The plugin bundles the MCP server and the companion skill so Codex can learn to prefer the read-only Obsidian tools for vault exploration. The plugin MCP config does not hard-code a vault name; set OBSIDIAN_READONLY_VAULT in your environment or pass the vault argument in tool calls.
Usage
Once loaded, tools are exposed as names such as:
mcp__obsidian_readonly__search
mcp__obsidian_readonly__read
mcp__obsidian_readonly__backlinks
mcp__obsidian_readonly__links
mcp__obsidian_readonly__outlineExample tool arguments:
{"query": "agent harness memory", "limit": 10}{"file": "Agent harness memory", "counts": true, "format": "json"}Local smoke test
With Obsidian open:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05"}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| python3 src/obsidian_readonly_mcp.pyEnd-to-end read:
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search","arguments":{"query":"agent harness","limit":3}}}' \
| python3 src/obsidian_readonly_mcp.pyCompanion Skill
This repo includes a Codex skill in skills/obsidian-readonly-mcp/. Install or copy that skill into your Codex skills directory if you want agents to automatically prefer these tools for Obsidian vault exploration.
Read-only diagnostics and timeouts
The read-only server writes local JSON-line diagnostic logs. MCP stdout remains reserved for protocol responses. Events record request receipt, command start, command completion or timeout, cleanup, and response delivery, linked by an internally generated trace ID. Logs include timestamps, server PID, tool name, exit code, elapsed milliseconds, and output byte counts. They exclude request IDs, arguments, vault/note paths, queries, note contents, and raw stdout/stderr. Raw stderr remains available in the tool response as before; it is not persisted to logs. This protects diagnostic logs but does not anonymize tool results.
Setting | Default | Purpose |
|
| Positive command timeout in seconds. |
| Platform default below | Local log directory; set to |
On macOS, logs are in ~/Library/Logs/obsidian-readonly-mcp/server.log.
Elsewhere they are in $XDG_STATE_HOME/obsidian-readonly-mcp/server.log, falling
back to ~/.local/state/obsidian-readonly-mcp/server.log. Logs rotate at 5 MiB,
keeping the current file and two backups (about 15 MiB total). New directories
are private and log files use mode 0600. Use a separate log directory for each
concurrently running server instance; rotation is intended for one writer.
A log setup failure reports a generic warning on stderr and does not prevent
serving requests. Logging adds no dependencies.
Call health with {} or {"vault":"Your Vault Name"} to check both the server
and an actual vault command (vault info=name). It returns responsiveness,
elapsed time and the configured timeout, with isError when the probe fails.
This checks the app connection, not just whether the CLI executable exists.
A failed probe does not by itself distinguish a closed app from an IPC failure.
MCP ping checks only the server.
On POSIX, timed-out CLI commands and helpers in their dedicated process group are killed. Pipe cleanup has its own one-second limit, avoiding an unbounded wait when a helper retains output pipes. The running Obsidian app is not targeted. On other platforms, only the direct CLI process is killed. Requests are handled serially, so concurrent client calls can queue behind an active command; the command timeout begins when that command is launched.
To investigate a delay, inspect the last event for a trace: command_started
without completion points to the CLI/app boundary; command_timeout confirms
the server timeout fired; response_sent means the response was flushed to the
client transport. No request_received event suggests checking startup or the
client connection (also verify logging is enabled and writable).
After modifying this repository, restart the MCP process using this checkout. A server configured to run a separate copied script does not pick up these changes automatically. The changes do not explain past hangs without diagnostic evidence.
Diagnostics and workflow regression tests run without Obsidian:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=src python3 -m unittest discover -s tests -vBounded retrieval and batches
read accepts either heading (exact ATX Markdown heading text without the
leading #, including subsections), or start_line / end_line (1-based,
inclusive). Full reads are unchanged. Use outline to discover headings and
search_context to find line numbers. Duplicate headings require a line range.
Setext headings are not matched by heading; use a line range for those.
Fenced code and YAML frontmatter are excluded from heading matching.
The MCP still fetches the note through Obsidian and filters it before returning
it; it does not cache notes or fall back to disk. A truncated source is rejected
for partial reads rather than silently returning an incomplete section.
{"path":"Notes/GPU.md","heading":"Memory hierarchy"}{"path":"Notes/GPU.md","start_line":20,"end_line":50}batch accepts 1–8 independent read-only calls. Results retain input order and
have individual isError flags. It runs commands sequentially under one shared
command timeout and output budget; nesting and unknown tools are rejected.
Arguments (including vault when supported) belong on each child call. Budget
exhaustion returns explicit errors for omitted results. Prefer narrow searches
and selected sections over batching many full documents. Batching reduces MCP
call overhead, not the number of underlying CLI operations by itself.
{"calls":[{"tool":"outline","arguments":{"path":"Notes/GPU.md"}},{"tool":"links","arguments":{"path":"Notes/GPU.md"}}]}The runtime exposes run_command and main(request_handler) so a project adapter
can retain stricter schemas and a pinned vault while reusing logging and timeout
fixes. Updating a repository checkout affects new server processes; already
running servers must restart. Detached script copies do not update automatically.
Protocol compatibility
This server uses local stdio and the legacy initialize handshake. It does not
store conversation history or note caches, but it has not implemented the modern
2026-07-28 stateless wire protocol (server/discover and per-request metadata).
Initialization negotiates a supported legacy version instead of echoing an
unsupported modern version. No HTTP endpoint or session service is introduced.
Evaluating real tool use
Unit tests verify correctness; they cannot establish whether an agent chooses a
tool or produces a better answer. Use a fresh, read-only agent session with a
fixed task/model and codex exec --json, keeping the prompt independent of the
feature being evaluated. Save traces outside the repo: they contain private note
contents. Run the same prompt with baseline and candidate tool catalogs, then:
python3 scripts/summarize_trace.py /path/to/run.jsonl --server-log /path/to/server.logThe summary excludes note text and arguments. Compare logical operations as well as MCP calls, partial-read use, output size, budget/error events, cached versus uncached input tokens, and a manually scored answer-quality rubric. Repeat tasks before claiming stable savings. Suggested fixtures: a short note, a long lecture with relevant subsections, ambiguous headings, a missing note, a timeout, and a large search response. Check that conclusions remain supported, contradictory notes are noticed, and missing/error results are not treated as evidence.
Failure cooldown
After two consecutive infrastructure failures for the same vault, commands return
an immediate tool error with status: cooldown and retry_after_seconds. The
process does not sleep or launch more CLI subprocesses during the pause, including
for children of a batch. OBSIDIAN_READONLY_COOLDOWN_SECONDS defaults to 60;
set it to 0 to disable. Counters are process-local, per vault, and bounded to 64
vault entries. Expiry permits another attempt; successful execution clears the
failure streak. A deliberate health call bypasses the pause and clears it if
successful. Repeated health polling defeats this protection.
Launch failures, command timeouts and infrastructure command errors count. Input validation failures and recognized missing-file/folder/property/heading responses do not. No missing note is treated as proof that the Obsidian app is broken. Cooldown state contains no note data or conversation history; it is operational health state, compatible with a future stateless protocol implementation.
The MCP always executes the Obsidian CLI directly (argv, no shell). It does not
replace failures with Bash commands, direct .md reads, or cached notes. The
companion skills likewise instruct agents to report unavailability and respect
the retry interval instead of falling back to direct filesystem retrieval.
Available Tools
55 toolsaliasesC
List aliases.
Inputs: vault, file, path, format, +4 more.
backlinksC
List backlinks to a note.
Inputs: vault, file, path, counts, +2 more.
base_queryD
Query a base view.
Inputs: vault, file, path, view, +1 more.
basesC
List base files.
Inputs: vault, total.
base_viewsB
List views in the current base file.
Inputs: vault.
bookmarksC
List bookmarks.
Inputs: vault, format, total, counts, +1 more.
commandsC
List available commands.
Inputs: vault, filter.
daily_pathB
Return the daily note path.
Inputs: vault.
daily_readC
Read the daily note.
Inputs: vault.
deadendsB
List files with no outgoing links.
Inputs: vault, total, all.
dev_consoleC
Show captured console messages.
Inputs: vault, limit, level.
dev_cssC
Inspect CSS with source locations.
Inputs: selector (required), vault, prop.
dev_domC
Query DOM elements.
Inputs: selector (required), vault, total, text, +4 more.
dev_errorsC
Show captured errors.
Inputs: vault.
diffC
List or diff local/sync versions.
Inputs: vault, file, path, from_version, +2 more.
fileC
Show file info.
Inputs: vault, file, path.
filesD
List files.
Inputs: vault, folder, ext, total.
folderC
Show folder info.
Inputs: path (required), vault, info.
foldersD
List folders.
Inputs: vault, folder, total.
helpA
Show Obsidian CLI help.
Inputs: vault, command.
historyC
List file history versions.
Inputs: vault, file, path.
history_listC
List files with history.
Inputs: vault.
history_readC
Read a file history version.
Inputs: vault, file, path, version.
hotkeyC
Get hotkey for a command.
Inputs: id (required), vault, verbose.
hotkeysC
List hotkeys.
Inputs: vault, format, total, verbose, +1 more.
linksC
List outgoing links from a note.
Inputs: vault, file, path, total.
orphansC
List files with no incoming links.
Inputs: vault, total, all.
outlineC
Return note headings / outline.
Inputs: vault, file, path, format, +1 more.
pluginC
Get plugin info.
Inputs: id (required), vault.
pluginsC
List installed plugins.
Inputs: vault, format, filter, versions.
plugins_enabledC
List enabled plugins.
Inputs: vault, format, filter, versions.
propertiesD
List properties.
Inputs: vault, file, path, name, +6 more.
property_readC
Read a property value from a note.
Inputs: name (required), vault, file, path.
random_readB
Read a random note without opening it.
Inputs: vault, folder.
readC
Read a note.
Inputs: vault, file, path.
recentsC
List recently opened files.
Inputs: vault, total.
searchC
Read-only Obsidian keyword search.
Inputs: query (required), vault, limit, path, +3 more.
search_contextC
Read-only Obsidian search with surrounding line context.
Inputs: query (required), vault, limit, path, +2 more.
snippetsC
List installed CSS snippets.
Inputs: vault.
snippets_enabledA
List enabled CSS snippets.
Inputs: vault.
tabsC
List open tabs.
Inputs: vault, ids.
tagC
Get tag info.
Inputs: name (required), vault, total, verbose.
tagsC
List tags.
Inputs: vault, file, path, format, +5 more.
tasksD
List tasks.
Inputs: vault, file, path, format, +7 more.
template_readD
Read a template.
Inputs: name (required), vault, resolve, title.
templatesC
List templates.
Inputs: vault, total.
themeC
Show active theme or theme info.
Inputs: vault, name.
themesC
List installed themes.
Inputs: vault, versions.
unresolvedC
List unresolved links.
Inputs: vault, format, total, counts, +1 more.
vaultC
Show vault info.
Inputs: vault, info.
vaultsC
List known vaults.
Inputs: vault, total, verbose.
versionA
Show Obsidian version.
Inputs: vault.
wordcountC
Count words and characters.
Inputs: vault, file, path, words, +1 more.
workspaceC
Show workspace tree.
Inputs: vault, ids.
workspacesC
List saved workspaces.
Inputs: vault, total.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 55 tools
Most tools have distinct purposes, but there is minor overlap (e.g., search and search_context, history variants). Overall, an agent can differentiate between them with careful reading.
Naming uses lowercase snake_case and a mix of verb+noun and noun-only patterns. Some inconsistencies (e.g., singular vs plural) but generally predictable.
55 tools is high for a readonly server, but the coverage is broad. It may overwhelm agents, yet each tool has a clear role within the Obsidian domain.
The tool surface covers nearly all observable aspects of Obsidian: files, notes, properties, tags, links, history, search, templates, plugins, themes, etc. No major gaps for a readonly use case.
Related MCP Connectors
Hosted MCP server for Obsidian Sync vaults: read, search, edit notes. Not affiliated with Obsidian.
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Read-only MCP server exposing a user ORANO library to their own AI agent.
Serve a folder of Markdown notes as an MCP server: hybrid search, reading, and sourced answers.
Related MCP Servers
- AlicenseAqualityDmaintenanceProvides read-only access to an Obsidian vault, enabling file listing, content reading, and text search across notes via MCP.42Apache 2.0
- AlicenseNot gradedqualityCmaintenanceExposes a local Obsidian vault as a stateless MCP server, letting AI coding agents search, read, browse, and edit notes in real time.20 npmMIT
- AlicenseNot gradedqualityBmaintenanceRead-only MCP server that exposes an Obsidian vault to LLM clients over Streamable HTTP, letting them browse the accessible tree, read notes, run full-text searches, and resolve wiki-links and backlinks within a share-filtered note set. Access is limited by an operator-editable sharing config that is default-deny and never writes to the vault.3,512 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables LLM clients to read, search, and write to a local Obsidian vault over MCP, with full-text search, vault analytics, health scoring, and policy-enforced file operations.1MIT