Skip to main content
Glama
bookbran

Smart Connections MCP Server

by bookbran
README.md
# Smart Connections MCP Server

A Model Context Protocol (MCP) server that provides semantic search and knowledge graph capabilities for Obsidian vaults using Smart Connections embeddings.

> **Fork note:** this is a fork maintained by [A Portland Career](https://aportlandcareer.com) for its second-brain pilot kit, based on the original [`smart-connections-mcp` by Daniel Glickman](https://github.com/msdanyg/smart-connections-mcp) (MIT). It adds true semantic `search_notes` (embeds the query with the vault's model instead of literal keyword matching); see "Query embedding" below. Original copyright and MIT license preserved in `LICENSE`.

## Overview

This MCP server allows Claude (and other MCP clients) to:
- **Search semantically** through your Obsidian notes using pre-computed embeddings
- **Find similar notes** based on content similarity
- **Build connection graphs** showing how notes are related
- **Query by embedding vectors** for advanced use cases
- **Access note content** with block-level granularity

## Features

### πŸ” Semantic Search
Uses the embeddings generated by Obsidian's Smart Connections plugin to perform fast, accurate semantic searches across your entire vault.

### πŸ•ΈοΈ Connection Graphs
Builds multi-level connection graphs showing how notes are related through semantic similarity, helping discover hidden relationships in your knowledge base.

### πŸ“Š Vector Similarity
Direct access to embedding-based similarity calculations using cosine similarity on 384-dimensional vectors (TaylorAI/bge-micro-v2 model).

### πŸ“ Content Access
Retrieve full note content or specific sections/blocks with intelligent extraction based on Smart Connections block mappings.

## Installation

### Prerequisites

- Node.js 18 or higher
- An Obsidian vault with Smart Connections plugin installed and embeddings generated
- Claude Desktop (or another MCP client)

### Setup

1. **Clone the repository:**
   ```bash
   git clone https://github.com/bookbran/smart-connections-mcp.git
   cd smart-connections-mcp
   ```

2. **Install dependencies:**
   ```bash
   npm install
   ```

3. **Build the TypeScript project:**
   ```bash
   npm run build
   ```

4. **Configure Claude Desktop:**

   Edit your Claude Desktop configuration file:
   - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
   - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

   Add the following to the `mcpServers` section:

   ```json
   {
     "mcpServers": {
       "smart-connections": {
         "command": "node",
         "args": [
           "/ABSOLUTE/PATH/TO/smart-connections-mcp/dist/index.js"
         ],
         "env": {
           "SMART_VAULT_PATH": "/ABSOLUTE/PATH/TO/YOUR/OBSIDIAN/VAULT"
         }
       }
     }
   }
   ```

   **Important**: Replace the paths with your actual paths:
   - Update the `args` path to point to your built `index.js` file
   - Update `SMART_VAULT_PATH` to your Obsidian vault path

5. **Restart Claude Desktop**

   The MCP server will automatically start when Claude Desktop launches.

## Available Tools

### 1. `get_similar_notes`

Find notes semantically similar to a given note.

**Parameters:**
- `note_path` (string, required): Path to the note (e.g., "Note.md" or "Folder/Note.md")
- `threshold` (number, optional): Similarity threshold 0-1, default 0.5
- `limit` (number, optional): Maximum results, default 10

**Example:**
```typescript
{
  "note_path": "MyNote.md",
  "threshold": 0.7,
  "limit": 5
}
```

**Returns:**
```json
[
  {
    "path": "RelatedNote.md",
    "similarity": 0.85,
    "blocks": ["#Overview", "#Key Points", "#Details"]
  }
]
```

### 2. `get_connection_graph`

Build a multi-level connection graph showing how notes are semantically connected.

**Parameters:**
- `note_path` (string, required): Starting note path
- `depth` (number, optional): Graph depth (levels), default 2
- `threshold` (number, optional): Similarity threshold 0-1, default 0.6
- `max_per_level` (number, optional): Max connections per level, default 5

**Example:**
```typescript
{
  "note_path": "MyNote.md",
  "depth": 2,
  "threshold": 0.7
}
```

**Returns:**
```json
{
  "path": "MyNote.md",
  "depth": 0,
  "similarity": 1.0,
  "connections": [
    {
      "path": "RelatedNote.md",
      "depth": 1,
      "similarity": 0.82,
      "connections": [...]
    }
  ]
}
```

### 3. `search_notes`

Semantic search by text query. Embeds the query with the **same** model used for the vault's note embeddings (bge-micro-v2) and ranks notes by cosine similarity, so it matches by meaning rather than exact words. Falls back to a multi-term keyword search if the embedding model can't be loaded (e.g. fully offline before the model has ever been cached).

> **First-query note:** the query-embedding model is downloaded from the Hugging Face hub on the **first** `search_notes` call of a server session (needs network once, ~30s), then cached under `node_modules/@huggingface/transformers/.cache` so every later call is offline and fast (~4ms). See "Query embedding" under Technical Details.

**Parameters:**
- `query` (string, required): Search query text
- `limit` (number, optional): Maximum results, default 10
- `threshold` (number, optional): Similarity threshold 0-1, default 0.4 (typical relevant matches score ~0.4-0.75; lower to widen recall)

**Example:**
```typescript
{
  "query": "project management",
  "limit": 5
}
```

### 4. `get_embedding_neighbors`

Find nearest neighbors for a given embedding vector (advanced use).

**Parameters:**
- `embedding_vector` (number[], required): 384-dimensional vector
- `k` (number, optional): Number of neighbors, default 10
- `threshold` (number, optional): Similarity threshold 0-1, default 0.5

### 5. `get_note_content`

Retrieve full note content with optional block extraction.

**Parameters:**
- `note_path` (string, required): Path to the note
- `include_blocks` (string[], optional): Specific block headings to extract

**Example:**
```typescript
{
  "note_path": "MyNote.md",
  "include_blocks": ["#Introduction", "#Main Points"]
}
```

**Returns:**
```json
{
  "content": "# Full note content...",
  "blocks": {
    "#Introduction": "Content of this section...",
    "#Main Points": "Content of this section..."
  }
}
```

### 6. `get_stats`

Get statistics about the knowledge base.

**Parameters:** None

**Returns:**
```json
{
  "totalNotes": 137,
  "totalBlocks": 1842,
  "embeddingDimension": 384,
  "modelKey": "TaylorAI/bge-micro-v2"
}
```

## Usage Examples

Once configured, you can ask Claude to use these tools naturally:

- **"Find notes similar to my project planning document"**
- **"Show me a connection graph starting from my main research note"**
- **"Search my notes for information about [your topic]"**
- **"What's in my note about [topic]?"**
- **"Give me stats about my knowledge base"**

## Architecture

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Claude Desktop                         β”‚
β”‚                    (MCP Client)                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
                          β”‚ MCP Protocol (stdio)
                          β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Smart Connections MCP Server                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  index.ts (MCP Server + Tool Handlers)             β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                   β”‚                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  search-engine.ts (Semantic Search Logic)          β”‚   β”‚
β”‚  β”‚  - getSimilarNotes()                               β”‚   β”‚
β”‚  β”‚  - getConnectionGraph()                            β”‚   β”‚
β”‚  β”‚  - searchByQuery()                                 β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                   β”‚                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  smart-connections-loader.ts (Data Access)         β”‚   β”‚
β”‚  β”‚  - Load .smart-env/smart_env.json                  β”‚   β”‚
β”‚  β”‚  - Load .smart-env/multi/*.ajson embeddings        β”‚   β”‚
β”‚  β”‚  - Read note content from vault                    β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                   β”‚                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  embedding-utils.ts (Vector Math)                  β”‚   β”‚
β”‚  β”‚  - cosineSimilarity()                              β”‚   β”‚
β”‚  β”‚  - findNearestNeighbors()                          β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                          β”‚
                          β”‚ File System Access
                          β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            Obsidian Vault + .smart-env/                     β”‚
β”‚  - smart_env.json (config)                                  β”‚
β”‚  - multi/*.ajson (embeddings for 137 notes)                 β”‚
β”‚  - *.md (markdown note files)                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## Technical Details

### Embedding Model
- **Model**: TaylorAI/bge-micro-v2
- **Dimensions**: 384
- **Similarity Metric**: Cosine similarity

### Query embedding (`search_notes`)
`get_similar_notes` / `get_connection_graph` compare notes against each other using the vectors Smart Connections already stored, so they need no model at runtime. `search_notes` is different: it must turn an arbitrary text query into a vector to compare against those stored vectors. It does this with `@huggingface/transformers` (transformers.js), loading the **same** model the vault is configured for (read from `smart_env.json`, e.g. `TaylorAI/bge-micro-v2`) so the query and note vectors live in the same space.

- The model repo is tried in order: the vault's configured `model_key`, then `SC_EMBED_MODEL` (env override), then `TaylorAI/bge-micro-v2`, then `Xenova/bge-micro-v2`.
- ONNX weights download from the Hugging Face hub on first use and are cached under `node_modules/@huggingface/transformers/.cache`. First `search_notes` call: ~30s; subsequent calls: ~4ms, offline.
- If no model can be loaded (offline with an empty cache), `search_notes` transparently falls back to a multi-term keyword search so it still returns useful results.
- Override the model with the `SC_EMBED_MODEL` env var if your vault uses a different embedding model. It must be a bge-micro-v2-family model, or the query vectors won't match the stored note vectors.

### Data Format
The server reads from Obsidian's Smart Connections `.smart-env/` directory:
- `smart_env.json`: Configuration and model settings
- `multi/*.ajson`: Per-note embeddings and block mappings

### Performance
- **Load time**: ~2-5 seconds for 137 notes
- **Search**: Near-instant (<50ms) using pre-computed embeddings
- **Memory**: ~20-30MB for embeddings + note index

## Development

### Build
```bash
npm run build
```

### Watch Mode
```bash
npm run watch
```

### Run Locally
```bash
export SMART_VAULT_PATH="/path/to/your/vault"
npm run dev
```

### Project Structure
```
smart-connections-mcp/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ index.ts                    # MCP server & tool handlers
β”‚   β”œβ”€β”€ search-engine.ts            # Semantic search logic
β”‚   β”œβ”€β”€ smart-connections-loader.ts # Data loading
β”‚   β”œβ”€β”€ embedding-utils.ts          # Vector math utilities
β”‚   └── types.ts                    # TypeScript type definitions
β”œβ”€β”€ dist/                           # Compiled JavaScript (generated)
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
└── README.md
```

## Troubleshooting

### "Smart Connections directory not found"
- Ensure your vault has the Smart Connections plugin installed
- Verify embeddings have been generated (check `.smart-env/multi/` directory)
- Check that `SMART_VAULT_PATH` points to the correct vault

### "Configuration file not found"
- Run Smart Connections in Obsidian at least once to generate configuration
- Check for `.smart-env/smart_env.json` in your vault

### "No embeddings found for note"
- Some notes may not have embeddings if they're too short (< 200 chars)
- Re-run Smart Connections embedding generation in Obsidian

### Server not appearing in Claude Desktop
- Verify the configuration file syntax (JSON must be valid)
- Check the file paths are absolute paths, not relative
- Restart Claude Desktop completely
- Check Claude Desktop logs for error messages

## License

MIT

## Author

- Original: Daniel Glickman ([msdanyg/smart-connections-mcp](https://github.com/msdanyg/smart-connections-mcp))
- Fork maintained by: A Portland Career (semantic `search_notes` + pilot-kit integration)

## Acknowledgments

- Built for use with [Obsidian](https://obsidian.md/)
- Integrates with [Smart Connections](https://github.com/brianpetro/obsidian-smart-connections) plugin
- Uses [Model Context Protocol](https://modelcontextprotocol.io/) by Anthropic

TDQS

A3.9/5.0

Scored across 10 tools

Disambiguation4/5

Most tools are clearly distinct by resource and action, but get_similar_notes, search_notes, and get_embedding_neighbors all touch semantic retrieval and could cause misselection. The descriptions do a good job separating note-based, query-based, and vector-based retrieval, so only one or two tools are genuinely confusable.

Naming Consistency5/5

Every tool follows a consistent snake_case verb_noun pattern: get_* for retrieval, search_notes for querying, resolve_link for resolution, and check_* for diagnostics. The naming is predictable and makes the toolset easy to navigate.

Tool Count5/5

Ten tools is well-scoped for a read-only vault intelligence server. Each tool earns its place by covering a distinct retrieval, linking, analysis, or health-check function without unnecessary duplication.

Completeness4/5

The toolset covers the core domain well: semantic search, similar notes, backlinks, connection graphs, note content, wikilink resolution, and retrieval health. Minor gaps exist around explicit vault browsing/list-all-notes or index rebuilding, but agents can complete normal workflows without dead ends.