Coverage for mcp/tools/examples.py: 96%
58 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-15 15:07 +0000
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-15 15:07 +0000
1"""Example manifest discovery MCP tool.
3Wraps the ``EXAMPLE_METADATA`` catalog defined in ``mcp/resources/docs.py``
4and exposes a single ``find_examples`` tool that the LLM can call with a
5free-text query plus optional category/gpu/opt_in filters. Scoring is a
6deterministic weighted sum over keyword/summary/name/use_case substring
7matches; results are sorted by score descending then name ascending so a
8caller iterating with ``limit`` always sees a stable ordering.
9"""
11from audit import audit_logged
12from resources.docs import EXAMPLE_METADATA
13from server import mcp
16def _coerce_bool_flag(value: str | bool | None) -> bool | None:
17 """Normalise a string/bool tri-state filter to True/False/None.
19 Returns ``None`` when the caller did not supply a filter (so the
20 search loop knows to skip the gpu/opt_in checks entirely).
21 """
22 if value is None:
23 return None
24 if isinstance(value, bool):
25 return value
26 return value.strip().lower() in ("yes", "true", "1")
29def _has_gpu(meta: dict[str, str | list[str]]) -> bool:
30 """Treat anything other than ``"no"``/``""`` as GPU-bearing.
32 The ``gpu`` field carries values like ``"NVIDIA"``, ``"NVIDIA + EFA"``,
33 ``"Trainium"``, ``"Inferentia"``, ``"NVIDIA (time-sliced)"``, and
34 ``"optional"`` — all of which should match ``gpu="yes"``.
35 """
36 return str(meta.get("gpu", "no")) not in ("", "no")
39def _search(
40 query: str | None,
41 category: str | None,
42 gpu: str | bool | None,
43 opt_in: str | bool | None,
44) -> list[tuple[str, int]]:
45 """Filter and score examples; return ``[(name, score), ...]`` sorted desc."""
46 want_gpu = _coerce_bool_flag(gpu)
47 want_opt_in = _coerce_bool_flag(opt_in)
48 q = query.lower() if query else None
50 results: list[tuple[str, int]] = []
51 for name, meta in EXAMPLE_METADATA.items():
52 # Hard filters — drop the entry when any non-matching filter is set.
53 if category and str(meta.get("category", "")).lower() != category.lower(): 53 ↛ 54line 53 didn't jump to line 54 because the condition on line 53 was never true
54 continue
55 if want_gpu is not None and _has_gpu(meta) != want_gpu:
56 continue
57 if want_opt_in is not None and bool(meta.get("opt_in", "")) != want_opt_in:
58 continue
60 # Scoring runs only when there's a query — without one, every entry
61 # that survived the filters is included with score 0.
62 score = 0
63 if q:
64 keywords = meta.get("keywords", [])
65 if isinstance(keywords, list): 65 ↛ 69line 65 didn't jump to line 69 because the condition on line 65 was always true
66 for kw in keywords:
67 if q in str(kw).lower():
68 score += 5
69 if q in str(meta.get("summary", "")).lower():
70 score += 2
71 if q in name.lower():
72 score += 3
73 use_cases = meta.get("use_cases", [])
74 if isinstance(use_cases, list): 74 ↛ 78line 74 didn't jump to line 78 because the condition on line 74 was always true
75 for uc in use_cases:
76 if q in str(uc).lower():
77 score += 3
78 if score == 0:
79 continue
80 results.append((name, score))
82 # Sort by score desc, then name asc for stable ordering across calls.
83 results.sort(key=lambda x: (-x[1], x[0]))
84 return results
87def _format(name: str) -> dict[str, object]:
88 """Format a metadata entry for the tool response."""
89 meta = EXAMPLE_METADATA.get(name, {})
90 return {
91 "name": name,
92 "category": meta.get("category", ""),
93 "summary": meta.get("summary", ""),
94 "gpu": meta.get("gpu", "no"),
95 "opt_in": meta.get("opt_in", ""),
96 "submission": meta.get("submission", ""),
97 "keywords": meta.get("keywords", []),
98 "use_cases": meta.get("use_cases", []),
99 "related": meta.get("related", []),
100 }
103@mcp.tool(tags={"safe", "examples"})
104@audit_logged
105async def find_examples(
106 query: str | None = None,
107 category: str | None = None,
108 gpu: str | None = None,
109 opt_in: str | None = None,
110 limit: int = 10,
111) -> list[dict[str, object]]:
112 """`find_examples` — search the example-manifest catalog by keyword and filters.
114 Args:
115 query: Natural-language query matched against keywords, summary,
116 name, and use_cases (case-insensitive substring match).
117 category: Filter by category (case-insensitive exact match).
118 gpu: Pass ``"yes"``/``"true"`` to require GPU examples;
119 ``"no"``/``"false"`` to exclude. Omit to leave unconstrained.
120 opt_in: Pass ``"yes"`` to require an opt-in feature flag;
121 ``"no"`` to exclude.
122 limit: Maximum results (default 10). ``limit <= 0`` returns ``[]``.
124 Returns a list of dicts with ``name``, ``category``, ``summary``,
125 ``gpu``, ``opt_in``, ``submission``, ``keywords``, ``use_cases``, and
126 ``related`` for each match.
127 """
128 if limit <= 0:
129 return []
131 no_filters = not query and not category and gpu is None and opt_in is None
132 if no_filters:
133 # Stable alpha-sorted listing for the no-arg case.
134 names = sorted(EXAMPLE_METADATA.keys())[:limit]
135 return [_format(name) for name in names]
137 matches = _search(query, category, gpu, opt_in)
138 return [_format(name) for name, _score in matches[:limit]]