Quick start¶
pip install pydocs-mcp # from PyPI — the usual path
Prebuilt wheels bundle the Rust acceleration core for Linux (x86_64 /
aarch64), macOS (Apple Silicon), and Windows (x86_64) — no toolchain
needed. On any other platform pip builds from the sdist. To build from source
instead — for development, or to compile the Rust core on an unlisted platform:
git clone https://github.com/msobroza/pydocs-mcp && cd pydocs-mcp
pip install maturin && maturin develop --release
Linux needs OpenBLAS for the vector store (macOS and Windows already ship it):
sudo apt-get install -y libopenblas-pthread-dev
Then index your project and start the server:
pydocs-mcp serve . # index project + deps, serve over MCP (stdio)
pydocs-mcp serve . --gpu # …same, with CUDA-accelerated embeddings
pydocs-mcp search "batch inference" # the same search, from the CLI
pydocs-mcp refs requests.auth.HTTPBasicAuth --direction inherits
Embeddings run on CPU by default. Add --gpu to serve / index / watch
(or the benchmark runner) to move all embedder inference — FastEmbed, the
sentence_transformers provider, and PyLate — onto CUDA. It’s a latency knob only: no YAML change, no
re-index, identical results. Needs the matching GPU runtime — see
INSTALL.md.
Live re-indexing¶
The file watcher is part of the default install — no extra step. If you
edit code while you want the index to stay fresh, pick one of two modes —
both debounce edits to the watched file types into a single reindex. By
default the watcher follows every file type the project scope indexes
(extraction.discovery.project.include_extensions, which covers the config
and code files described in
Beyond Python); list extensions
under serve.watch.extensions to override it.
pydocs-mcp serve . --watch # MCP server + watcher (for AI clients)
pydocs-mcp watch . # watcher only (no MCP server; index stays fresh for CLI `search` / `symbol` / `refs`)
Both modes share the same YAML tunables: debounce, file extensions, and
ignored paths live under serve.watch.* in your pydocs-mcp.yaml (see
DOCUMENTATION.md).
Exclude directories from indexing¶
Keep generated docs, test fixtures, or vendored trees out of search results.
Declare additional exclusions in your project’s own pyproject.toml — they
travel with the repo:
[tool.pydocs-mcp]
exclude_dirs = ["docs/generated", "fixtures"]
Bare names ("fixtures") match at any depth; paths ("docs/generated")
match only that directory. Entries are additive over the built-in floor
(.git, .venv, …) — you can exclude more, never less. A server-side YAML
equivalent (extraction.discovery.project.exclude_dirs, plus a dependency
sibling) covers both project and dependency walks; see
DOCUMENTATION.md.
Beyond Python — multilanguage indexing¶
Most Python projects carry more than .py: docs, config, and sometimes a
second-language source tree. pydocs-mcp indexes the ones that carry real
search value.
Indexed by default: Python (
.py), notebooks (.ipynb), and the text/config formats — Markdown, reStructuredText, plain text, TOML, YAML, INI/CFG, and JSON. Docs and config are the bulk of what real pull requests touch beyond code, so they are on out of the box. Files are split into searchable sections (headings for prose, top-level keys/tables for config) with real line numbers. Your project’s own JavaScript, TypeScript/TSX, C headers/sources, Rust, and Java files are indexed by default too, as structural symbols (functions, classes, structs, interfaces) parsed with tree-sitter grammars that ship in the default install.Dependencies stay text/config by default: second-language code inside installed packages is often vendored or generated, so dependency walks skip it unless you opt in. Name the extensions you want for dependencies, or narrow the project list to opt your own code out:
# pydocs-mcp.yaml extraction: discovery: dependency: # the list REPLACES the default: Python, Markdown, notebooks, # Rust and TypeScript (add ".toml", ".yaml", … to keep config) include_extensions: [".py", ".md", ".ipynb", ".rs", ".ts"] project: # your project: Python, Markdown, and notebooks only include_extensions: [".py", ".md", ".ipynb"]
Without usable grammar wheels (for example, an install built from the source distribution), code files still index as searchable text — you lose the symbol outline, not the file. A one-line log tells you why symbols are missing.
What works per language (today): full-text search, symbol outlines, and
surrounding-context expansion work for every indexed language. The call/
import/reference graph covers Python and the code languages; get_references
reports its resolution as syntactic (precision-biased and matched by name and
import alias, not scope-resolved). When the index was built without a
language’s grammar, it reports resolution as unavailable rather than
pretending. Per-symbol member
listings remain Python-only. Vendored trees (node_modules, extern,
third_party, and the like) and binary assets are never indexed.
Multi-repo search (optional)¶
One MCP server can host several already-indexed repos. Index each once (every
project writes a portable {name}_{hash}.db + .tq bundle under --cache-dir),
then serve them all — each query searches across every loaded repo, or one via
the project scope:
# index a few repos into a shared directory of db bundles
pydocs-mcp index ~/code/frontend --cache-dir ~/pydocs-index
pydocs-mcp index ~/code/backend --cache-dir ~/pydocs-index
# serve them all from ONE MCP server (read-only — no reindex/watch)
pydocs-mcp serve --workspace ~/pydocs-index
pydocs-mcp serve --db ~/pydocs-index/backend_1a2b3c4d5e.db # or specific bundles
# query across all loaded repos, or scope to one by name
pydocs-mcp search "db pool" --workspace ~/pydocs-index
pydocs-mcp search "db pool" --workspace ~/pydocs-index --project backend
PYDOCS_CACHE_DIR sets the same bundle root for a whole shell session (child
processes inherit it); --cache-dir overrides it per command, and both override
the ~/.pydocs-mcp default.
On the MCP surface the selector is the project filter, a sibling of
package/scope: search_codebase(query="db pool", project="backend") /
get_symbol(target="app.db.Pool", project="backend"); omit it to search every loaded
repo. When the same package appears in several repos, a root-project copy wins
over a dependency copy, and among duplicate dependencies the most-recently-indexed
one is kept. Every loaded db must share the configured embedder — a mismatch
fails fast (a read-only load can’t re-embed an absent project).
Calling get_overview with no selector on a multi-repo server returns a
workspace orientation card — one line per loaded repo with its package
count — so an agent that has just connected can see everything on offer before
it narrows to a project.
Cross-repo references (multi-repo workspaces)¶
When several bundles are served together, a link pass resolves each
bundle’s unresolved references against its siblings’ symbols, so
get_references answers cross repository boundaries: callers of a mylib
symbol include its backend call sites (project-qualified rows), impact
walks across repos, and previously-unresolved callees become navigable
targets. References the local index already resolved always take precedence —
cross-links only add what a single bundle could not see.
Links live in a disposable sidecar next to the bundles
(pydocs-links.sqlite3); the bundles themselves are never modified. Serve
refreshes stale links automatically at startup; pydocs-mcp link --workspace ~/pydocs-index pre-bakes them (CI images, read-only deployments) and
--check gates on freshness. Tuning lives under reference_graph.cross_repo
in YAML (enabled: true by default — inert for single-repo serving): linked
edge kinds, alias resolution for re-exports, workspace-level ranking scores
(PageRank needs the [graph] extra; degree ranking works everywhere), and an
opt-in embedding-similarity kind. The workspace overview card reports link
freshness (cross-repo links: fresh | stale(...)).
Ask your docs — chat agent (optional)¶
The first in-tree harness on the retrieval backbone: a LangGraph ReAct agent
plus a Streamlit chat UI over the MCP server, for asking questions across your
indexed repos in natural language. Install the harness-ask-your-docs extra and run
its command:
pip install 'pydocs-mcp[harness-ask-your-docs]'
harness-ask-your-docs --workspace ~/pydocs-index
Sidebar pickers pin a project / package / own-code-vs-dependency slice (enforced
on every tool call, not left to the model), and answers cite project +
package.module with a runnable usage snippet. An activity panel above each
answer shows what the agent searched, opened and (when the endpoint returns it)
reasoned; the model’s reasoning can be incomplete or unfaithful and can quote the
files it read, so treat it as working notes. The sidebar’s Connection dialog
also carries the model settings — a Thinking switch plus Temperature, Max
output tokens, Top p and Seed, prefilled from ask_your_docs.llm.params — and
offers only the ones this model and endpoint can honour: what it hides, it does
not send, and Test connection reports exactly what went out. Configuration
and the GPU-index / CPU-serve recipe live in
examples/harness/ask_your_docs_agent.
Fast dependency indexing (selective embedding)¶
Everything is BM25/FTS-indexed, but dense embedding is selective by package tier — embedding is the dominant indexing cost, and big dependencies (torch, sklearn) carry tens of thousands of code chunks:
Tier |
What gets dense vectors |
Selected by |
|---|---|---|
Project / subprojects |
every chunk (dense + graph, unchanged) |
automatic |
Promoted dependencies |
every chunk — project-grade |
|
Regular dependencies |
documentation only: one docstring page per module (module + public signatures + docstrings) plus |
default ( |
So torch indexes in seconds (≈one embedding per module) instead of an hour,
while its docs stay semantically searchable and all of its code stays
keyword-searchable + navigable (get_symbol, kind="api"). scope=deps queries
automatically route to a BM25 ∥ dense fusion pipeline that covers both. Set
dependency_policy: full to restore embed-everything, or none for BM25-only
dependencies:
pydocs-mcp index . --full-dep my-internal-lib --full-dep "acme-*"
Point any MCP-capable AI coding client or editor at it over stdio — copy-paste
client configs are in DOCUMENTATION.md,
and install troubleshooting (including the libopenblas fallback) is in
INSTALL.md.