Skip to main content
Glama

Deeplore

Self-hosted RAG-powered knowledge base built for depth, served over MCP.

Keeping a knowledge base current and having it plug cleanly into any AI-powered system is harder than it should be. Deeplore is an attempt to bridge that gap.

It runs an optimized RAG pipeline over structured notes and transcripts to surface the most relevant context, and exposes each project as a native MCP tool. Feed it expert talks, seminar series, or your own notes. Once indexed, answers come from what the source actually says, not what an LLM approximates from training data.

See the architecture.


Quick Start

git clone https://github.com/bholagabbar/deeplore.git
cd deeplore
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

cp .env.example .env
# Fill in .env:
#   OPENROUTER_API_KEY=sk-or-...      required — LLM for notes generation + answers
#   APIFY_API_TOKEN=...               required for YouTube ingest (free tier at apify.com)
#   DEEPLORE_API_KEY=your-secret      recommended — bearer token for the MCP server

The repo ships with Homer's Iliad (Alexander Schmid lecture series) pre-configured as the example collection. To index and run it:

# 1. Fetch transcripts + generate structured notes for each lecture (~$0.22 via Apify)
python scripts/ingest_youtube.py \
  --playlist "https://www.youtube.com/playlist?list=PLKdLUOouUfd5No6YtRq5X6IbrM1q8odKN" \
  --collection iliad \
  --series "Iliad"

# 2. Build the indexes (summary, hierarchical, sentence-window)
python scripts/ingest.py --collection iliad

# 3. Start the MCP server
docker compose up          # includes reranker sidecar (recommended)
# or: python mcp/deeplore_server.py

The server is now running on port 5673. GET /health returns status and available projects. POST /mcp/ is the MCP endpoint.

Once running, check health and query via CLI:

curl -s http://localhost:5673/health
# {"status":"ok","collections":["iliad"],"auth":false}

# Using mcpc (npm install -g @apify/mcpc) — universal CLI MCP client
mcpc connect -H "Authorization: Bearer your-secret" "http://localhost:5673/mcp/" @deeplore
mcpc @deeplore tools-list
mcpc @deeplore tools-call ask collection:=iliad question:="What is the significance of Achilles' withdrawal from battle?"
Achilles' withdrawal is the structural engine of the entire poem. Menis, the
opening word, signals this from line one: not ordinary anger, but a divine,
consuming wrath with consequences that cascade outward. The quarrel with
Agamemnon is a rupture in the honor economy that holds the Greek coalition
together. Agamemnon takes Briseis, the material proof of Achilles' valor, and
Achilles responds by removing the one force the Greeks cannot replace. What
follows is not a war with a gap in it. It is a war that bleeds at the gap.
Every Greek death after Book I carries his fingerprints.

Sources: [Book I (menis, quarrel with Agamemnon), Books VIII-XII (Hector's aristeia)]

Related MCP server: knowledgine

Projects

A project is a folder of source documents plus a config file in projects/. The config defines where data lives, how retrieval is tuned, and what persona the LLM uses when composing answers.

projects/
  iliad.yaml       # tracked — the example collection
  aeneid.yaml      # your personal projects, gitignored
  ...

projects/*.yaml is gitignored except for iliad.yaml. Put your personal project configs there and they will never be committed.

Config fields (projects/iliad.yaml):

name: iliad
display_name: "Homer's Iliad: Alexander Schmid Lecture Series"

data_dir: data/iliad/raw        # where raw JSON source files live
storage_dir: storage/iliad      # where built indexes are persisted

# Retrieval tuning
chunk_sizes: [2048, 512, 128]   # hierarchical levels: lecture, section, sentence
sentence_window_size: 3          # context window around transcript chunks
similarity_top_k: 8              # vector search candidates
rerank_top_n: 4                  # kept after cross-encoder reranking

# Models
embedding_model: text-embedding-3-large
llm_answer_model: anthropic/claude-sonnet-4-5

# MCP
mcp_tool_name: ask_iliad

# Persona — shapes how the LLM composes the final answer
persona: |
  You are an expert on Homer's Iliad and this lecture series.
  Ground every answer in the lectures. Make the epic feel alive.

To add a new project:

# 1. Copy the example config and edit it
cp projects/iliad.yaml projects/my_collection.yaml
# edit: name, display_name, data_dir, storage_dir, mcp_tool_name, persona

# 2. Ingest a YouTube playlist
python scripts/ingest_youtube.py \
  --playlist "YOUR_PLAYLIST_URL" \
  --collection my_collection \
  --series "My Series"

# 3. Build indexes
python scripts/ingest.py --collection my_collection

# 4. Restart the server — new tool is available immediately

Cost: ~$0.007/video via Apify. A 30-lecture series is under $0.25.

To update when new videos are added to a playlist:

# Re-run both steps — the ingestor skips videos already fetched
python scripts/ingest_youtube.py --playlist URL --collection my_collection --series "My Series"
python scripts/ingest.py --collection my_collection

MCP Integration

Deeplore runs as an HTTP server using the Streamable HTTP transport (MCP spec 2025-03-26). Every indexed project is exposed as a named tool (mcp_tool_name in the config). The server auto-discovers all configured projects on startup.

# With Docker (recommended — includes reranker sidecar)
docker compose up

# Note: on first run the reranker container downloads the cross-encoder model from HuggingFace.
# If it fails with "Header etag is missing" (HF rate limit), pre-download and seed the volume:
#   python3 -c "from huggingface_hub import snapshot_download; snapshot_download('cross-encoder/ms-marco-MiniLM-L-6-v2', cache_dir='/tmp/hf-cache')"
#   docker run --rm -v deeplore_reranker-cache:/data -v /tmp/hf-cache:/src alpine cp -r /src/models--cross-encoder--ms-marco-MiniLM-L-6-v2 /data/
# Then re-run docker compose up.

# Without Docker
OPENROUTER_API_KEY=sk-or-... DEEPLORE_API_KEY=your-secret python mcp/deeplore_server.py

Endpoints:

GET  /health   server status + available projects (no auth)
POST /mcp/     MCP Streamable HTTP (bearer token auth)

Client config:

{
  "mcpServers": {
    "deeplore": {
      "url": "https://your-host/mcp/",
      "headers": { "Authorization": "Bearer your-secret" }
    }
  }
}

Tools available after indexing:

ask(collection, question)   query any project by name
list_projects()             list all indexed projects

Architecture

Ingest

  Source (YouTube playlist, Granola, PDF, ...)
    │
    ├── Structured notes (LLM-generated per lecture)
    │     └── Summary index + Hierarchical index (2048 / 512 / 128 token levels)
    │
    └── Verbatim transcript
          └── Sentence-window index (chunk + ±3 sentence context stored as metadata)

Query

  Query
    │
    ├── 1. Theme retrieval      which lectures are relevant? (document summaries)
    │
    ├── 2. Section retrieval    what does the source say? (hierarchical, auto-merging)
    │        └── if multiple child chunks from the same section fire, promote to full section
    │
    └── 3. Verbatim retrieval   exact words? (sentence window from transcript)
             └── returns matched chunk + surrounding context, not just the fragment

  All results → cross-encoder reranker → composed answer (LLM + project persona) → MCP tool

Most RAG systems split documents into chunks and retrieve the closest matches. This works for factual lookup. It fails for learning material, where the answer lives across an argument that spans a full lecture.

Deeplore runs three retrieval layers in parallel and fuses the results. Theme retrieval uses document-level summaries to identify which lectures are relevant before touching any chunk, eliminating the core failure mode of naive RAG: fragments retrieved from the wrong source. Section retrieval uses hierarchical chunking with auto-merging: when multiple sentence-level chunks from the same section score well, the retriever promotes to the full section, preserving the argument not just the fragment. Verbatim retrieval indexes transcripts separately from notes and stores a sentence window around each chunk, so the answer has the speaker's actual words with surrounding context, not an isolated sentence.

All candidates are then reranked by a cross-encoder, which scores each passage against the actual query directly. Vector cosine similarity is fast but blunt. Cross-encoder reranking is surgical.

Why separate notes and transcripts?

Notes and transcripts serve different retrieval purposes. Notes are structured: they contain the argument, the key thinkers, the synthesis. Transcripts are raw: they contain the exact phrasing, the analogy the speaker reached for, the moment the argument lands. Indexing them separately means the retriever can find the structured argument from notes and the verbatim confirmation from the transcript, then compose them together. Indexing them jointly degrades both.

Why a document summary index?

Without it, a query about "the role of the gods" might retrieve fragments from 12 different lectures with no coherent signal about which ones actually develop that argument. The summary index acts as a coarse filter, scoring each lecture against the query at the topic level before any chunk-level retrieval happens. This alone eliminates most hallucination about lecture content.

Why hierarchical chunking with auto-merging?

Small chunks improve recall (they match specific phrases) but destroy coherence (the surrounding argument is gone). Large chunks improve coherence but reduce recall. The hierarchical approach gets both: retrieve at the sentence level for precision, then promote to the section level when the evidence clusters. The AutoMergingRetriever does this automatically based on a threshold of child nodes firing from the same parent.

Why sentence window instead of raw transcript chunks?

If you embed and retrieve a single sentence from a transcript, you get the sentence with no context. The sentence window stores the surrounding context as metadata and swaps it in at retrieval time. The embedding is still precise (single sentence). The answer has context (the surrounding window).

Why cross-encoder reranking?

Embedding models produce vectors that capture general semantic similarity. A cross-encoder reads the query and the retrieved passage together and produces a relevance score specific to that pair. It is much slower (runs only on the top-K candidates, not the full index) but significantly more accurate for distinguishing "this passage is about Achilles' rage in the context of honor" from "this passage mentions Achilles and anger in a different context."

Why two LLMs?

A cheap, fast model handles document summarization at ingest time, a batch operation that runs once. A stronger model handles final answer composition at query time, which requires real reasoning over the retrieved context. The split keeps per-query cost low without touching answer quality.


Eval

LLM-as-judge eval harness is included but still a work in progress. Each question is answered by deeplore and a baseline LLM with no context; a judge scores both on groundedness, specificity, and accuracy and picks a winner.

python scripts/eval.py --collection iliad --questions eval/iliad_questions.yaml
F
license - not found
-
quality - not tested
D
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bholagabbar/deeplore'

If you have feedback or need assistance with the MCP directory API, please join our Discord server