Files
obsidian-mcp-server/CLAUDE.md
Bill cc4e71f920 refactor: remove 'obsidian' from plugin ID and update branding
- Change plugin ID from 'obsidian-mcp-server' to 'mcp-server'
- Remove 'Obsidian' from plugin description per guidelines
- Update documentation to use new plugin folder name
- Update installation paths to .obsidian/plugins/mcp-server/
- Update package name to match new plugin ID
- Simplify README title and description
2025-10-26 16:47:36 -04:00

12 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

This is an Obsidian plugin that exposes vault operations via the Model Context Protocol (MCP) over HTTP. It runs an Express server within Obsidian to enable AI assistants and other MCP clients to interact with the vault programmatically.

Development Commands

Building and Development

npm install              # Install dependencies
npm run dev             # Watch mode for development (auto-rebuild on changes)
npm run build           # Production build (runs type check + esbuild)

Testing

npm test                # Run all tests
npm run test:watch      # Run tests in watch mode
npm run test:coverage   # Run tests with coverage report

Type Checking

The build command includes TypeScript type checking via tsc -noEmit -skipLibCheck.

Installing in Obsidian

After building, the plugin outputs main.js to the root directory. To test in Obsidian:

  1. Copy main.js, manifest.json, and styles.css to your vault's .obsidian/plugins/mcp-server/ directory
  2. Reload Obsidian (Ctrl/Cmd + R in dev mode)
  3. Enable the plugin in Settings → Community Plugins

Architecture

High-Level Structure

The codebase follows a layered architecture:

src/
├── main.ts                    # Plugin entry point (MCPServerPlugin)
├── server/                    # HTTP server layer
│   ├── mcp-server.ts         # Express server + MCP protocol handler
│   ├── routes.ts             # Route setup
│   └── middleware.ts         # Auth, CORS, origin validation
├── tools/                     # MCP tool implementations
│   ├── index.ts              # ToolRegistry - routes tool calls
│   ├── note-tools.ts         # File operations (CRUD)
│   └── vault-tools.ts        # Vault operations (search, list, metadata)
├── utils/                     # Shared utilities
│   ├── path-utils.ts         # Path validation and normalization
│   ├── frontmatter-utils.ts  # YAML frontmatter parsing
│   ├── search-utils.ts       # Search and regex utilities
│   ├── link-utils.ts         # Wikilink resolution
│   ├── waypoint-utils.ts     # Waypoint plugin integration
│   ├── glob-utils.ts         # Glob pattern matching
│   ├── version-utils.ts      # ETag/versionId for concurrency control
│   └── error-messages.ts     # Consistent error messaging
├── ui/                        # User interface components
│   ├── notifications.ts      # NotificationManager for tool call notifications
│   └── notification-history.ts # History modal
├── types/                     # TypeScript type definitions
│   ├── mcp-types.ts          # MCP protocol types
│   └── settings-types.ts     # Plugin settings
└── settings.ts                # Settings UI tab

Key Components

1. MCPServerPlugin (src/main.ts)

  • Main plugin class that extends Obsidian's Plugin
  • Lifecycle management: starts/stops HTTP server
  • Registers commands and ribbon icons
  • Manages plugin settings and notification system

2. MCPServer (src/server/mcp-server.ts)

  • Wraps Express HTTP server
  • Handles JSON-RPC 2.0 requests per MCP protocol
  • Routes to ToolRegistry for tool execution
  • Supports methods: initialize, tools/list, tools/call, ping
  • Binds to 127.0.0.1 only for security

3. ToolRegistry (src/tools/index.ts)

  • Central registry of all available MCP tools
  • Dispatches tool calls to NoteTools or VaultTools
  • Manages NotificationManager integration
  • Returns tool definitions with JSON schemas

4. NoteTools (src/tools/note-tools.ts)

  • File-level CRUD operations
  • Tools: read_note, create_note, update_note, delete_note, update_frontmatter, update_sections, rename_file, read_excalidraw
  • Implements concurrency control via versionId/ETag system
  • Handles conflict strategies for creates

5. VaultTools (src/tools/vault-tools.ts)

  • Vault-wide operations
  • Tools: search, list, stat, exists, get_vault_info, search_waypoints, get_folder_waypoint, is_folder_note, validate_wikilinks, resolve_wikilink, backlinks
  • Advanced search with regex and glob filtering
  • Wikilink resolution using Obsidian's MetadataCache

Important Patterns

Path Handling

  • All paths are vault-relative (no leading slash)
  • PathUtils validates paths against leading/trailing slashes, absolute paths, and .. traversal
  • Path normalization handles cross-platform differences

Concurrency Control

  • VersionUtils generates ETags based on file mtime + size
  • ifMatch parameter on write operations enables optimistic locking
  • Prevents lost updates when multiple clients modify the same file

Error Handling

  • ErrorMessages utility provides consistent error formatting
  • All tool results return CallToolResult with structured content
  • isError: true flag indicates failures

Frontmatter

  • FrontmatterUtils parses YAML frontmatter using regex
  • update_frontmatter enables surgical metadata updates without full file rewrites
  • Reduces race conditions vs full content updates
  • LinkUtils handles wikilink resolution via Obsidian's MetadataCache
  • Supports heading links ([[note#heading]]) and aliases ([[note|alias]])
  • validate_wikilinks checks all links in a note
  • backlinks uses MetadataCache for reverse link lookup
  • SearchUtils implements multi-file search with regex support
  • GlobUtils provides file filtering via glob patterns
  • Returns structured results with line/column positions and snippets

Testing

Tests are located in tests/ and use Jest with ts-jest. The test setup includes:

  • Mock Obsidian API in tests/__mocks__/obsidian.ts
  • Test files follow *.test.ts naming convention
  • Coverage excludes type definition files

MCP Protocol Implementation

The server implements MCP version 2024-11-05:

  • JSON-RPC 2.0 over HTTP POST to /mcp endpoint
  • Capabilities: { tools: {} }
  • All tool schemas defined in ToolRegistry.getToolDefinitions()
  • Tool call results use MCP's content array format with text/image types

Security Model

  • Server binds to 127.0.0.1 only (no external access)
  • Host header validation prevents DNS rebinding attacks
  • CORS fixed to localhost-only origins (http(s)://localhost:*, http(s)://127.0.0.1:*)
  • Mandatory authentication via Bearer token (auto-generated on first install)
  • API keys encrypted using Electron's safeStorage API (system keychain: macOS Keychain, Windows Credential Manager, Linux Secret Service)
  • Encryption falls back to plaintext on systems without secure storage (e.g., Linux without keyring)

Settings

MCPPluginSettings (src/types/settings-types.ts):

  • port: HTTP server port (default: 3000)
  • autoStart: Start server on plugin load
  • apiKey: Required authentication token (encrypted at rest using Electron's safeStorage)
  • enableAuth: Always true (kept for backward compatibility during migration)
  • notificationsEnabled: Show tool call notifications in Obsidian UI
  • showParameters: Include parameters in notifications
  • notificationDuration: Auto-dismiss time for notifications
  • logToConsole: Log tool calls to console

Removed settings (as of implementation plan 2025-10-25):

  • enableCORS: CORS is now always enabled with fixed localhost-only policy
  • allowedOrigins: Origin allowlist removed, only localhost origins allowed

Waypoint Plugin Integration

The plugin has special support for the Waypoint community plugin:

  • Waypoints are comment blocks: %% Begin Waypoint %% ... %% End Waypoint %%
  • Used to auto-generate folder indexes
  • search_waypoints: Find all waypoints in vault
  • get_folder_waypoint: Extract waypoint from specific folder note
  • is_folder_note: Detect folder notes by basename match or waypoint presence

Development Guidelines

Code Organization Best Practices

  • Keep main.ts minimal - Focus only on plugin lifecycle (onload, onunload, command registration)
  • Delegate feature logic to separate modules - All functionality lives in dedicated modules under src/
  • Split large files - If any file exceeds ~200-300 lines, break it into smaller, focused modules
  • Use clear module boundaries - Each file should have a single, well-defined responsibility
  • Use TypeScript strict mode - The project uses "strict": true
  • Prefer async/await over promise chains
  • Handle errors gracefully - Provide helpful error messages to users

Performance Considerations

  • Keep startup light - Defer heavy work until needed; avoid long-running tasks during onload
  • Batch disk access - Avoid excessive vault scans
  • Debounce/throttle expensive operations - Especially for file system event handlers
  • Be mindful of memory on mobile platforms (though this plugin is desktop-only)

Platform Compatibility

This plugin is desktop-only (isDesktopOnly: true) because it uses Node.js HTTP server (Express). If extending to mobile:

  • Avoid Node/Electron APIs
  • Don't assume desktop-only behavior
  • Test on iOS and Android

Security and Privacy

  • Default to local/offline operation - This plugin already binds to localhost only
  • No hidden telemetry - Don't collect analytics without explicit opt-in
  • Never execute remote code - Don't fetch and eval scripts
  • Minimize scope - Read/write only what's necessary inside the vault
  • Do not access files outside the vault
  • Respect user privacy - Don't collect vault contents without consent
  • Clean up resources - Use this.register* helpers so the plugin unloads safely

UI/UX Guidelines

  • Use sentence case for headings, buttons, and titles
  • Use bold to indicate literal UI labels in documentation
  • Use arrow notation for navigation: "Settings → Community plugins"
  • Prefer "select" for user interactions
  • Keep in-app strings short, consistent, and free of jargon

Versioning and Releases

  • Use Semantic Versioning (SemVer) for version in manifest.json
  • Update versions.json to map plugin version → minimum Obsidian app version
  • Never change the plugin id after release
  • Never rename command IDs after release - they are stable API
  • Create GitHub releases with tags that exactly match manifest.json version (no v prefix)
  • Attach required assets to releases: manifest.json, main.js, styles.css

GitHub Release Workflow

A GitHub Actions workflow automatically handles releases:

Location: .github/workflows/release.yml

Trigger: Push of semantic version tags (e.g., 1.2.3)

Process:

  1. Validates version consistency across package.json, manifest.json, and git tag
  2. Runs full test suite (blocks release if tests fail)
  3. Builds plugin with production config
  4. Creates draft GitHub release with main.js, manifest.json, and styles.css

Developer workflow:

npm version patch  # or minor/major - updates manifest.json via version-bump.mjs
git commit -m "chore: bump version to X.Y.Z"
git tag X.Y.Z
git push && git push --tags  # Triggers workflow

After workflow completes:

  1. Go to GitHub Releases
  2. Review draft release and attached files
  3. Write release notes
  4. Publish release

Build Artifacts

  • Never commit build artifacts to version control (main.js, node_modules/, etc.)
  • All TypeScript must bundle into a single main.js file via esbuild
  • Release artifacts must be at the top level of the plugin folder

Command Stability

  • Add commands with stable IDs - don't rename once released
  • Commands are registered in src/main.ts with IDs like start-mcp-server, stop-mcp-server, etc.

References