Skip to main content
Glama
QianJue-CN

ObsidianReaderMCP

by QianJue-CN

ObsidianReaderMCP

GitHub Python MCP License

A comprehensive Python MCP (Model Context Protocol) server for managing Obsidian vaults through the obsidian-local-rest-api plugin.

Features

Core CRUD Operations

  • Create: Create new notes with content, metadata, and tags

  • Read: Retrieve note content and metadata by path

  • Update: Modify existing notes (content, metadata, tags)

  • Delete: Remove notes from the vault

Extended Functionality

  • Batch Operations: Create, update, or delete multiple notes at once

  • Template System: Create and use note templates with variables

  • Link Analysis: Analyze relationships between notes

  • Search & Filter: Advanced search by content, tags, date range, word count

  • Vault Statistics: Generate comprehensive vault analytics

  • Backup Management: Create and manage vault backups

MCP Server Integration

  • Full MCP protocol support for AI assistant integration

  • Async/await support for high performance

  • Comprehensive error handling and logging

  • Rate limiting and connection management

Related MCP server: Advanced Obsidian MCP Server

Installation

Prerequisites

  1. Obsidian with the obsidian-local-rest-api plugin installed and configured

  2. Python 3.10+

The easiest way to use ObsidianReaderMCP is with uvx, which allows you to run it without installation:

# Run directly without installation
uvx obsidianreadermcp

# Or install as a tool
uv tool install obsidianreadermcp
obsidianreadermcp

Method 2: Using pip

# Install from PyPI
pip install obsidianreadermcp

# Run the server
obsidianreadermcp

Method 3: Install from Source

# Clone the repository
git clone https://github.com/QianJue-CN/ObsidianReaderMCP.git
cd ObsidianReaderMCP

# Install dependencies
uv sync

# Or with pip
pip install -e .

Configuration

Environment Variables

Create a .env file in the project root (copy from .env.example):

# Obsidian API Configuration
OBSIDIAN_HOST=localhost
OBSIDIAN_PORT=27123
OBSIDIAN_API_KEY=your_api_key_here
OBSIDIAN_USE_HTTPS=false
OBSIDIAN_TIMEOUT=30
OBSIDIAN_MAX_RETRIES=3
OBSIDIAN_RATE_LIMIT=10

# MCP Server Configuration
LOG_LEVEL=INFO
ENABLE_DEBUG=false

Obsidian Setup

  1. Install the obsidian-local-rest-api plugin from the Community Plugins

  2. Enable the plugin in Obsidian settings

  3. Configure the plugin:

    • Set API port (default: 27123)

    • Generate an API key

    • Enable CORS if needed

  4. Start the local REST API server

Usage

As a Python Library

import asyncio
from obsidianreadermcp import ObsidianClient
from obsidianreadermcp.config import ObsidianConfig
from obsidianreadermcp.models import NoteMetadata

async def main():
    # Create configuration
    config = ObsidianConfig()  # Uses environment variables

    # Create and connect client
    async with ObsidianClient(config) as client:
        # Create a note
        metadata = NoteMetadata(
            tags=["example", "demo"],
            frontmatter={"title": "My Note", "author": "Me"}
        )

        note = await client.create_note(
            path="my_note.md",
            content="# My Note\n\nThis is my note content.",
            metadata=metadata
        )

        # Read the note
        retrieved_note = await client.get_note("my_note.md")
        print(f"Note content: {retrieved_note.content}")

        # Update the note
        await client.update_note(
            path="my_note.md",
            content="# Updated Note\n\nThis content has been updated."
        )

        # Search notes
        results = await client.search_notes("updated")
        print(f"Found {len(results)} matching notes")

        # Delete the note
        await client.delete_note("my_note.md")

asyncio.run(main())

As an MCP Server

# Method 1: Using uvx (recommended)
uvx obsidianreadermcp

# Method 2: Using installed package
obsidianreadermcp

# Method 3: Using Python module
python -m obsidianreadermcp.server

# Method 4: Programmatically
python -c "
import asyncio
from obsidianreadermcp.server import main
asyncio.run(main())
"

Claude Desktop Integration

Add to your Claude Desktop configuration file:

{
  "mcpServers": {
    "obsidian": {
      "command": "uvx",
      "args": ["obsidianreadermcp"],
      "env": {
        "OBSIDIAN_HOST": "localhost",
        "OBSIDIAN_PORT": "27123",
        "OBSIDIAN_API_KEY": "your-api-key-here"
      }
    }
  }
}

Or if you have it installed globally:

{
  "mcpServers": {
    "obsidian": {
      "command": "obsidianreadermcp",
      "env": {
        "OBSIDIAN_HOST": "localhost",
        "OBSIDIAN_PORT": "27123",
        "OBSIDIAN_API_KEY": "your-api-key-here"
      }
    }
  }
}

Extended Features

from obsidianreadermcp.extensions import ObsidianExtensions

async with ObsidianClient(config) as client:
    extensions = ObsidianExtensions(client)

    # Create a template
    template = extensions.create_template(
        name="daily_note",
        content="# {{date}}\n\n## Tasks\n- {{task}}\n\n## Notes\n{{notes}}",
        description="Daily note template"
    )

    # Create note from template
    note = await extensions.create_note_from_template(
        template_name="daily_note",
        path="daily/2024-01-15.md",
        variables={
            "date": "2024-01-15",
            "task": "Review project status",
            "notes": "All systems operational"
        }
    )

    # Batch operations
    batch_notes = [
        {"path": "note1.md", "content": "Content 1", "tags": ["batch"]},
        {"path": "note2.md", "content": "Content 2", "tags": ["batch"]},
    ]
    result = await extensions.batch_create_notes(batch_notes)

    # Analyze vault
    stats = await extensions.generate_vault_stats()
    print(f"Vault has {stats.total_notes} notes with {stats.total_words} words")

    # Find orphaned notes
    orphaned = await extensions.find_orphaned_notes()
    print(f"Found {len(orphaned)} orphaned notes")

MCP Tools

When running as an MCP server, the following tools are available:

Core Operations

  • create_note: Create a new note with content and metadata

  • get_note: Retrieve a note by path

  • update_note: Update an existing note

  • delete_note: Delete a note

  • list_notes: List all notes in vault or folder

  • search_notes: Search notes by content

Vault Management

  • get_vault_info: Get vault information and statistics

  • get_tags: List all tags used in the vault

  • get_notes_by_tag: Find notes with specific tags

API Reference

ObsidianClient

The main client class for interacting with Obsidian.

Methods

  • async create_note(path: str, content: str = "", metadata: Optional[NoteMetadata] = None) -> Note

  • async get_note(path: str) -> Note

  • async update_note(path: str, content: Optional[str] = None, metadata: Optional[NoteMetadata] = None) -> Note

  • async delete_note(path: str) -> bool

  • async list_notes(folder: str = "") -> List[str]

  • async search_notes(query: str, limit: int = 50, context_length: int = 100) -> List[SearchResult]

  • async get_vault_info() -> VaultInfo

  • async get_tags() -> List[str]

  • async get_notes_by_tag(tag: str) -> List[Note]

ObsidianExtensions

Extended functionality for advanced vault management.

Methods

  • async batch_create_notes(notes_data: List[Dict], continue_on_error: bool = True) -> Dict

  • async batch_update_notes(updates: List[Dict], continue_on_error: bool = True) -> Dict

  • async batch_delete_notes(paths: List[str], continue_on_error: bool = True) -> Dict

  • create_template(name: str, content: str, variables: Optional[List[str]] = None, description: Optional[str] = None) -> Template

  • async create_note_from_template(template_name: str, path: str, variables: Optional[Dict[str, str]] = None, metadata: Optional[NoteMetadata] = None) -> Note

  • async create_backup(backup_path: str, include_attachments: bool = True) -> BackupInfo

  • async analyze_links() -> List[LinkInfo]

  • async find_orphaned_notes() -> List[str]

  • async find_broken_links() -> List[LinkInfo]

  • async generate_vault_stats() -> VaultStats

  • async search_by_date_range(start_date: Optional[datetime] = None, end_date: Optional[datetime] = None, date_field: str = "created") -> List[Note]

  • async search_by_word_count(min_words: Optional[int] = None, max_words: Optional[int] = None) -> List[Note]

Testing

Run the test suite:

# With uv
uv run pytest

# With pip
pytest

# With coverage
pytest --cov=obsidianreadermcp --cov-report=html

Examples

See the examples/ directory for more detailed usage examples:

  • basic_usage.py: Demonstrates core CRUD operations

  • advanced_features.py: Shows extended functionality

  • mcp_integration.py: MCP server integration examples

Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

  1. Fork the repository

  2. Create a feature branch (git checkout -b feature/amazing-feature)

  3. Make your changes

  4. Add tests for new functionality

  5. Run the test suite (uv run pytest)

  6. Commit your changes (git commit -m 'Add some amazing feature')

  7. Push to the branch (git push origin feature/amazing-feature)

  8. Open a Pull Request

Issues and Support

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Star History

Star History Chart

Related MCP Connectors

Related MCP Servers