movq

librarian-mcp (2.0.0)

Published 2026-04-15 21:52:21 +00:00 by ahknight

Installation

pip install --index-url  --extra-index-url https://pypi.org/simple librarian-mcp

About this package

MCP server for document indexing and semantic search

Librarian MCP Server

A semantic knowledge graph server implementing the Model Context Protocol (MCP). Librarian transforms your markdown files into a persistent, queryable knowledge base with semantic annotations, full-text search, and graph-based context building.

Features

  • Semantic Annotations: Categorized observations and typed relations create a knowledge graph
  • Full-Text Search: SQLite FTS5 with BM25 ranking for fast, relevant searches
  • Multi-Vault Support: Manage multiple isolated knowledge bases
  • Real-Time Sync: File watcher automatically indexes external changes (with polling mode for containers)
  • MCP Resources: Notes exposed as standard MCP resources with URI-based navigation
  • MCP Tools: 15 tools for creating, editing, searching, and analyzing knowledge
  • MCP Prompts: 6 pre-built workflows for common knowledge management tasks
  • Local-First: All data stored in plain markdown files you own

Quick Start

Installation

Requires Python 3.12+ and uv.

# Install from the package registry
uv pip install librarian-mcp --index-url https://code.movq.us/api/packages/movq/pypi/simple/

# Or install with pip
pip install librarian-mcp --index-url https://code.movq.us/api/packages/movq/pypi/simple/

Development Setup

# Clone the repository
git clone https://code.movq.us/movq/librarian-mcp.git
cd librarian-mcp

# Install with dev dependencies
uv sync --extra dev

Initialize Your First Vault

# Initialize a vault from an existing directory of markdown files
uv run librarian init ~/Documents/notes --set-default

# Or create a new vault
mkdir -p ~/knowledge-base
uv run librarian init ~/knowledge-base --name main --set-default

Start the MCP Server

# Start with stdio transport (for Claude Desktop)
uv run librarian mcp

# Start with HTTP transport for network access
uv run librarian mcp --transport http --host 0.0.0.0 --port 4242

# Use polling watcher (required in containers with bind mounts)
uv run librarian mcp --poll-watcher

Configure Your MCP Client

Claude Desktop

Add to your Claude Desktop MCP settings (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "librarian": {
      "command": "uvx",
      "args": [
        "--index-url", "https://code.movq.us/api/packages/movq/pypi/simple/",
        "librarian-mcp",
        "mcp"
      ]
    }
  }
}

Usage

Creating Notes with Semantic Annotations

Librarian recognizes two types of semantic annotations in your markdown:

Observations

Categorized facts within notes:

## Observations
- [decision] Chose PostgreSQL for ACID compliance #database #architecture
- [requirement] Must support 10k concurrent users #scale
- [technique] Use connection pooling with pgbouncer #performance

Standard categories: idea, decision, fact, technique, requirement, question, insight

Relations

Directional links between notes using WikiLink syntax:

## Relations
- implements [[System Architecture]]
- requires [[Database Schema]]
- relates_to [[Performance Requirements]]

Standard relation types: relates_to, implements, requires, extends, part_of, contrasts_with, preceded_by, influenced_by

Using MCP Tools

Available Tools

Tool Purpose
ping Echo a message (diagnostic)
status Server status information (diagnostic)
write_note Create or update a markdown note
edit_note Modify existing notes (append, prepend, find_replace, replace_section)
delete_note Remove notes or directories
move_note Relocate notes while maintaining WikiLink references
read_note Read a note by title, permalink, or path
update_note_properties Update frontmatter properties on a note
get_note_properties Read frontmatter properties from a note
bulk_update_properties Update properties across multiple notes
build_context Graph traversal to gather related entities
recent_activity View recent changes across the vault
find_stale_notes Find notes not updated in N days
canvas_tool Create visual knowledge maps (Obsidian-compatible)
recall Search the knowledge base (full-text + semantic)
remember Store an observation in the memory layer
forget Remove a remembered observation
journal Create a write-only journal entry

Example: Creating a Note

// Via MCP tool call
{
  "tool": "write_note",
  "arguments": {
    "title": "API Design Decisions",
    "directory": "projects",
    "content": `# API Design Decisions

## Context
Evaluated options for the new service API.

## Observations
- [decision] REST over GraphQL for simplicity #api
- [requirement] Must support pagination #api
- [technique] Use OpenAPI 3.0 for documentation

## Relations
- implements [[System Architecture]]
- requires [[Authentication]]`
  }
}

Using MCP Resources

Notes are exposed as standard MCP Resources with URI-based navigation:

librarian://notes/{vault}/{permalink}           # Full note
librarian://notes/{vault}/{permalink}#Section   # Specific section
librarian://notes/{vault}/{permalink}?page=2    # Paginated

Example: Reading a Note Section

{
  "method": "resources/read",
  "params": {
    "uri": "librarian://notes/main/api-design#Observations"
  }
}

Using MCP Prompts

Prompts provide pre-built workflows for common tasks:

  1. restore - Restore session context from the knowledge base
  2. consolidate - Extract knowledge from current conversation
  3. done - End session: consolidate + append to journal
  4. search - Search with guided follow-up instructions
  5. connect - Find potential connections for a note (TF-IDF keyword extraction)
  6. review - Surface maintenance issues (orphaned notes, unresolved references, stale content)

Example: Using the Review Prompt

{
  "method": "prompts/get",
  "params": {
    "name": "review",
    "arguments": {
      "days_stale": 90
    }
  }
}

Configuration

Vault Management

# List all vaults
uv run librarian vault list

# Add a new vault
uv run librarian vault add work ~/work/knowledge-base --default

# Remove a vault (files are not deleted)
uv run librarian vault remove old-vault

# Get/set default vault
uv run librarian vault default           # Show current default
uv run librarian vault default main      # Set 'main' as default

# Re-index a vault
uv run librarian vault index             # Default vault
uv run librarian vault index main --force  # Full re-index

Configuration Files

Located at ~/.config/librarian/:

config.toml:

default_vault = "main"

vaults.toml:

[main]
path = "~/Documents/notes"

[work]
path = "~/work/knowledge-base"

Vault-Level Database

Each vault stores its index in {vault_path}/.librarian/librarian.db by default. Add this to your .gitignore:

.librarian/

The database is regenerated from markdown files, so it doesn't need to be version controlled.

Development

Setup

# Install development dependencies
uv sync --extra dev

# Run tests
uv run pytest

# Run tests with coverage
uv run pytest --cov=librarian --cov-report=term-missing

# Type checking
uv run basedpyright

# Linting and formatting
uv run ruff check src tests
uv run ruff format src tests

# Run pre-commit hooks on all files
uv run pre-commit run --all-files

Running Tests

The project has 1000+ tests covering:

  • Domain models (Note, Observation, Relation)
  • Database operations (CRUD, indexing, FTS5)
  • Search and graph traversal
  • MCP tools, resources, and prompts
  • MCP client integration tests (full protocol stack)
  • File watching and synchronization
  • Error handling and edge cases
# Run all tests
uv run pytest

# Run specific test file
uv run pytest tests/test_models.py

# Run with verbose output
uv run pytest -v

# Run with coverage
uv run pytest --cov=librarian --cov-report=html
open htmlcov/index.html

Project Structure

librarian/
├── src/librarian/
│   ├── models/              # Domain models (Note, Observation, Relation)
│   ├── database/            # SQLAlchemy ORM and repository
│   ├── indexer/             # File-to-database synchronization
│   ├── search/              # Full-text search and graph traversal
│   ├── markdown/            # Markdown parsing and formatting
│   ├── vault/               # Vault and note lifecycle management
│   ├── mcp/                 # MCP interface layer
│   │   ├── tools/           # Tool implementations
│   │   ├── resources.py     # Resource handlers
│   │   ├── prompts/         # Prompt implementations
│   │   └── templates/       # Handlebars templates for prompts
│   ├── watcher.py           # File system monitoring
│   ├── server.py            # FastMCP server setup
│   └── cli.py               # Command-line interface
├── container/               # Container deployment (Dockerfile, entrypoint)
├── tests/                   # Unit tests
│   ├── integration/         # MCP client integration tests
│   └── fixtures/            # Fixture markdown files for integration tests
├── docs/                    # Documentation
│   ├── librarian-spec.md    # Complete specification
│   └── PLAN.md              # Implementation plan
└── CLAUDE.md                # Development instructions

Architecture

Librarian follows a layered architecture with dependency injection:

  1. Domain Layer: Pure Python models (Note, Observation, Relation) that own their markdown syntax
  2. Database Layer: Repository pattern with SQLAlchemy ORM and FTS5 integration
  3. Indexer Layer: Bridges markdown files and database
  4. Vault Layer: Coordinates note lifecycle and cross-note operations
  5. Search Layer: Graph traversal and semantic search
  6. MCP Layer: Thin wrappers exposing domain layer via MCP protocol

This design enables:

  • Testability: All layers use dependency injection
  • Separation of Concerns: Each layer has a single responsibility
  • Maintainability: Clear boundaries between components

Container Deployment

Librarian can run in a container (Docker/Podman) with HTTP transport:

# Build the image
podman build -t librarian .

# Run with a bind-mounted vault
podman run -d -p 4242:4242 -v ~/Documents/notes:/app/data librarian

Or use compose.yaml:

services:
  librarian:
    build: .
    ports:
      - "4242:4242"
    volumes:
      - ~/Documents/Librarian/main:/app/data
    environment:
      LIBRARIAN_VAULT_NAME: main
      LIBRARIAN_VAULT_PATH: /app/data
      LIBRARIAN_PORT: "4242"
      LIBRARIAN_LOG_LEVEL: INFO
      # LIBRARIAN_POLL_WATCHER: "false"  # Disable polling (e.g., Linux host with local volumes)

The container entrypoint enables --poll-watcher by default because OS-native file watching (inotify) cannot see host filesystem changes through bind mounts on macOS. Set LIBRARIAN_POLL_WATCHER=false if running on a Linux host with local volumes where inotify works natively.

Troubleshooting

Server won't start

# Check if port is already in use (HTTP transport)
lsof -i :4242

# Verify vault configuration
uv run librarian vault list

# Check logs
uv run librarian mcp --log /tmp/librarian.log --loglevel DEBUG
tail -f /tmp/librarian.log

Notes not indexing

# Verify file permissions
ls -la ~/Documents/notes

# Check database exists
ls -la ~/Documents/notes/.librarian/

# Force re-index
uv run librarian vault index --force

# Or delete database and restart server
rm -rf ~/Documents/notes/.librarian/
uv run librarian mcp

File changes not detected in container

If running in a container and file changes aren't being picked up, ensure --poll-watcher is enabled (it is by default in the container entrypoint). Check container logs for "polling mode" in the watcher startup message.

Search not finding notes

  • Ensure notes have been indexed (check .librarian/librarian.db exists)
  • Verify FTS5 is enabled in your SQLite build: sqlite3 :memory: "pragma compile_options;"
  • Check that your search terms match note content, titles, or tags

Contributing

Contributions are welcome! Please:

  1. Read CLAUDE.md for development guidelines
  2. Follow the existing code style (enforced by pre-commit hooks)
  3. Add tests for new features
  4. Update documentation as needed
  5. Run the full test suite before submitting PRs

License

MIT License

Acknowledgments

Built with:

Requirements

Requires Python: >=3.12
Details
PyPI
2026-04-15 21:52:21 +00:00
0
Adam Knight
440 KiB
Assets (2)
Versions (5) View all
1.0.1 2026-04-15
2.0.0 2026-04-15
1.0.0b4 2026-02-15
1.0.0 2026-02-07
1.0.0b2 2026-02-07