Generated banner for Semantic Notes Vault MCP
Generated logo for Semantic Notes Vault MCP

Semantic Notes Vault MCP

Memory server

Created by Aaron Bockelie

Published in aaronsb/obsidian-mcp-plugin. Publisher page

468 GitHub stars

What it is

Read, write, search, and traverse your vault from Claude Desktop and any AI assistant — via a built-in MCP server, no external server to run.
— the description by Aaron Bockelie

AutoGineer overview, from the brain’s own files

Semantic Notes Vault MCP transforms your Obsidian vault into a persistent, intelligent memory layer for AI agents. By running an MCP server directly within the Obsidian application, it eliminates the need for external processes or complex API bridges. The plugin exposes eight tool families that allow AI assistants to perform comprehensive vault operations, including file management, graph traversal, and semantic search. It is designed to respect user-defined security permissions, ensuring that AI interactions remain within controlled boundaries while enabling deep, context-aware knowledge synthesis.

How it works

Memory model: Knowledge graph

AutoGineer overview, from the brain’s own files

The plugin functions as a built-in Model Context Protocol (MCP) server inside Obsidian. It uses a direct Obsidian API integration to read, write, and traverse your notes. It organizes your data into a knowledge graph, allowing AI to navigate links, tags, and backlinks. The system includes a security layer for path validation and permissions, and it supports Dataview queries and database views for advanced data retrieval.

Folders
FolderRoleFiles
.githubagent-config11
docsdocs34
scriptsscripts9
srcsource-code79
teststests74
wikiwiki1
.claude-githubfolder2
mcpbfolder2

The agent reads

The agent writes

Requirements

Apps: Obsidian

Runtimes: Node.js

API keys (by name): YOUR_API_KEY

Works with: Claude Code, OpenCode

Set it up

  1. This brain runs code automatically: 1 plugin code item (obsidian), 1 script. Review Makefile, manifest.json before opening the folder in Claude Code. No step below turns a hook on for you.

    • Makefile: npm install
    • manifest.json: runs inside Obsidian once the plugin is enabled
  2. Download main.js, manifest.json and styles.css of release 0.13.0: https://github.com/aaronsb/obsidian-mcp-plugin/releases/tag/0.13.0
  3. Check main.js against the SHA-256 AutoGineer recorded:

    curl -fsSLO https://github.com/aaronsb/obsidian-mcp-plugin/releases/download/0.13.0/main.js && shasum -a 256 main.js  # expect 47da0b5aa8880e21d50bb7c883c976d8563d19694620ae8f3a3f84ead28d65b8
  4. Copy the three files into <vault>/.obsidian/plugins/semantic-vault-mcp/ and enable the plugin in Settings → Community plugins.

  5. You will install release 0.13.0, which AutoGineer scanned (main.js SHA-256 47da0b5aa8880e21d50bb7c883c976d8563d19694620ae8f3a3f84ead28d65b8).

  6. Not scanned by AutoGineerObsidian installs the plugin’s current release; AutoGineer scanned release 0.13.0 (2026-11-12). https://obsidian.md/plugins?id=semantic-vault-mcp
  7. Created by Aaron Bockelie. Published in aaronsb/obsidian-mcp-plugin. Licence: MIT License.

Usage

AutoGineer overview, from the brain’s own files

  • Connect Claude Desktop to your Obsidian vault for direct note interaction.
  • Enable AI agents to perform complex research by traversing your note graph.
  • Automate note creation and organization based on existing knowledge structures.
  • Execute Dataview queries via AI prompts to summarize or analyze your vault data.
“What are my recent thoughts on project X?”
The AI searches your vault for notes related to project X and synthesizes your recent entries.
“Find connections between my psychology and philosophy notes”
The AI traverses your knowledge graph to identify shared concepts and bridge notes between the two domains.

Documentation by Aaron Bockelie

Semantic Notes Vault MCP

Image: GitHub stars Image: GitHub forks Image: Downloads Image: Latest Release Image: License

📦 Available in the Obsidian Community Plugin directory →

Read, write, search, and traverse your Obsidian vault from any AI assistant — through an MCP server that runs inside Obsidian.

No external Node process to launch, no separate REST-API plugin to bridge through: the server is the plugin. Setup is a drag-and-drop — drop the .mcpb bundle onto Claude Desktop, paste your key, done.

It exposes 8 powerful tools — each a whole family of operations, not a single call (the vault tool alone handles 13: list, read, create, search, move, split, combine, and more) — with first-class Dataview and Bases support plus graph traversal across links, tags, and backlinks. And every operation respects the permissions you set — a read-only mode, per-operation controls, and path allow/block lists — so the AI only ever does what you've allowed, not unrestricted run of your vault.

Works with any MCP-compatible client — Claude Desktop, Claude Code, Cline, Continue.dev, and anything that speaks local MCP.

New to MCP? The Model Context Protocol is the open standard that lets AI assistants interact with external tools and data. You don't need to understand it to use this — the Quick Start is three steps.

Quick Start

Prerequisites: an MCP-compatible AI client like Claude Desktop, Claude Code, or Continue.dev.

📦 ──drag──▶ 🤖💬

Download the .mcpb bundle from the plugin's config page → drag it onto Claude Desktop → paste your key. Done.

For most people that's the entire setup. The numbered steps below spell it out, then cover other MCP clients.

1. Install the Plugin

Via Obsidian Community Plugins

  • Open Settings → Community plugins → Browse

  • Search for "Semantic Notes Vault MCP"

  • Install and enable — or install straight from the plugin listing

Via BRAT (for beta testing)

  • Install BRAT

  • Add beta plugin: aaronsb/obsidian-mcp-plugin

2. Configure Your AI Client

Three onboarding paths, ordered by audience. All three are also shown in the plugin's Settings tab with copy-ready values.

📦 → 🤖 Claude Desktop — one-click .mcpb install (recommended)

Download obsidian-mcp-<version>.mcpb — either from the plugin's Settings tab (button right on the config page) or the latest release — then either drag it onto the Claude Desktop window or double-click it. Claude Desktop opens an install dialog with two fields — paste the URL and API key shown in the plugin's Settings tab, hit Save, and you're done.

Cross-platform note: .mcpb files install via Claude Desktop's bundled handler. If double-click doesn't trigger Claude on your system, drag the file onto Claude Desktop's window instead, or right-click → "Open with…" and pick Claude Desktop (then "always open with" if your OS asks). Behavior varies by platform: macOS usually auto-associates, Windows may need a one-time association, Linux varies by desktop environment.

Claude Code — one command (copy the ready-made version with your API key from the plugin's Settings tab):

claude mcp add --transport http obsidian http://localhost:3001/mcp --header "Authorization: Bearer YOUR_API_KEY"

For HTTPS, use https://localhost:3443/mcp instead — see Trusting the self-signed certificate below. Claude Code runs on Bun, which does not read the macOS system keychain, so you will need to set NODE_EXTRA_CA_CERTS.

Other MCP clients (Cline, Continue, custom integrations, multi-vault setups)

Add an entry to the client's MCP config file — one entry per vault if you run multiple Obsidian instances on different ports:

{
  "mcpServers": {
    "obsidian-vault": {
      "transport": {
        "type": "http",
        "url": "http://localhost:3001/mcp",
        "headers": {
          "Authorization": "Bearer YOUR_API_KEY"
        }
      }
    }
  }
}

Advanced: custom .mcpb per vault

For multi-vault setups that want one-click install per vault, clone this repo and run the maker:

node scripts/make-mcpb.mjs
# Prompts for display name, URL, and API key
# Outputs obsidian-mcp-<slug>.mcpb with everything pre-filled

Drop the resulting bundle into Claude Desktop and click Install — no fields to type.

Trusting the self-signed certificate

The plugin's HTTPS server uses a self-signed certificate auto-generated on first start and stored under .obsidian/plugins/semantic-vault-mcp/certificates/default.crt inside your vault. MCP clients reject self-signed certificates by default, so you need to explicitly trust it before connecting over HTTPS. Pick the method that matches your client runtime.

macOS Keychain (for clients that use the system trust store — Claude Desktop, browser-based tools, Node with --use-system-ca):

sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain \
  /path/to/vault/.obsidian/plugins/semantic-vault-mcp/certificates/default.crt

NODE_EXTRA_CA_CERTS (required for Claude Code and other Bun-based runtimes):

Bun does not consult the macOS system keychain for TLS trust, so trusting the certificate via Keychain Access alone has no effect — this is almost always the real reason an HTTPS connection from Claude Code fails. Bun only honors certificates listed in NODE_EXTRA_CA_CERTS:

# Point directly at the plugin cert, or append it to an existing CA bundle:
export NODE_EXTRA_CA_CERTS=/path/to/vault/.obsidian/plugins/semantic-vault-mcp/certificates/default.crt

# Propagate to GUI apps launched from the macOS dock (including Claude Code):
launchctl setenv NODE_EXTRA_CA_CERTS /path/to/vault/.obsidian/plugins/semantic-vault-mcp/certificates/default.crt

Re-run these whenever the plugin regenerates its certificate (e.g. after the 1-year validity expires).

Avoid NODE_TLS_REJECT_UNAUTHORIZED=0. It disables TLS verification process-wide — for every HTTPS connection the client makes, not just this plugin — and masks legitimate certificate problems (expired, revoked, tampered). Trust the certificate explicitly instead.

3. Start Using

Once connected, simply chat with your AI assistant about your notes! For example:

  • "What are my recent thoughts on project X?"

  • "Find connections between my psychology and philosophy notes"

  • "Summarize my meeting notes from this week"

  • "Create a new note linking my ideas about Y"

Your AI assistant now has these capabilities:

  • Navigate your vault's link structure

  • Search and rank across all notes

  • Read, edit, and create notes

  • Analyze your knowledge graph

  • Work with Dataview queries (if installed)

  • Manage Obsidian Bases (database views)

Why It's Different

Traditional file access gives AI a narrow view — one document at a time. This plugin gives it the whole connected picture:

  • Graph Navigation: AI follows links between notes, understanding relationships and context

  • Concept Discovery: Search and graph traversal surface related ideas across your vault

  • Contextual Awareness: AI understands where information lives in your knowledge structure

  • Intelligent Synthesis: Combine fragments from multiple notes to answer complex questions

Core Tools

The plugin provides 8 powerful tools that give AI comprehensive vault access — each one a family of related operations, all subject to the permissions you set:

ToolPurposeKey Actions
📁 vaultFile operationslist, read, create, search, move, split, combine
✏️ editContent modificationwindow editing, append, patch sections
👁️ viewContent displayview files, windows, active note
🕸️ graphLink navigationtraverse, find paths, analyze connections
💡 workflowContextual hintssuggest next actions based on state
📊 dataviewQuery notesExecute DQL queries (if installed)
🗃️ basesDatabase viewsQuery and export Bases (if available)
ℹ️ systemVault infoServer status, commands, web fetch (off by default), running allowlisted commands (off by default)

Documentation

Detailed documentation for each tool and feature:

In Practice

This plugin doesn't just give AI access to files — it lets AI work across your vault as a connected whole:

Example: Research Assistant
User: "Summarize my research on machine learning optimization"

AI uses these tools to:
1. Search for notes with ML optimization concepts
2. Traverse graph to find related papers and techniques  
3. Follow backlinks to discover applications
4. Synthesize findings from multiple connected notes
Example: Knowledge Explorer
User: "What connections exist between my notes on philosophy and cognitive science?"

AI uses graph tools to:
1. Find notes tagged with both topics
2. Analyze shared concepts via graph traversal
3. Identify bridge notes that connect domains
4. Map the conceptual overlap

Features

  • Advanced query operators: tag:, path:, content:

  • Regular expressions and phrase matching

  • Relevance ranking and snippet extraction

Graph Intelligence
  • Multi-hop traversal with depth control

  • Backlink and forward-link analysis

  • Path finding between concepts

  • Tag-based navigation

Content Operations
  • Fuzzy text matching for edits

  • Structure-aware modifications (headings, blocks)

  • Batch operations (split, combine, move)

  • Template support

Integration
  • Dataview query execution

  • Bases database operations

  • Web content fetching

  • Read-only mode for safety

Plugin Settings

Access settings via: Settings → Community plugins → Semantic Notes Vault MCP

Key configuration options:

  • Server Ports: HTTP (3001) and HTTPS (3443)

  • Authentication: API key protection

  • Security: Path validation and permissions

  • Performance: Connection pooling and caching

Support

License

MIT

CLAUDE.md

Claude Development Guidelines for Obsidian MCP Plugin

Project Context

This is a hybrid Obsidian plugin that combines:

  • Local REST API functionality (from coddingtonbear's plugin)

  • Semantic MCP operations (from aaronsb/obsidian-semantic-mcp)

  • Direct Obsidian API integration for enhanced performance

The critical architectural pattern is preserving the ObsidianAPI abstraction layer while replacing HTTP calls with direct Obsidian plugin API calls. This allows reuse of all existing MCP server logic while gaining performance benefits.

Code Quality Guidelines

SOLID Principles Application
  • Single Responsibility:

    • ObsidianAPI class handles only vault operations abstraction

    • MCPServer class handles only MCP protocol operations

    • HTTPServer class handles only REST endpoint management

    • Plugin main class handles only Obsidian plugin lifecycle

  • Open/Closed:

    • ObsidianAPI interface remains stable for extensions

    • MCP operations extensible through semantic router pattern

    • HTTP endpoints extensible without modifying core logic

  • Liskov Substitution:

    • New ObsidianAPI implementation must be drop-in replacement

    • All method signatures and return types must match exactly

    • Error handling behavior must be preserved

  • Interface Segregation:

    • Separate interfaces for vault operations, search operations, and workspace operations

    • MCP protocol separated from HTTP REST protocol

    • Plugin settings separated from server configuration

  • Dependency Inversion:

    • Depend on Obsidian Plugin API abstractions, not concrete implementations

    • MCP server depends on ObsidianAPI interface, not specific implementation

    • HTTP server depends on operation interfaces, not direct vault access

Architecture Patterns
Critical Abstraction Layer
// This interface MUST remain stable
interface IObsidianAPI {
  getFile(path: string): Promise<ObsidianFileResponse>;
  listFiles(directory?: string): Promise<string[]>;
  searchSimple(query: string): Promise<any[]>;
  // ... all existing methods preserved
}

// Implementation changes from HTTP to direct API
class ObsidianAPI implements IObsidianAPI {
  constructor(private app: App) {} // Direct plugin access
  
  async getFile(path: string): Promise<ObsidianFileResponse> {
    // Direct vault access instead of HTTP call
    const file = this.app.vault.getAbstractFileByPath(path);
    // ... implementation
  }
}
Performance-Critical Patterns
  • Caching Layer: Implement intelligent caching for frequently accessed files

  • Lazy Loading: Load heavy operations only when needed

  • Batch Operations: Combine multiple vault operations where possible

  • Memory Management: Proper cleanup of file handles and event listeners

Error Handling Patterns
// Preserve exact error types and messages from HTTP implementation
class VaultError extends Error {
  constructor(message: string, public code: string, public status: number) {
    super(message);
  }
}

// Maintain compatibility with existing error handling
async getFile(path: string): Promise<ObsidianFileResponse> {
  try {
    const file = this.app.vault.getAbstractFileByPath(path);
    if (!file) {
      throw new VaultError(`File not found: ${path}`, 'ENOENT', 404);
    }
    // ... rest of implementation
  } catch (error) {
    // Transform plugin errors to match HTTP API errors
    throw this.transformError(error);
  }
}

Development Workflow

BRAT Development Release Process

When developing with Obsidian BRAT (Beta Reviewer's Auto-update Tool) for plugin side-loading:

Development vs Release Workflow

Development (no releases created):

  • Push commits to main freely - no automatic releases triggered

  • Test locally with npm run build

  • Iterate as needed without polluting release history

Creating a Release (manual trigger):

Via GitHub UI:

  1. Go to Actions → Create Release

  2. Click Run workflow

  3. Optionally add release notes

  4. Click Run

Via CLI:

# Simple release
gh workflow run release.yml

# With release notes
gh workflow run release.yml -f release_notes="Fixed VS Code compatibility, improved search"
Version Updates
  • ONLY update package.json version - sync-version.mjs automatically syncs to manifest.json and version.ts

  • DO NOT manually update manifest.json - the automation handles this

  • Bump version in package.json before triggering a release

Tag Management
  • Release workflow creates tags automatically (no 'v' prefix)

  • If a tag already exists for that version, the release is skipped

  • To re-release same version, delete existing tag first:

    git tag -d X.Y.Z && git push origin :refs/tags/X.Y.Z
BRAT User Experience
  • Users install via: aaronsb/obsidian-mcp-plugin in BRAT

  • BRAT checks GitHub releases for updates automatically

  • Version detection relies on manifest.json version field

  • Release assets (main.js, manifest.json, styles.css) are auto-generated by workflow

Version Naming Convention
  • Major releases: X.Y.Z (e.g., 0.4.4) - NO 'v' prefix

  • Patch releases: X.Y.Za, X.Y.Zb (e.g., 0.4.4a, 0.4.4b) - NO 'v' prefix

  • Pre-releases: All releases ship as prereleases by default (make release-patch etc.)

  • Promoting to stable: When a release is bounded and proven, run make promote (or make promote TAG=X.Y.Z) to flip it from prerelease → stable and mark it as GitHub's "Latest" release. This is also what makes the /releases/latest/download/<asset> URL resolve — the MCPB download link in plugin Settings relies on it.

  • IMPORTANT: Obsidian requires release tags WITHOUT 'v' prefix

  • Exploratory releases: Use letter suffix (a, b, c) for testing new features

File Organization
src/
├── main.ts                 # Plugin entry point
├── obsidian-api.ts        # Direct API implementation (CRITICAL)
├── mcp-server.ts          # MCP protocol handling
├── http-server.ts         # REST API endpoints
├── semantic/              # Reused from obsidian-semantic-mcp
│   ├── router.ts         # Semantic operations router
│   └── operations/       # Individual operation implementations
├── types/                # TypeScript type definitions
└── utils/                # Shared utilities
Testing Strategy
  • Unit Tests: Each ObsidianAPI method tested against expected interface

  • Integration Tests: Full MCP and REST workflows tested

  • Performance Tests: Benchmarking against HTTP-based implementation

  • Compatibility Tests: Existing client code works without changes

Build Pipeline
{
  "scripts": {
    "dev": "tsc --watch",
    "build": "tsc && node build-plugin.js",
    "test": "jest",
    "test:performance": "node performance-tests.js",
    "package": "npm run build && npm run test && node package-release.js"
  }
}
Pre-Push Quality Checks

ALWAYS run these commands before pushing to GitHub:

# 1. Build the project to catch TypeScript errors
npm run build

# 2. Run linting to ensure code quality
npm run lint

# 3. Run tests to catch regressions
npm test

# 4. If all pass, commit and push
git add -A && git commit -m "..."
git push origin main

Quick one-liner for all checks:

npm run build && npm run lint && npm test && echo "✅ All checks passed!"

Note: Even for "simple" changes like updating descriptions or documentation, running these checks ensures no accidental syntax errors or regressions are introduced.

Supply Chain — 7-Day Hold on Routine Upgrades (security fixes exempt)

npm supply-chain attacks have been ticking up — malicious package versions get published, ingested by automation, then discovered/yanked days later. To buy time for community discovery:

Land routine upgrades only to versions published more than 7 days ago — but security fixes are exempt and land immediately.

The exemption exists because the hold otherwise fights our own freshness goal: it delays exactly the vuln patches that Obsidian's plugin review (and npm audit) flag, leaving us knowingly exposed to a named, real CVE to guard against a hypothetical malicious republish. For a security fix the known exposure wins.

Practical application:

  • Security fix → land it now. If a dependabot/manual upgrade resolves a flagged vulnerability (dependabot security PR, npm audit advisory, or an Obsidian-review dependency warning), merge as soon as it's green regardless of version age.

  • Routine bump → 7-day hold. For non-security version bumps, check the PR's open date. If it's ≥7 days old, proceed; if younger, hold (don't close — it ages into eligibility on its own).

  • The same split applies to manual npm install / npm update: security fixes immediately, routine pins to versions older than 7 days.

Re-run npm audit after each merge batch to see what residual exposure remains.

The hold covers what ships. Analysis tooling is exempt.

The hold protects users from a malicious version reaching them through main.js. Linters, formatters, type-checkers and test tooling never get bundled, so holding them back protects nobody and costs something real: our lint drifts from the portal's, and findings then arrive after a release is cut instead of before. Hold anything that ships. Land analysis tooling as soon as it's green.

Two honest caveats. This is not zero-risk — an ESLint plugin executes on the maintainer's machine during npm run lint, so the exposure moves from users to the developer rather than disappearing.

Second, the mitigating control is weaker than it looks. Install scripts are blocked pending approval (npm install-scripts ls shows what is waiting), but that is npm 12+ default behaviour, not something this repo enforces — there is no .npmrc or package.json setting pinning it. On an older npm a new dev dependency's postinstall runs freely. So the exemption rests on the toolchain the installer happens to be using. Approve install scripts one package at a time and never with --all; if this needs to be guaranteed rather than assumed, that means enforcing it in the repo, which is not done today.

Anything with a runtime import path — including a dev dependency that ends up bundled — is "what ships" and keeps the hold.

Plugin-Specific Guidelines

Obsidian Plugin Lifecycle
export default class ObsidianMCPPlugin extends Plugin {
  private obsidianAPI: ObsidianAPI;
  private mcpServer: MCPServer;
  private httpServer: HTTPServer;

  async onload() {
    // 1. Initialize API abstraction layer FIRST
    this.obsidianAPI = new ObsidianAPI(this.app);
    
    // 2. Initialize servers with API dependency
    this.mcpServer = new MCPServer(this.obsidianAPI);
    this.httpServer = new HTTPServer(this.obsidianAPI);
    
    // 3. Start servers
    await this.startServers();
    
    // 4. Register UI components
    this.addSettingTab(new MCPSettingTab(this.app, this));
  }

  async onunload() {
    // Clean shutdown in reverse order
    await this.stopServers();
    this.obsidianAPI.cleanup();
  }
}
Performance Monitoring
// Add performance tracking for optimization
class PerformanceTracker {
  static async measure<T>(name: string, operation: () => Promise<T>): Promise<T> {
    const start = performance.now();
    const result = await operation();
    const duration = performance.now() - start;
    console.log(`${name}: ${duration.toFixed(2)}ms`);
    return result;
  }
}

// Usage in ObsidianAPI methods
async getFile(path: string): Promise<ObsidianFileResponse> {
  return PerformanceTracker.measure(`getFile:${path}`, async () => {
    // ... implementation
  });
}
Settings Management
interface MCPPluginSettings {
  httpEnabled: boolean;
  httpPort: number;
  httpsPort: number;
  enableSSL: boolean;
  debugLogging: boolean;
  performanceMetrics: boolean;
}

const DEFAULT_SETTINGS: MCPPluginSettings = {
  httpEnabled: true,
  httpPort: 27123,
  httpsPort: 27124,
  enableSSL: true,
  debugLogging: false,
  performanceMetrics: false
};

Migration Guidelines

From HTTP-based Setup
  1. Configuration Migration: Automatically detect and import REST API plugin settings

  2. Port Compatibility: Default to same ports as REST API plugin

  3. Feature Parity: All existing functionality must work identically

  4. Performance Communication: Clearly communicate performance improvements

Backward Compatibility Requirements
  • API Responses: Identical JSON structure and field names

  • Error Codes: Same HTTP status codes and error messages

  • Authentication: Support existing API key mechanisms

  • Headers: Preserve expected request/response headers

Documentation Standards

Code Documentation
/**
 * Enhanced file retrieval with direct vault access
 * 
 * @param path - File path relative to vault root
 * @returns Promise resolving to file content and metadata
 * @throws VaultError when file not found or access denied
 * 
 * Performance: ~1-5ms (vs ~50-100ms HTTP implementation)
 * Compatibility: 100% compatible with HTTP API response format
 */
async getFile(path: string): Promise<ObsidianFileResponse> {
  // Implementation...
}
API Documentation
  • Maintain compatibility documentation showing HTTP vs Direct API equivalence

  • Performance benchmarks for each operation

  • Migration examples for common use cases

  • Troubleshooting guide for plugin-specific issues

Success Metrics

Performance Targets
  • File Operations: <10ms (vs ~50-100ms HTTP)

  • Search Operations: <50ms (vs ~100-300ms HTTP)

  • Directory Listing: <5ms (vs ~30-60ms HTTP)

  • Memory Usage: Stable, no leaks during extended operation

Quality Targets
  • API Compatibility: 100% backward compatible

  • Test Coverage: >90% code coverage

  • Error Handling: Graceful degradation for all failure modes

  • Documentation: Complete user and developer documentation

Community Targets
  • BRAT Testing: 100+ beta installations

  • Feedback Integration: Active response to community feedback

  • Migration Success: Smooth transition for existing users

  • Plugin Directory: Successful submission and approval

Obsidian Community Plugin Distribution

Status: The plugin is live in the community directory as of 2026-05-16 (in submittal since 2025-07-04). It is a maintained, shipped plugin now — not a submission in progress.

Historical note: Obsidian previously required a pull request against the obsidianmd/obsidian-releases repo (editing community-plugins.json) plus a manual "keep the PR alive" refresh dance. That process is defunct. Obsidian moved submission and maintenance to the community developer portal (community.obsidian.md). Do not recreate the obsidian-releases fork workflow — there is no PR to maintain anymore.

Architecture: portal is the source of truth, community-plugins.json is a mirror

The developer portal (community.obsidian.md) is now authoritative. A bot mirrors approved plugins from the portal into obsidianmd/obsidian-releases/community-plugins.json (commits titled chore: Mirror community plugins and themes). The desktop app's in-app community browser still reads that mirror, not the portal directly.

Consequence: after a plugin is approved/updated on the portal, there is a propagation lag before it appears in the in-app browser search — the mirror bot has to run, then the app refreshes its cached list (a restart helps only after the mirror includes you). This is normal, not a bug. The portal page and its "Add to Obsidian" deep link work immediately. If still absent from the mirror after ~24h / many mirror commits, that's a real pipeline miss worth raising with Obsidian — not a repo-side fix.

make promote now auto-updates real users, not just BRAT testers. The prerelease → BRAT → promote loop is the safety rail, not ceremony.

How distribution works now

The portal scans this repo's GitHub Releases. For the plugin to be scanned and distributed:

  • There must be a stable (non-prerelease) "Latest" release whose tag exactly matches the version field in manifest.json.

  • Release tags use no v prefix — 0.11.21, not v0.11.21. The release workflow already produces the correct format.

  • versions.json must contain a "<version>": "<minAppVersion>" entry matching manifest.json (make release-* maintains this).

  • Releases ship as prereleases by default, and a prerelease is invisible to the portal. Run make promote to flip the proven release to stable + "Latest". This is the step that makes the directory (and the MCPB releases/latest/download/... link) pick up the new version.

Validating before publishing

The developer portal offers a "preview a branch scan" that accepts a branch, tag, or commit SHA — use it to confirm a release candidate passes Obsidian's checks before running make promote. Treat it as a validation/dry-run tool: end-user installs and updates still come from the matching stable release tag's assets, not from a scanned branch.

Where the listing text comes from
  • Short description — repo-driven. Single source of truth is package.json; sync-version.mjs propagates it to manifest.json; use make set-description DESC='...' (never hand-edit the JSON). The portal's one-liner refreshes on the next scan after release.

  • Long "About" — repo-extracted from the README on scan. Verified on the 0.11.39 portal scan (2026-07-04): the About rendered the README's top-of-file content — from the "Available in the directory" line through the "New to MCP?" aside — verbatim. This supersedes the earlier belief that the About was portal-only and decoupled from the repo: editing the README opening does change the About on the next scan. Consequence: the README opening now doubles as the directory About, so author it to read well in both places — README-only chrome (the "Available in the directory" self-link, which is circular on the portal; "Quick Start is three steps", which points at a section the About doesn't render) reads awkwardly there. (Historical note: Obsidian did seed a decoupled portal-side About during the community.obsidian.md migration; at some point the portal switched to extracting from the README. Trust a fresh scan over this doc if they diverge again.)

Reading the scorecard as free signal — make scorecard

The public plugin page (community.obsidian.md/plugins/semantic-vault-mcp) renders an automated Health grade and Review scan — deterministic static analysis, not an LLM. It is free signal we can pull without logging in:

make scorecard          # prose + freshness delta, for reading
node scripts/scorecard.mjs --json   # one JSON line, for diffing across releases

Key facts about this tool:

  • Freshness is not guaranteed. A fresh scan is only triggered from the authenticated developer portal. The public page reflects the last release Obsidian scanned, so the script reports a freshness delta (portal version vs manifest.json). STALE means a logged-in re-scan is still needed.

  • Drift guard. The findings come from Obsidian's Next.js RSC payload, so the parser is coupled to their internal serialization. If the page structure changes, the script exits non-zero with a SCRAPER DRIFT banner — that means review scripts/scorecard.mjs, not that the scorecard is clean. The scorecard body itself never gates anything.

Known accepted review findings

The full submit review passed (0.11.23). Not every finding is a defect — several are deliberate trade-offs or false positives. Do not "fix" these by reverting the decision behind them; they were analysed and accepted:

  • .mcpb additional files (Releases, Recommendation) — expected. Per ADR-102, releases intentionally ship the .mcpb bundles so releases/latest/download/obsidian-mcp.mcpb (the plugin Settings download) resolves. Clearing it would break ADR-102. Confirmed non-gating.

  • Direct filesystem access (Behavior, Warning) — fs is used in path-validator.ts (boundary enforcement via realpathSync — itself a security control) and certificate-manager.ts (self-signed TLS certs). Load-bearing; cannot remove without dropping HTTPS.

  • Vault enumeration (Behavior, Recommendation) — vault.getFiles etc. is the plugin's core purpose (semantic search / graph). By design.

  • Clipboard access (Behavior, Recommendation) — write-only, user-initiated (copy API key / copy buttons). Benign.

  • README "unfilled placeholder text" (Warning) — false positive. No real placeholders exist; the heuristic trips on legitimate markdown badge/link labels ([BRAT], [MIT], …). Mangling valid markdown to appease it is the wrong action — leave it.

  • "… scan not available" disclosures — neutral. Where Obsidian's malware/obfuscation scanners do not run, that is not a failure. The dependency scan does run, so treat a regression there as real, not a disclosure.

    The portal scan and local npm audit can diverge, and the local audit is the leading indicator — it sees advisories published since the last portal scan. Re-run npm audit before reading a portal result as current.

Reading npm audit — procedure, not a headline

The bare vulnerability count conflates two things with very different consequences for a plugin that ships a bundle: what reaches users, and what only runs on the maintainer's build machine. Judge them separately, in this order.

  1. npm audit --omit=dev is the number that gates a release. Anything it reports reaches users through main.js. Non-zero means fix now — per the supply-chain rule above, security fixes are exempt from the 7-day hold.

  2. If that is zero, the bare count is not a release problem. Confirm the remainder is dev-only rather than assuming it: npm ls <pkg> --omit=dev shows whether a package is in the production tree at all, and a production copy can sit outside an advisory's range while an older nested copy inside the toolchain sits within it. Note the count can be much larger than the number of distinct advisories — one issue in a widely-shared transitive package fans out across every dependent, so read the advisory IDs, not the total.

  3. Do not run npm audit fix --force to clear a dev-only finding. It moves dev dependencies across majors. Breaking the toolchain that verifies the security work is a worse trade than carrying a vulnerability reachable only by our own build processing adversarial input. Plain npm audit fix (lockfile-only, no package.json change) is the tool for this job.

  4. Record the split in the release notes when the two numbers differ, so the next reader is not startled by a large count that represents no user exposure.

Do not chase the headline count to zero — the same trap as the new Function scorecard finding below. Zero on --omit=dev is the goal; the bare count is context.

Dynamic code execution — the Bases-evaluator new Function (the exploitable vector: arbitrary JS from a synced/shared .base) is removed per ADR-201, implemented in #180 (PR #185 corpus/baseline, PR #186 expression-eval swap). The evaluator now parses with expression-eval (jsep grammar, no globals) plus a tested constructor/__proto__/prototype/ this denylist; a differential corpus proves behavioural parity.

Caveat for future scans: grep "new Function" main.js is not zero by design. The residual occurrences are transitive-dependency codegen — ajv @6.14.0 schema-validator compilation + a library deprecation shim — a different, library-internal class not reachable from vault content. They were already in 0.11.25's bundle, the release that passed full review with only the fs Warning, so they were non-gating then. The "Dynamic Code Execution" Recommendation was attributed to the Bases path specifically; this clears that path.

Settled at 0.11.42: the re-scan does still raise the "Dynamic Code Execution" Recommendation, and it is the transitive class — verified at scan time: src/ contains zero new Function/eval (the only match is a comment in expression-evaluator.ts saying it avoids them), while main.js has exactly 3, all library-internal (1× depd deprecation shim, 2× ajv's makeValidate compiler). The heuristic greps the bundle, not the reachable call graph, so ADR-201 cannot make it go away. It is a Recommendation, never an Error — do not chase it to zero; the closed Bases vector is what mattered and it stays closed.

Actionable findings became issues/PRs: #163/#164/#170 (SSL + attestation, shipped), #171/#173 (build-dep + CSS, shipped), #174 (js-yaml, shipped), #180 (sandboxed Bases evaluator, PR #185/#186), #176 (this doc). Scorecard CI-gate idea: #165.

Passing the source-code review — fix, never suppress

The portal's Source code check forbids disabling a specific set of lint rules. An inline eslint-disable for @typescript-eslint/no-deprecated, obsidianmd/prefer-window-timers, or obsidianmd/no-global-this is itself a gating Error ("Disabling 'X' is not allowed"). It also errors on any directive comment with no inline description ("Unexpected undescribed directive comment"). So the reflex "just eslint-disable it" fails the review — louder than the warning it was hiding. Fix the code or use a non-deprecated path; never suppress these.

Nuance: our local eslint config disabling a rule for a whole file surfaces as a non-gating Warning in the review (tolerated). It's the inline directive comment that's banned. And the review is fail-fast — it halts at the first Error, so clearing one can reveal the next.

Patterns that worked (the 0.11.32 → 0.11.33 cleanup):

  • PluginSettingTab.display() deprecated (1.13.0) → superseded. Since 0.12.7 minAppVersion is 1.13.0 and the tab is declarative: every setting is a control item in getSettingDefinitions(), custom blocks are named render items, side effects live in a setControlValue override, and re-renders call update() (#224). display() no longer exists in source.

  • ButtonComponent.setWarning() deprecated → setDestructive() (1.13.0), now that minAppVersion allows it (#224; the interim setClass('mod-warning') workaround from #229 is gone).

  • Global setTimeout/setInterval and globalThis → window.* / window.console, and alias window → globalThis in tests/setup.ts so the node test env resolves them (#227).

  • Low-level Server deprecated; McpServer.registerTool wants Zod → construct McpServer (non-deprecated) and register our raw JSON-Schema handlers on its .server accessor (a non-deprecated property); the Server symbol then never appears in source (#230). Verified McpServer registers its own tool handlers lazily (only via registerTool, which we never call), so it can't shadow ours — no empty-tools/list regression (cf. #221).

Allowed: a described disable of a non-forbidden rule (e.g. no-require-imports, no-control-regex with -- reason) passes review — the ban is that specific rule set, not all directives. minAppVersion is 1.13.0 (#224), so the 1.13.0 APIs are available outright; obsidianmd/no-unsupported-api reads that field from manifest.json, and MIN_OBSIDIAN in the Makefile feeds versions.json at release — keep the two equal.

Keep eslint-plugin-obsidianmd current, or local lint lies

The portal's source-code review runs its own copy of this plugin. When ours lags, npm run lint passes while the portal reports warnings we cannot reproduce — findings then arrive after a release is already cut. A rule can even be present in our installed dist/ and still never fire, because it isn't wired into the recommended config yet in that version; the file existing proves nothing.

Before a release scan, confirm parity:

node -e "console.log(require('eslint-plugin-obsidianmd/package.json').version)"
npm view eslint-plugin-obsidianmd version

If they differ, upgrade and re-lint before scanning. A new minor of this plugin usually means new rules, and it is cheaper to see them locally than to read them off a scorecard. Treat a warning that only the portal can see as a tooling gap, not as a portal quirk. This upgrade does not wait on the 7-day hold — see the analysis-tooling exemption in the supply-chain section above.

Do not satisfy a rule by faking the implementation. The prefer-setting-definitions warning can be silenced with a getSettingDefinitions() that declares a few settings, which would leave users a settings search that finds some and silently misses the rest. That is worse than the warning, and the same failure mode as suppressing a rule: the check goes green while the real problem grows. Either implement it fully or leave the warning and track the work.

Important Notes

  • Critical Path: The ObsidianAPI abstraction layer is the cornerstone of this architecture

  • Performance Focus: Every operation should demonstrate measurable improvement

  • Compatibility First: When in doubt, maintain compatibility over new features

  • Plugin Ecosystem: Consider integration opportunities with other Obsidian plugins

  • Community Feedback: BRAT testing phase is crucial for identifying issues


This plugin represents the evolution of Obsidian AI integration. Maintain the highest standards as we build the definitive solution for AI-Obsidian connectivity.

236 files · 218 not served here

Advantages

  • No external Node process to launch, no separate REST-API plugin to bridge through: the server is the plugin. evidence: README.md
  • Every operation respects the permissions you set — a read-only mode, per-operation controls, and path allow/block lists. evidence: README.md

Limitations

  • MCP clients reject self-signed certificates by default, so you need to explicitly trust it before connecting over HTTPS. evidence: README.md

Security

Risk level: Medium risk

This brain runs

Agent instructions that run or fetch something (42)

  • CLAUDE.md: Test locally with `npm run build`
  • CLAUDE.md: Click **Run workflow**
  • CLAUDE.md: Click **Run**
  • CLAUDE.md: git tag -d X.Y.Z && git push origin :refs/tags/X.Y.Z · uses the network
  • CLAUDE.md: Users install via: `aaronsb/obsidian-mcp-plugin` in BRAT
  • CLAUDE.md: **Pre-releases**: All releases ship as prereleases by default (`make release-patch` etc.)
  • CLAUDE.md: **Promoting to stable**: When a release is bounded and proven, run `make promote` (or `make promote TAG=X.Y.Z`) to flip it from prerelease → stable and mark it as GitHub's "Latest" release.
  • CLAUDE.md: "package": "npm run build && npm run test && node package-release.js"
  • CLAUDE.md: **ALWAYS run these commands before pushing to GitHub:**
  • CLAUDE.md: npm run build
  • CLAUDE.md: # 2. Run linting to ensure code quality
  • CLAUDE.md: npm run lint
  • CLAUDE.md: # 3. Run tests to catch regressions
  • CLAUDE.md: npm test
  • CLAUDE.md: git add -A && git commit -m "..."
  • CLAUDE.md: git push origin main · uses the network
  • CLAUDE.md: npm run build && npm run lint && npm test && echo "✅ All checks passed!"
  • CLAUDE.md: npm supply-chain attacks have been ticking up — malicious package versions get published, ingested by automation, then discovered/yanked days later.
  • CLAUDE.md: The exemption exists because the hold otherwise fights our own freshness goal: it delays exactly the vuln patches that Obsidian's plugin review (and `npm audit`) flag, leaving us knowingly exposed to a *named, real* CVE to guard against a *hypothetical* malicious republish.
  • CLAUDE.md: **Security fix → land it now.** If a dependabot/manual upgrade resolves a flagged vulnerability (dependabot security PR, `npm audit` advisory, or an Obsidian-review dependency warning), merge as soon as it's green regardless of version age.
  • CLAUDE.md: The same split applies to manual `npm install` / `npm update`: security fixes immediately, routine pins to versions older than 7 days.
  • CLAUDE.md: Re-run `npm audit` after each merge batch to see what residual exposure remains.
  • CLAUDE.md: This is not zero-risk — an ESLint plugin executes on the maintainer's machine during `npm run lint`, so the exposure moves from users to the developer rather than disappearing.
  • CLAUDE.md: Install scripts are blocked pending approval (`npm install-scripts ls` shows what is waiting), but that is **npm 12+ default behaviour, not something this repo enforces** — there is no `.npmrc` or `package.json` setting pinning it.
  • CLAUDE.md: Approve install scripts one package at a time and never with `--all`;
  • CLAUDE.md: Consequence: after a plugin is approved/updated on the portal, there is a **propagation lag** before it appears in the in-app browser search — the mirror bot has to run, then the app refreshes its cached list (a restart helps *only after* the mirror includes you).
  • CLAUDE.md: `make promote` now auto-updates **real users**, not just BRAT testers.
  • CLAUDE.md: `versions.json` must contain a `"<version>": "<minAppVersion>"` entry matching `manifest.json` (`make release-*` maintains this).
  • CLAUDE.md: Run `make promote` to flip the proven release to stable + "Latest".
  • CLAUDE.md: The developer portal offers a **"preview a branch scan"** that accepts a branch, tag, or commit SHA — use it to confirm a release candidate passes Obsidian's checks *before* running `make promote`.
  • CLAUDE.md: Treat it as a validation/dry-run tool: end-user installs and updates still come from the matching stable release tag's assets, not from a scanned branch.
  • CLAUDE.md: make scorecard # prose + freshness delta, for reading
  • CLAUDE.md: node scripts/scorecard.mjs --json # one JSON line, for diffing across releases
  • CLAUDE.md: The **dependency** scan *does* run, so treat a regression there as real, not a disclosure.
  • CLAUDE.md: The portal scan and local `npm audit` **can diverge**, and the local audit is the leading indicator — it sees advisories published since the last portal scan.
  • CLAUDE.md: Re-run `npm audit` before reading a portal result as current.
  • CLAUDE.md: **`npm audit --omit=dev` is the number that gates a release.** Anything it reports reaches users through `main.js`.
  • CLAUDE.md: **If that is zero, the bare count is not a release problem.** Confirm the remainder is dev-only rather than assuming it: `npm ls <pkg> --omit=dev` shows whether a package is in the production tree at all, and a production copy can sit *outside* an advisory's range while an older nested …[+41 chars]
  • CLAUDE.md: Plain `npm audit fix` (lockfile-only, no `package.json` change) is the tool for this job.
  • CLAUDE.md: When ours lags, `npm run lint` passes while the portal reports warnings we cannot reproduce — findings then arrive *after* a release is already cut.
  • CLAUDE.md: node -e "console.log(require('eslint-plugin-obsidianmd/package.json').version)"
  • CLAUDE.md: npm view eslint-plugin-obsidianmd version

Install scripts (1)

  • Makefile (make install): npm install

Plugin code (1)

  • manifest.json: runs inside Obsidian once the plugin is enabled
  • Medium risk: runs-instruction-instruction-network (CLAUDE.md:136)
  • Medium risk: runs-instruction-instruction-network (CLAUDE.md:202)
  • Low risk: scripts-present (esbuild.config.mjs)
  • Low risk: scripts-present (scripts/build-mcpb.mjs)
  • Low risk: scripts-present (scripts/scorecard-findings.mjs)
  • Low risk: scripts-present (scripts/scorecard.mjs)
  • Low risk: scripts-present (src/formatters/dataview.ts)
  • Low risk: scripts-present (src/formatters/graph.ts)
  • Low risk: scripts-present (src/formatters/index.ts)
  • Low risk: scripts-present (src/formatters/search.ts)
  • Low risk: scripts-present (src/formatters/system.ts)
  • Low risk: scripts-present (src/formatters/utils.ts)
  • Low risk: scripts-present (src/formatters/vault.ts)
  • Low risk: scripts-present (src/formatters/view.ts)
  • Low risk: scripts-present (src/indexing/adaptive-index.ts)
  • Low risk: scripts-present (src/indexing/fragment-retriever.ts)
  • Low risk: scripts-present (src/indexing/proximity-index.ts)
  • Low risk: scripts-present (src/indexing/semantic-chunk-index.ts)
  • Low risk: scripts-present (src/main.ts)
  • Low risk: scripts-present (src/mcp-server.ts)
  • Low risk: scripts-present (src/plugin-settings.ts)
  • Low risk: scripts-present (src/security/http-auth.ts)
  • Low risk: scripts-present (src/security/index.ts)
  • Low risk: scripts-present (src/security/mcp-ignore-manager.ts)
  • Low risk: scripts-present (src/security/path-validator.ts)
  • Low risk: scripts-present (src/security/safe-fetch.ts)
  • Low risk: scripts-present (src/security/secure-obsidian-api.ts)
  • Low risk: scripts-present (src/security/url-validator.ts)
  • Low risk: scripts-present (src/security/vault-security-manager.ts)
  • Low risk: scripts-present (src/semantic/operations/bases.ts)
  • Low risk: scripts-present (src/semantic/operations/edit.ts)
  • Low risk: scripts-present (src/semantic/operations/graph.ts)
  • Low risk: scripts-present (src/semantic/operations/router-context.ts)
  • Low risk: scripts-present (src/semantic/operations/shared.ts)
  • Low risk: scripts-present (src/semantic/operations/system.ts)
  • Low risk: scripts-present (src/semantic/operations/vault.ts)
  • Low risk: scripts-present (src/semantic/operations/view.ts)
  • Low risk: scripts-present (src/semantic/operations/workflow.ts)
  • Low risk: scripts-present (src/semantic/router.ts)
  • Low risk: scripts-present (src/semantic/state-tokens.ts)
  • Low risk: scripts-present (src/tools/dataview-tool.ts)
  • Low risk: scripts-present (src/tools/fetch.ts)
  • Low risk: scripts-present (src/tools/graph-search-tag-traversal.ts)
  • Low risk: scripts-present (src/tools/graph-search-tool.ts)
  • Low risk: scripts-present (src/tools/graph-search-traversal.ts)
  • Low risk: scripts-present (src/tools/graph-search.ts)
  • Low risk: scripts-present (src/tools/graph-tag-tool.ts)
  • Low risk: scripts-present (src/tools/semantic-tools.ts)
  • Low risk: scripts-present (src/tools/window-edit.ts)
  • Low risk: scripts-present (src/types/bases-yaml.ts)

The plugin you install is release 0.13.0, which AutoGineer scanned.

Attribution and licence

Created by
Aaron Bockelie
Declared authors
  • Aaron Bockelie (declared in package.json)
  • Aaron Bockelie (declared in manifest.json)
Publisher
Aaron Bockelie (Community)
Source
aaronsb/obsidian-mcp-plugin · commit 103ee96
Licence
MIT License (files served here)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Aaron Bockelie.

Install from source

218 of its files are not served here, so there is no ZIP. aaronsb/obsidian-mcp-plugin at 103ee96