obsidian-mcp-server

Read, search and edit notes, headings, tags and frontmatter in an Obsidian vault

By Casey Hand (individual) TypeScript Local · stdio ProductivityKnowledge and memory

70 popularity score ★ 702 ↓ 7.7k/wk

Connect your client

Local (npm)

Add in one click

Opens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.

Or add it by command or config

It needs OBSIDIAN_API_KEY. Replace each <NAME> with your value.

Claude CodeTerminal
claude mcp add obsidian -e OBSIDIAN_API_KEY=<OBSIDIAN_API_KEY> -- npx -y obsidian-mcp-server
Claude Desktopclaude_desktop_config.json
{
  "mcpServers": {
    "obsidian": {
      "command": "npx",
      "args": [
        "-y",
        "obsidian-mcp-server"
      ],
      "env": {
        "OBSIDIAN_API_KEY": "<OBSIDIAN_API_KEY>"
      }
    }
  }
}
Cursor~/.cursor/mcp.json
{
  "mcpServers": {
    "obsidian": {
      "command": "npx",
      "args": [
        "-y",
        "obsidian-mcp-server"
      ],
      "env": {
        "OBSIDIAN_API_KEY": "<OBSIDIAN_API_KEY>"
      }
    }
  }
}
VS Code.vscode/mcp.json
{
  "servers": {
    "obsidian": {
      "type": "stdio",
      "command": "npx",
      "args": [
        "-y",
        "obsidian-mcp-server"
      ],
      "env": {
        "OBSIDIAN_API_KEY": "<OBSIDIAN_API_KEY>"
      }
    }
  }
}
Codex~/.codex/config.toml
[mcp_servers.obsidian]
command = "npx"
args = ["-y", "obsidian-mcp-server"]

[mcp_servers.obsidian.env]
OBSIDIAN_API_KEY = "<OBSIDIAN_API_KEY>"
Gemini CLITerminal
gemini mcp add --scope user -e OBSIDIAN_API_KEY=<OBSIDIAN_API_KEY> obsidian npx -- -y obsidian-mcp-server
Local (container)

Add in one click

Opens the app, which asks before it adds the server. Then replace each <NAME> in its settings with your value.

Or add it by command or config

It needs OBSIDIAN_API_KEY. Replace each <NAME> with your value.

Claude CodeTerminal
claude mcp add obsidian -e OBSIDIAN_API_KEY=<OBSIDIAN_API_KEY> -- docker run -i --rm -e MCP_TRANSPORT_TYPE=stdio -e OBSIDIAN_API_KEY -e OBSIDIAN_BASE_URL=http://host.docker.internal:27123 ghcr.io/cyanheads/obsidian-mcp-server:latest
Claude Desktopclaude_desktop_config.json
{
  "mcpServers": {
    "obsidian": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "MCP_TRANSPORT_TYPE=stdio",
        "-e",
        "OBSIDIAN_API_KEY",
        "-e",
        "OBSIDIAN_BASE_URL=http://host.docker.internal:27123",
        "ghcr.io/cyanheads/obsidian-mcp-server:latest"
      ],
      "env": {
        "OBSIDIAN_API_KEY": "<OBSIDIAN_API_KEY>"
      }
    }
  }
}
Cursor~/.cursor/mcp.json
{
  "mcpServers": {
    "obsidian": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "MCP_TRANSPORT_TYPE=stdio",
        "-e",
        "OBSIDIAN_API_KEY",
        "-e",
        "OBSIDIAN_BASE_URL=http://host.docker.internal:27123",
        "ghcr.io/cyanheads/obsidian-mcp-server:latest"
      ],
      "env": {
        "OBSIDIAN_API_KEY": "<OBSIDIAN_API_KEY>"
      }
    }
  }
}
VS Code.vscode/mcp.json
{
  "servers": {
    "obsidian": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "-e",
        "MCP_TRANSPORT_TYPE=stdio",
        "-e",
        "OBSIDIAN_API_KEY",
        "-e",
        "OBSIDIAN_BASE_URL=http://host.docker.internal:27123",
        "ghcr.io/cyanheads/obsidian-mcp-server:latest"
      ],
      "env": {
        "OBSIDIAN_API_KEY": "<OBSIDIAN_API_KEY>"
      }
    }
  }
}
Codex~/.codex/config.toml
[mcp_servers.obsidian]
command = "docker"
args = ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "-e", "OBSIDIAN_API_KEY", "-e", "OBSIDIAN_BASE_URL=http://host.docker.internal:27123", "ghcr.io/cyanheads/obsidian-mcp-server:latest"]

[mcp_servers.obsidian.env]
OBSIDIAN_API_KEY = "<OBSIDIAN_API_KEY>"
Gemini CLITerminal
gemini mcp add --scope user -e OBSIDIAN_API_KEY=<OBSIDIAN_API_KEY> obsidian docker -- run -i --rm -e MCP_TRANSPORT_TYPE=stdio -e OBSIDIAN_API_KEY -e OBSIDIAN_BASE_URL=http://host.docker.internal:27123 ghcr.io/cyanheads/obsidian-mcp-server:latest

Settings 11

OBSIDIAN_API_KEY secret Required: API key from the Local REST API plugin settings.
OBSIDIAN_BASE_URL Optional: Plugin address; use https://127.0.0.1:27124 for the HTTPS port or host.docker.internal from Docker. Default http://127.0.0.1:27123.
OBSIDIAN_VERIFY_SSL Optional: Check the plugin's TLS certificate; off because the plugin's certificate is self-signed. Default false.
OBSIDIAN_READ_ONLY Optional: true turns off every write tool and the command tools. Default false.
OBSIDIAN_ENABLE_COMMANDS Optional: true enables the two command-palette tools. Default false. For obsidian_list_commands, obsidian_execute_command.
OBSIDIAN_DELETE_ELICITATION Optional: true asks the user to confirm each delete; needs a client that supports elicitation. Default false. For obsidian_delete_note.
OBSIDIAN_OMNISEARCH_URL Optional: Omnisearch server address; taken from the base URL host on port 51361 when unset. For obsidian_search_notes.
OBSIDIAN_REQUEST_TIMEOUT_MS Optional: Per-request timeout in milliseconds. Default 30000.
Set together: path policy
OBSIDIAN_READ_PATHS Optional: Comma-separated vault folders the tools may read; unset means the whole vault.
OBSIDIAN_WRITE_PATHS Optional: Comma-separated vault folders the tools may write; unset means the read scope or the whole vault.
OBSIDIAN_DENY_PATHS Optional: Comma-separated paths kept from reads and writes alike, overriding both allowlists.

In a container, a setting goes in with -e NAME=value after docker run.

What it does

obsidian-mcp-server talks to a running Obsidian vault through the community Local REST API plugin. The server never opens vault files on disk. Each read and write goes as an HTTP call to the plugin inside the Obsidian app, so Obsidian has to stay open while you use this.

On the read side you get a note whole, as a structured record, as a map of its headings, or one section at a time. Folder and tag listings come with filters. Search takes plain text or a JSONLogic query over path, content, frontmatter and tags, and a ranked omnisearch mode shows up when the Omnisearch plugin answers at startup. Edits can be aimed at a single heading, block reference or frontmatter field instead of the whole file. There are also tools for regex replacements, frontmatter keys, tags and deletes. Every write reports the note's size before and after the change, which makes an accidental clobber easy to spot. Three resources sit next to the tools: a note by path, the full tag list, and the plugin's status.

Of the 14 tools, 12 are active by default. The two command-palette tools stay off until OBSIDIAN_ENABLE_COMMANDS is true. With OBSIDIAN_READ_ONLY set, the seven write tools and the command pair drop out and five remain. One of those five is obsidian_open_in_ui, because the code registers it with the read tools (src/index.ts and tools/definitions/index.ts). Folder allowlists and a deny list narrow what the tools can reach. The config code lowercases each entry and refuses to start when one is an absolute path, contains '..' or names the vault root (src/config/server-config.ts). Transport is stdio by default, with a local Streamable HTTP mode as the other choice.

What it needs

RuntimeNode.js 24+
Runs onmacOS, Windows, Linux
Also
  • Obsidian running with the Local REST API plugin v4.0.0 or later enabled

Copy the API key from Settings, Community Plugins, Local REST API in Obsidian and set it as OBSIDIAN_API_KEY. The default address http://127.0.0.1:27123 needs the plugin's 'Non-encrypted (HTTP) Server' switched on. Otherwise set OBSIDIAN_BASE_URL to https://127.0.0.1:27124. From Docker, point OBSIDIAN_BASE_URL at http://host.docker.internal:27123. Periodic-note targets on plugin v5.0.2 and later need the periodic-notes API extension. The omnisearch mode needs the Omnisearch plugin's HTTP server.

Limits

  • Limit Works only while Obsidian is open with the plugin reachable; calls fail with obsidian_unreachable otherwise
  • Not covered No backlinks tool; a JSONLogic regex search over content stands in
  • Size cap obsidian_list_notes stops at 1,000 entries and walks 2 levels by default, up to 20 with depth
  • Size cap obsidian_list_tags returns 200 tags by default, up to 10000 with limit
  • Size cap Omnisearch keeps its top 50 hits before quoted phrases and exclusions are applied
  • Limit Omnisearch is probed once at startup, so the server must restart to pick it up
  • Not covered With OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS set, JSONLogic searches lose the links and backlinks fields
  • Limit On plugin v4.x, heading writes to notes with CRLF line endings land mid-line
  • Limit Plugin v6.0 breaks two table-row write forms; target the table by an ID on its last row instead
  • Limit Deletes have no undo beyond Obsidian's local trash

Works only while Obsidian is open with the plugin reachable; calls fail with obsidian_unreachable otherwise. There is no backlinks tool (a JSONLogic regex search stands in). obsidian_list_notes stops at 1,000 entries and goes 2 levels deep by default (up to 20); obsidian_list_tags returns 200 tags by default, up to 10000; omnisearch results are capped at 50 by the plugin. Omnisearch is probed once at startup, so the server must restart to pick it up. On plugin v4.x, heading writes to notes with CRLF line endings land mid-line; plugin v6.0 breaks two table-row write forms. Deletes have no undo beyond Obsidian's trash.

Who it suits

Suits someone who keeps notes in Obsidian on their own machine and wants an assistant to make targeted edits (one heading, one frontmatter key, a tag) rather than rewrite whole files, and who is willing to install the Local REST API plugin. The read and write allowlists, the deny list and the read-only switch make it workable for letting an assistant into part of a vault while keeping a private folder out. It is a community project, not from Obsidian. It is not a fit for a vault on a machine where Obsidian is not running, such as a server or a synced folder read without the app.

Tools 14

5 only read, 6 change things, 3 can delete or overwrite.

obsidian_get_note
Reads a note as raw text, structured form, heading map or one section
obsidian_list_notes
Lists files and folders under a vault path with depth, extension and name filters
obsidian_list_tags
Lists vault tags ranked by how often they are used
obsidian_search_notes
Searches notes by text, a JSONLogic query, or Omnisearch when available
obsidian_write_note deletes
Creates a note, replaces one section, or overwrites a file when told to
obsidian_append_to_note changes
Adds text to the end of a note or of one heading, block or frontmatter field
obsidian_patch_note changes
Appends, prepends or replaces content at one heading, block or field
obsidian_replace_in_note changes
Runs literal or regex find-and-replace within one note
obsidian_manage_frontmatter changes
Gets, sets or removes a single frontmatter key
obsidian_manage_tags changes
Adds, removes or lists a note's tags in frontmatter, body or both
obsidian_delete_note deletes
Deletes a note, with optional user confirmation first
obsidian_open_in_ui changes
Opens a file in the Obsidian window, creating it if asked to
obsidian_list_commands
Lists Obsidian command-palette commands; off unless enabled
obsidian_execute_command deletes
Runs a command-palette command by its ID; off unless enabled

Resources 3

obsidian://vault/{+path}
One note's content, frontmatter, tags and file details
obsidian://tags
Every readable tag with its count, unsorted and uncapped
obsidian://status
Plugin reachability, sign-in state, versions and API extensions

Similar servers