Skip to main content
Glama
maxkuminov

Obsidian MCP (pgvector + Ollama, self-hosted)

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
CHUNK_SIZENoApproximate tokens per chunk (4-char heuristic)512
OLLAMA_URLNoOllama server URL (required when provider is ollama)
SECRET_KEYYesSecret key for itsdangerous signer
VAULT_PATHYesIn-container path to the Obsidian vault (default: /obsidian)
DATABASE_URLYesPostgreSQL connection string (e.g., postgresql+asyncpg://user:pass@host/db)
CHUNK_OVERLAPNoToken overlap between chunks0
OPENAI_API_KEYNoOpenAI API key (required when provider is openai)
EMBEDDING_MODELNoOllama model name (used when provider is ollama)bge-m3
MULTI_USER_MODENoEnable multi-user mode (requires SECRET_KEY)false
OPENAI_BASE_URLNoBase URL for OpenAI-compatible APIhttps://api.openai.com/v1
MCP_SANDBOX_MODENoRegistry-eval only. Skips DB, indexer, embedding provider, and /mcp auth so introspection works without external deps. Do not enable in production.false
EMBEDDING_PROVIDERNoEmbedding provider: 'ollama' or 'openai'ollama
EMBEDDING_DIMENSIONSNopgvector column width (must match model output)1024
INDEX_INTERVAL_SECONDSNoPeriodic reindex cadence in seconds300
OPENAI_EMBEDDING_MODELNoOpenAI embedding model nametext-embedding-3-small

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
keyword_searchA

Full-text keyword search via PostgreSQL tsvector. Use this for exact identifiers, code symbols, proper nouns, or known phrases — anywhere semantic noise hurts.

For conceptual or paraphrased queries, use semantic_search instead.

Args: query: Keywords or phrase to match (websearch tsquery syntax: "foo bar", "foo OR bar", "-bar"). folder: Optional folder prefix (e.g. "Cards/", "Projects/"). limit: Maximum number of results (default 20). tags: Optional list of tag names; only notes carrying ALL listed tags match (e.g. ["project", "active"]). frontmatter: Optional dict of frontmatter key/value pairs; only notes whose JSONB frontmatter contains every pair match. Strict type matching — string "0" does not match integer 0 (e.g. {"status": "draft"}).

read_noteA

Read a note from the Obsidian vault by its relative path.

Args: path: Vault-relative path to the note (e.g. "Cards/My Note.md")

list_notesA

List notes in a vault folder, sorted by most recently modified.

Results come from the index, so a note that exists on disk but has not yet been picked up by the indexer will not appear (lag is bounded by the index interval, typically up to 5 minutes).

Args: folder: Vault-relative folder path (e.g. "Cards/", "Projects/"). Empty for vault root. limit: Maximum number of results (default 50). tags: Optional list of tag names; only notes carrying ALL listed tags match (e.g. ["idea"]). frontmatter: Optional dict of frontmatter key/value pairs; strict type match (e.g. {"status": "active"}).

get_tagsB

List all tags used across the vault with note counts.

Args: limit: Maximum number of tags to return (default 50)

get_recentA

Get recently modified notes.

Args: limit: Number of recent notes to return (default 20). folder: Optional folder prefix to filter (e.g. "Projects/"). tags: Optional list of tag names; only notes carrying ALL listed tags match (e.g. ["meeting"]). frontmatter: Optional dict of frontmatter key/value pairs; strict type match (e.g. {"status": "active"}).

semantic_searchA

Vector similarity search using bge-m3 embeddings. Use this for conceptual or paraphrased queries — anywhere exact word matching would miss the point.

For exact identifiers, code symbols, proper nouns, or known phrases, use keyword_search instead.

Each result is one note (deduped) with its best-matching chunk as a ~500-char preview. Call read_note on a result's path to get the full note content.

Args: query: Natural language description of what you're looking for. limit: Maximum number of distinct notes to return (default 15). folder: Optional folder prefix (e.g. "Projects/"). tags: Optional list of tag names; only notes carrying ALL listed tags match (e.g. ["product"]). frontmatter: Optional dict of frontmatter key/value pairs; strict type matching — string "0" does not match integer 0 (e.g. {"status": "active"}).

create_noteA

Create a new markdown note in the Obsidian vault. Requires a readwrite API key.

See get_vault_guide for Obsidian syntax and any vault-specific conventions (naming, folder placement, frontmatter, tags).

Args: path: Vault-relative path for the new note (e.g. "Cards/New Topic.md"). The .md extension is added if missing. content: Full markdown content for the note, including any frontmatter.

edit_noteA

Edit an existing note in the Obsidian vault. Requires a readwrite API key.

See get_vault_guide for Obsidian syntax and any vault-specific conventions (naming, folder placement, frontmatter, tags).

Four mutually exclusive modes (set at most one of append/find/section):

  1. Full replace (default): provide only content; the entire file is overwritten.

  2. Append: append=True; content is added at the end (preceded by a single newline).

  3. Find & replace: find=<exact text>; replaced with content. Must match exactly once unless replace_all=True.

  4. Section: section=<heading>; replaces the body under the named ATX heading. Use the path-style form Parent/Child to disambiguate when the same heading appears more than once. Setext (====/----) headings are not matched.

Flags:

  • replace_all=True: with find, replace every occurrence rather than failing on multiple matches. Ignored when find is unset.

  • dry_run=True: compute the would-be result and return a unified diff without writing. Works for all four modes.

Writes are atomic (tmp file + os.replace) so a crash mid-write cannot truncate the destination. Frontmatter mutation is better done via set_frontmatter — PyYAML serialization there discards YAML comments.

Args: path: Vault-relative path to the note. content: New full content, replacement text, text to append, or section body. append: If True, append content to the end of the note. find: Exact text to find and replace. section: ATX heading text identifying the section whose body to replace. Use Parent/Child to disambiguate repeated headings. replace_all: With find, replace every match instead of requiring uniqueness. dry_run: Return a unified diff and do not write.

get_vault_guideA

Returns a two-part guide for working with this Obsidian vault:

  1. Obsidian primer — generic syntax (wikilinks, embeds, block refs, heading refs, tags, frontmatter, callouts, comments, highlights, math, mermaid, footnotes, tasks, plugin literals).

  2. Vault-specific conventions — folder structure, naming rules, frontmatter requirements, and tag taxonomy as configured by the vault owner in CLAUDE.md. If CLAUDE.md is absent, the response includes instructions for creating one.

get_backlinksA

Notes that link TO path. Use this to discover what references a given note — projects citing a card, daily notes mentioning a person, etc.

Resolved links only (dangling references are not counted as backlinks).

Args: path: Vault-relative path to the target note (e.g. "Cards/Foo.md"). limit: Maximum results (default 50, hard cap 500).

get_linksA

Outgoing links from path — both resolved and dangling.

Useful for "what does this note depend on?" or finding broken references that need follow-up notes.

Args: path: Vault-relative path to the source note.

get_neighborhoodA

The connected subgraph reachable from path via links or backlinks, up to depth hops (treated as undirected).

Use this when an agent needs the local cluster around a topic — e.g. "summarize everything connected to this project". Prefer this over find_related when explicit links are the signal you want; prefer find_related when the connection is conceptual rather than linked.

Args: path: Vault-relative path to the seed note. depth: Maximum BFS depth (default 1, capped at 5). limit: Maximum distinct neighbor notes (default 50, hard cap 200).

find_relatedA

Semantically similar notes based on the source note's chunk embeddings, averaged then queried via pgvector.

Independent of the link graph — useful when the source is sparsely linked or when looking for thematic neighbors. For link-based exploration use get_neighborhood. For arbitrary topic queries use semantic_search.

Args: path: Vault-relative path to the source note. limit: Maximum results (default 10, hard cap 50).

find_orphansA

Notes with zero incoming AND zero outgoing resolved links — useful for vault hygiene ("what's disconnected?") and cleanup decisions.

Args: folder: Optional vault-relative folder prefix to scope the search (e.g. "Cards/"). limit: Maximum results (default 50, hard cap 500).

move_noteA

Move or rename a note inside the vault. Requires a readwrite API key.

Updates notes_metadata.file_path for the moved note and note_links.target_path rows whose stored target matched the old path. Backlinks via target_note_id keep working without rewriting source notes (the moved note's id is unchanged).

With rewrite_links=True, also opens every source note that linked to this note and rewrites the link title in-place: [[Old]][[New]], [[Old|alias]][[New|alias]], [[Old#anchor]][[New#anchor]], ![[Old]]![[New]], and path-style [[folder/Old]][[new/folder/New]]. Aliases and anchors are preserved; only the title portion is rewritten.

Writes are atomic. See get_vault_guide for vault folder conventions.

Args: from_path: Vault-relative path of the existing note. to_path: Vault-relative path of the destination. Must not exist. Parent directories are created automatically. rewrite_links: If True, also rewrite incoming wikilinks and embeds in source notes. Off by default — opting in is destructive (it modifies other notes' bodies).

delete_noteA

Delete a note from the vault. Requires a readwrite API key.

By default this is a soft-delete: the file is moved to .trash/<YYYYMMDD-HHMMSS>-<basename> inside the vault root. The indexer skips dot-prefixed directories, so search and embeddings drop the note automatically on the next reindex pass (≤ 5 minutes). Soft-deleted files accumulate in .trash/ — emptying that directory is the user's responsibility.

With permanent=True, the file is os.unlink-ed directly with no recovery path inside this server. Existing backups are the rollback story.

Dangling backlinks left behind by a delete are surfaced via get_backlinks and find_orphans. See get_vault_guide for context.

Args: path: Vault-relative path to the note. permanent: If True, unlink instead of soft-deleting.

set_frontmatterA

Mutate a note's YAML frontmatter without touching its body. Requires a readwrite API key.

Parses the existing frontmatter, merges in updates (overwriting matching keys, adding any new ones), then drops keys listed in remove. The note body is preserved byte-for-byte. If the note has no frontmatter (no --- fence on line 1), a fresh block is prepended ahead of the unchanged body.

Re-serialization uses yaml.safe_dump(default_flow_style=False, sort_keys=False, allow_unicode=True). Caveat: PyYAML does NOT preserve YAML comments — any # comment in the original frontmatter will be lost on the first set_frontmatter call.

See get_vault_guide for vault frontmatter conventions.

Args: path: Vault-relative path to the note. updates: Mapping of keys to set. Use the empty dict (or omit) to skip. remove: List of keys to delete from the frontmatter. Missing keys are silently ignored.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/maxkuminov/obsidian-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server