obsidian-remote-mcp

obsidian-remote-mcp

Self-hosted MCP server that provides remote AI clients with read and write access to Obsidian vaults over HTTPS without needing the Obsidian desktop app running.

Category
Visit Server

README

obsidian-remote-mcp

A self-hosted MCP server for headless Obsidian vaults. It gives remote AI clients read and write access to a vault over HTTPS without requiring the Obsidian desktop app to be running on the same machine.

Features

This is meant for home servers, NAS boxes, VPSes, and other setups where your vault lives on disk and you want to expose it through a remote MCP endpoint for apps like Claude.ai.

  • OAuth (browser sign-in) or a fixed bearer token, depending on what the client supports.
  • vault_context serves your vault guide note (defaults to AGENTS.md) plus a shallow folder tree so agents can see your vault structure.
  • Daily notes from a path template you configure.
  • Create, edit, update, and trash notes; wikilinks and YAML frontmatter work as usual.
  • Edit just one section under a heading, without replacing the whole note.
  • Full-note updates can check the version you last read, so another agent's edit is not overwritten by accident.
  • Set or read individual frontmatter properties.
  • Read one section under a heading, or list headings first, instead of pulling the whole note every time.
  • Search by filename or regex in note text; content search can be scoped to a folder.
  • Block paths with .mcpignore; set VAULT_READ_ONLY to turn off writes.
  • Optional JSONL logs of tool calls (note bodies redacted) if you want to review what agents did.

Security and scope

Built-in safeguards:

  • OAuth 2.1 + PKCE with optional MCP_CLIENT_SECRET on token exchange
  • Fixed bearer token (MCP_STATIC_BEARER_TOKEN) for clients that skip browser auth
  • CORS allowlisting (CORS_ALLOWED_ORIGINS) for browser-based clients
  • Vault path sandboxing — all paths validated against the vault root
  • .mcpignore to block specific paths from MCP access
  • VAULT_READ_ONLY mode to prevent all writes

What it includes

The server currently exposes these tools:

Tool Description
vault_context Read the vault guidance note configured by VAULT_CONTEXT_PATH, or fall back to AGENTS.md / CLAUDE.md
vault_read Full note text (mode full, default) with a version for safe updates, or list one folder level (mode list; path "" = vault root)
vault_outline All # headings in a note (one per line); use before vault_read_section
vault_read_section Body under a single heading (heading = text without #, case-insensitive)
vault_frontmatter Read YAML frontmatter from a note; optional property for a single key
vault_links Read outgoing wikilinks and optional backlinks
vault_create Create a new note
vault_update Replace a note's full contents; optional base_version rejects stale writes
vault_set_frontmatter_property Set one frontmatter property without rewriting the note body
vault_edit Append, prepend, or replace exact text within a note
vault_edit_section Append, prepend, or replace the body under one heading
vault_trash Move a note to .trash
vault_search_title Find notes by filename (partial or exact); returns paths for vault_read
vault_search_content Regex search in note bodies; optional folder to scope large vaults
vault_daily_note Read or create a daily note using a configurable path template

Quick start

Runtime. The server is TypeScript on Bun. There is no separate build step for normal use: Bun runs src/server.ts directly. Install dependencies with bun install (Express, the MCP SDK, and a few libraries—see package.json).

Vault on disk. Point the server at your vault root with VAULT_PATH, or leave it unset and use Obsidian’s obsidian.json discovery (details under Vault path below).

git clone https://github.com/nweii/obsidian-remote-mcp.git
cd obsidian-remote-mcp
bun install
export VAULT_PATH=/absolute/path/to/your/vault
export MCP_CLIENT_ID=dev
bun run src/server.ts

bun start runs the same entrypoint (package.json maps it to bun run src/server.ts).

The process listens on port 3456 by default. The MCP endpoint is POST /mcp on that port; OAuth metadata is served under /.well-known/oauth-authorization-server.

To make the server reachable from the internet (required by Claude.ai and other remote clients), see Deployment below.

Deployment

Docker

Save this as docker-compose.yml in the cloned repo directory:

services:
  obsidian-remote-mcp:
    image: oven/bun:1              # pre-built Bun runtime — no local Bun install needed
    working_dir: /app
    restart: unless-stopped
    environment:
      MCP_CLIENT_ID: your-client-id
      MCP_CLIENT_SECRET: your-client-secret     # optional
      MCP_BASE_URL: https://mcp.example.com
      VAULT_PATH: /vault                         # path inside the container (mapped by volumes below)
      CORS_ALLOWED_ORIGINS: https://claude.ai
      PORT: 3456
      # TOKEN_STORE_PATH: /app/data/tokens.json  # uncomment to persist OAuth sign-ins across restarts
    volumes:
      - ./:/app                                  # mounts the repo into the container
      - /path/to/your/vault:/vault               # left = path on your machine, right = path inside container
      # - ./data:/app/data                       # uncomment to persist TOKEN_STORE_PATH on the host
    command: ["bun", "run", "src/server.ts"]
    ports:
      - "3456:3456"                              # host:container — access on http://localhost:3456
docker compose up -d      # start in background
docker compose logs -f    # watch output

HTTPS and MCP_BASE_URL

Remote MCP and OAuth require HTTPS. Use a reverse proxy (Caddy, nginx, Cloudflare Tunnel, etc.) to handle TLS in front of the app and expose a public URL like https://mcp.example.com. Set MCP_BASE_URL on the server to that origin without the /mcp path — it must match what users see in the browser bar.

If you use Cloudflare Zero Trust, a practical pattern is to put the identity gate only on /authorize, so users log in to approve access while /.well-known/*, /oauth/token, and /mcp stay reachable for the protocol.

Environment variables

Set these on the server process (Compose environment:, Portainer, shell export, etc.):

MCP_CLIENT_ID=your-client-id
MCP_CLIENT_SECRET=your-client-secret   # optional
MCP_BASE_URL=https://mcp.example.com
MCP_ALLOWED_REDIRECT_URIS=https://claude.ai/api/mcp/auth_callback # optional
VAULT_PATH=/path/to/your/vault         # optional if obsidian.json is available
OBSIDIAN_VAULT_ID=personal             # optional when obsidian.json contains multiple vaults
VAULT_DISPLAY_NAME=Personal            # optional; defaults to the vault directory name
VAULT_CONTEXT_PATH=AGENTS.md           # optional; defaults to AGENTS.md, then CLAUDE.md
DAILY_NOTE_PATH_TEMPLATE=Daily/{YYYY}-{MM}-{DD}.md
CORS_ALLOWED_ORIGINS=https://claude.ai # optional; defaults to *
TOKEN_STORE_PATH=./tokens.json         # optional
MCP_STATIC_BEARER_TOKEN=               # optional; fixed Bearer for /mcp (e.g. mcp-remote + Antigravity)
VAULT_READ_ONLY=true                   # optional
PORT=3456

VAULT_PATH, VAULT_CONTEXT_PATH, and DAILY_NOTE_PATH_TEMPLATE are also documented under Vault path, Vault context note, and Daily note paths later in this readme.

Authenticating to /mcp

Every POST /mcp request must send Authorization: Bearer …. You can offer OAuth, fixed bearer, or both; each client then uses whichever path it supports.

OAuth (browser)

For clients where the user can open a browser. The server uses OAuth 2.1: approve on /authorize, exchange the short-lived code at POST /oauth/token, then send the issued access token in Authorization on /mcp.

Server: MCP_CLIENT_ID is required. MCP_CLIENT_SECRET is optional; if you set it on the server, configure the same value in each OAuth client so they send it at POST /oauth/token. If you leave it unset, that step does not use a shared secret—use HTTPS and limit who can reach /authorize. MCP_BASE_URL must match the public site origin (no /mcp). MCP_ALLOWED_REDIRECT_URIS is an optional comma-separated list; Claude’s callback is allowed by default.

Persisted sign-in: TOKEN_STORE_PATH (default ./tokens.json) stores OAuth-issued tokens after login so clients survive server restarts.

Add as a remote MCP connector to Claude (paid plans only): Use your base URL with /mcp included. Under advanced settings, set OAuth client ID to your MCP_CLIENT_ID, optionally OAuth client secret to your MCP_CLIENT_SECRET (required at token exchange if the server has a secret configured). On the server, set MCP_BASE_URL to the same origin as the connector URL, without /mcp.

Cursor mcp.json:

{
  "mcpServers": {
    "obsidian-vault": {
      "url": "https://your-host/mcp",
      "auth": {
        "CLIENT_ID": "your-mcp-client-id",
        "CLIENT_SECRET": "your-mcp-client-secret (optional)"
      }
    }
  }
}

Cursor uses the redirect URI cursor://anysphere.cursor-mcp/oauth/callback. Add it to MCP_ALLOWED_REDIRECT_URIS on the server (comma-separated alongside any other clients, e.g. Claude’s callback).

Fixed bearer (non-browser)

For scripts, Antigravity + mcp-remote, or any client that cannot run the browser flow. Do not set both auth and headers on the same mcp.json entry—pick OAuth or fixed bearer.

Set MCP_STATIC_BEARER_TOKEN on the server; the client sends that exact string as Authorization: Bearer … on every /mcp request. This skips /authorize, POST /oauth/token, TOKEN_STORE_PATH for that client.

Antigravity mcp_config.json:

"obsidian-vault": {
  "serverUrl": "https://your-host/mcp",
  "headers": {
    "Authorization": "Bearer YOUR_MCP_STATIC_BEARER_TOKEN"
  }
}

Cursor mcp.json:

{
  "mcpServers": {
    "obsidian-vault": {
      "url": "https://your-host/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_MCP_STATIC_BEARER_TOKEN"
      }
    }
  }
}

Bridging over stdio with mcp-remote:

"obsidian-vault": {
  "command": "bunx",
  "args": ["-y", "mcp-remote", "https://your-host/mcp", "--transport", "http-only", "--header", "Authorization: Bearer YOUR_MCP_STATIC_BEARER_TOKEN"]
}

CORS

CORS_ALLOWED_ORIGINS limits which browser origins may call the API from JavaScript. It is separate from OAuth and from MCP_STATIC_BEARER_TOKEN. Default is * (allow all). To restrict:

CORS_ALLOWED_ORIGINS=https://claude.ai
CORS_ALLOWED_ORIGINS=https://claude.ai,http://localhost:3000

When set, only listed origins get a reflected Access-Control-Allow-Origin; other browser preflights fail.

Vault path

The server resolves the vault root in this order:

  1. VAULT_PATH, if set
  2. .config/obsidian/obsidian.json, found by walking up from the current working directory or from the package directory

If obsidian.json contains multiple vaults, set OBSIDIAN_VAULT_ID to the vault entry you want to use.

The display name used in the OAuth approval page defaults to the resolved vault directory name. You can override that with VAULT_DISPLAY_NAME.

Headless / no Obsidian installed

Most users should just set VAULT_PATH and skip this. If you prefer the automatic discovery path on a machine without Obsidian, create the config file yourself:

mkdir -p ~/.config/obsidian

~/.config/obsidian/obsidian.json:

{
  "vaults": {
    "personal": {
      "path": "/home/user/vaults/personal"
    }
  }
}

With multiple vaults, add more entries and set OBSIDIAN_VAULT_ID to the one you want. Use absolute paths — ~ is not expanded inside obsidian.json.

Vault context note

vault_context is meant to help agents learn your vault structure before they start writing. By default it looks for AGENTS.md or CLAUDE.md.

If your vault uses a different bootstrap file, set VAULT_CONTEXT_PATH to the relative path you want the tool to read.

If you do not want to maintain one, the server still works without it.

Daily note paths

vault_daily_note uses DAILY_NOTE_PATH_TEMPLATE, which defaults to:

Daily/{YYYY}-{MM}-{DD}.md

This is only a convenience tool. Many vaults use different daily note layouts, so you will likely want to override it.

Supported tokens:

  • {YYYY}: 4-digit year
  • {YY}: 2-digit year
  • {MM}: 2-digit month
  • {M}: month without zero padding
  • {DD}: 2-digit day
  • {D}: day without zero padding
  • {MMM}: short month name like Mar
  • {MMMM}: full month name like March
  • {dd}: short weekday name like Th
  • {ddd}: short weekday name like Mon
  • {dddd}: full weekday name like Monday

Examples:

DAILY_NOTE_PATH_TEMPLATE=Daily/{YYYY}/{YYYY}-{MM}-{DD}.md
DAILY_NOTE_PATH_TEMPLATE=Journal/{YYYY}-{MM}-{DD}-{dddd}.md
DAILY_NOTE_PATH_TEMPLATE=Journal/{YYYY}/{MMM}/{D}-{ddd}.md

Notes

MCP and HTTP

  • GET /mcp returns 405, not 404, so streamable HTTP clients know the server only accepts MCP over POST.

Vault access and tool defaults

  • All vault paths are validated against the resolved vault root to prevent directory traversal.
  • .mcpignore in the vault root can block paths from all MCP access.
  • VAULT_READ_ONLY=true blocks all write operations.
  • vault_search_title defaults to limit=50; vault_search_content defaults to limit=20. Limits are adjustable; 0 means no limit.
  • vault_frontmatter and vault_set_frontmatter_property let agents work with frontmatter properties without reading or rewriting the whole note body.
  • vault_read returns a version block. Pass it to vault_update as base_version if you want stale full-note updates to fail instead of overwriting another edit.

Tests

bun test

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured