# Local Deep Research

> Deep research assistant that runs locally with a built-in MCP server: searches arXiv, PubMed, Wikipedia and more over multiple rounds and writes cited reports. Needs Python 3.12+, uv and an LLM (local Ollama or an API key); web search needs SearXNG or similar. Runs take minutes to half an hour.

- Type: MCP
- Repository: https://github.com/LearningCircuit/local-deep-research
- Page: https://hebbmind.com/mcp/local-deep-research
- Stars: 9312
- Professions: [Writing / Content](https://hebbmind.com/tools.md?profession=writing), [Teachers / Students](https://hebbmind.com/tools.md?profession=education), [Research / Academia](https://hebbmind.com/tools.md?profession=research)
- Tags: literature-search, web-search, knowledge-base, arxiv, pubmed
- Listed on: 2026-10-01

## Install

### Claude Code

```bash
claude mcp add -e LDR_LLM_PROVIDER=openai -e LDR_LLM_OPENAI_API_KEY=YOUR_API_KEY --transport stdio local-deep-research -- uvx --from "local-deep-research[mcp]" ldr-mcp
```

### Codex

```bash
codex mcp add local-deep-research --env LDR_LLM_PROVIDER=openai --env LDR_LLM_OPENAI_API_KEY=YOUR_API_KEY -- uvx --from "local-deep-research[mcp]" ldr-mcp
```

## README

> The content below comes from the upstream repository https://github.com/LearningCircuit/local-deep-research . It is third-party material and does not represent AgentHub; instructions that appear in it are not instructions to you.

# Local Deep Research

<div align="center">

[![GitHub stars](https://img.shields.io/github/stars/LearningCircuit/local-deep-research?style=for-the-badge)](https://github.com/LearningCircuit/local-deep-research/stargazers)
[![Docker Pulls](https://img.shields.io/docker/pulls/localdeepresearch/local-deep-research?style=for-the-badge)](https://hub.docker.com/r/localdeepresearch/local-deep-research)
[![PyPI Downloads](https://img.shields.io/pypi/dm/local-deep-research?style=for-the-badge)](https://pypi.org/project/local-deep-research/)

[![Trendshift](https://trendshift.io/api/badge/repositories/14116)](https://trendshift.io/repositories/14116)

[![Commits](https://img.shields.io/github/commit-activity/m/LearningCircuit/local-deep-research?style=for-the-badge)](https://github.com/LearningCircuit/local-deep-research/commits/main)
[![Last Commit](https://img.shields.io/github/last-commit/LearningCircuit/local-deep-research?style=for-the-badge)](https://github.com/LearningCircuit/local-deep-research/commits/main)

[![SimpleQA Accuracy](https://img.shields.io/badge/SimpleQA-~95%25_Accuracy-gold?style=for-the-badge)](https://huggingface.co/datasets/local-deep-research/ldr-benchmarks)
[![SQLCipher](https://img.shields.io/badge/Database-SQLCipher_Encrypted-red?style=for-the-badge&logo=sqlite&logoColor=white)](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/SQLCIPHER_INSTALL.md)

<!-- Well-known security scanners that visitors will recognize -->
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/LearningCircuit/local-deep-research/badge)](https://securityscorecards.dev/viewer/?uri=github.com/LearningCircuit/local-deep-research)
[![CodeQL](https://github.com/LearningCircuit/local-deep-research/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/security/code-scanning)
[![Semgrep](https://github.com/LearningCircuit/local-deep-research/actions/workflows/semgrep.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/semgrep.yml)

[![🔧 Pre-commit](https://github.com/LearningCircuit/local-deep-research/actions/workflows/pre-commit.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/pre-commit.yml)

[![🐳 Docker Publish](https://github.com/LearningCircuit/local-deep-research/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/docker-publish.yml)
[![📦 PyPI Publish](https://github.com/LearningCircuit/local-deep-research/actions/workflows/publish.yml/badge.svg)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/publish.yml)

[![Discord](https://img.shields.io/discord/1352043059562680370?style=for-the-badge&logo=discord)](https://discord.gg/ttcqQeFcJ3)
[![Reddit](https://img.shields.io/badge/Reddit-r/LocalDeepResearch-FF4500?style=for-the-badge&logo=reddit)](https://www.reddit.com/r/LocalDeepResearch/)
[![YouTube](https://img.shields.io/badge/YouTube-Channel-red?style=for-the-badge&logo=youtube)](https://www.youtube.com/@local-deep-research)


**AI-powered research assistant for deep, agentic research**

*Performs deep, agentic research using multiple LLMs and search engines with proper citations*

🧪 **First open-source project — fully-local on a single RTX 3090 (Qwen3.6-27B) — to report ~95% SimpleQA (n=500) and 77% xbench-DeepSearch (n=100) on local hardware.** See the [r/LocalLLaMA announcement](https://www.reddit.com/r/LocalLLaMA/comments/1t1n6o8/we_are_finally_there_qwen3627b_agentic_search_957/) and the [benchmark dataset](https://huggingface.co/datasets/local-deep-research/ldr-benchmarks).

<a href="https://www.youtube.com/watch?v=pfxgLX-MxMY&t=1999">
  ▶️ Watch Review by The Art Of The Terminal
</a>

</div>

## 🚀 What is Local Deep Research?

AI research assistant you control. Run locally for privacy, use any LLM and build your own searchable knowledge base. You own your data and see exactly how it works.

## ⚡ Quick Start

**Option 1: Docker Run (Linux)**
```bash
# Step 1: Pull and run Ollama
docker run -d -p 11434:11434 --name ollama ollama/ollama
docker exec ollama ollama pull gpt-oss:20b

# Step 2: Pull and run SearXNG for optimal search results
docker run -d -p 8080:8080 --name searxng searxng/searxng

# Step 3: Pull and run Local Deep Research
# (the URL line pins SearXNG's address AND marks it operator-approved —
#  private/localhost engine URLs are otherwise blocked by default since
#  v1.10.3. The URL becomes read-only in the web UI; docs/SearXNG-Setup.md
#  lists the alternatives, e.g. an origin allowlist.)
docker run -d --network host \
  --name local-deep-research \
  --volume "deep-research:/data" \
  -e LDR_DATA_DIR=/data \
  -e LDR_SEARCH_ENGINE_WEB_SEARXNG_DEFAULT_PARAMS_INSTANCE_URL=http://localhost:8080 \
  localdeepresearch/local-deep-research
```

> **Mac / Windows / WSL2 users:** `--network host` only works on native Linux. On Docker Desktop it silently fails to publish port 5000 *and* leaves `localhost` pointing at the LDR container itself (so it can't reach Ollama/SearXNG). Use **Option 2** below, or see the [Windows/WSL2 FAQ entry](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/faq.md#port-5000-not-accessible-on-windows) for a working `docker run` recipe.

**Option 2: Docker Compose**

CPU-only (all platforms):
```bash
curl -O https://raw.githubusercontent.com/LearningCircuit/local-deep-research/main/docker-compose.yml && docker compose up -d
```

With NVIDIA GPU (Linux):
```bash
curl -O https://raw.githubusercontent.com/LearningCircuit/local-deep-research/main/docker-compose.yml && \
curl -O https://raw.githubusercontent.com/LearningCircuit/local-deep-research/main/docker-compose.gpu.override.yml && \
docker compose -f docker-compose.yml -f docker-compose.gpu.override.yml up -d
```

Open http://localhost:5000 after ~30 seconds. For GPU setup, environment variables, and more, see the [Docker Compose Guide](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/docker-compose-guide.md).

**Option 3: pip install**
```bash
pip install local-deep-research
python -m local_deep_research.web.app   # starts the web UI on http://localhost:5000
```
> You'll also need Ollama (or any OpenAI-compatible LLM endpoint) and SearXNG running — see the [pip install guide](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/install-pip.md) for the full recipe.
> Works on Windows, macOS, and Linux. SQLCipher encryption is included via pre-built wheels — no compilation needed.
> PDF export on Windows requires Pango ([setup guide](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html)).
> **Development fallback:** `LDR_BOOTSTRAP_ALLOW_UNENCRYPTED=true` permits plaintext SQLite only when SQLCipher is unavailable or nonfunctional; working SQLCipher still takes priority. The fallback does not verify account passwords and is unsuitable for shared deployments. See [fallback limitations](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/developing.md#unencrypted-development-fallback).

**Detailed install guides:** [Docker](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/installation.md#docker) · [Docker Compose](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/docker-compose-guide.md) · [pip](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/install-pip.md) · [Unraid](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/deployment/unraid.md) · [full install reference](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/installation.md)

> **Older CPU (x86-64)?** LDR needs an AVX-capable CPU — Intel Sandy Bridge / AMD Bulldozer (2011) or newer. Several scientific Python dependencies (pandas, scikit-learn) ship wheels that crash with `Illegal instruction` on older CPUs. ARM64 (aarch64) is fully supported. Every release is smoke-tested against this floor, including AVX-without-AVX2 CPUs ([#4480](https://github.com/LearningCircuit/local-deep-research/issues/4480)).

## 🏗️ How It Works

### Research

You ask a complex question. Local Deep Research (LDR):
- Does the research for you automatically
- Searches across web, academic papers, and your own documents
- Synthesizes everything into a report with proper citations

Choose the research strategy that fits: quick pipeline modes for fast facts, or fully agentic deep research for complex analysis and academic work.

**LangGraph Agent Strategy** — An autonomous agentic research mode where the LLM decides what to search, which specialized engines to use (arXiv, PubMed, Semantic Scholar, etc.), and when to synthesize. It adaptively switches between search engines based on what it finds and collects significantly more sources than pipeline-based strategies — this is the strategy behind the ~95% SimpleQA result above. Select `langgraph-agent` in Settings.

### Build Your Knowledge Base

```mermaid
flowchart LR
    R[Research] --> D[Download Sources]
    D --> L[(Library)]
    L --> I[Index & Embed]
    I --> S[Search Your Docs]
    S -.-> R
```

Every research session finds valuable sources. Download them directly into your encrypted library — academic papers from ArXiv, PubMed articles, web pages. LDR extracts text, indexes everything, and makes it searchable. Next time you research, ask questions across your own documents and the live web together. Your knowledge compounds over time.

## 🛡️ Security

<div align="center">

<!-- Static Analysis (additional scanners beyond CodeQL/Semgrep) -->
[![DevSkim](https://github.com/LearningCircuit/local-deep-research/actions/workflows/devskim.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/devskim.yml)
[![Bearer](https://github.com/LearningCircuit/local-deep-research/actions/workflows/bearer.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/bearer.yml)

<!-- Dependency & Secrets Scanning -->
[![OSV-Scanner](https://github.com/LearningCircuit/local-deep-research/actions/workflows/osv-scanner.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/osv-scanner.yml)
[![npm-audit](https://github.com/LearningCircuit/local-deep-research/actions/workflows/npm-audit.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/npm-audit.yml)
[![Retire.js](https://github.com/LearningCircuit/local-deep-research/actions/workflows/retirejs.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/retirejs.yml)

<!-- Container Security -->
[![Container Security](https://github.com/LearningCircuit/local-deep-research/actions/workflows/container-security.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/container-security.yml)
[![Dockle](https://github.com/LearningCircuit/local-deep-research/actions/workflows/dockle.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/dockle.yml)
[![Hadolint](https://github.com/LearningCircuit/local-deep-research/actions/workflows/hadolint.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/hadolint.yml)
[![Checkov](https://github.com/LearningCircuit/local-deep-research/actions/workflows/checkov.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/checkov.yml)

<!-- Workflow & Runtime Security -->
[![Zizmor](https://github.com/LearningCircuit/local-deep-research/actions/workflows/zizmor-security.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/zizmor-security.yml)
[![OWASP ZAP](https://github.com/LearningCircuit/local-deep-research/actions/workflows/owasp-zap-scan.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/owasp-zap-scan.yml)
[![Security Tests](https://github.com/LearningCircuit/local-deep-research/actions/workflows/security-tests.yml/badge.svg?branch=main)](https://github.com/LearningCircuit/local-deep-research/actions/workflows/security-tests.yml)

</div>

```mermaid
flowchart LR
    U1[User A] --> D1[(Encrypted DB)]
    U2[User B] --> D2[(Encrypted DB)]
```

Your data stays yours. Each user gets their own isolated SQLCipher database encrypted with AES-256, with the key derived from your password. Your password is never stored — login works by attempting to decrypt your database, so the database files on their own are unusable to anyone who obtains them. Per-user LLM API keys live encrypted inside the same personal database rather than in a shared server-level store.

The [Docker setup](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docker-compose.yml) ships with `cap_drop: ALL`, `no-new-privileges`, and a non-root runtime, with the bundled Ollama and SearXNG images pinned by digest. With a model running in the bundled Ollama, inference runs on your own hardware. LDR judges locality mainly by provider and endpoint address, not by where the model runs, so a local endpoint that relays to a hosted model (an Ollama cloud model, or a local LiteLLM or other OpenAI-compatible proxy in front of a hosted API) can still send your prompts off the machine without being detected. A self-hosted SearXNG still forwards your search queries to its upstream engines on the internet. To keep a run on private sources, search only local sources (collections, your library) under the *Private only* egress scope described below. That scope is a best-effort guard rail, not an air gap: see the Egress control section below and [SECURITY.md](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/SECURITY.md#egress-policy-module).

**In-memory credentials**: Like any application that uses secrets at runtime, credentials are held in process memory during active sessions — mitigated with session-scoped credential lifetimes and core dump exclusion. See the [Security Policy](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/SECURITY.md#in-memory-credentials) for the full threat model.

**Supply Chain Security**: Docker images are signed with [Cosign](https://github.com/sigstore/cosign) using GitHub's keyless OIDC flow, include SLSA provenance attestations, and ship with attested SPDX SBOMs. See [Verifying images and SBOMs](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/SECURITY.md#verifying-images-and-sboms) for the step-by-step verification commands.

**Security Transparency**: Scanner suppressions are documented with justifications in [Security Alerts Assessment](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/.github/SECURITY_ALERTS.md), [Scorecard Compliance](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/.github/SECURITY_SCORECARD.md), [Container CVE Suppressions](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/.trivyignore), and [SAST Rule Rationale](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/bearer.yml). Some alerts (Dependabot, code scanning) can only be dismissed or are very difficult to suppress outside the [GitHub Security tab](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/viewing-and-updating-dependabot-alerts), so the files above do not cover every dismissed finding.

[Detailed Architecture →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/architecture.md) | [Security Policy →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/SECURITY.md) | [Security Review Process →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/processes/security-review-process)

### 🌐 Egress control

Every research run has an **egress scope** that restricts, on a best-effort basis, where its traffic may go: which search engines run, whether the LLM and embeddings may be cloud services, and which result URLs may be fetched. Set it under *Settings → Egress Scope* or in the *Privacy & Egress* panel on the research form; choosing a scope in the panel also saves it as your default setting, so later runs use it too.

| Mode | Search sources | Inference |
|---|---|---|
| **Adaptive** *(default)* | follows your primary engine: a public engine behaves like *Public only*, a private collection or your library like *Private only*; a locally stored collection you marked public as primary allows any classified engine, private collections included | forced local only when the run is private |
| **Public only** | public web and academic engines; private collections and local document stores classified as private are excluded (a document store whose URL resolves to a public host, or does not resolve, is normally treated as public) | your configured providers; a document-store primary with a cloud model is normally refused at start for research run through the web research form (not for entry points that call the research functions directly, such as `/api/v1/quick_summary` or MCP) |
| **Private only** | local sources only (collections, library, local document stores such as Paperless) | **forced local**; cloud providers are normally refused |
| **Strict** | your single primary engine, no expansion | not forced local, but cloud models usually do not work under Strict (see the Strict section of [Egress modes](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/egress-modes.md)) |
| **Unprotected** | anything; unavailable unless the operator sets `LDR_POLICY_ALLOW_UNPROTECTED_EGRESS=true` | normally anything (an operator's environment lock on the require-local settings still applies) |

*Strict* and *Private only* (including an Adaptive run that resolves to it) also arm a best-effort socket-level guard on the research run that normally refuses connections to public addresses. It is a backstop with known gaps, not a guarantee; the Strict section of [Egress modes](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/egress-modes.md) lists some of them, such as a forward proxy or a relay on a private address. Under *Strict*, public engines, public result fetches and cloud model calls therefore usually fail, so pair it with a local engine and local models.

A locally-hosted SearXNG counts as a public engine — it proxies searches to the internet — so it is not available under *Private only*. Under *Strict* it may still run as your primary engine, and it then forwards your queries to its upstream engines on the internet.

LDR's guarded HTTP layer, used for most search-result and document fetches, also checks destinations against SSRF rules (including known cloud-metadata addresses such as `169.254.169.254`) and re-checks redirects. With JavaScript rendering enabled (`web.enable_javascript_rendering`, off by default), pages that need it are loaded in headless Chromium, which resolves addresses itself. See the [egress module README](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/src/local_deep_research/security/egress/README.md) for the details.

Whether a signed-in user can point a search engine at a private address depends on the kind of engine:

- **Public engines with a configurable address** (currently SearXNG): a private or loopback URL is normally refused unless the operator opts in through the environment (`LDR_SEARCH_ALLOW_PRIVATE_ENGINE_URLS=true`, an exact origin in `LDR_SEARCH_PRIVATE_ENGINE_URL_ALLOWLIST`, or pinning the URL itself with `LDR_SEARCH_ENGINE_WEB_SEARXNG_DEFAULT_PARAMS_INSTANCE_URL`). A SearXNG on `localhost` therefore normally needs one of these.
- **Local document-store engines** (Paperless-ngx and Elasticsearch): their address settings, `search.engine.web.paperless.default_params.api_url` and `search.engine.web.elasticsearch.default_params.hosts`, are meant to point at private hosts, so currently no opt-in applies: any signed-in user can set them to an internal address and the server will connect to it. If that is not acceptable, pin these settings with their environment variables (`LDR_` plus the key in upper case with dots replaced by underscores, for example `LDR_SEARCH_ENGINE_WEB_PAPERLESS_DEFAULT_PARAMS_API_URL`), or lock settings changes with `LDR_APP_LOCK_SETTINGS=true`.

The egress policy is an in-process guard rail against misconfiguration and prompt-injected fetches, not an air gap or a hard security boundary: for example, hostnames are still resolved through the configured DNS resolver before a fetch is refused, and it cannot reliably constrain code that runs inside the LDR process. For a hard boundary, add egress filtering at the container or firewall level as well.

[Egress modes →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/egress-modes.md) | [Threat model and caveats →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/SECURITY.md#egress-policy-module) | [Enforcement points →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/src/local_deep_research/security/egress/README.md)

### 🔒 Privacy & Data

Local Deep Research contains **no telemetry, no analytics, and no tracking**. We do not collect, transmit, or store any data about you or your usage. No analytics SDKs, no phone-home calls, no crash reporting, no external scripts. Usage metrics stay in your local encrypted database.

The only network calls LDR makes are ones **you** initiate: search queries (to engines you configure), LLM API calls (to your chosen provider), and notifications (only if you set up Apprise).

Since we don't collect any usage data, we rely on you to tell us what works, what's broken, and what you'd like to see next — [bug reports](https://github.com/LearningCircuit/local-deep-research/issues), feature ideas, and even which features you love or never use all help us improve LDR.

## 📊 Benchmarks

Headline results from the [community benchmarks](https://huggingface.co/datasets/local-deep-research/ldr-benchmarks) using the `langgraph-agent` strategy with Serper (cloud) search and the models running locally in Ollama:

| Model | SimpleQA | xbench-DeepSearch |
|---|---|---|
| Qwen3.6-27B | 95.7% (287/300) | 77.0% (77/100) |
| Qwen3.5-9B  | 91.2% (182/200) | 59.0% (59/100) |
| gpt-oss-20B | 85.4% (295/346) | – |

Caveats: small samples, LLM-grader noise, and SimpleQA contamination risk on newer base models.

Picking a local model? The same community-maintained dataset tracks accuracy across models, search engines, and research strategies — the fastest way to see which Ollama / LM Studio / llama.cpp models actually work well for deep research before you download multi-GB weights. **[Browse the full leaderboard on Hugging Face →](https://huggingface.co/datasets/local-deep-research/ldr-benchmarks)**

[Submit your own results →](https://github.com/LearningCircuit/ldr-benchmarks) (contributors are listed in [CONTRIBUTORS.md](https://github.com/LearningCircuit/ldr-benchmarks/blob/main/CONTRIBUTORS.md)), or [run benchmarks locally →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/BENCHMARKING.md).

## ✨ Key Features

### 🔍 Research Modes
- **Quick Summary** - Get answers in 30 seconds to 3 minutes with citations
- **Detailed Research** - Comprehensive analysis with structured findings
- **Report Generation** - Professional reports with sections and table of contents
- **Document Analysis** - Search your private documents with AI

### 🛠️ Advanced Capabilities
- **[LangChain Integration](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/LANGCHAIN_RETRIEVER_INTEGRATION.md)** - Use any vector store as a search engine
- **[REST API](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/api-quickstart.md)** - Authenticated HTTP access with per-user databases
- **[Benchmarking](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/BENCHMARKING.md)** - Test and optimize your configuration
- **[Analytics Dashboard](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/analytics-dashboard.md)** - Track costs, performance, and usage metrics
- **[Journal Quality System](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/journal-quality.md)** - Automatic journal reputation scoring with 212K+ indexed sources, predatory detection, and quality dashboard. Powered by [OpenAlex](https://openalex.org) (CC0), [DOAJ](https://doaj.org) (CC0), and [Stop Predatory Journals](https://predatoryjournals.org) (MIT). See the [v1.6.0 announcement](https://www.reddit.com/r/LocalDeepResearch/comments/1svndjb/v160_highly_anticipated_journal_quality_filter/).
- **Real-time Updates** - WebSocket support for live research progress
- **[Chat Mode](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/features.md#chat-mode)** - Multi-turn research conversations with streaming progress and accumulated context across turns
- **Export Options** - Download results as PDF or Markdown
- **Research History** - Save, search, and revisit past research
- **Adaptive Rate Limiting** - Intelligent retry system that learns optimal wait times
- **Keyboard Shortcuts** - Navigate efficiently (ESC, Ctrl+Shift+1-4)

### 📰 News & Research Subscriptions
- **Automated Research Digests** - Subscribe to topics or specific queries; AI filters and summarizes only the most relevant developments
- **Customizable Delivery** - Daily, weekly, or custom schedules, as markdown reports or structured summaries

### 🌐 Search Sources

#### Free Search Engines
- **Academic**: arXiv, PubMed, Semantic Scholar
- **General**: Wikipedia, SearXNG
- **Technical**: GitHub, Elasticsearch
- **Archived pages**: Wayback Machine URL fetching
- **News**: The Guardian, Wikinews

#### Premium Search Engines
- **Tavily** - AI-powered search
- **Google** - Via SerpAPI or Programmable Search Engine
- **Brave Search** - Privacy-focused web search

#### Custom Sources
- **Local Documents** - Search your files with AI
- **LangChain Retrievers** - Any vector store or database
- **Meta Search** - Combine multiple engines intelligently

LDR respects `robots.txt` and identifies itself honestly when fetching web pages — no stealth or anti-detection techniques. In rare cases this means a page that blocks automated access won't be fetched, which we consider the right trade-off.

[Full Search Engines Guide →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/search-engines.md)

## 💻 Usage Examples

### Python API
```python
from local_deep_research.api import LDRClient, quick_query

# Option 1: Simplest - one line research
summary = quick_query("username", "password", "What is quantum computing?")
print(summary)

# Option 2: Client for multiple operations
client = LDRClient()
client.login("username", "password")
result = client.quick_research("What are the latest advances in quantum computing?")
print(result["summary"])
```

### HTTP API

*The code example below shows the basic API structure - for working examples, see the link below*

```python
import requests
from bs4 import BeautifulSoup

# Create session and authenticate
session = requests.Session()
login_page = session.get("http://localhost:5000/auth/login")
soup = BeautifulSoup(login_page.text, "html.parser")
login_csrf = soup.find("input", {"name": "csrf_token"}).get("value")

# Login and get API CSRF token
session.post("http://localhost:5000/auth/login",
            data={"username": "user", "password": "pass", "csrf_token": login_csrf})
csrf = session.get("http://localhost:5000/auth/csrf-token").json()["csrf_token"]

# Make API request
response = session.post("http://localhost:5000/api/start_research",
                       json={"query": "Your research question"},
                       headers={"X-CSRF-Token": csrf})
```

🚀 **[Ready-to-use HTTP API Examples → examples/api_usage/http/](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/examples/api_usage/http)**
- ✅ **Automatic user creation** - works out of the box
- ✅ **Complete authentication** with CSRF handling
- ✅ **Result retry logic** - waits until research completes
- ✅ **Progress monitoring** and error handling

### Command Line Tools

```bash
# Run benchmarks from CLI
python -m local_deep_research.benchmarks.cli.benchmark_commands simpleqa --examples 50
```

See the [Command Line Tools guide](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/cli-tools.md) for the full reference.

## 🔗 Bring Your Own Knowledge Base

Connect LDR to your existing knowledge base. Unlike the HTTP client above, `quick_summary()` runs LDR in-process — no server needed — so you can pass it live Python objects such as LangChain retrievers:

```python
from local_deep_research.api import quick_summary

# Use your existing LangChain retriever
result = quick_summary(
    query="What are our deployment procedures?",
    retrievers={"company_kb": your_retriever},
    search_tool="company_kb"
)
```

Works with: FAISS, Chroma, Pinecone, Weaviate, Elasticsearch, and any LangChain-compatible retriever.

[Integration Guide →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/LANGCHAIN_RETRIEVER_INTEGRATION.md)

## 🔌 MCP Server (Claude Integration)

LDR provides an MCP (Model Context Protocol) server that allows AI assistants like Claude Desktop and Claude Code to perform deep research. Full setup details in the [MCP Server guide](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/mcp-server.md).

> ⚠️ **Security Note**: This MCP server is designed for **local use only** via STDIO transport (e.g., Claude Desktop). It has no built-in authentication or rate limiting. Do not expose over a network without implementing proper security controls. See the [MCP Security Best Practices](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices) for network deployment requirements.

### Installation

```bash
# Install with MCP extras
pip install "local-deep-research[mcp]"
```

### Claude Desktop Configuration

Add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "local-deep-research": {
      "command": "ldr-mcp",
      "env": {
        "LDR_LLM_PROVIDER": "openai",
        "LDR_LLM_OPENAI_API_KEY": "sk-..."
      }
    }
  }
}
```

### Claude Code Configuration

Add to your `.mcp.json` (project-level) or `~/.claude/mcp.json` (global):

```json
{
  "mcpServers": {
    "local-deep-research": {
      "command": "ldr-mcp",
      "env": {
        "LDR_LLM_PROVIDER": "ollama",
        "LDR_LLM_OLLAMA_URL": "http://localhost:11434"
      }
    }
  }
}
```

Any LDR setting can be passed in `env` as an `LDR_*` variable — see the auto-generated [Full Configuration Reference](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/CONFIGURATION.md) for the complete list.

### Available Tools

| Tool | Description | Duration | LLM Cost |
|------|-------------|----------|----------|
| `search` | Raw results from a specific engine (arxiv, pubmed, wikipedia, ...) | 5-30s | None |
| `quick_research` | Fast research summary | 1-5 min | Yes |
| `detailed_research` | Comprehensive analysis | 5-15 min | Yes |
| `generate_report` | Full markdown report | 10-30 min | Yes |
| `analyze_documents` | Search local collections | 30s-2 min | Yes |
| `list_search_engines` | List available search engines | instant | None |
| `list_strategies` | List research strategies | instant | None |
| `get_configuration` | Get current config | instant | None |

### Individual Search Engines

The `search` tool lets you query specific search engines directly and get raw results (title, link, snippet) — no LLM processing, no cost, fast. This is especially useful for **monitoring and subscriptions** where you want to check for new content regularly without burning LLM tokens.

```
# Search arXiv for recent papers
search(query="transformer architecture improvements", engine="arxiv")

# Search PubMed for medical literature
search(query="CRISPR clinical trials 2024", engine="pubmed")

# Search Wikipedia for quick facts
search(query="quantum error correction", engine="wikipedia")

# Search GitHub for code and repositories
search(query="agentic research frameworks", engine="github")

# Use list_search_engines() to see all available engines
```

### Example Usage

```
"Use quick_research to find information about quantum computing applications"
"Search arxiv for recent papers on diffusion models"
"Generate a detailed research report on renewable energy trends"
```


## 🤖 Supported LLMs

### Local Models
- **Ollama** — connect to its native API (default `http://localhost:11434`)
- **LM Studio** — connect to its OpenAI-compatible server (default `http://localhost:1234/v1`)
- **llama.cpp** — connect to `llama-server`'s OpenAI-compatible endpoint (default `http://localhost:8080/v1`); start with `llama-server -m <model.gguf>`
- Common models: Llama, Mistral, Gemma, DeepSeek, Qwen
- LLM processing runs on your own hardware when the model itself runs locally (search queries still go to the web). A local endpoint that relays to a hosted model, such as an Ollama cloud model or a local LiteLLM proxy, is not detected as remote. No API costs for locally-run models.

> 💡 **Which local model should I pick?** See the [community benchmarks](#-benchmarks) — community-submitted accuracy numbers across local and cloud models, so you can compare before downloading.

### Cloud Models
- OpenAI
- Anthropic Claude
- Google Gemini
- 100+ models via OpenRouter
- 100+ models via Requesty

### Custom Endpoints
- **OpenAI-Compatible Endpoint** — any service speaking the OpenAI chat-completions API (vLLM, llama.cpp, gateways)
- **Anthropic-Compatible Endpoint** — a self-hosted service speaking the Anthropic Messages API (`/v1/messages`); set `llm.anthropic_endpoint.url`

[Model Setup →](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/env_configuration.md)

## 🔄 Upgrading from Earlier Versions

For the first FastAPI-based major release, read the
[upgrade and rollback guide](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/deployment/upgrading.md) before updating.
Back up the data first: the release's new database revisions prevent a
direct rollback to v1.10.7 against the upgraded databases. Copy each user's
automatic pre-migration backup from `encrypted_databases/backups/` to storage
outside the data directory immediately after that user's first sign-in on the
new release, and in any case before any downgrade attempt: a login backup on
a later UTC day or a password change replaces it with a copy of the upgraded
database. Never start v1.10.7 against the upgraded databases: each sign-in
attempt there also replaces that backup.

- **`llm.model` no longer has a default.** Pre-1.6.3 installs auto-filled `gemma3:12b` (Ollama) when no model was configured, which silently downloaded a multi-GB binary. The field is now empty by default — pick a model in Settings → LLM, or research will fail loudly with a clear error.
- **The `auto` and `parallel` meta search engines were removed.** The default langgraph-agent strategy selects engines dynamically per query, which replaces them. Stored settings are migrated automatically (removed values become `searxng`); update any explicit `search_tool="auto"` API calls or `LDR_SEARCH_TOOL=auto` env overrides to a concrete engine such as `searxng`.
- **Behind a reverse proxy: the rate limiter reads the client address differently.** Up to v1.10.7 the login and registration rate limits keyed on the left-most `X-Forwarded-For` entry, or else `X-Real-IP`, sent by *any* peer, so any client could choose its own bucket. What happens now depends on your configuration:
  - **`TRUST_PROXY_HEADERS` unset (the default):** those headers are read only when the direct peer is a private, loopback or link-local address (6to4, Teredo and NAT64 tunnel addresses do not count), or, once you set `LDR_SERVER_FORWARDED_ALLOW_IPS` to your proxy's address(es), only when it is listed there. The key is the **right-most** `X-Forwarded-For` entry, or `X-Real-IP` when no `X-Forwarded-For` is present.
  - **`TRUST_PROXY_HEADERS=true` with `LDR_SERVER_FORWARDED_ALLOW_IPS` set to your proxy's address(es):** forwarded headers count only from a listed peer. uvicorn derives the client address from a listed peer's `X-Forwarded-For`, and `X-Real-IP` (used when no `X-Forwarded-For` is present) is read only from a listed peer. Any other peer is keyed on its own address.
  - **`TRUST_PROXY_HEADERS=true` without `LDR_SERVER_FORWARDED_ALLOW_IPS`:** the key is the **right-most** `X-Forwarded-For` entry sent by **any** peer, public addresses included. That stops a client behind one appending proxy from choosing its bucket with a forged prefix, but a client that connects straight to LDR writes the whole header and still chooses its own key. `X-Real-IP` is read only from a private, loopback or link-local peer. LDR logs a startup warning for this configuration: set `LDR_SERVER_FORWARDED_ALLOW_IPS`.

  What it means for an existing proxy setup:
  - **Proxy reaching LDR from a public or carrier-grade-NAT address** (a cloud load balancer, a Tailscale `100.64.x.x` peer, a public nginx host): without `TRUST_PROXY_HEADERS` its forwarded headers are now ignored; with `TRUST_PROXY_HEADERS=true` and no allow-list its `X-Real-IP` is ignored, while its `X-Forwarded-For` still counts. When the header your proxy sends is ignored, every client shares the proxy's one rate-limit bucket and a few failed logins lock everyone out. Set `LDR_SERVER_FORWARDED_ALLOW_IPS` to the proxy's IP address(es).
  - **Proxy on a private or Docker network:** (if you set a restricted `LDR_SERVER_FORWARDED_ALLOW_IPS`, only listed peers are trusted, so list this private proxy too.) With a single proxy, clients keep separate buckets, provided the proxy **overwrites** `X-Forwarded-For` with the address it observed (nginx: `$remote_addr`) and overwrites `X-Real-IP` the same way. A proxy that passes a client-sent header through unchanged still lets that client choose its key; a proxy that sets only `X-Real-IP` must also clear `X-Forwarded-For`, because LDR prefers it. Appending (`$proxy_add_x_forwarded_for`) gives the right rate-limit key, but under `TRUST_PROXY_HEADERS=true` without `LDR_SERVER_FORWARDED_ALLOW_IPS` uvicorn reports the left-most, client-written entry as the client address.
  - **Two or more proxies in a chain** (CDN → nginx): by default every client is keyed on the outer proxy's address, so all of them share one rate-limit bucket and a few failed logins lock everyone out. Either have the inner proxy resolve the real client itself (nginx `realip`) and overwrite with that address, or make every inner proxy append and set a multi-hop setting: `LDR_SERVER_TRUSTED_PROXY_COUNT`, or, with `TRUST_PROXY_HEADERS=true`, every hop listed in `LDR_SERVER_FORWARDED_ALLOW_IPS`. See [Proxy chains](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/deployment/reverse-proxy.md#proxy-chains).

  With `TRUST_PROXY_HEADERS=true` but no `LDR_SERVER_FORWARDED_ALLOW_IPS`, LDR logs a startup warning naming the variable (or refuses to start if `LDR_SERVER_TRUSTED_PROXY_COUNT` is also set to a positive value, or if the allow-list is set explicitly to `*` or another value that trusts every peer). See [Reverse proxy configuration](https://github.com/LearningCircuit/local-deep-research/blob/HEAD/docs/deployment/reverse-proxy.md).

> The README is long; the above is its first 40000 characters. Full content: https://github.com/LearningCircuit/local-deep-research
