Retrieval¶
The MCP server turns a search query into evidence the model can quote and citations the widget can show. It searches three Milvus collections, one per source.
| Tool | Collection | Use it for |
|---|---|---|
search_kubeflow_docs |
kubeflow_docs |
How-to and conceptual content, versions and releases |
search_github_issues |
issues_rag |
Error messages, symptoms, "has anyone hit this" |
search_kubeflow_code |
code_rag |
Defaults, CRD fields, manifests, controller behaviour |
The agent chooses the tool. Each tool's description says what it is for and what it is not for, so the routing decision is explicit.
Hybrid search and the intent router¶
Embeddings are good at meaning and weak at exact tokens such as version numbers, config keys and error strings. The docs collection therefore stores both a dense vector and a native BM25 sparse vector, and a rule-based intent router picks the search mode for each query. No model is involved.
| Intent | Example | Mode | After search |
|---|---|---|---|
comparison |
"compare 1.9 and 1.10" | hybrid, deeper candidate pool | – |
release_date |
"when was 1.10 released" | BM25 | prefer chunks for that version |
temporal |
"what is the latest release" | BM25 on release notes | sort by release date |
exact |
a version, a dotted config key, an error string | BM25 | – |
conceptual |
"how", "why", "explain" | hybrid | – |
general |
anything else | hybrid | – |
Hybrid search fuses the dense and BM25 results with reciprocal rank fusion
(hybridRanker: rrf; a weighted ranker is available). A collection without the
BM25 field falls back to dense search. The chosen intent, mode and reason are
returned with every result and recorded in the trace.
Collection schema (docs)¶
| Field | Purpose |
|---|---|
content_text |
Chunk text; also the BM25 input |
vector |
768-dimension dense embedding (MPNet, from the TEI service) |
sparse_vector |
BM25 output, computed by Milvus |
document_id, chunk_index |
Stable document key and chunk position |
title, section_path |
Page title and heading breadcrumb |
citation_url, file_path |
Public URL and source path |
doc_type, version, release_date |
Release notes vs docs, the Kubeflow version, and its release date |
Indexes: a flat cosine index on vector and an inverted BM25 index on
sparse_vector.
The citation contract¶
| Part | Responsibility |
|---|---|
| MCP evidence | Chunk text with URLs removed, labelled [c1], [c2], … |
| MCP structured result | citations: a list of {id, url, score, section, version, …} |
| Agent | Answers from the evidence and never prints URLs or source lists |
| Widget | Renders the Sources panel from the structured citations |
Because the model never sees a URL, it cannot invent one. Links always come from the index.
Code¶
docs-agent-mcp/mcp-server/server.py: the three MCP toolsdocs-agent-mcp/mcp-server/intent_router.py: intent rulesdocs-agent-mcp/mcp-server/milvus_search.py: dense, BM25 and hybrid searchdocs-agent-mcp/mcp-server/citations.py: evidence and citation formatting