Architecture¶
Hexagonal layout. application/ services
(IndexingService, ProjectIndexer, DocsSearch, ApiSearch, PackageLookup,
ModuleInspector, LookupService, ReferenceService, TreeService,
OverviewService, DecisionService, MultiProjectSearch) depend
only on Protocols defined in storage/protocols.py — ChunkStore,
PackageStore, ModuleMemberStore, DocumentTreeStore, ReferenceStore,
UnitOfWork, TextSearchable, VectorSearchable, HybridSearchable,
FilterAdapter, plus the newer GraphSearchable, NodeScoreStore,
DecisionStore, and MultiVectorStore — and in retrieval/protocols.py
(Embedder, ResultFuser). Concrete adapters
(Sqlite* repositories, TurboQuantVectorStore / TurboQuantUnitOfWork,
the SearchBackend / SqliteCompositeBackend capability factory,
CompositeUnitOfWork) live behind them.
The composition root (server.py + __main__.py + storage/factories.py)
builds one uow_factory closure and threads it through every service. Rust
acceleration sits behind a substitution boundary: _fast.py resolves to
_native (compiled) or _fallback.py (pure Python) with identical signatures on
both sides, so the package works with or without the compiled extension.
pydocs-mcp/
├── Cargo.toml # Rust dependencies
├── pyproject.toml # Python package config (maturin mixed layout)
├── src/lib.rs # Rust: walker, hasher, parser, file readers (PyO3)
└── python/pydocs_mcp/
├── __main__.py # CLI entry (serve / index / search / symbol / refs / …)
├── _fast.py / _fallback.py # Rust-or-pure-Python substitution boundary
├── db.py # SQLite schema + cache lifecycle + FTS rebuild
├── deps.py # Dependency resolution
├── models.py # Value objects (Package / Chunk / SearchQuery, content hashing)
├── filters.py # Canonical backend-neutral filter-tree module
├── multirepo.py # Multi-repo bundle loading (serve --workspace / --db)
├── extraction/ # Chunkers, member extractors, ingestion pipeline, embedders
├── application/ # Use-case services (indexing, search, lookup, references, trees)
├── storage/ # SQLite + TurboQuant adapters, Protocols, UnitOfWork
├── retrieval/ # RetrieverStep ABC + RetrieverPipeline + steps/
├── serve/ # File watcher (pydocs-mcp watch / serve --watch)
├── ask_your_docs/ # Optional [harness-ask-your-docs] extra: LangGraph agent + Streamlit UI
├── defaults/ # Shipped default_config.yaml
├── pipelines/ # Built-in pipeline YAML blueprints
└── server.py # MCP server (nine task-shaped tools)
Every layer boundary is a Protocol and every swappable component has a registry, so new backends and steps land as one file each with no modification to existing code. The full extension menu — vector-store backends, filter formats, pipeline steps, fusion algorithms, planned features — is catalogued in EXTENSIONS.md; the contributor-facing rules (SOLID, async patterns, MCP API rules, single-source-of-truth defaults) are in CLAUDE.md.