obsidian-mcp-server

repository·main·Indexed 20 days ago

https://github.com/cyanheads/obsidian-mcp-server

An MCP server that enables AI agents to read, write, search, and surgically edit Obsidian vault notes, tags, and frontmatter. It supports STDIO and Streamable HTTP transport and provides tools for note retrieval, content patching, tag management, and command execution via the Obsidian Local REST API plugin.

Tokens
140.4K
Snippets
265
Records
482
Agent score
71%

What's inside obsidian-mcp-server

  1. Overview of Obsidian MCP Server Tools

    main

    The obsidian-mcp-server provides fourteen tools categorized by their primary function to allow an agent to interact with an Obsidian vault:

    • Readers: Fetch notes and metadata using obsidian_get_note, obsidian_list_notes, obsidian_list_tags, and obsidian_search_notes.
    • Writers: Create or surgically edit content using obsidian_write_note, obsidian_append_to_note, obsidian_patch_note, and obsidian_replace_in_note.
    • Managers: Reconcile tags and frontmatter using obsidian_manage_tags and obsidian_manage_frontmatter.
    • Command Execution: A guarded escape hatch to dispatch Obsidian command-palette commands via obsidian_execute_command and obsidian_list_commands (requires opt-in).
    • Other: obsidian_delete_note for file removal and obsidian_open_in_ui to open files in the Obsidian app.
  2. Explore the obsidian-mcp-server directory structure

    main

    The obsidian-mcp-server repository is organized into several functional areas:

    • src/: The core source code.
      • src/mcp-server/tools/definitions/: Contains the implementation of all Obsidian MCP tools (e.g., obsidian-get-note.tool.ts, obsidian-write-note.tool.ts).
      • src/mcp-server/resources/definitions/: Defines the MCP resources available (e.g., obsidian-vault-note.resource.ts).
      • src/services/obsidian/: Contains the underlying logic for interacting with Obsidian, including path-policy.ts and frontmatter-ops.ts.
    • skills/: A collection of specialized documentation and workflows (SKILL.md) used for development and orchestration.
    • tests/: Comprehensive test suites mirroring the src/ structure, covering tools, services, and resources.
    • docs/: Project documentation, including the OpenAPI specification (openapi.yaml).
    • scripts/: Utility scripts for building, linting, and maintaining the project.
    • changelog/: Version-specific history files.
  3. Understand the project structure

    main

    The repository is organized into the following key directories:

    DirectoryPurpose
    src/index.tscreateApp() entry point — registers tools/resources and inits the Obsidian service.
    src/configServer-specific environment variable parsing (OBSIDIAN_*) with Zod.
    src/services/obsidianLocal REST API client, frontmatter operations, section extractor, domain types.
    src/mcp-server/toolsTool definitions (*.tool.ts) and shared input schemas.
    src/mcp-server/resourcesResource definitions (*.resource.ts).
    src/mcp-server/promptsPrompt definitions (currently empty).
    tests/Vitest tests mirroring src/.
    docs/Upstream OpenAPI spec for the Local REST API plugin and the generated tree.md.
    changelog/Per-version release notes; CHANGELOG.md is the regenerated rollup.
  4. Understand the `Context` object in tool and resource handlers

    main

    Every tool and resource handler in @cyanheads/mcp-ts-core receives a single Context (ctx) argument. This object provides the necessary infrastructure for the current request, including identity, structured logging, tenant-scoped storage, cancellation signals, and task progress.

    Key Mental Model:

    • Use ctx.log for logging events related to the specific request.
    • Use ctx.state for storing data that should be scoped to the current tenant.
    • Use the global logger or StorageService only for lifecycle code (like setup()) or background tasks where no request context exists.
    • The framework automatically instruments handlers with OTel spans and metrics.
    import type { Context } from '@cyanheads/mcp-ts-core';
    
    // Every handler receives this:
    async function myToolHandler(ctx: Context) {
      // ...
    }
  5. Understand the two-layer configuration model in @cyanheads/mcp-ts-core

    main

    Configuration in @cyanheads/mcp-ts-core is split into two distinct layers that should never be merged. Understanding this distinction is critical for correctly managing environment variables and domain-specific settings:

    1. Core Config: Managed by the framework. It is driven by environment variables and handles infrastructure-level settings (Identity, Transport, Auth, etc.).
    2. Server Config: Managed by your specific implementation. You define this using your own Zod schema to handle domain-specific environment variables required by your MCP server.

    To interact with the configuration system, import the necessary utilities from @cyanheads/mcp-ts-core/config.

    import { AppConfig, config, parseConfig, resetConfig, ConfigSchema } from '@cyanheads/mcp-ts-core/config';
  6. Determine the appropriate version bump type

    main

    When preparing a release, determine the new version based on the nature of the changes in the diff:

    BumpWhen to use
    patchBug fixes, dependency updates, metadata changes, or documentation updates.
    minorNew tools, new features, new environment variables, or behavioral changes.
    majorBreaking changes to tool schemas, removal of tools, or incompatible configuration changes.

    Default to patch unless the changes clearly warrant a minor or major bump.

  7. Audit LLM-facing injection surfaces (Axis 1)

    main

    Anything sent to the client that reaches the LLM's context is a potential injection vector. This includes tool outputs, resource content, prompt text, and metadata used for tool selection.

    What to check:

    • Tool Outputs: Check *.tool.ts for output schemas and the format() method. Ensure untrusted content is wrapped in delimiters (e.g., blockquotes, <data> tags).
    • Resource Content: Ensure resources/read content is framed similarly to tool outputs.
    • Prompt Templates: Check *.prompt.ts for interpolation of untrusted data without escaping.
    • Metadata/Descriptions: Ensure description, title, annotations, and inputSchema descriptions are static. Templated descriptions (e.g., description: Look up ${tenant.customLabel}``) enable "tool poisoning."
  8. Ensure format parity for multi-client compatibility

    main

    Different MCP clients read different surfaces:

    • Claude Code reads structuredContent from output.
    • Claude Desktop reads content[] from format().

    To ensure every client sees the same data, you must maintain format parity. A thin format() that only returns a count or title will leave content[]-only clients blind to the actual data.

    Best Practice: Use format-parity to ensure every field in output is also rendered in format() using structured markdown (headers, bold labels, lists) for readability.

    Contextual Information: To ensure agent-facing context (like empty-result notices or pagination totals) reaches both surfaces, use an enrichment block via ctx.enrich(...). This automatically merges into structuredContent and is mirrored into the content[] trailer in format().

  9. Access Obsidian vault data via Resources

    main

    The server exposes several URI-based resources that clients can attach to conversations or inspect directly:

    • obsidian://vault/{+path}: Represents a specific note. Provides content, frontmatter, tags, and file metadata.
    • obsidian://tags: Provides all tags found across the entire vault, including usage counts. Note that tag listing is vault-wide and is not restricted by OBSIDIAN_READ_PATHS.
    • obsidian://status: Provides server reachability, authentication status, plugin/Obsidian version information, and the plugin manifest.
  10. Classify MCP primitives (Tools, Resources, Prompts, Apps)

    main

    When designing the interface, classify your capabilities into the following MCP primitives:

    PrimitiveUse Case
    ToolThe default. Any operation or data access an agent needs. Design the surface to be self-sufficient so tool-only clients can accomplish the server's purpose.
    App ToolRare. Only when a human interacts with results in real-time via an MCP-capable client. Requires syncing two surfaces (UI and text twin).
    ResourceUse when data is addressable by a stable URI, is read-only, and serves as useful injectable context (e.g., schemas, config).
    PromptReusable message templates that structure how the LLM approaches a task (e.g., analysis frameworks, checklists).
    NeitherInternal details or administrative tasks not useful to an LLM (e.g., token refresh, migrations).

    Critical Design Rules

    • Avoid Data Locking: Do not hide essential data behind Resources if a tool-only agent needs it. If it's needed for a workflow, provide a Tool path.
    • Avoid CRUD Explosion: Instead of mapping every REST endpoint to a tool, consolidate related operations on the same noun into one tool using an operation or mode parameter.
    • Exclude Irreversible Operations: If an operation's failure is catastrophic and unrecoverable (e.g., deleting a production database table or a primary admin role), it should not be a tool. These belong in a vendor UI with confirmation dialogs, not in an agent's toolset. (Note: This is different from destructiveHint, which is for recoverable destructive actions like deleting a task).
  11. Understand OpenTelemetry runtime support and behavior

    main

    The framework's instrumentation behavior varies depending on the runtime environment:

    Node.js / Bun

    • Node.js: Full NodeSDK support. Includes auto-instrumentation for HTTP servers (skipping /healthz) and Pino logs (injecting trace_id/span_id).
    • Bun: NodeSDK works, but Node's HTTP auto-instrumentation is a no-op. To get HTTP coverage on Bun, you must install @hono/otel and use the httpInstrumentationMiddleware on the MCP endpoint. Manual spans, custom metrics, and OTLP export work normally.

    Cloudflare Workers / V8 isolates

    • NodeSDK is unavailable and initialization will silently no-op.
    • While helper calls like createCounter, createHistogram, and withSpan will function via the global OTel API, they will produce no output unless you manually wire a Worker-compatible exporter and use ctx.waitUntil() to flush data.
  12. How obsidian_search_notes handles match context length

    main
    When using obsidian_search_notes, the contextLength parameter controls both the amount of data returned in structuredContent and the length of the rendered text in the content[] array. Previously, the rendered content[] was hard-capped at 240 characters regardless of the requested length. In version 3.2.12 and later, the rendered text will respect the contextLength provided by the caller, allowing for full match context rendering without silent clipping.