French version: README.fr.md Operations guide: OPERATIONS.md Guide d’exploitation (FR): OPERATIONS.fr.md
MCP (Model Context Protocol) server for Obsidian with shared local caching, integrated Tasks tools, and semantic search powered by Smart Connections.
npm install
npm run build
node dist/stdio-proxy.jsRecommended for Codex: point your MCP config to dist/stdio-proxy.js, not directly to dist/index.js.
- Node.js >= 22.7.5
- Obsidian Desktop for
livemode. Headless modes only require a local vault path. - Plugins:
- Local REST API (required for live REST tools): https://github.com/coddingtonbear/obsidian-local-rest-api
- Smart Connections (required for semantic search): https://github.com/brianpetro/obsidian-smart-connections
- Bases Bridge (REST) (required for live/plugin-backed
.basetools, bundled in this repo) - Obsidian Tasks plugin (required for canonical Tasks behavior)
- For semantic search, make sure your vault has a
.smart-envfolder
From source:
git clone https://github.com/optimikelabs/optimike-obsidian-mcp.git
cd optimike-obsidian-mcp
npm install
npm run buildRun the recommended local MCP entrypoint:
node dist/stdio-proxy.js- Connect Obsidian to MCP agents (Codex, IDEs, etc.)
- Expose Obsidian REST tools (read/write, frontmatter, tags, search)
- Provide local vector search via Smart Connections (
.smart-env) - Keep one durable local backend instead of re-spawning heavy state on every stdio run
Optimike Obsidian MCP gives agents a structured way to work with an Obsidian vault:
- read, list, update, and search notes
- manage frontmatter and tags
- query and update Obsidian Bases through the bundled Bases Bridge
- inspect and query Obsidian Tasks
- run semantic search against a Smart Connections index
- check server health, cache state, degraded mode, and write policy
In short: it does more than read notes. It exposes the vault as an operational MCP surface, with read/write tools, structured metadata operations, Tasks, Bases, semantic search, and server health/status observability.
The durable backend is useful in local setups, but it becomes especially valuable in shared or remote backend setups.
Instead of syncing and indexing the vault separately for every agent client, clients can talk to one MCP backend that owns the cache, semantic metadata, task cache, and Obsidian operations. The backend still needs access to the vault itself, a mounted vault path, or the Obsidian REST API, but the agent clients do not each need their own full vault sync or indexing layer.
This makes the MCP a practical boundary between agents and Obsidian: agents call tools, the backend handles the vault.
- Complete MCP toolset (notes, frontmatter, tags, global search, etc.)
- Integrated Tasks tools:
list_all_tasksandquery_tasks - Local semantic search
smart_semantic_search - Server health/status tools:
obsidian_runtime_statusandobsidian_runtime_maintenance - Read-only degraded mode for
obsidian_read_noteandobsidian_list_noteswhen Obsidian REST is down - Shared SQLite store for vault content, task cache, and semantic manifest data
- Embedder‑agnostic: query embedding aligned to the vault model
- Ollama / OpenAI support (env overrides); Xenova / Transformers is disabled until its vulnerable ONNX/protobuf chain can be safely reintroduced
- Obsidian + plugins (Local REST API, Bases Bridge, Smart Connections)
- Optimike Obsidian MCP (this server)
- MCP Agents (Codex, IDEs, etc.)
The server acts as a bridge between agents and Obsidian, adds a “Base” layer for .base files, and persists shared runtime state locally so Codex can stay fast and stable across runs.
Obsidian has no native API for Bases (.base).
The Bases Bridge (REST) plugin fills the gap by adding dedicated REST endpoints.
Official prefix (recommended):
GET /extensions/obsidian-bases-bridge/bases
List all available bases.GET /extensions/obsidian-bases-bridge/bases/:id/schema
Return the schema (properties, formulas, views).POST /extensions/obsidian-bases-bridge/bases/:id/query
Query a base (filters, sorting, pagination, evaluate).POST /extensions/obsidian-bases-bridge/bases/:id/upsert
Bulk frontmatter upsert.POST /extensions/obsidian-bases-bridge/bases
Create/validate a.basefile.GET /extensions/obsidian-bases-bridge/bases/:id/config
Read the base YAML.PUT /extensions/obsidian-bases-bridge/bases/:id/config
Update the base YAML.
Legacy aliases (MCP compat):
GET /basesGET /bases/:id/schemaPOST /bases/:id/queryPOST /bases/:id/upsertPOST /basesGET /bases/:id/configPUT /bases/:id/config
When evaluate: true, the bridge returns:
source: "engine": auto‑cache + formula evaluation (no Bridge view)source: "fallback": partial on‑disk evaluation if engine is OFF
This server exposes “Base” MCP tools:
bases_list: list basesbases_get_schema: fetch schemabases_query: paged query with filters/sortbases_upsert_rows: bulk frontmatter updatebases_upsert_config: validate or update base YAML/JSON configbases_create: create/validate a.base
bases_upsert_rows is batch-safe by default for live Obsidian writes: it runs a live preflight, supports dryRun, configurable chunkSize, delayMs, maxRetries, retryBackoffMs, and requestTimeoutMs, and returns a structured summary with changed_count, failed_count, failed_operations, and retry metadata. For large or sensitive batches, prefer dryRun: true first, then chunkSize: 1, continueOnError: true, and maxRetries: 2.
Protected or virtual keys (file.*, formula.*, création/creation, modification) are refused before writing. Bridge-side processFrontMatter timeouts are surfaced as retryable write_timeout errors; treat them as an Obsidian busy/indexing/locked signal before blaming the payload.
The repo supports two local runtime modes:
stdio proxy(recommended for Codex): a lightweight stdio process that auto-starts a local Streamable HTTP backend if neededhttp backend: the actual long-lived backend process that owns the heavy cache / warmup work
The backend persists vault content to a shared SQLite store and keeps only a bounded hot set in RAM. By default the cache lives at:
<vault>/.obsidian/optimike-mcp/shared-cache.sqlite
The same database also stores:
file_cachefor note contenttask_file_cachefor parsed Tasks datasemantic_manifestandsemantic_vectorsfor semantic metadata
If OBSIDIAN_VAULT is not set, the server falls back to the parent vault inferred from SMART_ENV_DIR, then to the project root.
Useful env overrides:
OBSIDIAN_RUNTIME_MODE=live|hybrid|headless-readonly|headless-guarded|headless-filesystemto choose the runtime contractOBSIDIAN_SHARED_CACHE_DB_PATHto move the shared SQLite fileOBSIDIAN_CONTENT_HOT_CACHE_LIMITto tune the bounded in-memory hot setOBSIDIAN_CACHE_SOURCE=auto|filesystem|restto choose cache refresh source (autoprefers the local vault path when available)OBSIDIAN_CACHE_CONCURRENCYto bound local filesystem refresh workOBSIDIAN_VAULT_EXCLUDE_PATTERNSto add comma- or newline-separated gitignore-style exclusions on top of the built-in vault safety policyMCP_WRITE_MODE=readonly|guarded|fullto enforce server-side write safety (fullis the default; setguardedorreadonlyexplicitly to harden a host)MCP_GUARDED_MAX_WRITE_CHARSandMCP_GUARDED_MAX_BATCH_OPERATIONSto tune guarded-mode limits
For production-like tests on a real vault, set OBSIDIAN_SHARED_CACHE_DB_PATH outside the vault so validation databases do not pollute the synced note tree.
Vault exclusion policy:
- Built-in filesystem/cache exclusions cover
.obsidian,.trash,.git,.tmp,tmp,node_modules, screenshots folders, build/cache folders, SQLite/DB files, and log files. OBSIDIAN_VAULT_EXCLUDE_PATTERNSlets an operator add project-specific exclusions, for exampletmp/**,**/tmp/**,Efforts/Archives/**.- Exclusions apply to filesystem cache refreshes and local Bases fallback scans. They do not claim Desktop parity and they do not stop Obsidian Sync itself from downloading files; use a clean server vault or Sync-side hygiene for that.
npm run check:vault-exclusions -- --vault=/path/to/vaultprints the policy effect before running a long headless validation.
This runtime exposes the Tasks surface directly from the main MCP, so Codex no longer needs a second dedicated optimike-obsidian-tasks-mcp entry when using this server.
Warm semantic refreshes now load from SQLite first instead of re-reading the whole .smart-env path every time.
live(default): Obsidian Desktop + Local REST API. Full REST, write, and Bases Bridge surface.hybrid: starts from the local vault/cache and uses Local REST API whenOBSIDIAN_API_KEYis configured. The API startup check is non-blocking. If no API key is configured,OBSIDIAN_VAULTis required.headless-readonly: no Obsidian Desktop, no Local REST API, noOBSIDIAN_API_KEY. RequiresOBSIDIAN_VAULT; setOBSIDIAN_CACHE_SOURCE=filesystemfor an explicit server profile. Exposes read/list/search/tasks/semantic/runtime tools plus local readonlybases_list,bases_get_schema, andbases_query.headless-guarded: no Obsidian Desktop; exposes the headless read surface plus guarded filesystem writes forobsidian_update_note,obsidian_search_replace, andobsidian_manage_frontmatter. Note updates are append/prepend only; overwrite remains blocked by the guarded write policy. Local readonly Bases fallback is also available.headless-filesystem: no Obsidian Desktop; exposesheadless-guardedplus bounded filesystem features: frontmatter/inline tags, local tag index/audit and dry-run rename, admin move/archive/delete operations withexpectedHashorexpectedMtime, batch frontmatter with dry-run,.baseYAML create/config, Bases rows as Markdown frontmattersetoperations, and minimal JSON Canvas create/validate helpers.
Headless means Optimike MCP running over a synchronized Markdown vault. It does not mean Obsidian Desktop, community plugins, command palette, active file, or Bases Bridge are available without Desktop.
Smoke tests and runtime contract:
npm run test:runtime
npm run smoke:headless-readonly
npm run smoke:hybrid-unavailable
npm run smoke:hybrid-api-available
npm run smoke:headless-guarded
npm run smoke:headless-filesystem
npm run smoke:headless-status
npm run check:vault-exclusions -- --vault=/path/to/vault
npm run test:headless-long-run
npm run snapshot:vault
npm pack --dry-runnpm run test:runtime runs the build, all runtime mode smokes, and the HTTP health/status smoke. It uses temporary vaults and does not require a real Obsidian vault or API key. The headless smokes also assert that excluded tmp/** content is not indexed.
For a mode-by-mode comparison, see Runtime Capability Matrix. For the dedicated server path, see Headless Server Profile. For agent routing across MCP, Desktop/API, filesystem, CLI, and format skills, see MCP Routing Guide.
Format validation:
obsidian_validate_formatis available in every runtime mode.- It validates Obsidian Markdown,
.baseYAML, and JSON Canvas structure before writes. - It catches local format issues but does not render Obsidian, load plugins, or evaluate exact Bases UI semantics.
Local Bases fallback:
bases_list,bases_get_schema, andbases_queryare available in headless modes withsource: "local-fallback".- The fallback reads
.baseYAML fromOBSIDIAN_VAULTand cached Markdown frontmatter from the shared cache. - It supports direct equality filters, arrays,
contains,in, comparisons, simple sorting, pagination, and schema inspection. - It does not evaluate Obsidian formulas, plugin-specific filters, calculated properties, or exact UI view semantics.
Useful scripts:
npm run build
npm run start:proxy
npm run start:httpHealth endpoint when running the backend directly:
curl http://127.0.0.1:3010/healthzExtended health / maintenance:
GET /healthz?integrity=1adds a SQLite integrity check- MCP tool
obsidian_runtime_statusreturns process, cache, semantic, and degraded-mode status - MCP tool
obsidian_runtime_maintenancesupports:integrity_checkrun_maintenancerefresh_vault_cacherefresh_semantic_cacherefresh_tasks_cacherefresh_all
Runtime write safety:
readonlyblocks all write tools except validation-only operationsguardedallows bounded explicit writes and blocks destructive operations such as delete, overwrite, frontmatter unset, broad regex replace-all, and large batchesfullis the default and keeps unrestricted write behavior for trusted local environments
Guarded filesystem writes support optional expectedHash and expectedMtime preconditions. Use expectedHash for multi-agent or synced-vault writes so stale updates fail as explicit conflicts instead of overwriting newer content.
Agent-context controls:
obsidian_list_notessupportsresponseMode="compact",limit, andcursorobsidian_global_searchsupportsresponseMode="compact"while keeping existing page/pageSize paginationlist_all_tasksandquery_taskssupportresponseMode="compact"|"detailed",responseLimit, andcursor- reading an identified note remains full-fidelity through
obsidian_read_note
Typical checks:
curl http://127.0.0.1:3010/healthz
curl http://127.0.0.1:3010/healthz?integrity=1In ~/.codex/config.toml:
[mcp_servers.optimike-obsidian-mcp-stdio]
command = "node"
args = ["/path/to/optimike-obsidian-mcp/dist/stdio-proxy.js"]
tool_timeout_sec = 900
[mcp_servers.optimike-obsidian-mcp-stdio.env]
MCP_HTTP_HOST = "127.0.0.1"
MCP_HTTP_PORT = "3010"
MCP_PROXY_START_TIMEOUT_MS = "20000"
OBSIDIAN_VAULT = "/path/to/<vault>"
# Smart Connections
SMART_ENV_DIR = "/path/to/<vault>/.smart-env"
ENABLE_QUERY_EMBEDDING = "true"
# Recommended: auto (do not set)
# QUERY_EMBEDDER = "auto"
# Obsidian REST (if Local REST API plugin is active)
OBSIDIAN_BASE_URL = "http://127.0.0.1:27123"
OBSIDIAN_API_KEY = "<token>"
# Startup behavior (optional, recommended for faster startup)
# OBSIDIAN_STARTUP_BLOCKING=false starts MCP immediately and runs health check in background.
OBSIDIAN_STARTUP_MAX_RETRIES = "2"
OBSIDIAN_STARTUP_RETRY_DELAY_MS = "1200"
OBSIDIAN_STARTUP_BLOCKING = "false"
# Shared cache tuning (optional)
# OBSIDIAN_SHARED_CACHE_DB_PATH = "/path/to/<vault>/.obsidian/optimike-mcp/shared-cache.sqlite"
# OBSIDIAN_CONTENT_HOT_CACHE_LIMIT = "64"Notes:
- Keep this config local in
~/.codex/config.toml(do not commit personal machine paths). - Use logical placeholders in documentation (
/path/to/...) and keep real paths only in local config. dist/index.jsis still the backend entrypoint, but Codex should point todist/stdio-proxy.js.
Local REST API plugin repo: https://github.com/coddingtonbear/obsidian-local-rest-api
In Obsidian:
- install and enable Local REST API
- enable the HTTP server
- copy the API key
- set
OBSIDIAN_BASE_URLandOBSIDIAN_API_KEYin your MCP env
Example:
export OBSIDIAN_BASE_URL=http://127.0.0.1:27123
export OBSIDIAN_API_KEY=<your_api_key>- Keep
OBSIDIAN_API_KEYprivate and local. - Do not expose the Obsidian REST API to the public internet.
- Keep
OBSIDIAN_API_KEYandOPENAI_API_KEYin environment variables, not committed config files. - Current dependency check:
npm auditreports 0 known vulnerabilities, andnpm audit signaturesverifies registry signatures for the installed npm dependency tree. - If you expose the MCP over HTTP beyond localhost, enable
MCP_AUTH_MODE=jwtorMCP_AUTH_MODE=oauth, use a strong secret/provider, and keepMCP_ALLOWED_ORIGINSnarrow. The defaultstdio/127.0.0.1profile is the safer local setup.
Most local setups should use the native stdio entrypoint and 127.0.0.1.
This section is only for older or explicit WSL2 setups where Obsidian runs on
Windows and Codex runs inside WSL2:
127.0.0.1from WSL points to WSL, not Windows- use the Windows host IP (the WSL gateway) for
OBSIDIAN_BASE_URL
Example:
GW=$(ip route | awk '/default/ {print $3; exit}')
export OBSIDIAN_BASE_URL=http://$GW:27123If you use a Windows portproxy, adapt the port accordingly.
The main MCP now includes:
- note tools: read, list, update, search-replace, tags, frontmatter
- Bases tools: list, schema, query, create, upsert config, upsert rows
- Tasks tools:
list_all_tasks,query_tasks - semantic tools:
smart_semantic_search,smart_search,smart-search - server health/status tools:
obsidian_runtime_status,obsidian_runtime_maintenance
The main MCP now owns the Tasks surface directly.
What this means:
- Codex does not need a separate
optimike-obsidian-tasks-mcpentry anymore list_all_tasksandquery_tasksare exposed by this main server- parsed task data is persisted in
task_file_cacheinside the shared SQLite database
Dependencies for Tasks support:
- Obsidian vault access
- shared cache database
- Obsidian Tasks plugin configuration file at:
<vault>/.obsidian/plugins/obsidian-tasks-plugin/data.json
How it works:
- note content is indexed in
file_cache - task parsing reuses that content instead of rescanning the vault from scratch
- parsed tasks are stored in
task_file_cache list_all_tasksandquery_tasksreuse that persisted layer
This gives you one MCP surface, one runtime, and one durable local data path.
Required depending on the MCP surfaces you use:
- Local REST API: Obsidian API used by MCP.
- Bases Bridge (REST):
.basesupport via REST. - Smart Connections: vector index and
.smart-envfor semantic search.
Tool: smart_semantic_search (aliases: smart_search, smart-search).
Example:
{ "query": "publication X threads", "top_k": 10, "with_snippets": false }The server:
- reads
.smart-env/multi/*.ajson - selects the dominant dimension
- embeds the query with the same model as the vault
- persists a semantic manifest in SQLite for faster warm refreshes
- prewarms semantic search on startup by loading the snapshot and warming the query embedder
- returns
timings_ms,vector_count, andfiltered_countfor operational diagnosis
Important:
- semantic query execution still requires a reachable query embedder provider
- if the vault was built with Ollama embeddings, an unreachable Ollama instance will produce a clear error instead of a silent hang
- set
SEMANTIC_SEARCH_PREWARM=falseto disable startup prewarm
That means:
- the semantic metadata path is now durable and observable
- the final query still depends on a live embedding provider at request time
Ollama (local)
export QUERY_EMBEDDER=ollama
export QUERY_EMBEDDER_MODEL=snowflake-arctic-embed2
export OLLAMA_BASE_URL=http://127.0.0.1:11434Xenova / Transformers
The local Xenova provider is disabled for now because its ONNX/protobuf dependency chain was affected by npm audit vulnerabilities. Use Ollama locally, or OpenAI in cloud mode.
OpenAI (cloud)
export QUERY_EMBEDDER=openai
export QUERY_EMBEDDER_MODEL=text-embedding-3-small
export OPENAI_API_KEY=...
# export OPENAI_EMBEDDING_DIMENSIONS=1024For shared MCP setups, avoid hard‑coding OLLAMA_BASE_URL inside the vault.
Keep auto mode and let each user override via env vars.
optimike-obsidian-tasks-mcp can still exist as a legacy standalone repo, but Codex no longer needs it when using this main server. The main MCP is now the canonical surface.
- Product overview and install: this README
- Runtime and maintenance guide: OPERATIONS.md
- Mode-by-mode capability matrix: docs/runtime-capability-matrix.md
- Dedicated headless server profile: docs/headless-server-profile.md
- Agent routing guide: docs/mcp-routing-guide.md
- Current tool surface: docs/obsidian_mcp_tools_spec.md
- French README: README.fr.md
- Created by Optimike (Mickaël Ahouansou)
See LICENSE.
