diff --git a/src/perspicacite/mcp/usage_guide.py b/src/perspicacite/mcp/usage_guide.py index 71293e3..ce52f2d 100644 --- a/src/perspicacite/mcp/usage_guide.py +++ b/src/perspicacite/mcp/usage_guide.py @@ -171,6 +171,30 @@ "when_to_use": "Ingest specific DOIs into a KB.", "key_knobs": [], }, + { + "name": "ensure_kb", + "purpose": ( + "Idempotently create and ingest a per-paper KB for a DOI, using the ASB " + "binder slug convention. Returns immediately if the KB already has chunks." + ), + "when_to_use": ( + "Before grounding questions against a single paper, to guarantee its KB " + "exists without re-ingesting it on every call." + ), + "key_knobs": ["doi (required)", "mode — reserved, currently unused"], + }, + { + "name": "ground_paper", + "purpose": ( + "Answer a research question against one paper's dedicated KB, ensuring " + "that KB exists first (via ensure_kb)." + ), + "when_to_use": ( + "Ask a question of a specific paper by DOI in one call. Set tier='si' to " + "prefer evidence from the paper's supplementary information." + ), + "key_knobs": ["doi (required)", "question (required)", "tier (paper|si) — default: paper"], + }, { "name": "ingest_local_documents", "purpose": "Ingest local files (PDF/text) into a KB.", diff --git a/tests/unit/test_web_app_routes.py b/tests/unit/test_web_app_routes.py index 0957cd5..a454dfa 100644 --- a/tests/unit/test_web_app_routes.py +++ b/tests/unit/test_web_app_routes.py @@ -54,12 +54,27 @@ def _load_app(): return app +def _flatten_routes(routes): + """Yield leaf routes, descending into routers wrapped by include_router. + + FastAPI >= 0.139 no longer copies an included router's routes onto the app. + It appends one wrapper per include_router() call, exposing the router as + ``original_router``. Older versions flatten, leaving nothing to descend into. + """ + for route in routes: + included_router = getattr(route, "original_router", None) + if included_router is None: + yield route + else: + yield from _flatten_routes(included_router.routes) + + def _route_methods_by_path(app): - """Return {path: {methods}} for every APIRoute on the app.""" + """Return {path: {methods}} for every APIRoute reachable from the app.""" from fastapi.routing import APIRoute out: dict[str, set[str]] = {} - for r in app.routes: + for r in _flatten_routes(app.routes): if isinstance(r, APIRoute): out.setdefault(r.path, set()).update(r.methods or set()) else: