Expertise-as-a-Service over the Model Context Protocol.
An MCP server that turns any ExpertPack into a live, queryable knowledge service. Any MCP-compatible agent (Claude Desktop, Cursor, Windsurf, Claude.ai, custom hosts) can connect and retrieve high-quality, provenance-rich domain expertise via standardized tools.
🌐 expertpack.ai · 📦 ExpertPack Framework · 📖 Architecture
87.8% retrieval hit rate / 0 full misses on a 22-question benchmark against a production v4.1 atomic-conceptual pack (658 chunks). Prior v4.0 baseline on the same pack and eval set: 65.1%.
- Schema-aware hybrid retrieval: BM25 + vector search (sqlite-vec), metadata boosting, MMR re-ranking, adaptive threshold filtering, length penalty
requires:expansion (schema v4.1 atomic-conceptual): When a top-K atom's frontmatter declaresrequires: [B, C], the referenced atoms are auto-appended to results as additionalSearchResults flaggedrequires_expanded=True. Directional (A→B does not imply B→A), transitive up to 2 hops, capped at 3 atoms and 3,500 cumulative tokens, appended after themax_resultsslice so it never displaces top-K.- Intent-aware routing: Automatic query classification (ENTITY/HOW/WHY/WHEN/GENERAL) adjusts vector/BM25 fusion weights per query — no LLM required
- Query embedding cache: SQLite-backed cache keyed by
(model, dimension, sha256(query))— warm query latency drops from ~4s (live Gemini API) to ~1ms - Deep graph traversal: Optional multi-hop BFS expansion over knowledge graph edges with per-hop score decay and drift prevention
- Reserved-slot graph merge: Configurable protected slots in the final top-K reserved for graph-expanded neighbors so related files surface even against dominant core results
- BM25 fallback mode: Embedding provider outage degrades gracefully to lexical-only search (coverage + density + path + length scoring) instead of a hard error
- Cross-encoder reranker: Optional second-pass precision reranker (disabled by default, ~70ms warm latency when enabled)
- Provenance-first: Every result includes
id,content_hash,verified_at,source_file - EP-native chunking: Files are treated as atomic retrieval units (split at
##only for oversized content) - Frontmatter aware: Strips metadata for embedding, extracts
type,tags,requires, etc. for boosting/expansion/filtering - Incremental indexing: Content-hash based staleness detection — only re-embeds changed files
- Multi-pack support: Path-based routing at
/packs/{slug}/mcp - Graph-aware retrieval: optional post-retrieval expansion via
_graph.yamladjacency - Tools:
ep_search— primary hybrid retrieval toolep_list_topics— browse pack structure and available content typesep_graph_traverse
- Resources:
ep://{slug}/manifestep://{slug}/filesep://{slug}/file/{path}(raw content with frontmatter)
- Transports: Streamable HTTP (primary, cloud-ready), stdio (local dev)
- HTTP endpoint:
GET /search(for non-MCP HTTP clients) - Auth: API key (Phase 1), designed for OAuth 2.1 (Phase 2)
git clone https://github.com/brianhearn/ep-mcp.git
cd ep-mcp
pip install -e .[dev]Create config.yaml:
server:
host: "127.0.0.1"
port: 8000
log_level: "info"
packs:
- slug: "my-pack"
path: "/path/to/your/expertpack"
api_keys:
- "your-secret-key-here"
embedding:
provider: "gemini"
model: "gemini-embedding-001"
azure_endpoint: null
azure_api_version: "2024-10-21"
azure_deployment: null
retrieval:
vector_weight: 0.7
text_weight: 0.3
candidate_multiplier: 8
mmr_enabled: true
mmr_lambda: 0.7
min_score: 0.35
default_max_results: 10
type_match_boost: 0.05
tag_match_boost: 0.03
always_tier_boost: 0.02
graph_expansion_enabled: false
graph_expansion_depth: 1
graph_expansion_discount: 0.85
graph_expansion_min_score: 0.20
graph_expansion_confidence_threshold: 0.38
graph_expansion_structural_bonus: 1.0
# Adaptive + length + intent:
adaptive_threshold: true
activation_floor: 0.15
score_ratio: 0.55
absolute_floor: 0.20
length_penalty_threshold: 80
length_penalty_factor: 0.15
intent_routing_enabled: true
graph_expansion_deep: false
graph_expansion_deep_max_bonus: 5
# Schema v4.1 `requires:` expansion (enabled by default):
requires_expansion_enabled: true
requires_expansion_max_depth: 2
requires_expansion_max_atoms: 3
requires_expansion_token_budget: 3500
requires_expansion_score: 0.30Note: All retrieval: fields are optional — defaults shown above. See ARCHITECTURE.md §5 for the full 8-step pipeline.
ep-mcp serve --config config.yamlThe server builds/updates the SQLite index (FTS5 + sqlite-vec) on startup.
The GET /search HTTP endpoint is available at:
GET /search?q=<query>&pack=<slug>&n=10
with header Authorization: Bearer <key>.
Only the key for your configured embedding provider is required.
| Variable | Required | Description |
|---|---|---|
GEMINI_API_KEY |
If embedding.provider: gemini |
Gemini embedding API key |
AZURE_OPENAI_API_KEY |
If embedding.provider: azure_openai |
Azure OpenAI embedding API key |
OPENAI_API_KEY |
If embedding.provider: openai |
OpenAI embedding API key |
EP_MCP_KEY_{SLUG} |
Optional | Per-pack API key override (uppercase slug, hyphens→underscores) |
EP_MCP_REMOTE_HOST |
Optional | Deploy target for scripts/deploy.sh |
EP_MCP_REMOTE_SRC |
Optional | Remote source path for deploy.sh |
EP_MCP_REMOTE_SITE_PKG |
Optional | Remote site-packages path for deploy.sh |
- Language: Python 3.12+
- MCP Framework: FastMCP (from the official
mcpSDK) - Storage: SQLite with FTS5 + sqlite-vec
- Embeddings: Gemini (default), configurable Azure OpenAI / OpenAI providers
- Web: Starlette + Uvicorn (for Streamable HTTP)
- CLI: Click
See pyproject.toml for exact dependencies.
See ARCHITECTURE.md for detailed module breakdown, retrieval pipeline, indexing strategy, and design decisions.
- expertpack.ai — Framework website, community packs, and documentation
- ExpertPack — Schema framework, validation tools, Obsidian compatibility, community packs
- ExpertPacks — Published knowledge packs (private)
- EasyTerritory MCP (future) — Domain-specific layer built on top of this repo +
ezt-designerpack
Apache 2.0 © 2026 Brian Hearn
Brian Hearn — Built in 3 days (April 10–11, 2026).