Skip to main content
Glama
Obsidian Readonly MCP

Obsidian Readonly MCP

by eothL

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 app

The 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 traversal

  • builds obsidian argv directly without shell interpolation

  • caps 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 command

  • obsidian eval

  • app 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:

  • search

  • search_context

  • read

  • daily_read

  • daily_path

  • backlinks

  • links

  • outline

  • aliases

  • tags

  • tag

  • unresolved

  • properties

  • property_read

  • help

Filesystem/index reads through Obsidian:

  • file

  • files

  • folder

  • folders

  • wordcount

  • deadends

  • orphans

  • recents

  • random_read

  • vault

  • vaults

  • version

Structured features:

  • bases

  • base_query

  • base_views

  • bookmarks

  • tasks

  • templates

  • template_read

  • snippets

  • snippets_enabled

History/config metadata reads:

  • diff

  • history

  • history_list

  • history_read

  • hotkey

  • hotkeys

  • plugins

  • plugin

  • plugins_enabled

  • theme

  • themes

  • tabs

  • workspace

  • workspaces

  • commands

Developer read-only inspection:

  • dev_console

  • dev_errors

  • dev_css

  • dev_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.json

  • skills/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__outline

Example 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.py

End-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.py

Companion 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

OBSIDIAN_READONLY_TIMEOUT_SECONDS

30

Positive command timeout in seconds.

OBSIDIAN_READONLY_LOG_DIR

Platform default below

Local log directory; set to off to disable.

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 -v

Bounded 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.log

The 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 tools
aliasesC

List aliases.

Inputs: vault, file, path, format, +4 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.

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.

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

    C2.7/5.0

    Scored across 55 tools

    Disambiguation4/5

    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 Consistency4/5

    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.

    Tool Count3/5

    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.

    Completeness5/5

    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

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Provides read-only access to an Obsidian vault, enabling file listing, content reading, and text search across notes via MCP.
      4
      2
      Apache 2.0
    • A
      license
      Not graded
      quality
      B
      maintenance
      Read-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 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables 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.
      1
      MIT