phenoforge.mcp¶
The thin MCP server. Tool registration only — every tool here delegates
directly to a phenoforge.engine function (AGENTS.md’s thin-server rule).
phenoforge.mcp.server¶
Thin MCP server exposing the engine’s lookup, expansion, and retrieval.
Tool registration only — no business logic (AGENTS.md). Every tool here
delegates directly to a phenoforge.engine function; if a change requires
new logic rather than a new call, it belongs in engine, not here.
Transport is flag-controlled: stdio by default, matching what Claude Desktop and other MCP clients expect today with no hosting step, but switching to HTTP later only requires a different –transport value, not a rewrite.
- phenoforge.mcp.server.configure(db_path, *, library_dir=<object object>, index_path=<object object>, dense_embed_fn=<object object>)[source]¶
Point the server at a different vocabulary database, phenotype library, and/or dense index.
Resets any already-open connection and cached retrievers so the next tool call rebuilds them against the new path(s). Intended for tests; the console entry point never needs to call this.
- Parameters:
db_path (
Path) – Path to a DuckDB database built byscripts/load_vocab.py.library_dir (
Path|None) – Path to a directory of fetched cohort JSON files built byscripts/fetch_phenotype_library.py. Omit to leave unchanged; passNoneexplicitly to reset to the module default.index_path (
Path|None) – Path to a LanceDB index built byscripts/build_index.py. Omit to leave unchanged; passNoneexplicitly to reset to the module default.dense_embed_fn (
Callable[[list[str]],list[list[float]]] |None) – Embedding function for the dense retriever. Tests pass a fake embedder here so they never download BioLORD-2023. Omit to leave unchanged; passNoneexplicitly to reset to the real default embedder.
- Return type:
- phenoforge.mcp.server.lookup_concept(concept_code)¶
Look up a single ICD-10-CM concept by its exact billing code.
Use this when the caller already has a specific code, e.g.
"E11.21", and wants its name and OMOP metadata. Not a search tool — for free-text clinical terms usesearch_conceptsinstead.
- phenoforge.mcp.server.expand_hierarchy(seed_code)¶
Expand an ICD-10-CM code to every code beneath it in the billing hierarchy.
For example, expanding
"E11"(Type 2 diabetes mellitus) returns all of its more specific subtypes, such as"E11.21"(with diabetic nephropathy). Every returned concept is taggedgeneratedprovenance: it is a structural consequence of the vocabulary hierarchy, not a clinically validated inclusion, and should be treated as ungrounded until a human confirms it belongs in the target population.- Parameters:
seed_code (
str) – An exact ICD-10-CM code to expand from, e.g."E11".- Returns:
All descendant concepts, or an empty set if
seed_codedoes not exist or has no descendants.- Return type:
- phenoforge.mcp.server.search_concepts(query, k=10)¶
Search ICD-10-CM concept names for a free-text clinical term or phrase.
Combines lexical (BM25) and semantic (embedding) matching, fused by reciprocal rank, so paraphrased or loosely-worded descriptions (e.g. “sugar disease” for diabetes) are found even without shared exact wording — always prefer this over guessing at exact terminology yourself. Every returned concept is tagged
generatedprovenance and should be treated as ungrounded until a human confirms it.- Parameters:
- Returns:
Fused, deduplicated results ordered by combined relevance. If nothing matches,
conceptsis empty andunmappableexplains why.- Return type:
- phenoforge.mcp.server.find_curated_definition(query)¶
Search the OHDSI Phenotype Library for a validated cohort definition.
Use this FIRST for any population description that plausibly matches a peer-reviewed phenotype (e.g. “type 2 diabetes”, “diabetic ketoacidosis”) — a match here is
curatedprovenance, safe to use as-is citing the cohort id. Only fall back tosearch_concepts/expand_hierarchyif nothing matches; those returngeneratedprovenance requiring human confirmation. This demo bundles a small, hand-picked set of cohorts spanning several disease areas, not the full ~1,100-cohort library — a miss here does not mean no curated definition exists.- Parameters:
query (
str) – Free-text population description.- Returns:
The best-matching cohort’s resolved ICD-10-CM concepts tagged
curated, or an empty set with an explanatoryunmappableentry if nothing in the bundled set matches or the library has not been fetched yet.- Return type:
- phenoforge.mcp.server.explain_inclusion(concept_code, query=None, seed_code=None)¶
Explain why a specific code would be included in a result, via which path.
Nothing from a prior
search_concepts/find_curated_definition/expand_hierarchycall is kept server-side, so this re-runs whichever of those a caller would have made and reports whetherconcept_codeactually comes back — passqueryto check the curated and free-text search paths,seed_codeto check the hierarchy-expansion path, or both to check everything at once. Useful for auditing a generated candidate before confirming it, or for double-checking why a curated code is in a cohort.- Parameters:
concept_code (
str) – An exact ICD-10-CM code to explain, e.g."E11.21".query (
str|None) – Free-text population description or clinical term, to check the same pathsfind_curated_definition/search_conceptswould use.seed_code (
str|None) – An exact ICD-10-CM code, to check the same hierarchy pathexpand_hierarchywould use.
- Returns:
Every path that actually produced
concept_code; emptypathsif none did, including if the code doesn’t exist.- Return type:
- phenoforge.mcp.server.main()[source]¶
Entry point for the
phenoforge-mcpconsole script.- Raises:
FileNotFoundError – If
data/vocab.duckdbhas not been built yet viascripts/load_vocab.py.- Return type: