Skip to main content
Glama
DanielChahine0

obsidian-mcp-server

obsidian-mcp-server

A remote MCP server that exposes an Obsidian vault stored in a GitHub repo as callable tools, so Claude can search and read your notes on demand, including on unattended scheduled runs like the morning brief.

Your laptop does not need to be on. The server reads from GitHub, not from your machine.

Why this instead of the GitHub Integration

The GitHub Integration in Claude lets you attach repo files by hand. That is a manual click, so a scheduled task cannot use it. This is a real MCP connector: Claude gets tools it can call by itself.

It also understands Obsidian rather than treating notes as code. It parses YAML frontmatter, block-style and inline tag lists, inline #tags (ignoring # inside code fences), [[wikilinks]], and daily-note dates from filenames.

Related MCP server: obsidianMCP

Tools

Tool

Purpose

obsidian_search_notes

Full-text search, ranked. Title matches beat tag matches beat body frequency. All terms must be present.

obsidian_get_note

Read one note by path, path without extension, or bare title.

obsidian_list_notes

Browse by folder, tag, or date range. This is the daily-notes tool.

obsidian_list_tags

Every tag with note counts, so you can discover the vocabulary before filtering.

obsidian_vault_status

Vault size, folder breakdown, snapshot freshness. refresh=true forces a refetch.

How it reads the vault

It downloads the repo tarball in one API call and builds an in-memory index, rather than making one call per note. A vault with thousands of notes costs a single request. The snapshot is cached for CACHE_TTL_MS (default 5 minutes), and concurrent tool calls share one refresh instead of triggering several downloads.

Setup

1. Vault to GitHub

Install the Obsidian Git community plugin and point it at a private repo. Set auto-commit to something like 10 minutes. Everything downstream reads from that repo.

2. Fine-grained GitHub token

Create one at Settings → Developer settings → Personal access tokens → Fine-grained tokens.

  • Repository access: only the vault repo

  • Permissions: Contents → Read-only

Nothing else. Whatever this token can reach, the server can reach.

3. Shared secret

openssl rand -hex 32

This is MCP_AUTH_TOKEN. It is the only thing between a stranger who finds your URL and your entire vault.

4. Environment

Variable

Required

Default

Notes

GITHUB_OWNER

yes

—

Your GitHub username

GITHUB_REPO

yes

—

Vault repo name

GITHUB_TOKEN

yes

—

The fine-grained token

MCP_AUTH_TOKEN

yes

—

The shared secret

GITHUB_REF

no

main

Branch to read

VAULT_SUBPATH

no

""

Index only a subfolder

CACHE_TTL_MS

no

300000

Snapshot lifetime

PORT

no

3000

Host usually sets this

5. Run locally

npm install
npm run build
GITHUB_OWNER=you GITHUB_REPO=vault GITHUB_TOKEN=ghp_... MCP_AUTH_TOKEN=secret npm start

Verify with the MCP Inspector:

npx @modelcontextprotocol/inspector

Connect to http://localhost:3000/mcp over Streamable HTTP with header Authorization: Bearer <MCP_AUTH_TOKEN>.

There is also an end-to-end test that stubs GitHub with a synthetic vault and drives the real HTTP endpoint:

node test-e2e.mjs

Endpoints

Path

Auth

Purpose

POST /mcp

required

The MCP endpoint. This is the URL you give Claude.

GET /health

none

Liveness probe. Does no index work, so a platform health check can never trigger a GitHub fetch. Reports cache warmth and note count.

GET /warm

required

Rebuilds the index if it has expired. Point your uptime pinger here. Add ?refresh=true to force a rebuild.

Staying warm

The index is built on startup, right after the port binds rather than before it, so deploys are not delayed by a vault download and a large vault cannot fail the platform's health check.

That handles restarts. The other half is idle shutdown. On any tier that sleeps, a once-a-day scheduled run always arrives at a cold server and pays twice: once for the container to boot, once for the index to rebuild. If the total exceeds the connector timeout, the tool call fails, and a brief section that returns nothing is dropped silently rather than showing an error.

To avoid that, point a free pinger (cron-job.org, UptimeRobot) at /warm every 10 minutes:

https://your-service.onrender.com/warm
Header: Authorization: Bearer <MCP_AUTH_TOKEN>

If your pinger cannot send custom headers on its free plan, the token is also accepted as a query parameter:

https://your-service.onrender.com/warm?token=<MCP_AUTH_TOKEN>

Prefer the header. URLs routinely end up in access logs; headers usually do not.

Ping /warm rather than /health, because /health keeps the container alive but leaves the index cold, which only fixes half the problem.

Raise CACHE_TTL_MS well above the 5 minute default once pinging is in place. The default is tuned for interactive use, where you want recent edits to show up quickly. For a scheduled brief, something like 6 hours (21600000) means far fewer rebuilds while still picking up the day's notes.

If your brief runs at a fixed time, schedule an extra ping 10 to 15 minutes before it, so both the container and the index are hot when it fires.

Deploy

Any host that gives you an HTTPS URL works. Anthropic connects from its own cloud, not from your machine, so the server must be reachable on the public internet. A VPN-only or firewalled host will not connect even if you can reach it yourself.

Set the four required env vars as secrets, deploy, confirm GET /health returns {"status":"ok"}.

Notes on hosting choices:

  • Render / Railway / Fly all work with this code unchanged. Check current free-tier terms before relying on one.

  • Cold starts matter here. See "Staying warm" above; on a sleeping tier this is the single thing most likely to break a scheduled run.

Add to Claude

Settings → Connectors → Add custom connector.

  • URL: https://your-host.example.com/mcp

  • Under Advanced settings, add request header Authorization: Bearer <MCP_AUTH_TOKEN>

If your account does not yet show a request-headers field, that capability is still rolling out. Until it does, treat the URL itself as a secret and deploy on an unguessable path.

Then enable the connector in a conversation via the + menu. A connector enabled mid-conversation generally will not appear until you start a new chat.

Using it in the morning brief

The brief accepts a Sections: list with the invocation and makes one targeted fetch per entry against whatever connected tool serves it. Once this connector is live, a section like:

Sections: Open TODOs from my daily notes this week

will route to obsidian_list_notes with folder="Daily" and a since date. A section that finds nothing is dropped from the page rather than rendering an empty block.

Security

  • The GitHub token is read-only and scoped to one repo, so a compromise cannot write to your vault.

  • The bearer check is constant-time, so it does not leak the token's length or prefix through timing.

  • Every tool is read-only and annotated as such.

  • There are no write tools. Claude cannot modify your notes through this server.

Troubleshooting

Symptom

Cause

Connector adds but shows no tools

Transport mismatch. This server speaks Streamable HTTP at POST /mcp.

401 on every call

Header missing or malformed. It must be Authorization: Bearer <token>.

"Repository or ref not found"

Wrong owner/repo/ref, or the token cannot see a private repo.

"GitHub denied access"

Rate limit, or the token is missing Contents:Read.

Notes missing from results

Check VAULT_SUBPATH, and note that .obsidian/, .trash/, .git/ are skipped by design.

Stale results after syncing

Snapshot cache. Call obsidian_vault_status with refresh=true, or lower CACHE_TTL_MS.

First call each morning fails

Host cold start. Ping /warm on a schedule; see "Staying warm".

/warm returns 401

Token missing. Use the Authorization header, or ?token= if your pinger cannot send headers.

/health shows "warm": false

Index not built yet. Normal for a few seconds after a deploy; persistent means the startup warm failed, so check the logs for the reason.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides Claude with read, search, and write access to an Obsidian vault through MCP tools.
    10,054 npm
    Apache 2.0
  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables reading and writing an Obsidian vault stored on GitHub, allowing Claude to list, search, read, and write notes with commits via the GitHub Contents API.
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables Claude to read, search, and analyze your entire knowledge vault locally via MCP tools like search, drafting, and linting.
    57 npm
    4
    MIT