Port docs-mcp-template upgrades (eval, citations, prefixes) (#26)
Image rebuild (skip scrape) / build (push) Successful in 3m58s

Co-authored-by: claude <[email protected]>
This commit was merged in pull request #26.
This commit is contained in:
2026-09-30 08:22:55 -04:00
committed by claude
parent 4de5aaa02f
commit 817c0d6c20
12 changed files with 509 additions and 53 deletions
+43
View File
@@ -0,0 +1,43 @@
"""Markdown formatters for MCP tool output.
No third-party imports — tests can run without mcp/httpx/chromadb.
Citation numbers are per-call (1-based, dense) and stateless.
"""
from __future__ import annotations
def format_search_hit(
n: int,
title: str,
url: str,
text: str,
extra: str = "",
) -> str:
"""One numbered hit. `n` is 1-based in final reranked/fused order."""
head = f"[{n}] **{title}**"
if url:
head += f" — {url}"
parts = [head]
if extra:
parts.append(extra)
body = (text or "").strip()
if body:
parts.append(body)
return "\n".join(parts)
def format_search_hits(
hits: list[tuple[str, str, str, str]],
) -> str:
"""Render hits as `[1] **title** — url` then text.
`hits` is a list of (title, url, text, extra) in display order.
Empty list → empty string (no invented citations).
"""
if not hits:
return ""
blocks = [
format_search_hit(n, title, url, text, extra)
for n, (title, url, text, extra) in enumerate(hits, start=1)
]
return "\n\n".join(blocks) + "\n"
+38 -45
View File
@@ -29,6 +29,7 @@ from mcp.server.mcpserver import MCPServer
from mcp.server.transport_security import TransportSecuritySettings
from pydantic import Field
from .format import format_search_hit
from .usage import TimedCall
log = logging.getLogger(__name__)
@@ -182,6 +183,17 @@ def _collection():
return _chroma_collection
def _query_dense(col, query: str, n: int, where: dict | None = None, **extra):
"""Dense query with nomic prefixes applied at embed time only."""
from rag.embeddings import EMBED_QUERY_PREFIX, embed_texts
qvec = embed_texts([query], prefix=EMBED_QUERY_PREFIX)[0]
kwargs: dict[str, Any] = {"query_embeddings": [qvec], "n_results": n}
if where:
kwargs["where"] = where
kwargs.update(extra)
return col.query(**kwargs)
def _bm25_index():
"""Return the BM25 index, or None if it doesn't exist on disk."""
global _bm25
@@ -257,10 +269,8 @@ def _read_markdown(source: str, source_key: str) -> str | None:
return None
def _format_hit(doc: str, meta: dict, distance: float | None = None) -> str:
"""Render one retrieval hit as a fenced markdown block with full
provenance attached. ``doc`` is the chunk text; ``meta`` is the
chunk's metadata dict."""
def _format_hit(n: int, doc: str, meta: dict, distance: float | None = None) -> str:
"""Render one numbered retrieval hit. ``n`` is 1-based display order."""
src_url = meta.get("source_url") or ""
src_key = meta.get("source_key") or ""
src = meta.get("source") or ""
@@ -268,15 +278,10 @@ def _format_hit(doc: str, meta: dict, distance: float | None = None) -> str:
brand = meta.get("brand") or ""
crop = meta.get("crop") or ""
name = meta.get("product_name") or src_key
header = (
f"### {name} \n"
f"`{src}::{src_key}` — {vendor} / {brand} / {crop} \n"
f"<{src_url}>"
)
extra = f"`{src}::{src_key}` — {vendor} / {brand} / {crop}"
if distance is not None:
header += f" \n_(distance={distance:.4f})_"
return f"{header}\n\n{doc.strip()}\n"
extra += f" distance={distance:.4f}"
return format_search_hit(n, name, src_url, doc, extra)
def _rrf_fuse(rankings: list[list[str]], k: int = RRF_K) -> list[str]:
@@ -548,7 +553,8 @@ def search_docs(
"""Search the seed-variety corpus for hybrids/varieties matching a query.
Returns the top-k variety chunks with their full source URLs, ratings,
maturity, traits, and regional listings. Optional filters narrow to one
maturity, traits, and regional listings. Hits are numbered [1]… for
citation. Optional filters narrow to one
crop, brand, vendor, or scraper source. Use **list_versions()** first
to discover valid facet values. Use **lookup_variety()** on any
candidate the user is serious about — that returns the canonical
@@ -593,11 +599,7 @@ def search_docs(
)
try:
dense = col.query(
query_texts=[query],
n_results=pool_size,
where=where,
)
dense = _query_dense(col, query, pool_size, where)
except Exception as exc: # noqa: BLE001
_call.set(error_dense=str(exc), hits_returned=0)
return f"_(retrieval failed: {exc})_"
@@ -702,20 +704,21 @@ def search_docs(
)
blocks: list[str] = []
for cid in final_ids:
for n, cid in enumerate(final_ids, start=1):
doc = id_to_doc.get(cid, "")
meta = id_to_meta.get(cid, {})
dist = id_to_dist.get(cid) if not used_hybrid else None
blocks.append(_format_hit(doc, meta, dist))
blocks.append(_format_hit(n, doc, meta, dist))
header = (
f"# Search results — {len(final_ids)} variety chunk"
f"{'s' if len(final_ids) != 1 else ''}"
f"{' (dense + BM25 hybrid)' if used_hybrid else ' (dense only)'}\n"
f"_Use `lookup_variety(source_key=...)` on any candidate "
f"to fact-check ratings from the canonical sidecar._\n\n---\n\n"
f"_Hits are numbered [1]… for citation. "
f"Use `lookup_variety(source_key=...)` on any candidate "
f"to fact-check ratings from the canonical sidecar._\n\n"
)
return header + "\n---\n\n".join(blocks)
return header + "\n\n".join(blocks) + "\n"
@mcp.tool()
@@ -975,11 +978,7 @@ def search_trials(
full_query = f"{query} {product}"
try:
dense = col.query(
query_texts=[full_query],
n_results=pool_size,
where=where,
)
dense = _query_dense(col, full_query, pool_size, where)
except Exception as exc: # noqa: BLE001
_call.set(error_dense=str(exc), hits_returned=0)
return f"_(trial retrieval failed: {exc})_"
@@ -1093,28 +1092,27 @@ def search_trials(
)
blocks: list[str] = []
for cid in final_ids:
for n, cid in enumerate(final_ids, start=1):
doc = id_to_doc.get(cid, "")
meta = id_to_meta.get(cid, {})
dist = id_to_dist.get(cid) if not used_hybrid else None
blocks.append(_format_trial_hit(doc, meta, dist))
blocks.append(_format_trial_hit(n, doc, meta, dist))
header = (
f"# Trial search results — {len(final_ids)} trial document"
f"{'s' if len(final_ids) != 1 else ''}"
f"{' (dense + BM25 hybrid)' if used_hybrid else ' (dense only)'}\n"
f"_Use `get_page(source=..., source_key=...)` to read the "
f"_Hits are numbered [1]… for citation. "
f"Use `get_page(source=..., source_key=...)` to read the "
f"full trial body. Use `lookup_variety(source_key=...)` on "
f"any product code to verify its identity (RM, traits, "
f"disease ratings)._\n\n---\n\n"
f"disease ratings)._\n\n"
)
return header + "\n---\n\n".join(blocks)
return header + "\n\n".join(blocks) + "\n"
def _format_trial_hit(doc: str, meta: dict, distance: float | None = None) -> str:
"""Trial-specific result header. Highlights crop/state/year and
sources URL (vs variety hits which emphasize brand + product
identity)."""
def _format_trial_hit(n: int, doc: str, meta: dict, distance: float | None = None) -> str:
"""Trial-specific numbered hit. Highlights crop/state/year."""
src_url = meta.get("source_url") or ""
src_key = meta.get("source_key") or ""
src = meta.get("source") or ""
@@ -1125,15 +1123,10 @@ def _format_trial_hit(doc: str, meta: dict, distance: float | None = None) -> st
title_bits = [b for b in [crop.title(), region or state, str(year) if year else ""] if b]
title = " · ".join(title_bits) if title_bits else src_key
header = (
f"### Trial: {title} \n"
f"`{src}::{src_key}` — {meta.get('vendor', '')} / {meta.get('brand', '')} \n"
f"<{src_url}>"
)
extra = f"`{src}::{src_key}` — {meta.get('vendor', '')} / {meta.get('brand', '')}"
if distance is not None:
header += f" \n_(distance={distance:.4f})_"
return f"{header}\n\n{doc.strip()}\n"
extra += f" distance={distance:.4f}"
return format_search_hit(n, f"Trial: {title}", src_url, doc, extra)
@mcp.tool()
+37
View File
@@ -0,0 +1,37 @@
"""Citation markdown renderer — no Chroma, no mcp."""
from __future__ import annotations
import unittest
from docs_mcp.format import format_search_hits
class FormatSearchHitsTests(unittest.TestCase):
def test_two_hits_numbered_with_urls(self) -> None:
out = format_search_hits([
("Install on Linux", "https://docs.example.com/install", "chunk one", ""),
("HA setup", "https://docs.example.com/ha", "chunk two", ""),
])
self.assertIn("[1] **Install on Linux** — https://docs.example.com/install", out)
self.assertIn("[2] **HA setup** — https://docs.example.com/ha", out)
self.assertIn("chunk one", out)
self.assertIn("chunk two", out)
self.assertNotIn("[3]", out)
# [1] before [2]
self.assertLess(out.index("[1]"), out.index("[2]"))
# each number is followed by its URL on the same logical hit
first = out.split("[2]")[0]
self.assertIn("https://docs.example.com/install", first)
self.assertNotIn("https://docs.example.com/ha", first)
def test_empty_hits_invents_nothing(self) -> None:
self.assertEqual(format_search_hits([]), "")
def test_missing_url_omits_emdash(self) -> None:
out = format_search_hits([("Title only", "", "body", "")])
self.assertIn("[1] **Title only**", out)
self.assertNotIn("—", out)
if __name__ == "__main__":
unittest.main()