Skip to main content
Glama
Obsidian Remote MCP Server

Obsidian Remote MCP Server

Obsidian Remote MCP Server

HOL Guard Version License: MIT Protocol Node.js Companion Skills

Obsidian Remote MCP Server is an enterprise-grade capability runtime connecting AI assistants and agents (ChatGPT, Claude, Codex, Cursor, and Hermes Agent) to an Obsidian vault. It provides strongly-typed semantic operations, timing-safe Bearer authentication, vault path isolation, dual-transport support (stdio and Streamable HTTP/SSE), and systemd supervision for production VPS hosting.

This server is accompanied by the official companion repository: Universal Obsidian Suite (dharmikbhesaniya/obsidian-skill), which provides standardized Agent Skills, prompt rules, and pre-packaged IDE plugins for these tools.


Table of Contents


Related MCP server: mcp-obsidian

System Architecture

                    Internet (Remote AI Clients)
                    [ChatGPT / Claude / Hermes]
                                │
                                │ HTTPS / MCP (Streamable HTTP)
                                ▼
               ┌─────────────────────────────────┐
               │    Reverse Proxy / TLS (HTTPS)  │
               │         Caddy / Nginx           │
               │      https://mcp.domain.com     │
               └────────────────┬────────────────┘
                                │ HTTP (127.0.0.1:9435)
                                ▼
               ┌─────────────────────────────────┐
               │       Obsidian MCP Server       │
               │                                 │
               │  • Bearer Token Authentication  │
               │  • Scoped Permission Matrix     │
               │  • Path Traversal Guard         │
               │  • Rate Limiting & Audit Log    │
               │  • Zod-validated tool inputs    │
               └────────────────┬────────────────┘
                                │
          ┌─────────────────────┼─────────────────────┐
          ▼                     ▼                     ▼
     Typed Tools            Resources              Prompts
    (51 Semantic)         (URI Context)         (Guided Workflows)
          │                     │                     │
          └─────────────────────┼─────────────────────┘
                                ▼
                       Service Layer
                    (Concurrency & Rules)
                                │
                                ▼
                      Obsidian CLI Adapter
                                │
                                ▼
                       Obsidian Desktop
                     (Headless via Xvfb)
                                │
                                ▼
                      Hostinger VPS Vault
                       (/srv/obsidian/vault)

Feature & Capability Summary

The server exposes 62 strongly-typed semantic tools (61 standard tools including sandboxed Git operations, plus an advanced CLI escape hatch gated behind ENABLE_ADVANCED_CLI=true), 7 direct read resources, and 7 guided prompts:

Tools (62 Semantic Capabilities)

  • Vault Domain (4 tools): obsidian_list_vaults (multi-vault inventory with deduplication), obsidian_get_vault, obsidian_list_files (recursive traversal & extension filtering), obsidian_get_file_info

  • Notes Domain (8 tools): obsidian_read_note, obsidian_create_note, obsidian_append_note, obsidian_prepend_note, obsidian_update_note, obsidian_patch_note (surgical search/replace/regex editing), obsidian_move_note, obsidian_delete_note (safe atomic trash by default)

  • Search Domain (2 tools): obsidian_search, obsidian_search_context

  • Context & Discovery Domain (3 tools): obsidian_get_note_context (selective include budget controls), obsidian_find_notes, obsidian_recent_changes

  • Daily Notes Domain (3 tools): obsidian_read_daily_note, obsidian_append_daily_note, obsidian_prepend_daily_note

  • Bookmarks Domain (2 tools): obsidian_list_bookmarks, obsidian_create_bookmark

  • Outline & Structure Domain (2 tools): obsidian_get_outline, obsidian_outline (hierarchical heading tree with line offsets)

  • Aliases Domain (2 tools): obsidian_list_aliases, obsidian_get_aliases (frontmatter alias resolution)

  • Templates Domain (2 tools): obsidian_list_templates, obsidian_read_template (variable resolution for {{title}}, {{date}}, {{time}})

  • Metrics & Stats Domain (1 tool): obsidian_word_count (words, characters, sentences, paragraphs, reading time)

  • Random & Unique Notes (2 tools): obsidian_random_note, obsidian_create_unique_note (Zettelkasten style with entropy and guaranteed collision prevention)

  • Desktop Integration (2 tools): obsidian_open_note, obsidian_open_in_app (desktop app launch with accurate execution reporting and URI fallback)

  • Plugins & Customization (2 tools): obsidian_list_plugins, obsidian_list_snippets

  • Commands Domain (2 tools): obsidian_list_commands, obsidian_execute_command

  • Properties Domain (4 tools): obsidian_get_properties, obsidian_get_property, obsidian_set_property, obsidian_remove_property

  • Tasks Domain (2 tools): obsidian_list_tasks, obsidian_toggle_task

  • Knowledge Graph Domain (6 tools): obsidian_get_backlinks, obsidian_get_links, obsidian_get_link_path, obsidian_get_orphans, obsidian_get_unresolved_links, obsidian_get_deadends

  • Tags & Bases Domain (4 tools): obsidian_get_tags, obsidian_get_tag_notes, obsidian_list_bases, obsidian_query_base

  • Git Subsystem Domain (8 tools): obsidian_git_status (working tree and branch inspection), obsidian_git_diff (staged or unstaged unified diffs), obsidian_git_log (structured commit history), obsidian_git_add (safe path staging), obsidian_git_commit (atomic commit creation), obsidian_git_push (upstream remote push), obsidian_git_pull (upstream remote fetch and merge), obsidian_git_exec (guarded allowlisted Git command execution)

  • Guarded Escape Hatch (1 tool): obsidian_cli (strictly allowlisted through central command registry, disabled by default, gated by ENABLE_ADVANCED_CLI=true and vault:developer scope)

Resources (7 Direct Read Contexts)

  • obsidian://vault: Vault statistics, file count, and connectivity state.

  • obsidian://daily/today: Content of today's daily note.

  • obsidian://tasks: Vault-wide pending task inventory.

  • obsidian://tags: Inventory of all tags and occurrence frequencies.

  • obsidian://note/{path}: Read-only access to a specific note.

  • obsidian://folder/{path}: Directory listing for a vault folder.

  • obsidian://base/{path}: Schema and view definitions of a .base file.

Prompts (7 Standard Workflows)

  • daily-work-report: Synthesizes today's daily note and tasks into an executive summary.

  • knowledge-capture: Extracts reusable lessons and decisions from meeting/daily notes into permanent knowledge notes.

  • weekly-review: Aggregates the last 7 daily notes and active project milestones.

  • monthly-review: Conducts a broad retrospective across deliverables and patterns.

  • project-review: Evaluates project status, open tasks, and incoming backlinks.

  • meeting-summary: Extracts action items and decision records from raw meeting notes.

  • vault-health-check: Scans for broken wikilinks and orphan notes.


Companion Agent Skills & Plugins Suite

For complete instruction sets, prompts, and IDE rule definitions that empower AI coding agents to use these MCP tools autonomously, see the official companion repository:

👉 Universal Obsidian Suite (dharmikbhesaniya/obsidian-skill)

What the Companion Suite Provides

  • obsidian-mcp Skill: Clear operational instructions, tool schemas, and best practices for AI agents executing these 62 semantic tools.

  • Claude Code & Antigravity Plugins: Discoverable plugins (.claude-plugin/) and pre-configured .mcp.json definitions to register this MCP server automatically.

  • Cursor Rules & MDC: Ready-to-copy .cursorrules and .cursor/rules/obsidian.mdc files.

  • OpenAI & ChatGPT Tools: Pre-built JSON function definitions (function_tools.json).

  • Complementary CLI & Markdown Skills: Official Obsidian CLI commands, Obsidian Flavored Markdown (OFM), database Bases (.base), and JSON Canvas (.canvas) specifications.


Access Control & Multi-Vault Architecture

Access control in the Obsidian MCP Server is an enterprise authorization framework that isolates knowledge vaults, restricts filesystem operations, and provides deterministic multi-vault access across all AI clients and agent runtimes.

The architecture eliminates directory escape vulnerabilities and guarantees that AI assistants operate strictly within authorized vaults.

flowchart TD
    AI["AI Client (ChatGPT / Claude / Cursor / Antigravity)"]
    Router["Multi-Vault Router & PathGuard"]
    
    subgraph Storage ["Mounted Storage"]
        V1["Primary Vault (read/write)"]
        V2["Personal Vault (read-only)"]
        Auto["Auto-Discovered Vaults (Configured Default Mode)"]
    end

    AI -->|"1. obsidian_list_vaults"| Router
    AI -->|"2. Tool Call { vault: 'name', ... }"| Router
    Router -->|"Permitted (Scope Check Passed)"| V1
    Router -->|"Permitted Read / Blocked Write (403)"| V2
    Router -->|"Dynamic Detection on Query"| Auto

Core Access Control Principles

  1. Vault Sandboxing (PathGuard): Every registered vault possesses an isolated filesystem boundary. AI agents cannot traverse directories (../), escape to host filesystems, or follow external symlinks.

  2. Granular Permissions (read/write vs. read-only): Vaults can be individually designated with full mutation rights or locked into immutable query-only modes.

  3. Dynamic Zero-Downtime Auto-Discovery: Placing a new folder containing .obsidian into the root vault directory registers the vault immediately without container restarts or configuration updates.

  4. Safety Defaults for Unlisted Vaults: When parent folders are scanned, unlisted vaults adopt the access mode specified by OBSIDIAN_AUTO_DISCOVERY_MODE (defaulting to safe read-only or agile read/write).

Capability Scopes Matrix

Tool Category

Required Scope

Permitted in Read-Only

Operations Included

Search & Discovery

vault:read

Yes

obsidian_search, obsidian_search_context, obsidian_list_vaults, obsidian_get_vault

Note Consumption

vault:read

Yes

obsidian_read_note, obsidian_list_files, obsidian_get_file_info, obsidian_read_daily_note

Structure & Graph

vault:read

Yes

obsidian_get_outline, obsidian_get_backlinks, obsidian_get_tags, obsidian_list_tasks

Content Mutation

vault:write

Blocked (403)

obsidian_create_note, obsidian_update_note, obsidian_patch_note, obsidian_append_note

Metadata Editing

vault:write

Blocked (403)

obsidian_set_property, obsidian_remove_property, obsidian_toggle_task

File Deletion

vault:delete

Blocked (403)

obsidian_delete_note, obsidian_move_note (atomic trash preservation)

Git Read Operations

vault:read

Yes

obsidian_git_status, obsidian_git_diff, obsidian_git_log

Git Mutation & Sync

vault:write

Blocked (403)

obsidian_git_add, obsidian_git_commit, obsidian_git_push, obsidian_git_pull

Git Advanced

vault:developer

Blocked (403)

Guarded allowlisted subcommand execution via obsidian_git_exec

Desktop CLI

vault:developer

Blocked (403)

Allowlisted CLI process executions via obsidian_execute_command

Universal AI Client Protocol

All AI clients operate through a standardized two-phase interaction lifecycle:

Phase 1: Vault Inventory Discovery

When an AI assistant initializes or begins a query, it executes obsidian_list_vaults to discover available vaults and their active permissions:

{
  "summary": "Found 2 configured vault(s): [work-vault, research-vault]. Active default vault: 'work-vault'.",
  "defaultVault": "work-vault",
  "totalVaults": 2,
  "vaults": [
    {
      "name": "work-vault",
      "path": "/vault/work-vault",
      "accessMode": "read/write",
      "readOnly": false,
      "totalFiles": 1240,
      "markdownFiles": 1180,
      "topLevelFolders": ["Projects", "Meetings", "Roadmap"]
    },
    {
      "name": "research-vault",
      "path": "/vault/research-vault",
      "accessMode": "read-only",
      "readOnly": true,
      "totalFiles": 450,
      "markdownFiles": 430,
      "topLevelFolders": ["Archive", "Papers", "Readings"]
    }
  ]
}

Phase 2: Targeted Execution & Default Fallback

  • Targeted Operations: The AI client passes the "vault" parameter in any tool call (e.g., obsidian_read_note({ "vault": "research-vault", "path": "Papers/Overview.md" })).

  • Default Fallback: If the user or AI omits the "vault" argument, the server automatically routes the operation to OBSIDIAN_DEFAULT_VAULT.

  • Deterministic 403 Rejection: If an AI attempts to modify a read-only vault, the server responds with a clear 403 Forbidden error payload:

    {
      "error": {
        "code": "FORBIDDEN",
        "message": "Tool 'obsidian_create_note' requires scope 'vault:write', but vault 'research-vault' is read-only or restricted to: [vault:read]"
      }
    }

    This signals the AI assistant that the action was rejected by security policy, preventing repetitive mutation loops.

Smart Alias Resolution & Ambiguity Protection

To provide natural collaboration without requiring manual configuration for every auto-discovered vault, the server features an intelligent alias resolution and ambiguity protection layer:

  1. Automatic Colloquial Aliases: When vault names are long (e.g., dharmik-professional-obsidian-vault and obsidian-daily-update-report), users naturally refer to them with conversational keywords (e.g., "daily", "daily vault", "daily update", "professional", or "dharmik"). The server tokenizes names, strips stop words (obsidian, vault, notes, my, the), and generates recognized alias combinations dynamically.

  2. Explicit Custom Aliases: Configured via OBSIDIAN_VAULT_ALIASES (e.g., daily=obsidian-daily-update-report,work=dharmik-professional-obsidian-vault or JSON format).

  3. Ambiguity Protection (AMBIGUOUS_VAULT): If an input is ambiguous across multiple vaults (e.g. generic references like "vault" or "obsidian" matching multiple vaults), the server returns an AMBIGUOUS_VAULT error payload containing all matching candidates:

    {
      "error": {
        "code": "AMBIGUOUS_VAULT",
        "message": "Ambiguous vault reference 'obsidian'. Multiple vaults matched: [dharmik-professional-obsidian-vault, obsidian-daily-update-report]. Please specify the exact vault name or choose from the candidates.",
        "details": {
          "query": "obsidian",
          "candidates": [
            "dharmik-professional-obsidian-vault",
            "obsidian-daily-update-report"
          ]
        }
      }
    }

    This error protocol informs the AI assistant (ChatGPT, Claude, Cursor) that multiple candidates exist, prompting the AI to request clarification from the user rather than guessing or modifying the wrong vault.

Sandboxed Vault Git Integration

The Sandboxed Vault Git Subsystem provides version control automation directly inside authorized Obsidian repositories. It enables AI coding assistants and autonomous agents to inspect repository status, review unified diffs, stage files, create atomic commits, and synchronize with upstream remotes within strict containment boundaries.

The architecture ensures that version control workflows remain isolated to the designated vault path, preventing command injection, unauthorized directory escape, and credential leakage.

flowchart TD
    Client["AI Assistant (Claude / ChatGPT / Cursor)"] --> Tools{"Git Semantic Tools"}
    Tools -->|"obsidian_git_status / diff / log"| ReadCheck["Scope: vault:read"]
    Tools -->|"obsidian_git_add / commit / push / pull"| WriteCheck["Scope: vault:write & Mode != :ro"]
    Tools -->|"obsidian_git_exec"| DevCheck["Scope: vault:developer & Allowlist Validated"]
    ReadCheck --> PathGuard["PathGuard (Boundary & RelPath Validation)"]
    WriteCheck --> PathGuard
    DevCheck --> Denylist["Argument Denylist Filter"]
    Denylist --> PathGuard
    PathGuard --> GitAdapter["GitCliAdapter (execFile)"]
    GitAdapter --> EnvGuard["Ceiling Directory & CWD Injection"]
    EnvGuard --> Exec["git binary (Isolated Child Process)"]
    Exec --> OutputFilter["Credential & Token Redaction"]
    OutputFilter --> Response["Clean JSON Result to Client"]

Security & Sandboxing Guarantees

  1. Root Boundary Containment:
    All Git subcommands execute with the working directory locked to cwd: vault.root. The process cannot wander into external parent directories or adjacent sibling folders.

  2. Ceiling Directory Trapping:
    The runtime automatically injects GIT_CEILING_DIRECTORIES: path.dirname(vault.root). If a vault does not contain an initialized .git repository, Git halts immediately instead of ascending into a parent workspace or system folder.

  3. Shell Injection Immunity:
    Subprocess invocations bypass the system shell by utilizing Node.js execFile("git", ...) directly. Metacharacters, command chaining operators, and shell expansions (|, ;, &&, $()) are treated as literal arguments.

  4. Dangerous Argument Denylist:
    The system blocks flags that execute commands or redirect repository boundaries. Banned arguments include --upload-pack, --receive-pack, arbitrary configuration injection (-c <key>=<value>), and path redirection flags (--work-tree, --git-dir).

  5. Read-Only Mode Isolation (:ro):
    Vaults registered with the :ro flag permit historical queries (status, diff, log) while deterministically blocking modifications (add, commit, push, pull, exec) with a 403 Forbidden error.

  6. PathGuard Relative Path Sanitization:
    All path parameters supplied to staging and diff routines undergo strict normalization. Attempts to reference parent paths (such as ../../etc/passwd) are rejected with PATH_INVALID before reaching the Git executable.

  7. Credential and Token Redaction:
    Output streams pass through a credential masking filter that replaces embedded authentication tokens and user passwords in remote URLs (https://***@github.com) before returning data to the AI assistant.

Git Tools Reference Table

Tool Name

Required Scope

Primary Operation

Key Parameters

obsidian_git_status

vault:read

Inspect branch state, tracking branch, staged, modified, and untracked files

vault (optional)

obsidian_git_diff

vault:read

Produce unified diffs for uncommitted or staged changes

file (optional), staged (boolean), vault (optional)

obsidian_git_log

vault:read

Retrieve structured commit history with hashes, authors, dates, and messages

max_count (1–100), file (optional), vault (optional)

obsidian_git_add

vault:write

Stage modified or newly created markdown notes for commit

files (array of relative paths), vault (optional)

obsidian_git_commit

vault:write

Author an atomic Git commit with a descriptive message and optional author

message (string), author_name (optional), author_email (optional), vault (optional)

obsidian_git_push

vault:write

Push committed local revisions to an upstream remote repository

remote (default: origin), branch (optional), vault (optional)

obsidian_git_pull

vault:write

Fetch and merge changes from an upstream remote repository

remote (default: origin), branch (optional), vault (optional)

obsidian_git_exec

vault:developer

Execute allowlisted low-level Git commands within vault boundary

args (array of arguments), vault (optional)

Tool Invocation Examples

1. Inspecting Working Tree Status (obsidian_git_status)
{
  "vault": "dharmik-professional-obsidian-vault"
}

Structured Response:

{
  "initialized": true,
  "branch": "main",
  "clean": false,
  "staged": [
    { "path": "Projects/Enterprise Architecture.md", "status": "modified" }
  ],
  "unstaged": [
    { "path": "Daily/2026-11-02.md", "status": "modified" }
  ],
  "untracked": [
    "Meeting Notes/Q4 Review.md"
  ]
}
2. Inspecting Working Tree Diffs (obsidian_git_diff)
{
  "file": "Projects/Enterprise Architecture.md",
  "staged": true,
  "vault": "professional"
}

Structured Response:

{
  "file": "Projects/Enterprise Architecture.md",
  "staged": true,
  "diff": "diff --git a/Projects/Enterprise Architecture.md b/Projects/Enterprise Architecture.md\n--- a/Projects/Enterprise Architecture.md\n+++ b/Projects/Enterprise Architecture.md\n@@ -12,3 +12,4 @@\n- Status: In Progress\n+ Status: Completed"
}
3. Staging and Creating an Atomic Commit (obsidian_git_add & obsidian_git_commit)
// Step 1: Stage specific relative files
{
  "files": ["Projects/Enterprise Architecture.md", "Daily/2026-11-02.md"],
  "vault": "professional"
}

// Step 2: Author atomic commit
{
  "message": "docs: update enterprise architecture review and daily summary",
  "author_name": "Antigravity Agent",
  "author_email": "agent@enterprise.local",
  "vault": "professional"
}

Structured Response:

{
  "success": true,
  "message": "Commit created successfully.",
  "output": "[main 4e8b1d2] docs: update enterprise architecture review and daily summary\n 2 files changed, 14 insertions(+), 3 deletions(-)"
}
4. Synchronizing with Upstream Remote (obsidian_git_push)
{
  "remote": "origin",
  "branch": "main",
  "vault": "professional"
}

Structured Response:

{
  "success": true,
  "remote": "origin",
  "branch": "main",
  "output": "To github.com:org/vault.git\n   3c7a91f..4e8b1d2  main -> main"
}

Remote Server Synchronization Setup

For remote production environments such as headless VPS nodes or Docker containers, upstream synchronization requires credential configuration on the host:

  1. SSH Deploy Keys (Recommended):
    Generate a dedicated ED25519 deploy key for the service user and grant read/write access in your repository settings:

    ssh-keygen -t ed25519 -C "obsidian-mcp@server" -f ~/.ssh/id_ed25519
  2. Personal Access Token via HTTPS:
    Configure the upstream remote URL with a personal access token. Any sensitive token embedded in the URL is masked automatically in tool responses:

    git remote set-url origin https://ghp_SECRET_TOKEN@github.com/organization/vault.git

Error Handling Protocol

Error Code

HTTP Status

Description

GIT_NOT_INITIALIZED

404 Not Found

The specified vault directory is not an initialized Git repository (missing .git).

GIT_ERROR

500 Server Error

The underlying Git process returned a non-zero exit code (e.g. merge conflict, network timeout).

FORBIDDEN

403 Forbidden

Mutation attempted on a :ro vault, or an invocation contained banned configuration flags (-c, --upload-pack).

PATH_INVALID

400 Bad Request

File path argument attempts directory escape outside the designated vault boundary.

Multi-Client Integration Guide

The server supports all primary AI client environments with uniform access control:

1. ChatGPT (OpenAI Custom MCP Plugin)

  • In ChatGPT &rarr; Plugins &rarr; Add custom MCP server:

    • Server URL: https://<domain>/s/<token>/mcp

    • Authentication: No authentication (token is verified via the secret path)

    • Icon: Use icon-chatgpt-256.png (256x256 PNG, 7.8 KB).

2. Claude Desktop & Claude Code

  • In claude_desktop_config.json:

    {
      "mcpServers": {
        "obsidian": {
          "url": "https://<domain>/mcp",
          "headers": {
            "Authorization": "Bearer <your_token>"
          }
        }
      }
    }

3. Cursor & Windsurf IDE

  • In .cursor/mcp.json:

    {
      "mcpServers": {
        "obsidian": {
          "command": "node",
          "args": ["/absolute/path/to/obsidian-mcp/dist/index.js"],
          "env": {
            "OBSIDIAN_VAULT_PATH": "/path/to/vault",
            "OBSIDIAN_AUTO_DISCOVERY_MODE": "read-only"
          }
        }
      }
    }

4. Google Antigravity & Gemini CLI

  • Declared in your agent configuration or loaded lazily:

    {
      "mcpServers": {
        "obsidian": {
          "url": "https://<domain>/mcp",
          "headers": {
            "Authorization": "Bearer <your_token>"
          }
        }
      }
    }

5. Open-Source Clients (LibreChat, Cline, Continue.dev)

  • Connect using Streamable HTTP (https://<domain>/mcp) or Server-Sent Events (https://<domain>/sse) with standard Bearer authorization headers.


Environment Variables & Configuration Reference

The Obsidian MCP Server is configured entirely via environment variables (in your .env file or within your AI client's MCP configuration).

Environment Variables Overview

Variable

Type

Default

Description

OBSIDIAN_VAULT_PATH

string

None

Absolute path to a single target vault or parent folder containing multiple auto-discovered vaults (e.g. /vault). Required unless OBSIDIAN_VAULTS is provided.

OBSIDIAN_VAULTS

string / JSON

None

Multi-vault custom inventory. Accepts comma-separated name=path:ro,name2=path2:rw pairs or JSON with custom paths and permissions. Can be combined with OBSIDIAN_VAULT_PATH.

OBSIDIAN_VAULT_ALIASES

string / JSON

None

Custom short aliases mapping colloquial names to full vault names (e.g. daily=obsidian-daily-update-report,work=dharmik-professional-obsidian-vault or JSON dictionary).

OBSIDIAN_DEFAULT_VAULT

string

First vault

Name of the primary vault used when tool calls omit an explicit vault argument.

OBSIDIAN_ENABLE_AUTO_DISCOVERY

boolean

true

When true, automatically discovers new sub-vaults containing .obsidian dynamically at runtime.

OBSIDIAN_AUTO_DISCOVER_READ_ONLY

boolean

false

Default access mode for auto-discovered vaults. Set true to restrict dynamically detected vaults to read-only (vault:read), preventing write/delete actions.

OBSIDIAN_AUTO_DISCOVERY_MODE

enum

read/write

Access mode for auto-discovered vaults: read-only (or ro) vs read/write (or rw).

OBSIDIAN_READ_ONLY_VAULTS

string

None

Comma-separated list of custom vault names to lock as read-only (e.g. personal,archive).

OBSIDIAN_VAULT_SCOPES

string / JSON

None

Per-vault scopes (e.g. personal=vault:read;work=vault:read,vault:write).

OBSIDIAN_BIN_PATH

string

obsidian

System path to the official Obsidian desktop binary (e.g. /usr/bin/obsidian or /Applications/Obsidian.app/Contents/MacOS/Obsidian).

AUTH_ENABLED

boolean

false

Enables cryptographic credential checks. Required (true) for production HTTP transport.

AUTH_TOKEN

string

None

Plain-text secret passkey. The server computes a SHA-256 hash internally for verification.

BEARER_TOKEN_HASH

string

None

Hexadecimal SHA-256 hash of your Bearer access token (for zero-trust setups where raw keys are never placed in configs).

READ_ONLY

boolean

false

Safety toggle. When true, restricts all connections strictly to vault:read scope, blocking file creation, editing, or deletion.

SCOPES

string

All

Comma-separated list of granted capabilities: vault:read, vault:write, vault:delete, vault:admin, vault:developer.

MCP_TRANSPORT

enum

stdio

Transport mechanism: stdio (local subprocess) or http (Streamable HTTP /mcp and SSE /sse).

PORT

number

9435

Network port when running over HTTP transport.

HOST

string

127.0.0.1

Network interface binding. Set to 127.0.0.1 behind reverse proxies (Caddy/Nginx).

RATE_LIMIT_PER_MINUTE

number

120

Maximum allowed tool invocations per minute per client session to prevent runaway AI loops.

COMMAND_TIMEOUT_MS

number

15000

Timeout threshold (in milliseconds) before CLI subsystem commands are aborted.

MAX_SEARCH_RESULTS

number

50

Maximum results returned by full-text and semantic search routines.

ENABLE_DESTRUCTIVE_TOOLS

boolean

true

Allows deletion tools. When enabled, obsidian_delete_note recycles files to .trash safely.

ENABLE_ADVANCED_CLI

boolean

false

Gated escape-hatch exposing raw obsidian_cli execution to clients with vault:developer scope.

ENABLE_GIT_TOOLS

boolean

true

Master toggle registering Git tools (obsidian_git_*). Set false to disable all Git operations.

GIT_BIN_PATH

string

git

System binary name or absolute path to the Git executable.

GIT_AUTHOR_NAME

string

None

Fallback commit author name when not configured in local vault repository.

GIT_AUTHOR_EMAIL

string

None

Fallback commit author email when not configured in local vault repository.

GIT_DEFAULT_REMOTE

string

origin

Default remote target for push and pull synchronization.

GIT_TIMEOUT_MS

number

30000

Process execution timeout (milliseconds) for Git operations.

AUDIT_LOG_ENABLED

boolean

true

Emits structured JSON audit records to standard error (stderr) for every tool invocation.


Architectural Importance of Variables

  1. Vault Sandboxing (OBSIDIAN_VAULT_PATH & OBSIDIAN_VAULTS)
    The server implements a strict path-containment engine (PathGuard). By defining vault boundaries, the server prevents path traversal (../), symlink escapes, and null-byte injection attacks. AI agents are contained entirely within authorized vault folders.

  2. Timing-Safe Credential Verification (AUTH_ENABLED & AUTH_TOKEN / BEARER_TOKEN_HASH)
    When exposed over HTTP or shared environments, authentication ensures only authorized callers interact with private notes. Token comparisons use crypto.timingSafeEqual over SHA-256 digests, eliminating side-channel timing analysis. In production HTTP mode, disabling auth triggers a startup safety violation.

  3. Immutable Guardrail (READ_ONLY)
    For read-only assistants, research agents, or untrusted models, setting READ_ONLY=true immediately strips write, delete, and execution scopes. Even if an AI attempts to call obsidian_create_note or obsidian_delete_note, the request is denied with a 403 Forbidden response.

  4. Blast Radius Control (SCOPES)
    Scopes provide least-privilege access. An agent tasked with drafting daily logs can be assigned vault:read,vault:write without granting vault:delete (preventing note loss) or vault:developer (preventing CLI access).

  5. Session Safety & Loop Prevention (RATE_LIMIT_PER_MINUTE)
    Autonomous AI agents occasionally enter repetitive retry loops. The sliding-window rate limiter prevents CPU exhaustion, excessive disk I/O, or Obsidian UI unresponsiveness.


Managing Variables Across Deployment Environments

The Obsidian MCP Server loads configuration from standard operating system environment variables, with native .env loading support via dotenv. Depending on the runtime platform, environment variables can be declared through configuration files, command-line arguments, container definitions, or service supervisors.

1. Standard .env File Configuration

When executing directly with Node.js or developing locally, copy .env.example to .env in the server root. Variables declared in .env load automatically on startup:

# Vault Discovery and Permissions
OBSIDIAN_VAULT_PATH=/srv/obsidian/vault
OBSIDIAN_VAULTS=dharmik-professional-obsidian-vault=/vault/dharmik-professional-obsidian-vault:rw,obsidian-daily-update-report=/vault/obsidian-daily-update-report:rw
OBSIDIAN_VAULT_ALIASES=daily=obsidian-daily-update-report,professional=dharmik-professional-obsidian-vault
OBSIDIAN_DEFAULT_VAULT=dharmik-professional-obsidian-vault
OBSIDIAN_AUTO_DISCOVERY_MODE=read/write

# Transport and Network Binding
MCP_TRANSPORT=http
PORT=9435
HOST=127.0.0.1

# Security and Credentials
AUTH_ENABLED=true
AUTH_TOKEN=your-32-byte-hex-token

2. Docker Run CLI Deployment

When running via docker run, pass environment variables using individual -e flags or pass an environment file using --env-file:

docker run -d \
  --name obsidian-mcp \
  --restart unless-stopped \
  -p 9435:9435 \
  -e PORT=9435 \
  -e AUTH_ENABLED=true \
  -e AUTH_TOKEN="your-secure-token" \
  -e OBSIDIAN_VAULT_PATH="/vault" \
  -e OBSIDIAN_AUTO_DISCOVERY_MODE="read/write" \
  -e OBSIDIAN_VAULT_ALIASES="daily=obsidian-daily-update-report,pro=dharmik-professional-obsidian-vault" \
  -e OBSIDIAN_DEFAULT_VAULT="dharmik-professional-obsidian-vault" \
  -v "/root/obsidian-vault:/vault" \
  obsidian-remote-mcp:latest

3. Docker Compose Deployment

Docker Compose centralizes variable management by referencing local environment files or declaring inline key-value pairs in docker-compose.yml:

services:
  obsidian-mcp:
    image: obsidian-remote-mcp:latest
    container_name: obsidian-mcp
    restart: unless-stopped
    ports:
      - "9435:9435"
    environment:
      PORT: 9435
      AUTH_ENABLED: "true"
      AUTH_TOKEN: "${AUTH_TOKEN}"
      OBSIDIAN_VAULT_PATH: "/vault"
      OBSIDIAN_AUTO_DISCOVERY_MODE: "read/write"
      OBSIDIAN_VAULT_ALIASES: "daily=obsidian-daily-update-report,pro=dharmik-professional-obsidian-vault"
      OBSIDIAN_DEFAULT_VAULT: "dharmik-professional-obsidian-vault"
    volumes:
      - /root/obsidian-vault:/vault

4. Systemd Service Configuration (Linux VPS)

For production Linux VPS deployments running directly on Ubuntu/Debian, store environment variables in a protected file such as /etc/obsidian-mcp/.env with 600 permissions and reference it via EnvironmentFile:

# /etc/systemd/system/obsidian-mcp.service
[Unit]
Description=Obsidian Remote MCP Server
After=network.target obsidian-xvfb.service

[Service]
Type=simple
User=obsidian
Group=obsidian
WorkingDirectory=/opt/obsidian-mcp
EnvironmentFile=/etc/obsidian-mcp/.env
ExecStart=/usr/bin/node /opt/obsidian-mcp/dist/index.js
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

5. Local AI Desktop Clients (Claude Desktop & Cursor IDE)

Desktop AI applications initialize the MCP server as a managed child process over stdio. Environment variables are declared inside the application's JSON configuration file:

{
  "mcpServers": {
    "obsidian": {
      "command": "node",
      "args": ["/absolute/path/to/obsidian-mcp/dist/index.js"],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/Users/dharmik/Documents/obsidian-vault",
        "OBSIDIAN_VAULT_ALIASES": "daily=obsidian-daily-update-report,pro=dharmik-professional-obsidian-vault",
        "OBSIDIAN_AUTO_DISCOVERY_MODE": "read/write",
        "READ_ONLY": "false"
      }
    }
  }
}

Access Management & Security Configuration Recipes

Access control in the Obsidian MCP Server operates on a dual-tier security model: filesystem boundary enforcement via PathGuard and semantic capability validation via scoped access tokens.

Recipe 1: Granting Read-Only vs. Read/Write Access to Specific Vaults

To allow an AI assistant to modify active project notes while strictly protecting personal records from alteration, assign the :ro (read-only) or :rw (read/write) suffix to each vault declaration:

# Syntax: name=path:mode
OBSIDIAN_VAULTS="work=/vault/work-vault:rw,personal=/vault/personal-vault:ro"
OBSIDIAN_DEFAULT_VAULT="work"

When an assistant executes obsidian_create_note or obsidian_update_note targeting personal-vault, the server halts the operation with a deterministic error:

{
  "error": {
    "code": "FORBIDDEN",
    "message": "Vault 'personal' is configured as read-only. Tool 'obsidian_create_note' requires scope 'vault:write', which is not permitted on this vault."
  }
}

Recipe 2: Safe Auto-Discovery for Enterprise Repositories

When hosting multiple folders inside a parent directory (e.g. /vault), administrators can enable dynamic vault detection while locking newly discovered vaults into read-only mode by default:

OBSIDIAN_VAULT_PATH="/vault"
OBSIDIAN_ENABLE_AUTO_DISCOVERY="true"
OBSIDIAN_AUTO_DISCOVERY_MODE="read-only"
OBSIDIAN_VAULTS="approved-editor=/vault/approved-editor:rw"

Under this configuration:

  • approved-editor retains authorized write permissions.

  • Any new vault created inside /vault is automatically discovered by AI clients without server restarts, but remains restricted to vault:read operations.

Recipe 3: Dual Read/Write Setup with Natural Colloquial Aliases

For environments where users frequently switch between long vault names (such as dharmik-professional-obsidian-vault and obsidian-daily-update-report), configure both repositories with full read/write access and short conversational aliases:

OBSIDIAN_VAULT_PATH="/vault"
OBSIDIAN_AUTO_DISCOVERY_MODE="read/write"
OBSIDIAN_VAULT_ALIASES="daily=obsidian-daily-update-report,pro=dharmik-professional-obsidian-vault,work=dharmik-professional-obsidian-vault"
OBSIDIAN_DEFAULT_VAULT="dharmik-professional-obsidian-vault"

The user can prompt ChatGPT or Claude using natural terminology:

  • "Add standup notes to my daily vault" &rarr; routes directly to obsidian-daily-update-report.

  • "Read my system architecture note in professional" &rarr; routes directly to dharmik-professional-obsidian-vault.

Recipe 4: Ambiguity Trapping Across Similar Vault Names

When multiple vaults share common words (such as "vault" or "obsidian"), the server rejects ambiguous calls using AMBIGUOUS_VAULT rather than guessing a target:

{
  "error": {
    "code": "AMBIGUOUS_VAULT",
    "message": "Ambiguous vault reference 'vault'. Multiple vaults matched: [dharmik-professional-obsidian-vault, obsidian-daily-update-report]. Please specify the exact vault name or choose from the candidates.",
    "details": {
      "query": "vault",
      "candidates": [
        "dharmik-professional-obsidian-vault",
        "obsidian-daily-update-report"
      ]
    }
  }
}

This response compels the AI client to stop execution and ask the user: "Did you mean dharmik-professional-obsidian-vault or obsidian-daily-update-report?", ensuring data integrity across all connected systems.


Local Development Setup

Prerequisites

  1. Node.js: v20+ LTS

  2. Obsidian Desktop: v1.12.0+ installed and running locally

  3. CLI Enabled: Inside Obsidian, open Settings &rarr; Command line interface &rarr; Toggle ON

Installation & Build

# 1. Clone repository
git clone https://github.com/dharmikbhesaniya/obsidian-mcp.git
cd obsidian-mcp

# 2. Install dependencies
npm install

# 3. Create local environment configuration
cp .env.example .env

Edit .env for your local path:

OBSIDIAN_VAULT_PATH=/Users/yourusername/Documents/MyVault
MCP_TRANSPORT=stdio
AUTH_ENABLED=false

Build and test:

# Run test suite (unit, integration, and security checks)
npm test

# Build TypeScript to dist/
npm run build

Running with Claude Desktop (stdio)

Add the server to your Claude Desktop configuration file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json


Single Vault vs. Multi-Vault Configuration

Scenario 1: Single Vault (Full Read & Write Access)

Use this setup when you have one primary vault and want your AI assistant to read notes, answer questions, draft content, and organize tags.

{
  "mcpServers": {
    "obsidian": {
      "command": "node",
      "args": [
        "/absolute/path/to/obsidian-mcp/dist/index.js"
      ],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/Users/yourusername/Documents/MyVault",
        "READ_ONLY": "false"
      }
    }
  }
}

Scenario 2: Single Vault (Safe Read-Only Mode)

Use this setup if you want an AI assistant to query and reference your vault without the ability to create, edit, or delete any files.

{
  "mcpServers": {
    "obsidian": {
      "command": "node",
      "args": [
        "/absolute/path/to/obsidian-mcp/dist/index.js"
      ],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/Users/yourusername/Documents/MyVault",
        "READ_ONLY": "true"
      }
    }
  }
}

Scenario 3: Multi-Vault with Per-Vault Permissions (e.g. Personal Read-Only, Work Read-Write)

Use this setup if you want a single AI assistant connected to multiple vaults, but want one vault to be strictly read-only (e.g. personal notes) while allowing full read-and-write on another (e.g. work projects).

Syntax A: Using :ro and :rw Suffixes (Recommended)
{
  "mcpServers": {
    "obsidian": {
      "command": "node",
      "args": [
        "/absolute/path/to/obsidian-mcp/dist/index.js"
      ],
      "env": {
        "OBSIDIAN_VAULTS": "personal=/Users/yourusername/Documents/PersonalVault:ro,work=/Users/yourusername/Documents/WorkVault:rw",
        "OBSIDIAN_DEFAULT_VAULT": "work"
      }
    }
  }
}
Syntax B: Using OBSIDIAN_READ_ONLY_VAULTS
{
  "mcpServers": {
    "obsidian": {
      "command": "node",
      "args": [
        "/absolute/path/to/obsidian-mcp/dist/index.js"
      ],
      "env": {
        "OBSIDIAN_VAULTS": "personal=/Users/yourusername/Documents/PersonalVault,work=/Users/yourusername/Documents/WorkVault",
        "OBSIDIAN_READ_ONLY_VAULTS": "personal",
        "OBSIDIAN_DEFAULT_VAULT": "work"
      }
    }
  }
}
Syntax C: Hybrid Custom Vaults + Dynamic Auto-Discovery
{
  "mcpServers": {
    "obsidian": {
      "command": "node",
      "args": [
        "/absolute/path/to/obsidian-mcp/dist/index.js"
      ],
      "env": {
        "OBSIDIAN_VAULTS": "vip-client=/custom/external/path:rw",
        "OBSIDIAN_VAULT_PATH": "/Users/yourusername/Documents/obsidian-vault",
        "OBSIDIAN_AUTO_DISCOVER_READ_ONLY": "true",
        "OBSIDIAN_DEFAULT_VAULT": "vip-client"
      }
    }
  }
}

How it works:

  • vip-client is explicitly added with custom path and read/write privileges.

  • Any vault inside /Users/yourusername/Documents/obsidian-vault is auto-discovered dynamically.

  • OBSIDIAN_AUTO_DISCOVER_READ_ONLY: true ensures all dynamically discovered vaults default to read-only access, while vip-client retains full write access.

How it works:

  • The AI discovers both vaults via obsidian_list_vaults, which explicitly reports readOnly: true for personal and readOnly: false for work.

  • Reading and querying notes in personal succeeds normally.

  • If the AI attempts to create, edit, patch, or delete a note targeting personal, the server halts execution with a 403 Forbidden error:
    Vault 'personal' is configured as read-only. Tool 'obsidian_create_note' requires scope 'vault:write', which is not permitted on this vault.

  • Modifying notes in work succeeds with full authoring capabilities.

  • Requests omitting the vault parameter automatically fall back to the default vault (work).

Scenario 4: Multi-Vault with Separate Server Blocks (Independent Permissions)

Use this setup when you want different security policies for each vault—such as allowing the AI to write to your work vault while keeping your personal vault strictly read-only.

{
  "mcpServers": {
    "obsidian_work": {
      "command": "node",
      "args": [
        "/absolute/path/to/obsidian-mcp/dist/index.js"
      ],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/Users/yourusername/Documents/WorkVault",
        "READ_ONLY": "false"
      }
    },
    "obsidian_personal": {
      "command": "node",
      "args": [
        "/absolute/path/to/obsidian-mcp/dist/index.js"
      ],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/Users/yourusername/Documents/PersonalVault",
        "READ_ONLY": "true"
      }
    }
  }
}

Running with Cursor IDE

Add the server to Cursor's MCP configuration in ~/.cursor/mcp.json or project .cursor/mcp.json:

{
  "mcpServers": {
    "obsidian": {
      "command": "node",
      "args": ["/absolute/path/to/obsidian-mcp/dist/index.js"],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/absolute/path/to/your/vault",
        "READ_ONLY": "false"
      }
    }
  }
}

Testing with MCP Inspector

Test tools interactively in the browser without an external client:

npx @modelcontextprotocol/inspector@latest node dist/index.js

Production VPS Setup (Hostinger / Ubuntu / Debian)

This guide walks through deploying the MCP server on a remote Linux VPS (e.g. Hostinger Ubuntu 24.04 LTS or Debian 12).

Step 1: Install Operating System Dependencies

Obsidian Desktop on Linux requires a virtual frame buffer (xvfb) when running in headless server environments:

sudo apt update
sudo apt install -y xvfb libnotify4 libnss3 libasound2 libgbm1 libsecret-1-0 nodejs npm caddy

Verify Node.js is v20+:

node -v

Step 2: Create Dedicated Service Account

Run all Obsidian and MCP processes under a restricted service account:

# Create service user
sudo useradd -r -m -d /opt/obsidian -s /bin/bash obsidian

# Create application and vault directories
sudo mkdir -p /srv/obsidian/vault
sudo mkdir -p /etc/obsidian-mcp
sudo mkdir -p /opt/obsidian-mcp

# Assign ownership
sudo chown -R obsidian:obsidian /srv/obsidian
sudo chown -R obsidian:obsidian /opt/obsidian-mcp

Step 3: Install Obsidian Desktop and Enable CLI

Download and install the official Obsidian .deb package:

wget https://github.com/obsidianmd/obsidian-releases/releases/download/v1.12.0/obsidian_1.12.0_amd64.deb
sudo dpkg -i obsidian_1.12.0_amd64.deb
sudo apt install -f -y

Verify the binary is available:

which obsidian
# Output: /usr/bin/obsidian

Step 4: Configure Headless Display (Xvfb Service)

Copy the Xvfb systemd service file:

sudo cp deploy/systemd/obsidian-xvfb.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now obsidian-xvfb.service

Verify that virtual display :5 is active:

sudo systemctl status obsidian-xvfb.service

Step 5: Deploy the MCP Server

# Clone repository to deployment location
sudo -u obsidian git clone https://github.com/dharmikbhesaniya/obsidian-mcp.git /opt/obsidian-mcp
cd /opt/obsidian-mcp

# Install dependencies and compile
sudo -u obsidian npm ci
sudo -u obsidian npm run build

Generate a secure Bearer access token:

# Generate random 32-byte secret token
TOKEN=$(openssl rand -hex 32)
echo "Your Raw Token: $TOKEN"

# Compute SHA-256 hash for configuration
TOKEN_HASH=$(echo -n "$TOKEN" | sha256sum | awk '{print $1}')
echo "Your Token Hash: $TOKEN_HASH"

Configure /etc/obsidian-mcp/.env:

NODE_ENV=production
MCP_TRANSPORT=http
PORT=9435
HOST=127.0.0.1
OBSIDIAN_VAULT_PATH=/srv/obsidian/vault
OBSIDIAN_BIN_PATH=/usr/bin/obsidian
AUTH_ENABLED=true
BEARER_TOKEN_HASH=<paste_computed_token_hash_here>
RATE_LIMIT_PER_MINUTE=120
MAX_SEARCH_RESULTS=50
COMMAND_TIMEOUT_MS=15000
ENABLE_DESTRUCTIVE_TOOLS=true
ENABLE_ADVANCED_CLI=false
LOG_LEVEL=info

Feature Flag Controls

  • ENABLE_DESTRUCTIVE_TOOLS: When set to false, destructive operations (obsidian_delete_note and obsidian_move_note) are excluded from MCP registration. Defaults to true.

  • ENABLE_ADVANCED_CLI: When set to true, the guarded obsidian_cli tool is registered for allowlisted commands (requires vault:developer scope). Defaults to false.

Set secure permissions on the environment configuration:

sudo chown obsidian:obsidian /etc/obsidian-mcp/.env
sudo chmod 600 /etc/obsidian-mcp/.env

Install and enable the systemd service:

sudo cp deploy/systemd/obsidian-mcp.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now obsidian-mcp.service

Step 6: Configure Reverse Proxy with TLS (Caddy or Nginx)

Edit /etc/caddy/Caddyfile:

mcp.yourdomain.com {
    encode gzip zstd

    reverse_proxy 127.0.0.1:9435 {
        header_up X-Real-IP {remote_host}
        header_up X-Forwarded-For {remote_host}
        header_up X-Forwarded-Proto {scheme}

        transport http {
            keepalive 120s
            response_header_timeout 600s
        }
    }

    log {
        output file /var/log/caddy/obsidian_mcp.log
        format json
    }
}

Restart Caddy:

sudo systemctl restart caddy

Option B: Nginx

Copy deploy/nginx/obsidian-mcp.conf to /etc/nginx/sites-available/obsidian-mcp.conf and obtain a certificate using Certbot:

sudo certbot --nginx -d mcp.yourdomain.com
sudo systemctl restart nginx

Step 7: Verify Health Endpoints

Test that the service is running and accessible:

# 1. Check MCP process health
curl https://mcp.yourdomain.com/health
# Response: {"status":"ok","mcp":"running","uptimeSeconds":42,"timestamp":"..."}

# 2. Check vault and Obsidian readiness
curl https://mcp.yourdomain.com/ready
# Response: {"status":"ready","vaultAccessible":true,"obsidianConnected":true,...}

Docker Deployment (Compose & Docker Run)

The server is fully containerized with default network port 9435 and can be launched using either docker run or docker compose.

Docker Run with Default Port (9435) and Bearer Token

The server listens on default port 9435:

# 1. Build the container image
docker build --load -t obsidian-remote-mcp .

# 2. Run with default port 9435 and Bearer token
docker run -d \
  --name obsidian-mcp \
  --restart unless-stopped \
  -p 9435:9435 \
  -e AUTH_ENABLED=true \
  -e AUTH_TOKEN=your-custom-secret-token \
  -v /path/to/your/obsidian/vault:/vault \
  obsidian-remote-mcp

Alternatively, map any custom host port (e.g. 8080) to the container's default port 9435:

docker run -d \
  --name obsidian-mcp \
  --restart unless-stopped \
  -p 8080:9435 \
  -e AUTH_ENABLED=true \
  -e AUTH_TOKEN=your-custom-secret-token \
  -v /path/to/your/obsidian/vault:/vault \
  obsidian-remote-mcp

Or pass a custom port inside the container using the -e PORT=... environment variable or --port=... CLI argument:

docker run -d \
  --name obsidian-mcp \
  --restart unless-stopped \
  -p 8080:8080 \
  -e PORT=8080 \
  -e AUTH_ENABLED=true \
  -e AUTH_TOKEN=your-custom-secret-token \
  -v /path/to/your/obsidian/vault:/vault \
  obsidian-remote-mcp

Docker Access Control Recipes

Recipe 1: Hybrid Setup (Custom Read/Write Vault + Auto-Discovered Read-Only Default)

Protects against unauthorized note mutations while granting write access to an approved primary vault:

docker run -d \
  --name obsidian-mcp \
  --restart unless-stopped \
  -p 9435:9435 \
  -e PORT=9435 \
  -e AUTH_ENABLED=true \
  -e AUTH_TOKEN=your-custom-secret-token \
  -e OBSIDIAN_VAULTS="primary-vault=/vault/primary-vault:rw" \
  -e OBSIDIAN_VAULT_PATH=/vault \
  -e OBSIDIAN_AUTO_DISCOVERY_MODE="read-only" \
  -e OBSIDIAN_DEFAULT_VAULT="primary-vault" \
  -v "/root/obsidian-vault:/vault" \
  obsidian-remote-mcp:latest

Recipe 2: Full Auto-Discovery with Read/Write Access

Automatically discovers any folder containing .obsidian and allows AI clients full read and write access:

docker run -d \
  --name obsidian-mcp \
  --restart unless-stopped \
  -p 9435:9435 \
  -e PORT=9435 \
  -e AUTH_ENABLED=true \
  -e AUTH_TOKEN=your-custom-secret-token \
  -e OBSIDIAN_VAULT_PATH=/vault \
  -e OBSIDIAN_AUTO_DISCOVERY_MODE="read/write" \
  -v "/root/obsidian-vault:/vault" \
  obsidian-remote-mcp:latest

Recipe 3: Strict Static Vaults (Auto-Discovery Disabled)

Permits access only to explicitly declared vaults, ignoring all other subfolders:

docker run -d \
  --name obsidian-mcp \
  --restart unless-stopped \
  -p 9435:9435 \
  -e PORT=9435 \
  -e AUTH_ENABLED=true \
  -e AUTH_TOKEN=your-custom-secret-token \
  -e OBSIDIAN_VAULTS="work=/vault/work:rw,archive=/vault/archive:ro" \
  -e OBSIDIAN_ENABLE_AUTO_DISCOVERY=false \
  -v "/root/obsidian-vault:/vault" \
  obsidian-remote-mcp:latest

Docker Compose Deployment

Configure .env in the repository directory (defaults to port 9435):

# Network Port (defaults to 9435)
PORT=9435

# Host Vault Mount Path
OBSIDIAN_VAULT_PATH=/path/to/your/obsidian/vault

# Authentication
AUTH_ENABLED=true
AUTH_TOKEN=your-custom-secret-token

Start the service:

docker compose up -d

Verify service status:

curl http://127.0.0.1:9435/health
curl http://127.0.0.1:9435/ready

Connecting Remote Clients

For a comprehensive guide covering token passing, OAuth vs. direct tokens, and cloud tunnel setup, see CLIENT_AUTHENTICATION_GUIDE.md.

The server provides two HTTP transport modes:

  • Streamable HTTP (/mcp): Native high-performance streaming transport for modern MCP clients.

  • Server-Sent Events (/sse): Dedicated SSE stream transport with /messages for legacy MCP clients.

Connecting ChatGPT (Plugins & Custom MCP Servers)

When connecting via ChatGPT &rarr; Plugins &rarr; Add custom MCP server:

  1. Icon (optional): Click + and upload assets/icon-chatgpt-256.png (256 × 256 PNG, 7.8 KB, fits within ChatGPT's 10 KB upload limit).

  2. Name: Obsidian MCP

  3. Description: Enterprise integration providing direct access to Obsidian knowledge vaults, notes, metadata, and full-text search.

  4. Connection: Select Server URL.

  5. Server URL: Enter your public HTTPS URL with the secret token path or query parameter:

    # Recommended: Secret path URL
    https://<your-domain>/s/<your-secret-token>/mcp
    
    # Alternative: Query parameter
    https://<your-domain>/mcp?token=<your-secret-token>
  6. Authentication: Select No authentication from the dropdown (the token is cryptographically validated directly from the URL path).

  7. Check "I understand and want to continue" and click Create as a plugin.

(Note: If hosting locally on Docker, expose port 9435 using a tunnel like ngrok http 9435 or Cloudflare Tunnel so ChatGPT's cloud servers can connect).

Connecting Remote Claude

Add the remote server to Claude Desktop via Streamable HTTP (/mcp) or SSE (/sse):

{
  "mcpServers": {
    "obsidian-remote": {
      "url": "https://mcp.yourdomain.com/mcp",
      "headers": {
        "Authorization": "Bearer <your_raw_bearer_token>"
      }
    }
  }
}

(For legacy Claude clients requiring SSE, use https://mcp.yourdomain.com/sse)

Connecting Hermes Agent (Nous Research)

In Hermes Agent, configure the remote MCP endpoint in ~/.hermes/config.yaml:

mcp:
  servers:
    obsidian:
      url: "https://mcp.yourdomain.com/sse"
      headers:
        Authorization: "Bearer <your_raw_bearer_token>"

Automated Test Suite

The repository contains an automated test matrix covering unit logic, path isolation, security policies, concurrency control, and protocol integration:

# Execute entire test suite
npm test

# Run build typecheck
npm run build

The test suite covers:

  • Frontmatter & YAML Serialization: Tests scalar properties, flow lists, block arrays, colons in titles, and wikilink parsing.

  • PathGuard Security: Strict rejection of leading slashes, path traversal (../), null byte injections, and symlink escapes.

  • Timing-Safe Authentication: Bearer token parsing, timing-safe SHA-256 verification, and scope validation.

  • Optimistic Concurrency: Conflict detection across note updates, appends, prepends, property mutations, and note deletions.

  • CLI Allowlist: Enforces command allowlisting rejecting unauthorized process execution.

  • Context Engine: Integration test of single-call note context assembly (content, metadata, headings, backlinks, and related notes).

  • Protocol Discovery: McpServer initialization and capability registration.


Security Model & Path Isolation

The server enforces four security boundaries:

  1. PathGuard: All note paths must be vault-relative. Attempts to traverse directories (../), inject null bytes (\0), use absolute paths (/etc/passwd), or follow external symlinks are rejected before touching disk.

  2. Bearer Token Authentication: Uses SHA-256 constant-time hash verification. Raw tokens are never stored on the server.

  3. Scope Enforcement:

    • vault:read: Allows searches, task reads, property inspection, and note reading.

    • vault:write: Allows note creation, appends, prepends, and property updates.

    • vault:delete: Required for note moves, renames, and deletion.

    • vault:developer: Required for allowlisted CLI command execution (obsidian_cli).

  4. Audit Trail: Every invocation logs structured JSON with request_id, client identity, tool name, and duration without recording private note content.


Error Handling Protocol

Every failure returns structured error payloads:

Error Code

HTTP Status

Meaning

AUTH_REQUIRED

401

Missing or invalid Bearer token credentials.

FORBIDDEN

403

Token lacks the required scope for the requested tool.

PATH_INVALID

400

Path attempted directory traversal or points outside the vault.

NOT_FOUND

404

Target note, folder, or property does not exist.

CONFLICT

409

Concurrent modification detected via revision hash.

OBSIDIAN_UNAVAILABLE

503

Obsidian desktop or CLI IPC is offline.

RATE_LIMITED

429

Client exceeded request quota.

VALIDATION_ERROR

400

Malformed arguments failing schema validation.

INTERNAL_ERROR

500

Unexpected service error.


NPM Package Distribution

The package is configured for direct zero-install execution via npx or standard installation via npm:

# Execute directly without cloning or building
OBSIDIAN_VAULT_PATH="/path/to/vault" npx -y obsidian-mcp

# Or install globally
npm install -g obsidian-mcp

Publishing to NPM

  1. Verify Tarball Contents:

    npm pack --dry-run

    Only dist/, package.json, README.md, and LICENSE are packaged.

  2. Authenticate with NPM:

    npm login
  3. Publishing Manually:

    # Automatically executes prepublishOnly (runs build and tests)
    npm publish --access public
  4. Automated Publishing via GitHub Actions: Create a new release or tag on GitHub (v0.1.0). The workflow in .github/workflows/publish.yml will automatically build, test, and publish the package to the NPM registry using the repository's NPM_TOKEN secret.


License

MIT License. See LICENSE for full details.

Available Tools

33 tools
obsidian_append_daily_noteC

Appends a work item or note to the daily note with revision check

Inputs: content (required), date, expectedRevision.

obsidian_append_noteC

Appends text content to an existing note with revision check

Inputs: path (required), content (required), ensureNewline, expectedRevision.

obsidian_create_noteC

Creates a new note with optional template

Inputs: path (required), content, template, overwrite.

obsidian_delete_noteC

Deletes or moves a note to the vault trash with revision check

Inputs: path (required), permanent, expectedRevision.

obsidian_find_notesC

Multi-criteria note discovery by title, tag, folder, property, or content query

Inputs: tag, limit, query, folder, +1 more.

obsidian_get_deadendsA

Lists notes with incoming links but zero outgoing links

Inputs: no named parameters.

obsidian_get_file_infoB

Retrieves size, modification timestamp, and metadata of a file

Inputs: path (required).

obsidian_get_note_contextC

Retrieves full note context (content, frontmatter, headings, backlinks, and related notes) with budget options

Inputs: path (required), include, maxRelatedNotes.

obsidian_get_orphansB

Discovers orphaned notes that have no internal links

Inputs: no named parameters.

obsidian_get_propertiesA

Retrieves all frontmatter properties of a note

Inputs: path (required).

obsidian_get_propertyC

Retrieves a specific frontmatter property value

Inputs: name (required), path (required).

obsidian_get_tag_notesC

Retrieves all notes tagged with a specific tag

Inputs: tag (required).

obsidian_get_tagsB

Returns all tags and their occurrence counts

Inputs: no named parameters.

obsidian_get_vaultB

Returns vault metadata, file counts, and connectivity status

Inputs: no named parameters.

obsidian_list_basesB

Lists all .base database schema files in the vault

Inputs: no named parameters.

obsidian_list_filesC

Lists files and subdirectories under a vault folder

Inputs: folder, recursive.

obsidian_list_tasksC

Lists pending or completed tasks vault-wide or in a note

Inputs: path, status.

obsidian_move_noteC

Moves or renames a note within the vault with revision check

Inputs: sourcePath (required), targetPath (required), expectedRevision.

obsidian_prepend_daily_noteC

Prepends text to the daily note with revision check

Inputs: content (required), date, expectedRevision.

obsidian_prepend_noteB

Prepends text content below frontmatter in an existing note with revision check

Inputs: path (required), content (required), expectedRevision.

obsidian_query_baseC

Queries a .base database view

Inputs: path (required), view.

obsidian_read_daily_noteB

Reads today's or a specific date's daily note

Inputs: date.

obsidian_read_noteC

Reads the full content and frontmatter of a vault note

Inputs: path (required).

obsidian_recent_changesC

Lists notes recently modified or created within the vault for change awareness

Inputs: limit, folder, sinceDays.

obsidian_remove_propertyB

Removes a frontmatter property from a note with revision check

Inputs: name (required), path (required), expectedRevision.

obsidian_search_contextC

Performs full-text vault search returning contextual lines around matches

Inputs: query (required), limit, contextLines.

obsidian_set_propertyC

Sets or updates a frontmatter property with structured types and revision check

Inputs: name (required), path (required), value, expectedRevision.

obsidian_toggle_taskC

Toggles the completion checkbox on a specific line of a note with verification

Inputs: line (required), path (required), expectedText, expectedRevision.

obsidian_update_noteC

Updates note content with optimistic revision control

Inputs: path (required), content (required), expectedRevision.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.9/5.0

    Scored across 33 tools

    Disambiguation4/5

    Most tools have distinct resource+action targets, but several pairs overlap: obsidian_read_note vs obsidian_get_note_context both retrieve note content and frontmatter; obsidian_search vs obsidian_search_context both perform full-text search with different output formats; obsidian_append_note vs obsidian_append_daily_note target the same operation on different note types. These could cause occasional misselection without careful reading.

    Naming Consistency4/5

    All tools use the obsidian_ prefix and snake_case, with a consistent verb_noun pattern (e.g., get_vault, list_files, create_note). Minor deviations like recent_changes (noun phrase) and search (bare verb) slightly break the pattern, but overall highly consistent.

    Tool Count2/5

    33 tools is above the typical 3-15 range and exceeds the 25+ threshold for 'too many'. While each tool covers a distinct Obsidian feature, the set is heavy and could be consolidated (e.g., get_property/get_properties, append_note/append_daily_note), indicating over-provisioning for a single MCP server.

    Completeness4/5

    The surface covers note CRUD, daily notes, properties, tasks, links, tags, bases, search, and discovery comprehensively. Minor gaps exist: no folder creation/deletion/rename, no explicit daily note creation, and no bulk operations, but agents can work around these.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Enables AI assistants to interact with Obsidian vaults for creating, reading, searching, and managing notes, daily notes, TODOs, session reports, and backlinks through both stdio and HTTP/SSE transports.
      10
      3,512 npm
      4
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Connects AI assistants to Obsidian vaults via the Local REST API to search notes, retrieve content, and perform semantic searches. It features self-healing multi-URL connectivity and supports both stdio and HTTP transports for flexible deployment.
      283 npm
      13
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Exposes 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.
      1
      PolyForm Noncommercial 1.0.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables reading, searching, and writing an Obsidian vault directly on disk without Obsidian running, and walking its wikilink graph for backlinks, N-hop neighbors, unresolved links, and orphans. Available over Streamable HTTP with bearer auth, stdio, or a Claude Desktop extension.
      20 npm
      MIT