Skip to main content
Glama

obsidian-mcp-server

Ein MCP-Server (Model Context Protocol) für einen Obsidian-Studien-Vault. Gibt Claude Zugriff auf Notizsuche, Notizinhalte, Flashcard-Erstellung im Decks-Format und die Lernplanung des Lerntracker-Plugins.

Python, MCP SDK 2.x, stdio-Transport.

Zweck

Bisher lief die Logik in zwei Obsidian-Plugins:

  • Decks (Fremdplugin) rendert Flashcards, erzeugt sie aber nicht — die Karten wurden von Hand geschrieben.

  • Lerntracker (eigenes Plugin) verwaltet Lernfortschritt und Lernplan, verteilt den Stoff aber bewusst nicht automatisch auf Tage.

Dieser Server schließt beide Lücken: Claude kann Karten direkt im bestehenden Dateiformat anlegen und einen Lernplan berechnen, der in die data.json des Lerntrackers zurückgeschrieben wird.

Installation

cd ~/Projects/obsidian-mcp-server
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Konfiguration

Beide Pfade kommen aus Umgebungsvariablen — nichts ist hardcodiert.

Variable

Default

Bedeutung

OBSIDIAN_VAULT_PATH

~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Sem_4

Wurzel des Vaults

LERNTRACKER_DATA_PATH

$OBSIDIAN_VAULT_PATH/.obsidian/plugins/lerntracker/data.json

Datenbank des Lerntrackers

Der Default für den Vault-Pfad passt zu einem iCloud-synchronisierten Obsidian; für ein anderes Setup genügt es, OBSIDIAN_VAULT_PATH zu setzen.

Der Lerntracker-Pfad ist separat einstellbar, weil Obsidian-Vaults verschachtelt sein können: liegt in einem Unterordner ein weiterer Vault, hat der eine eigene data.json. Der Default zeigt auf die des Hauptvaults.

Tools

Tool

Wirkung

search_notes(query, limit=20)

Sucht case-insensitiv in Dateinamen und Inhalten. Namenstreffer werden höher gewichtet; liefert Pfad + Textstellen. Read-only

get_note(path)

Gibt den vollständigen Inhalt einer Notiz zurück. Read-only

create_flashcard(front, back, note_path, deck="")

Schreibt. Hängt eine Karte an <Kurs>/Flashcards/<deck>.md an

generate_summary(note_path)

Bereitet eine Notiz strukturiert auf. Read-only

save_summary(note_path, summary)

Schreibt. Legt <Kurs>/Zusammenfassungen/<Notiz>.md an

generate_study_plan(courses, deadlines, hours_per_subtopic=1.5, dry_run=False)

Schreibt. Verteilt offene Unterthemen auf Tage und trägt sie in data.json ein

Alle Schemas werden vom SDK aus Type Hints und Docstrings erzeugt — im Code steht kein handgeschriebenes JSON-Schema.

Flashcard-Format

create_flashcard schreibt exakt das Format, das die vorhandenen Karten im Vault benutzen (Header-Paragraph), erweitert um einen Wikilink auf die Quelle:

---
tags: [decks]
---

## Was ist ein Signal?

Eine zeitabhängige, messbare physikalische Größe.

Quelle: [[01_Physikalische_Schicht]]

Die Zieldatei ergibt sich aus dem Kursordner der Quellnotiz; deck überschreibt den Dateinamen. Existiert die Datei nicht, wird sie mit tags: [decks] angelegt. Eine Karte mit identischer Vorderseite wird übersprungen statt doppelt angelegt.

Der Lernstand von Decks liegt in einer SQLite-Datenbank, nicht in den Markdown-Dateien. Der Server fasst sie nicht an — die FSRS-Historie bleibt unberührt.

Warum generate_summary nicht selbst zusammenfasst

Der Server hat kein Sprachmodell. Er liefert die Notiz strukturiert zurück (Gliederung, Kennzahlen, Volltext); die Zusammenfassung schreibt das Modell auf der Client-Seite — also Claude Desktop. Gespeichert wird sie anschließend mit save_summary. Das ist die übliche MCP-Rollenverteilung: der Server liefert Kontext und führt Aktionen aus, das Modell formuliert.

Soll der Server stattdessen selbst zusammenfassen, müsste er die Anthropic-API aufrufen und bräuchte einen eigenen API-Key.

Lernplan-Logik

generate_study_plan verteilt jedes offene Unterthema auf konkrete Tage:

  1. Kurse werden nach Klausurdatum sortiert — die früheste Klausur zuerst.

  2. Lernschluss = examDate − bufferDays; die Puffertage bleiben für Wiederholung frei.

  3. Lerntage kommen aus settings.weeklyHours (0 = Sonntag … 6 = Samstag). Tage mit 0 Stunden und alle blockedDates werden übersprungen.

  4. Jedes Unterthema kostet hours_per_subtopic (Default 1,5 h) und wird in den frühesten Tag mit Restkapazität gelegt. Passt es nicht in einen Tag, wird es über mehrere Tage gesplittet — das Plugin unterstützt mehrere dates.

  5. Bereits abgehakte Unterthemen und solche mit vorhandenen dates bleiben unangetastet.

  6. Was nicht mehr vor den Lernschluss passt, wird als Warnung gemeldet statt still verworfen.

Vor jedem Schreibvorgang entsteht ein Backup neben der Datei (data.backup-<Zeitstempel>.json); geschrieben wird atomar über eine Temp-Datei. dry_run=True zeigt nur den Plan.

Nach dem Schreiben in Obsidian Cmd+R drücken, damit das Plugin neu lädt.

Resources

URI

Inhalt

vault://structure

Ordnerbaum des Vaults mit Notizanzahl je Ordner

note://{+path}

Inhalt einer einzelnen Notiz, read-only

Das Template nutzt bewusst {+path} (Reserved Expansion) statt {path}. Normale Template-Variablen matchen keine Slashes — mit {path} würde jede Notiz in einem Unterordner stillschweigend nicht gefunden, und im Vault liegt praktisch jede Notiz in einem Kursordner.

Lokal testen mit dem MCP Inspector

Der Inspector wird über die CLI des SDK gestartet und öffnet eine Weboberfläche, in der sich Tools und Resources einzeln aufrufen lassen. Er braucht npx (Node.js) und uv.

source .venv/bin/activate && mcp dev main.py

Der Befehl gibt eine URL wie http://localhost:6274 aus (mit angehängtem Session-Token). Im Browser öffnen, links auf Connect, dann:

  • Reiter ToolsList Tools → ein Tool wählen, Argumente eintragen, Run Tool

  • Reiter ResourcesList Resourcesvault://structure anklicken

  • Für die templated Resource den URI direkt eingeben, nach dem Muster note://<Kursordner>/Flashcards/<Datei>.md

Mit abweichendem Vault:

OBSIDIAN_VAULT_PATH="$HOME/Pfad/zu/deinem/Vault" mcp dev main.py

Zum Ausprobieren der schreibenden Tools lohnt sich ein Wegwerf-Vault:

OBSIDIAN_VAULT_PATH=/tmp/testvault mcp dev main.py

Anbindung an Claude Desktop

Konfigurationsdatei: ~/Library/Application Support/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "obsidian-vault": {
      "command": "/Users/DEIN_NAME/Projects/obsidian-mcp-server/.venv/bin/python",
      "args": ["/Users/DEIN_NAME/Projects/obsidian-mcp-server/main.py"],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/Users/DEIN_NAME/Pfad/zu/deinem/Vault"
      }
    }
  }
}

Wichtig: absolute Pfade verwenden — ~ und $HOME werden hier nicht expandiert. Als command das Python aus dem venv angeben: Claude Desktop startet den Server ohne aktivierte Umgebung, ein bloßes "python3" fände das mcp-Paket nicht.

Existiert die Datei schon, nur den Eintrag "obsidian-vault" in das vorhandene mcpServers-Objekt einfügen. Danach Claude Desktop komplett beenden und neu starten; der Server erscheint dann im Werkzeug-Menü des Eingabefelds.

Sicherheit

Jeder Pfad aus einem Tool- oder Resource-Aufruf wird gegen den Vault geprüft: absolute Pfade und ..-Traversal werden abgelehnt, und das aufgelöste Ziel muss innerhalb von OBSIDIAN_VAULT_PATH liegen. .obsidian, .git, .trash, .claude und node_modules sind von Suche und Strukturauflistung ausgenommen — Plugin-Bundles würden die Ergebnisse sonst überschwemmen.

save_summary überschreibt keine existierende Datei, create_flashcard legt keine doppelte Karte an, und generate_study_plan sichert data.json, bevor es schreibt.

Getestet

Gegen mcp 2.0.0 auf Python 3.14: Tool-Schemas, Resource-Templates, stdio-Handshake mit einem echten ClientSession, Pfad-Guards, sowie die schreibenden Tools gegen einen Wegwerf-Vault (inklusive Mehrtages-Split, blockierten Tagen, Nullstunden-Wochentagen und dem Überlauffall).

Lizenz

MIT — siehe LICENSE.