# 🎛️ Obsidian Master Control MCP

> **The most comprehensive MCP server for Obsidian** — 46 tools covering vault operations, advanced search, Bases (databases), Canvas (visual graphs), periodic notes, commands, and more.

[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![MCP](https://img.shields.io/badge/MCP-Model%20Context%20Protocol-green.svg)](https://modelcontextprotocol.io/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

---

## 🔥 Why This One?

| Feature | Obsidian Master Control | [mcp-obsidian](https://github.com/MarkusPfundstein/mcp-obsidian) | [obsidian-mcp-server](https://github.com/cyanheads/obsidian-mcp-server) | [obsidian-mcp](https://github.com/StevenStavrakis/obsidian-mcp) | [smithery mcp-obsidian](https://github.com/smithery-ai/mcp-obsidian) |
|---------|------------------------|-----------------|----------------------|---------------|--------------------|
| **Total Tools** | **46** | 7 | 8 | 12 | ~3 |
| **Language** | Python | Python | TypeScript | TypeScript | TypeScript |
| **Bases Support** | ✅ Full CRUD | ❌ | ❌ | ❌ | ❌ |
| **Canvas Support** | ✅ 13 tools + graph analysis | ❌ | ❌ | ❌ | ❌ |
| **Simple Search** | ✅ | ✅ | ✅ | ✅ | ✅ |
| **JsonLogic Search** | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Dataview DQL** | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Periodic Notes** | ✅ 5 tools | ❌ | ✅ (targeting) | ❌ | ❌ |
| **Active File Ops** | ✅ 5 tools | ❌ | ✅ (targeting) | ❌ | ❌ |
| **Command Execution** | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Document Map** | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Patch Operations** | ✅ (heading/block/frontmatter) | ✅ | ✅ | ❌ | ❌ |
| **Open in Obsidian** | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Requires REST API Plugin** | ✅ | ✅ | ✅ | ❌ (filesystem) | ❌ (filesystem) |
| **Async/Typed** | ✅ httpx + Pydantic | ✅ | ✅ | ✅ | - |

---

## 📋 Prerequisites

### 1. Install the Obsidian Local REST API Plugin

This MCP server communicates with Obsidian through the **Local REST API** community plugin.

1. Open Obsidian → Settings → Community Plugins
2. Click **Browse** and search for **"Local REST API"**
3. Install and **Enable** the plugin
4. Go to the plugin settings and:
   - Copy your **API Key** (you'll need this)
   - Note the port (default: `27124` for HTTPS, `27123` for HTTP)
   - Optionally enable HTTPS (recommended)

> 📖 Plugin repo: [coddingtonbear/obsidian-local-rest-api](https://github.com/coddingtonbear/obsidian-local-rest-api)

### 2. Python Environment

```bash
pip install httpx pydantic mcp
```

Or use the requirements file:
```bash
pip install -r requirements.txt
```

---

## ⚙️ Configuration

Set these environment variables:

```bash
# Required
export OBSIDIAN_API_KEY="your-api-key-from-plugin-settings"

# Optional (defaults shown)
export OBSIDIAN_HOST="127.0.0.1"
export OBSIDIAN_PORT="27124"
export OBSIDIAN_USE_HTTPS="true"
export OBSIDIAN_VERIFY_SSL="false"  # Set to false for self-signed cert
```

Or create a `.env` file (see `.env.example`).

---

## 🛠️ All 46 Tools

### 📊 System
| Tool | Description |
|------|-------------|
| `obsidian_get_server_status` | Get REST API server status and auth state |

### 📁 Vault Operations (9 tools)
| Tool | Description |
|------|-------------|
| `obsidian_list_files` | List files/folders in a directory |
| `obsidian_get_file` | Get file content (markdown or JSON with frontmatter) |
| `obsidian_create_file` | Create a new file |
| `obsidian_update_file` | Replace entire file content |
| `obsidian_append_to_file` | Append content to end of file |
| `obsidian_patch_file` | Patch specific section (heading/block/frontmatter) |
| `obsidian_delete_file` | Delete a file |
| `obsidian_get_document_map` | Get document structure (headings, blocks, frontmatter) |
| `obsidian_open_file` | Open file in Obsidian UI (with optional line number) |

### 📝 Active File Operations (5 tools)
| Tool | Description |
|------|-------------|
| `obsidian_get_active_file` | Get currently open file content |
| `obsidian_update_active_file` | Replace active file content |
| `obsidian_append_to_active_file` | Append to active file |
| `obsidian_patch_active_file` | Patch section of active file |
| `obsidian_delete_active_file` | Delete the active file |

### 🔍 Search (3 tools)
| Tool | Description |
|------|-------------|
| `obsidian_simple_search` | Basic text search across vault |
| `obsidian_dataview_search` | SQL-like Dataview DQL queries (requires Dataview plugin) |
| `obsidian_jsonlogic_search` | JsonLogic queries with glob/regexp support |

### ⚡ Commands (2 tools)
| Tool | Description |
|------|-------------|
| `obsidian_list_commands` | List all available Obsidian commands |
| `obsidian_execute_command` | Execute any Obsidian command by ID |

### 📅 Periodic Notes (5 tools)
| Tool | Description |
|------|-------------|
| `obsidian_get_periodic_note` | Get daily/weekly/monthly/quarterly/yearly note |
| `obsidian_update_periodic_note` | Update periodic note content |
| `obsidian_append_to_periodic_note` | Append to periodic note |
| `obsidian_patch_periodic_note` | Patch section of periodic note |
| `obsidian_delete_periodic_note` | Delete periodic note |

### 🗃️ Bases — Database Views (8 tools)
| Tool | Description |
|------|-------------|
| `obsidian_list_bases` | List all .base files in vault |
| `obsidian_get_base` | Get base configuration and views |
| `obsidian_create_base` | Create new base with table/kanban views |
| `obsidian_update_base` | Update base configuration |
| `obsidian_delete_base` | Delete a base file |
| `obsidian_query_base_entries` | Query notes matching base filters |
| `obsidian_add_entry_to_base` | Create note with properties for a base |
| `obsidian_update_entry_properties` | Update frontmatter properties on a note |

### 🎨 Canvas — Visual Node Graphs (14 tools)
| Tool | Description |
|------|-------------|
| `obsidian_list_canvases` | List all .canvas files in vault |
| `obsidian_get_canvas` | Get full canvas with all nodes and edges |
| `obsidian_create_canvas` | Create a new empty canvas |
| `obsidian_delete_canvas` | Delete a canvas file |
| `obsidian_add_canvas_node` | Add a node (text, file, link, or group) |
| `obsidian_update_canvas_node` | Update node properties (position, size, content, color) |
| `obsidian_remove_canvas_node` | Remove a node and its connected edges |
| `obsidian_add_canvas_edge` | Add an edge (connection) between nodes |
| `obsidian_update_canvas_edge` | Update edge properties (sides, arrows, label, color) |
| `obsidian_remove_canvas_edge` | Remove an edge |
| `obsidian_batch_canvas_update` | Batch multiple node/edge operations in one read/write cycle |
| `obsidian_canvas_overview` | Get graph analysis (DAG detection, components, connectivity) |
| `obsidian_search_canvas_nodes` | Search nodes with filters, return subgraph |
| `obsidian_get_canvas_subgraph` | Extract specific nodes and their connections |

---

## 🔍 Search Comparison

### Simple Search
Basic full-text search. No plugins required.
```
query: "meeting notes"
```

### JsonLogic Search
Powerful JSON-based queries with glob and regexp. **No plugins required.**
```json
{"and": [
  {"glob": ["Projects/*.md", {"var": "path"}]},
  {"===": [{"var": "frontmatter.status"}, "active"]}
]}
```

### Dataview DQL Search
SQL-like syntax for complex queries. **Requires Dataview plugin.**
```sql
TABLE status, due FROM "Projects" WHERE status = "active" SORT due ASC
```

---

## 🗃️ Bases: Database-Like Views

Obsidian Bases let you create dynamic views (tables, kanbans) over your notes using frontmatter properties. This MCP server provides **full CRUD support** for Bases — a feature no other MCP server offers.

### Example: Create a Project Tracker

```python
# Create a base with table and kanban views
obsidian_create_base({
    "name": "Projects",
    "source_folder": "Projects",
    "views": [
        {
            "name": "All Projects",
            "view_type": "table",
            "fields": ["status", "priority", "due_date"]
        },
        {
            "name": "Kanban",
            "view_type": "cards",
            "group_by": "status"
        }
    ]
})

# Add entries with structured properties
obsidian_add_entry_to_base({
    "base_name": "Projects",
    "title": "Build MCP Server",
    "properties": {
        "status": "in-progress",
        "priority": "high",
        "due_date": "2024-02-15"
    }
})
```

---

## 🎨 Canvas: Visual Node Graphs

Obsidian Canvas lets you create infinite, spatial canvases with nodes (text, files, links, groups) connected by edges. This MCP server provides **full canvas manipulation** plus **graph analysis** — features no other MCP server offers.

### Node Types
| Type | Description | Key Fields |
|------|-------------|------------|
| `text` | Markdown text content | `text` |
| `file` | Embed a vault file | `file`, `subpath` (optional heading/block) |
| `link` | Embed a web URL | `url` |
| `group` | Visual container for organizing nodes | `label` |

### Example: Build a Knowledge Graph

```python
# Create a canvas
obsidian_create_canvas({"path": "Knowledge Graph.canvas"})

# Add concept nodes
obsidian_add_canvas_node({
    "path": "Knowledge Graph.canvas",
    "node_type": "text",
    "text": "# Machine Learning\nCore concepts and techniques",
    "x": 0, "y": 0,
    "width": 300, "height": 100,
    "color": "4"  # Green preset
})

obsidian_add_canvas_node({
    "path": "Knowledge Graph.canvas",
    "node_type": "text", 
    "text": "# Neural Networks",
    "x": 400, "y": 0,
    "color": "4"
})

# Connect them with a labeled edge
obsidian_add_canvas_edge({
    "path": "Knowledge Graph.canvas",
    "from_node": "<node1_id>",
    "to_node": "<node2_id>",
    "label": "includes",
    "to_end": "arrow"
})

# Embed an existing note
obsidian_add_canvas_node({
    "path": "Knowledge Graph.canvas",
    "node_type": "file",
    "file": "Notes/Deep Learning.md",
    "x": 400, "y": 200
})
```

### Batch Operations (Efficient)

Use `obsidian_batch_canvas_update` to apply multiple changes in a single read/write cycle:

```python
obsidian_batch_canvas_update({
    "path": "Architecture.canvas",
    "add_nodes": {
        "api": {"node_type": "text", "x": 0, "y": 0, "text": "# API Layer"},
        "db": {"node_type": "text", "x": 300, "y": 0, "text": "# Database"},
        "cache": {"node_type": "text", "x": 150, "y": 150, "text": "# Cache"}
    },
    "add_edges": {
        "e1": {"from_node": "api", "to_node": "db", "label": "queries"},
        "e2": {"from_node": "api", "to_node": "cache", "label": "reads"},
        "e3": {"from_node": "cache", "to_node": "db", "label": "misses"}
    }
})
# Returns node_id_mapping: {"api": "a1b2..", "db": "c3d4..", "cache": "e5f6.."}
```

Dict keys serve as temp_ids — edges can reference nodes created in the same batch.

### Graph Analysis

The `obsidian_canvas_overview` tool provides powerful graph analysis:

```python
obsidian_canvas_overview({"path": "Knowledge Graph.canvas"})

# Returns:
{
    "total_nodes": 15,
    "nodes_by_type": {"text": 8, "file": 5, "group": 2},
    "total_edges": 12,
    "is_dag": true,           # Directed Acyclic Graph detection
    "has_cycles": false,
    "connected_components": 1, # All nodes are connected
    "isolated_nodes": 0,
    "most_connected_nodes": [
        {"id": "abc123", "type": "text", "connections": 5, "preview": "Machine Learning"}
    ],
    "leaf_nodes": 4,          # Nodes with no outgoing edges
    "root_nodes": 1,          # Nodes with no incoming edges
    "groups": [
        {"id": "grp1", "label": "Core Concepts", "node_count": 6}
    ]
}
```

### Searching & Filtering

```python
# Find all text nodes containing "TODO"
obsidian_search_canvas_nodes({
    "path": "Project.canvas",
    "query": "TODO|FIXME",
    "node_types": ["text"]
})

# Find nodes connected to a specific node
obsidian_search_canvas_nodes({
    "path": "Project.canvas",
    "connected_to": "abc123",
    "expand_depth": 2  # Include neighbors up to 2 hops away
})

# Extract a subgraph of specific nodes
obsidian_get_canvas_subgraph({
    "path": "Project.canvas",
    "node_ids": ["node1", "node2", "node3"],
    "include_internal_edges": true
})
```

> ⚠️ **Note**: If a canvas is open in Obsidian's UI, Obsidian may overwrite API changes when it syncs. Close the canvas before making programmatic changes.

---

## 🚀 Usage

### With Claude Desktop

Add to your Claude Desktop config (`~/.config/claude/claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "obsidian": {
      "command": "python",
      "args": ["/path/to/obsidian-master-control-mcp/mcp_server.py"],
      "env": {
        "OBSIDIAN_API_KEY": "your-api-key",
        "OBSIDIAN_HOST": "127.0.0.1",
        "OBSIDIAN_PORT": "27124",
        "OBSIDIAN_USE_HTTPS": "true",
        "OBSIDIAN_VERIFY_SSL": "false"
      }
    }
  }
}
```

### With the Test Agent

A test agent is included for interactive testing:

```bash
# Set environment variables first
export OBSIDIAN_API_KEY="your-key"
export OPENROUTER_API_KEY="your-openrouter-key"  # For the agent's LLM

# Run the agent
python agent.py
```

Then chat naturally:
```
> List all my project notes
> Create a new daily note for today
> Search for notes about "machine learning"
> Create a base to track my reading list
```

---

## 📁 Project Structure

```
.
├── mcp_server.py          # MCP server with 46 tools
├── obsidian_client.py     # Core REST API client
├── bases_client.py        # Bases operations client
├── canvas.py              # Canvas models + graph analysis
├── canvas_operations.py   # Canvas high-level operations
├── agent.py               # Test agent for interactive use
├── agents/
│   └── obsidian.md        # Agent system prompt
├── docs/
│   └── changelog.md       # Version history
├── requirements.txt
└── .env.example
```

---

## 📜 License

MIT License — use it however you want.

---

## 🙏 Acknowledgments

- [Obsidian](https://obsidian.md/) — the incredible knowledge base app
- [Local REST API Plugin](https://github.com/coddingtonbear/obsidian-local-rest-api) — makes this all possible
- [Model Context Protocol](https://modelcontextprotocol.io/) — the MCP standard
