obsidian-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| OBSIDIAN_VAULTS | No | Inline JSON string that defines the vaults. | |
| OBSIDIAN_API_KEY | No | Legacy: API key for the Local REST API. | |
| OBSIDIAN_READ_ONLY | No | Set to 'true' to disable all mutating tools. Default is 'false'. | false |
| OBSIDIAN_HEALTH_TTL | No | Seconds to cache each vault's REST health probe. Default is '20'. | 20 |
| OBSIDIAN_VAULT_PATH | No | Legacy: path to the default vault. | |
| OBSIDIAN_PREFER_REST | No | Set to 'false' to always use the filesystem backend. Default is 'true'. | true |
| OBSIDIAN_VAULTS_FILE | No | Path to a TOML file that defines the vaults. | |
| OBSIDIAN_DEFAULT_VAULT | No | Name of the default vault. | |
| OBSIDIAN_REST_BASE_URL | No | Legacy: base URL for the Local REST API. | |
| OBSIDIAN_MAX_FILE_BYTES | No | Maximum file size in bytes that will be read. Default is '2000000'. | 2000000 |
| OBSIDIAN_REQUEST_TIMEOUT | No | Per-request timeout in seconds. Default is '15'. | 15 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
| experimental | {} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| obsidian_list_vaultsA | List every configured vault, showing which one unqualified calls will hit. Reads configuration only — it does not contact Obsidian, so it is cheap and safe to call first when you are unsure which vaults exist. For live reachability, use obsidian_backend_status. Returns: str: JSON of the shape {"current_vault": str, "count": int, "vaults": [{"name": str, "description": str|null, "path": str|null, "rest_base_url": str|null, "backends": ["rest"|"filesystem"], "is_current": bool, "is_default": bool}]} On failure: "Error: " |
| obsidian_switch_vaultA | Change which vault subsequent unqualified tool calls target. Changes this server session only — nothing in any vault is modified, and
Obsidian's own open vault is unaffected. Prefer passing Args: params (SwitchVaultInput): - vault (str): Name of the vault to make current Returns: str: JSON of the shape {"status": "ok", "current_vault": str, "previous_vault": str, "path": str|null, "rest_base_url": str|null, "backends": ["rest"|"filesystem"]} On failure: "Error: Unknown vault ''. Configured vaults: ..." |
| obsidian_list_notesA | List notes and folders in the vault, optionally scoped to one folder. Use this to discover the vault's structure before reading or writing. For finding a note by its contents, obsidian_search_notes is faster. Args: params (ListNotesInput): - folder (str): Vault-relative folder, empty for the root - recursive (bool): Walk nested folders too - response_format (str): 'markdown' or 'json' Returns: str: Markdown list, or JSON of the shape {"folder": str, "count": int, "backend": "rest"|"filesystem", "entries": [{"path": str, "is_folder": bool, "size_bytes": int, "modified_at": str}]} On failure: "Error: " |
| obsidian_read_noteA | Read a note's content plus its tags, frontmatter, and links. Backlinks are only populated when Obsidian is running, since they come from Obsidian's own link index. Args: params (ReadNoteInput): - path (str): Vault-relative path, '.md' optional - include_content (bool): False for metadata only - response_format (str): 'markdown' or 'json' Returns: str: Markdown rendering, or JSON of the shape {"path": str, "content": str, "frontmatter": object, "tags": [str], "links": [str], "backlinks": [str], "size_bytes": int, "modified_at": str, "source": "rest"|"filesystem"} On failure: "Error: " |
| obsidian_search_notesA | Full-text search across the vault, returning matching notes with context. When Obsidian is running this uses its native search index and results are ranked the way they would be in the app. Otherwise it falls back to scanning files directly, which matches all terms as substrings. Args: params (SearchNotesInput): - query (str): Search text; all terms must be present to match - context_length (int): Characters of context per match, 10-1000 - limit (int): Maximum notes returned, 1-100 - response_format (str): 'markdown' or 'json' Returns: str: Markdown results, or JSON of the shape {"query": str, "count": int, "backend": "rest"|"filesystem", "results": [{"path": str, "score": float, "context": [str]}]} On no matches: a message suggesting broader terms. On failure: "Error: " |
| obsidian_list_tagsA | List every tag in the vault with how many notes use it. Useful for orienting in an unfamiliar vault, or checking which tag spelling is already in use before adding a new one. Args: params (EmptyInput): - response_format (str): 'markdown' or 'json' Returns: str: Markdown table, or JSON of the shape {"count": int, "backend": "rest"|"filesystem", "tags": {"": int}} Tag names have no leading '#'. Counts may be 0 when the REST backend reports tags without usage data. On failure: "Error: " |
| obsidian_get_active_noteA | Read whichever note is currently open in the Obsidian window. Requires Obsidian to be running with the Local REST API plugin — there is no filesystem equivalent of "the active note". Args: params (EmptyInput): - response_format (str): 'markdown' or 'json' Returns: str: Same shape as obsidian_read_note. On failure: "Error: Cannot read the active note: " |
| obsidian_backend_statusA | Report, per vault, which backend is live and which capabilities are usable. Call this first when a tool fails with a connectivity error, or when you need to know whether backlinks, the active note, and commands work right now. With no vault named it probes every configured vault. Set verify=true to cross-check that each vault's REST port is really serving the vault at its configured path. Worth doing after adding a vault or changing ports, since every vault's plugin defaults to port 27124 and they collide. Args: params (StatusInput): - vault (Optional[str]): One vault to check; omit to check all - verify (bool): Also cross-check REST endpoint against vault path - response_format (str): unused; JSON is always returned Returns: str: JSON of the shape {"current_vault": str, "read_only": bool, "vaults": [{"vault": str, "rest_configured": bool, "rest_reachable": bool, "rest_base_url": str|null, "filesystem_configured": bool, "vault_path": str|null, "active_backend": "rest"|"filesystem", "obsidian_only_features_available": bool, "is_current": bool, "identity": {...}}]} On failure: "Error: " |
| obsidian_write_noteA | Create a note, or fully replace an existing one. This overwrites the entire file. To add to a note without losing what is already there, use obsidian_append_to_note or obsidian_patch_note instead. Parent folders are created as needed. Args: params (WriteNoteInput): - path (str): Vault-relative path - content (str): Complete markdown content - overwrite (bool): Required to replace an existing note Returns: str: JSON of the shape {"status": "ok", "action": "created"|"replaced", "path": str, "backend": "rest"|"filesystem"} On failure: "Error: " |
| obsidian_append_to_noteA | Add markdown to the end of a note, creating the note if it does not exist. Safe by design: nothing already in the note is modified. Repeated calls append repeatedly. Args: params (AppendNoteInput): - path (str): Vault-relative path - content (str): Markdown to append Returns: str: JSON of the shape {"status": "ok", "action": "appended", "path": str, "backend": "rest"|"filesystem"} On failure: "Error: " |
| obsidian_patch_noteA | Insert content at a specific heading, block reference, or frontmatter key. This is the surgical option: it edits one section and leaves the rest of the note byte-for-byte unchanged. Read the note first to confirm the target exists — a missing heading or block ID is an error, not a silent no-op. Args: params (PatchNoteInput): - path (str): Vault-relative path to an existing note - target_type (str): 'heading', 'block', or 'frontmatter' - target (str): Heading text ('::' separates nesting levels), block ID without '^', or frontmatter key - content (str): Markdown to insert, or the frontmatter value - operation (str): 'append', 'prepend', or 'replace' Returns: str: JSON of the shape {"status": "ok", "action": "patched", "path": str, "backend": "rest"|"filesystem"} On failure: "Error: ", including when the target heading or block ID does not exist. Examples: - Add today's entry under a Log heading -> target_type='heading', target='Log', operation='append' - Set a status field -> target_type='frontmatter', target='status', content='active', operation='replace' |
| obsidian_append_to_daily_noteA | Append to the daily note for today or a given date, creating it if needed. The daily note path is built from OBSIDIAN_DAILY_FOLDER and OBSIDIAN_DAILY_FORMAT, so it works whether or not Obsidian is running. Args: params (DailyNoteInput): - content (str): Markdown to append - day (Optional[str]): ISO date 'YYYY-MM-DD'; defaults to today - heading (Optional[str]): Append under an existing heading instead of at the end of the note Returns: str: JSON of the shape {"status": "ok", "action": "appended"|"patched", "path": str, "backend": "rest"|"filesystem", "note": str} On failure: "Error: " |
| obsidian_move_noteA | Move or rename a note, creating destination folders as needed. Wikilinks elsewhere in the vault are NOT rewritten — Obsidian only updates links when the move happens inside the app. Search for links to the old name afterwards if that matters. Args: params (MoveNoteInput): - source (str): Current vault-relative path - destination (str): New vault-relative path - overwrite (bool): Replace the destination if it exists Returns: str: JSON of the shape {"status": "ok", "action": "moved", "path": str, "backend": "rest"|"filesystem", "note": str} On failure: "Error: " |
| obsidian_delete_noteA | Delete a note, by default into the vault's .trash folder. A default delete is recoverable: the note moves to '.trash' inside the vault and can be restored from Obsidian. Pass permanent=true only when the note should be unrecoverable. Args: params (DeleteNoteInput): - path (str): Vault-relative path to delete - permanent (bool): True deletes outright and cannot be undone Returns: str: JSON of the shape {"status": "ok", "action": "trashed"|"deleted", "path": str, "backend": "rest"|"filesystem", "note": str} where path is the .trash location for a recoverable delete. On failure: "Error: " |
| obsidian_open_noteA | Bring a note up in the Obsidian window. Changes what the user sees but not the vault's contents. Requires Obsidian to be running with the Local REST API plugin. Args: params (OpenNoteInput): - path (str): Vault-relative path to open Returns: str: JSON {"status": "ok", "action": "opened", "path": str, "backend": "rest"} On failure: "Error: " |
| obsidian_list_commandsA | List every command in Obsidian's command palette, with IDs for running them. Requires Obsidian to be running with the Local REST API plugin. Returns: str: JSON of the shape {"count": int, "commands": [{"id": str, "name": str}]} On failure: "Error: " Args: params (VaultOnlyInput): - vault (Optional[str]): Which vault's Obsidian window to query |
| obsidian_run_commandA | Run a command from Obsidian's command palette by ID. Effects depend entirely on the command — some only change the view, others modify notes or invoke other plugins. Check obsidian_list_commands for valid IDs and prefer a specific tool when one exists. Args: params (RunCommandInput): - command_id (str): ID from obsidian_list_commands Returns: str: JSON {"status": "ok", "action": "executed", "command_id": str, "backend": "rest"} On failure: "Error: " |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 17 tools
Every tool targets a distinct resource and action: reading (read_note, get_active_note), discovery (list_notes, search_notes, list_tags), writing (write_note, append_to_note, patch_note), and vault administration (list_vaults, switch_vault, backend_status). Overlapping write tools are clearly differentiated by insertion point and cross-reference each other.
The overwhelming majority follow the obsidian_verb_noun pattern (list_notes, read_note, write_note, delete_note, run_command). Only obsidian_backend_status deviates by using a noun-noun name instead of a verb such as get_status or get_backend_status.
17 tools is slightly above the ideal 3-15 range, but each tool earns its place covering vaults, notes, search, commands, and status. The granularity between append, patch, and append_to_daily_note is justified by real workflow differences.
Note CRUD is fully covered (create via write, read, update via write/append/patch, move, delete), plus discovery, search, tag listing, vault management, and Obsidian command execution. Minor gaps exist—such as no restore-from-trash tool and no explicit folder creation—but agents can work around them.