mcpField
A
alterlab-open-notebook

alterlab-open-notebook

github.com/alterlab-ieuProductivity7866 installsUpdated 5w ago
Repository

About alterlab-open-notebook

alterlab-open-notebook is a skill for setting up and automating Open Notebook, a self-hosted research workspace for organizing sources and working with their content. It covers source-grounded chat, search, podcasts, transformations, and REST API workflows across configurable AI providers.

Key features

  1. 01Organize notebooks, sources, and notes
  2. 02Ingest documents, media, URLs, and text
  3. 03Chat with source context
  4. 04Search full text or semantically
  5. 05Generate multi-speaker podcasts
  6. 06Automate workflows through REST API

Use cases

  1. 01Create a private research workspace
  2. 02Search and discuss a personal research corpus
  3. 03Generate podcasts from notebook materials
  4. 04Automate source and note management

MCP support

A running Open Notebook instance can be accessed by MCP clients such as Claude Desktop and VS Code through the community `open-notebook-mcp` package. The README gives `uvx open-notebook-mcp` and the `OPEN_NOTEBOOK_URL` / `OPEN_NOTEBOOK_PASSWORD` settings.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/alterlab-ieu/alterlab-academic-skills --skill alterlab-open-notebook

After installing, type/alterlab-open-notebookin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

evals/
evals.json
references/
api_reference.md
architecture.md
configuration.md
examples.md
scripts/
chat_interaction.py
notebook_management.py
source_ingestion.py
test_open_notebook_skill.py

Details

From the project README

Open Notebook

Overview

Open Notebook is an open-source (MIT), self-hosted alternative to Google's NotebookLM that lets researchers organize materials, generate AI-powered insights, create podcasts, and hold context-aware conversations with their documents. It exposes a full REST API, works with 18+ AI providers through the Esperanto library, and runs entirely on your own infrastructure. This skill targets the 1.x line (v1.14 as of 2026-09; Docker image lfnovo/open_notebook:v1-latest).

Key advantages over NotebookLM:

  • Full REST API for programmatic access and automation
  • Choice of 18+ AI providers (not locked to Google models), including fully local Ollama / LM Studio
  • Multi-speaker podcast generation with 1-4 customizable speakers
  • Storage stays on your own machine or server
  • Open source and extensible (MIT license)

Data privacy: self-hosting keeps notebooks and files local, but source text is still sent to whichever cloud model provider you configure. For sensitive or IRB-restricted material, use a local provider (Ollama, LM Studio) or one covered by your data agreements.

Repository: https://github.com/lfnovo/open-notebook

When to Use This Skill

Use this skill when the user wants to:

  • Stand up a private, self-hosted NotebookLM-style research workspace
  • Ingest PDFs, URLs, audio/video, or text into notebooks and chat with them using source context
  • Search a personal research corpus with full-text or vector search
  • Generate multi-speaker podcasts or custom transformations (summaries, method extraction) from sources
  • Automate any of the above through the REST API
Does NOT Trigger
ScenarioUse Instead
One-shot conversion of a file to Markdown (no notebook, chat, or search)alterlab-markitdown
Located, cross-section Q&A inside a single PDFalterlab-pdf-explore
A one-row-per-paper evidence table across many PDFsalterlab-pdf-extract
Managing references, DOIs, or BibTeX in a Zotero libraryalterlab-pyzotero
Systematic searching and screening of new literaturealterlab-literature-review

Quick Start

Prerequisites
  • Docker with Docker Compose v2 (OrbStack or Docker Desktop both work; docker compose must be available)
  • API key for at least one AI provider (or local Ollama / LM Studio for free local inference)
Installation
# 1. Download the official compose file (SurrealDB v2 + lfnovo/open_notebook:v1-latest)
curl -o docker-compose.yml https://raw.githubusercontent.com/lfnovo/open-notebook/main/docker-compose.yml

# 2. Set the encryption key: the compose file hard-codes
#    OPEN_NOTEBOOK_ENCRYPTION_KEY=change-me-to-a-secret-string, so edit that line
#    (exporting a shell variable has no effect on this file)
sed -i.bak "s/change-me-to-a-secret-string/$(openssl rand -hex 32)/" docker-compose.yml

# 3. Launch the services
docker compose up -d

Access the application (allow 15-20 seconds for startup):

SurrealDB defaults to root:root and is bound to 127.0.0.1 only. Before exposing the instance beyond your machine, set SURREAL_USER / SURREAL_PASSWORD in a .env file next to the compose file, set OPEN_NOTEBOOK_PASSWORD, and put the UI/API behind HTTPS.

Configure AI Provider

In the UI, open Models, choose a provider, click + Add Configuration, paste the key, Test the connection, Sync Models, then use Auto-Assign Defaults under Default Model Assignments.

Or configure via the REST API:

import os
import requests

BASE_URL = "http://localhost:5055/api"
# Only needed when OPEN_NOTEBOOK_PASSWORD is set on the server
HEADERS = {"Authorization": f"Bearer {os.environ['OPEN_NOTEBOOK_PASSWORD']}"} if os.environ.get("OPEN_NOTEBOOK_PASSWORD") else {}

# Add a credential for an AI provider (stored encrypted with OPEN_NOTEBOOK_ENCRYPTION_KEY)
credential = requests.post(f"{BASE_URL}/credentials", headers=HEADERS, json={
    "provider": "openai",
    "name": "My OpenAI Key",
    "api_key": os.environ["OPENAI_API_KEY"],
}).json()

# Discover the provider's models: {"credential_id", "provider", "discovered": [{name, provider, model_type}, ...]}
discovered = requests.post(
    f"{BASE_URL}/credentials/{credential['id']}/discover", headers=HEADERS
).json()["discovered"]

# Register them (model_type: language, embedding, text_to_speech, speech_to_text)
requests.post(
    f"{BASE_URL}/credentials/{credential['id']}/register-models",
    headers=HEADERS,
    json={"models": [
        {"name": m["name"], "provider": m["provider"], "model_type": m["model_type"]}
        for m in discovered if m.get("model_type")
    ]},
).raise_for_status()

# Fill empty default slots (chat, transformation, large-context, embedding, TTS, STT, tools)
requests.post(f"{BASE_URL}/models/auto-assign", headers=HEADERS)

Core Features

The snippets below reuse BASE_URL and HEADERS from above.

Notebooks

Organize research into separate notebooks, each containing sources, notes, and chat sessions.

notebook = requests.post(f"{BASE_URL}/notebooks", headers=HEADERS, json={
    "name": "Cancer Genomics Research",
    "description": "Literature review on tumor mutational burden",
}).json()
notebook_id = notebook["id"]
Sources

Ingest PDFs, videos, audio, web pages, Office documents, or raw text. POST /api/sources takes multipart form fields; type is required (link, upload, or text), and embed="true" makes the source available to vector search.

# Add a web URL source (processed in the background)
source = requests.post(f"{BASE_URL}/sources", headers=HEADERS, data={
    "type": "link",
    "url": "https://arxiv.org/abs/2301.00001",
    "notebook_id": notebook_id,
    "embed": "true",
    "async_processing": "true",
}).json()

# Upload a PDF file
with open("paper.pdf", "rb") as f:
    upload = requests.post(
        f"{BASE_URL}/sources",
        headers=HEADERS,
        data={"type": "upload", "notebook_id": notebook_id, "embed": "true"},
        files={"file": ("paper.pdf", f, "application/pdf")},
    ).json()

# Poll background processing: status is queued / running / completed / failed
status = requests.get(f"{BASE_URL}/sources/{source['id']}/status", headers=HEADERS).json()
Notes

Create and manage notes (human or AI-generated) associated with notebooks.

requests.post(f"{BASE_URL}/notes", headers=HEADERS, json={
    "title": "Key Findings",
    "content": "TMB correlates with immunotherapy response in NSCLC...",
    "note_type": "human",
    "notebook_id": notebook_id,
})
Context-Aware Chat

Chat answers are grounded in the context you pass. Build it with /api/chat/context (an empty context_config includes every source and note in the notebook as short context; per-item values "full content", "insights", or "not in context" refine it), then send it with the message.

session = requests.post(f"{BASE_URL}/chat/sessions", headers=HEADERS, json={
    "notebook_id": notebook_id,
    "title": "TMB Discussion",
}).json()

ctx = requests.post(f"{BASE_URL}/chat/context", headers=HEADERS, json={
    "notebook_id": notebook_id,
    "context_config": {},   # or {"sources": {source_id: "full content"}, "notes": {...}}
}).json()

reply = requests.post(f"{BASE_URL}/chat/execute", headers=HEADERS, json={
    "session_id": session["id"],
    "message": "What are the key biomarkers for immunotherapy response?",
    "context": ctx["context"],
}).json()
print(reply["messages"][-1]["content"])   # messages: [{id, type: human|ai, content}, ...]
Search

Search across all materials using full-text ("text", default) or semantic ("vector", needs an embedding model) search, optionally scoped with notebook_id / notebook_ids.

results = requests.post(f"{BASE_URL}/search", headers=HEADERS, json={
    "query": "tumor mutational burden immunotherapy",
    "type": "vector",
    "limit": 10,
    "notebook_id": notebook_id,
}).json()   # {"results": [...], "total_count": n, "search_type": "vector"}

# Ask a question: the three model IDs are required (see GET /api/models or /api/models/defaults)
defaults = requests.get(f"{BASE_URL}/models/defaults", headers=HEADERS).json()
chat_model = defaults["default_chat_model"]
answer = requests.post(f"{BASE_URL}/search/ask/simple", headers=HEADERS, json={
    "question": "How does TMB predict checkpoint inhibitor response?",
    "strategy_model": chat_model,
    "answer_model": chat_model,
    "final_answer_model": chat_model,
    "notebook_id": notebook_id,
}).json()["answer"]

POST /api/search/ask takes the same body and streams Server-Sent Events.

Podcast Generation

Generate multi-speaker podcasts (1-4 speakers) from a notebook. Profiles are referenced by name: an episode profile (format, models, length) and a speaker profile (voices and personas).

episode_profile = requests.get(f"{BASE_URL}/episode-profiles", headers=HEADERS).json()[0]

job = requests.post(f"{BASE_URL}/podcasts/generate", headers=HEADERS, json={
    "episode_profile": episode_profile["name"],
    "speaker_profile": episode_profile["speaker_config_name"],   # or any name from /api/speaker-profiles
    "episode_name": "TMB and immunotherapy",
    "notebook_id": notebook_id,
}).json()

# Poll until the job reports "completed" (or "failed")
status = requests.get(f"{BASE_URL}/podcasts/jobs/{job['job_id']}", headers=HEADERS).json()["status"]

# Then locate the episode and download its audio
episodes = requests.get(f"{BASE_URL}/podcasts/episodes", headers=HEADERS).json()
episode = next(e for e in episodes if e["name"] == "TMB and immunotherapy")
audio = requests.get(f"{BASE_URL}/podcasts/episodes/{episode['id']}/audio", headers=HEADERS)
Content Transformations

Apply custom AI-powered transformations to content for summarization, extraction, and analysis.

transform = requests.post(f"{BASE_URL}/transformations", headers=HEADERS, json={
    "name": "extract_methods",
    "title": "Extract Methods",
    "description": "Extract methodology details from papers",
    "prompt": "Extract and summarize the methodology section...",
    "apply_default": False,
}).json()

result = requests.post(f"{BASE_URL}/transformations/execute", headers=HEADERS, json={
    "transformation_id": transform["id"],
    "input_text": "...",
    # "model_id": "model:...",   # optional; defaults to the transformation model
}).json()["output"]

Supported AI Providers

Open Notebook supports 18+ AI providers through the Esperanto library. A representative subset:

ProviderLLMEmbeddingSpeech-to-TextText-to-Speech
OpenAIYesYesYesYes
AnthropicYesNoNoNo
Google GenAIYesYesYesYes
Vertex AIYesYesNoYes
OllamaYesYesNoNo
GroqYesNoYesNo
MistralYesYesYesYes
Azure OpenAIYesYesYesYes
DeepSeekYesNoNoNo
xAIYesNoNoYes
OpenRouterYesYesYesYes
ElevenLabsNoNoYesYes
VoyageNoYesNoNo
OpenAI-compatible (LM Studio, vLLM, ...)YesYesYesYes

For the full, current matrix (including oMLX, Perplexity, Deepgram, Cohere, DashScope, MiniMax, Novita), see the upstream README.

Environment Variables

Key configuration variables for Docker deployment:

VariableDescriptionDefault
OPEN_NOTEBOOK_ENCRYPTION_KEYRequired. Encrypts stored provider credentials; keep it stable (changing or losing it makes saved credentials unreadable)None
OPEN_NOTEBOOK_PASSWORDOptional password; when set, API calls need Authorization: Bearer <password>None
SURREAL_URLSurrealDB connection URLws://surrealdb:8000/rpc
SURREAL_USER / SURREAL_PASSWORDSurrealDB credentials (override via .env before exposing the instance)root / root
SURREAL_NAMESPACEDatabase namespaceopen_notebook
SURREAL_DATABASEDatabase nameopen_notebook
OPEN_NOTEBOOK_WORKER_MAX_TASKSConcurrent background jobs; set 1 for a single local GPU5

API Reference

The REST API is available at http://localhost:5055/api, with interactive documentation at /docs.

Core endpoint groups:

  • /api/notebooks - Notebook CRUD, delete preview, and source association
  • /api/sources - Source ingestion, status, retry, insights, download
  • /api/notes - Note management
  • /api/chat/sessions, /api/chat/context, /api/chat/execute - Chat sessions, context building, messages
  • /api/search, /api/search/ask, /api/search/ask/simple - Full-text/vector search and question answering
  • /api/podcasts, /api/episode-profiles, /api/speaker-profiles - Podcast generation and profiles
  • /api/transformations - Content transformation pipelines
  • /api/models - Model registration, defaults, sync, auto-assign
  • /api/credentials - Provider credential management, discovery, registration

For request/response details see references/api_reference.md; runnable examples live in scripts/ and references/examples.md.

Architecture

Open Notebook uses a modern stack:

  • Backend: Python with FastAPI
  • Database: SurrealDB (document + relational)
  • AI Integration: LangChain / LangGraph with the Esperanto multi-provider library
  • Frontend: Next.js with React
  • Deployment: Docker Compose with persistent volumes (./notebook_data, ./surreal_data)

Important Notes

  • Open Notebook is deployed with Docker (a from-source setup exists for development)
  • At least one AI provider must be configured for AI features; vector search and Ask also need an embedding model
  • For free local inference without API costs, use Ollama or LM Studio
  • The OPEN_NOTEBOOK_ENCRYPTION_KEY must be set before first launch and kept consistent across restarts
  • Data lives in the bind-mounted notebook_data/ and surreal_data/ folders; back them up
  • MCP clients (Claude Desktop, VS Code) can reach a running instance through the community open-notebook-mcp package that the upstream docs reference (uvx open-notebook-mcp with OPEN_NOTEBOOK_URL / OPEN_NOTEBOOK_PASSWORD)

Part of the AlterLab Academic Skills suite.

Comments

Markdown supported