Skip to main content
Glama
clotho2

Notebook Library MCP Server

by clotho2
README.md
# Notebook Library MCP Server

Token-efficient document retrieval for substrate AI agents. Drop PDFs, text files, and markdown into notebook folders — they get chunked, embedded, and indexed for semantic search. Queries return only the most relevant passages (~2,500 tokens) instead of loading entire documents (50,000+).

## What It Does

Your AI agent gets a `notebook_library` tool with these actions:

| Action | Description |
|--------|-------------|
| `list_notebooks` | See all available notebooks |
| `create_notebook` | Create a new notebook collection |
| `query_notebook` | Semantic search within a notebook (the main one!) |
| `browse_notebook` | List documents in a notebook |
| `read_document` | Deep-read a specific document chunk by chunk |
| `notebook_stats` | Get statistics about a notebook |
| `sync_notebook` | Re-sync after adding/changing files |
| `remove_document` | Remove a document from the search index |

**Supported file formats:** `.pdf`, `.txt`, `.md`, `.text`, `.markdown`

## Architecture

```
data/
├── notebooks/               # Your document folders
│   ├── Research_Papers/     # Each subfolder = one notebook
│   │   ├── paper1.pdf
│   │   └── notes.md
│   └── Business_Docs/
│       └── plan.txt
└── notebook_chromadb/       # Vector database (auto-created)
    └── manifests/           # File change tracking

mcp_servers/
└── notebook_library/
    ├── server.py              # MCP server (if running standalone)
    ├── notebook_manager.py    # Core: ChromaDB ingestion + search
    ├── document_processor.py  # Text extraction + chunking
    ├── file_watcher.py        # Auto-ingestion on file changes
    └── requirements.txt

backend/tools/
├── notebook_library_tool.py        # Tool wrapper for consciousness loop
└── notebook_library_tool_schema.json  # Tool schema definition
```

**Embedding strategy (multi-tier fallback):**
1. **Hugging Face** (`jinaai/jina-embeddings-v2-base-de`) — local, free, multilingual
2. **Ollama** (`nomic-embed-text`) — local fallback if HF fails

No external API keys needed. Everything runs locally.

## Setup Guide

### 1. Install Dependencies

From your substrate root:

```bash
pip install -r mcp_servers/notebook_library/requirements.txt
```

Key dependencies:
- `chromadb==0.4.18` — vector database
- `transformers` + `torch` — Hugging Face embeddings (primary)
- `ollama` — embedding fallback
- `PyMuPDF` — PDF text extraction
- `watchdog` — file system monitoring

> **Note:** First run will download the Hugging Face embedding model (~270MB). This is a one-time download.

### 2. Create Data Directories

```bash
mkdir -p data/notebooks
mkdir -p data/notebook_chromadb
```

### 3. Copy the MCP Server Files

Copy the entire `mcp_servers/notebook_library/` directory into your substrate:

```
your_substrate/
└── mcp_servers/
    └── notebook_library/
        ├── __init__.py
        ├── server.py
        ├── notebook_manager.py
        ├── document_processor.py
        ├── file_watcher.py
        └── requirements.txt
```

### 4. Copy the Tool Wrapper

Copy these two files into your `backend/tools/` directory:

**`backend/tools/notebook_library_tool.py`** — The tool function your consciousness loop calls. This imports `NotebookManager` directly (no subprocess).

**`backend/tools/notebook_library_tool_schema.json`** — The tool schema so your agent knows how to call it.

### 5. Register the Tool in Your Consciousness Loop

Three integration points:

#### a) Import in `integration_tools.py`

Add to your imports:

```python
from tools.notebook_library_tool import notebook_library_tool as _notebook_library_tool
```

Add the wrapper method to your `IntegrationTools` class:

```python
def notebook_library(self, **kwargs) -> Dict[str, Any]:
    """
    Notebook Library — token-efficient document retrieval.
    """
    try:
        result = _notebook_library_tool(**kwargs)
        return result
    except Exception as e:
        return {
            "status": "error",
            "message": f"Notebook library error: {str(e)}"
        }
```

Add `'notebook_library_tool'` to your tool schema loading list so the JSON schema gets picked up.

#### b) Add tool call handler in `consciousness_loop.py`

In your tool execution block (where you handle `elif tool_name == "..."` cases), add:

```python
elif tool_name == "notebook_library":
    result = self.tools.notebook_library(**arguments)
```

#### c) Verify schema loading

The tool schema file (`notebook_library_tool_schema.json`) must be in `backend/tools/` alongside your other tool schemas. The schema loader should pick it up automatically if it follows the same pattern as your other tools.

### 6. Add Documents

Create notebook folders and drop files in:

```bash
mkdir -p data/notebooks/My_Research
cp ~/some_paper.pdf data/notebooks/My_Research/
cp ~/notes.md data/notebooks/My_Research/
```

Documents are auto-ingested when your agent first queries the notebook, or you can trigger a manual sync via the `sync_notebook` action.

## Environment Variables (Optional)

All have sensible defaults. Override only if needed:

| Variable | Default | Description |
|----------|---------|-------------|
| `NOTEBOOK_LIBRARY_PATH` | `data/notebooks` | Where notebook folders live |
| `NOTEBOOK_CHROMADB_PATH` | `data/notebook_chromadb` | Vector database storage |
| `OLLAMA_BASE_URL` | `http://192.168.2.175:11434` | Ollama server (fallback embeddings) |
| `OLLAMA_EMBEDDING_MODEL` | `nomic-embed-text` | Ollama model name |
| `NOTEBOOK_CHUNK_SIZE` | `2000` | Characters per chunk |
| `NOTEBOOK_CHUNK_OVERLAP` | `200` | Overlap between chunks |

**Important:** Update `OLLAMA_BASE_URL` to point to your own Ollama instance if you're using the Ollama fallback. The default points to the original developer's local network.

## How It Works

1. **Ingestion:** Documents are split into chunks (~2000 chars each with 200 char overlap), embedded using Hugging Face or Ollama, and stored in ChromaDB collections (one per notebook).

2. **Querying:** Your agent's query gets embedded with the same model, then ChromaDB finds the most similar chunks via cosine similarity. Only the top N passages are returned (default 5).

3. **File tracking:** A manifest system (MD5 hashes) tracks which files have been ingested. Changed files get re-processed; unchanged files are skipped.

4. **File watching:** A watchdog-based file watcher monitors notebook folders and auto-ingests new/modified files with a 2-second debounce.

## Example Agent Usage

Once integrated, your agent can use it like:

```
# List what's available
notebook_library(action="list_notebooks")

# Search for something specific
notebook_library(action="query_notebook", notebook="Research_Papers", query="transformer attention mechanisms")

# Browse a notebook's contents
notebook_library(action="browse_notebook", notebook="Research_Papers")

# Deep-read a specific document
notebook_library(action="read_document", notebook="Research_Papers", filename="paper1.pdf")

# Create a new notebook
notebook_library(action="create_notebook", name="Meeting_Notes", description="Weekly team meetings")
```

## Troubleshooting

**"No notebooks found"** — Make sure `data/notebooks/` exists and has at least one subfolder with files in it.

**Slow first query** — The first query to a notebook triggers ingestion (chunking + embedding all documents). Subsequent queries are fast. For large collections, run `sync_notebook` first.

**Embedding model download** — First run downloads the Jina embeddings model (~270MB). If this fails behind a firewall, the system falls back to Ollama. Make sure either HF model access or an Ollama instance is available.

**ChromaDB version mismatch** — Pin to `chromadb==0.4.18`. Newer versions may have breaking API changes.

**OLLAMA_BASE_URL** — If you see Ollama connection errors and you're not using Ollama, that's fine — it's just the fallback failing after HF already succeeded. If HF also fails, update this URL to your Ollama instance.