Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 69 additions & 5 deletions src/vfbquery/vfb_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ class TermInfoOutputSchema(Schema):
Meta = fields.Dict(keys=fields.String(), values=fields.String(), required=True)
Tags = fields.List(fields.String(), required=True)
Queries = fields.List(QueryField(), required=False)
# RelatedTools: MCP tools (other than run_query) that are useful for this entity.
# Each entry: {"tool": "<tool_name>", "label": "...", "default_args": {...}}.
# Distinct from Queries because these are not dispatched via run_query — the
# client should call the named tool directly with default_args.
RelatedTools = fields.List(fields.Dict(), required=False)
IsIndividual = fields.Bool(missing=False, required=False)
Images = fields.Dict(keys=fields.String(), values=fields.List(fields.Nested(ImageSchema()), missing={}), required=False, allow_none=True)
IsClass = fields.Bool(missing=False, required=False)
Expand Down Expand Up @@ -400,6 +405,7 @@ def term_info_parse_object(results, short_form):
termInfo["SuperTypes"] = []
termInfo["Tags"] = []
termInfo["Queries"] = []
termInfo["RelatedTools"] = []
termInfo["IsClass"] = False
termInfo["IsIndividual"] = False
termInfo["IsTemplate"] = False
Expand Down Expand Up @@ -740,8 +746,9 @@ def term_info_parse_object(results, short_form):

# NeuronsPartHere query - for Class+Anatomy terms (synaptic neuropils, etc.)
# Matches XMI criteria: Class + Synaptic_neuropil, or other anatomical regions
if contains_all_tags(termInfo["SuperTypes"], ["Class"]) and (
"Synaptic_neuropil" in termInfo["SuperTypes"] or
# Excluded for neuron classes: "neurons with some part in <a neuron>" is not a meaningful query
if contains_all_tags(termInfo["SuperTypes"], ["Class"]) and "Neuron" not in termInfo["SuperTypes"] and (
"Synaptic_neuropil" in termInfo["SuperTypes"] or
"Anatomy" in termInfo["SuperTypes"]
):
q = NeuronsPartHere_to_schema(termInfo["Name"], {"short_form": vfbTerm.term.core.short_form})
Expand Down Expand Up @@ -783,9 +790,11 @@ def term_info_parse_object(results, short_form):
q = ComponentsOf_to_schema(termInfo["Name"], {"short_form": vfbTerm.term.core.short_form})
queries.append(q)

# PartsOf query - for any Class
# PartsOf query - for any Class except neuron classes
# Matches XMI criteria: Class (any)
if contains_all_tags(termInfo["SuperTypes"], ["Class"]):
# Excluded for neuron classes: anatomical sub-parts of a neuron type are not modelled
# in the ontology in a way that makes this query useful at the class level.
if contains_all_tags(termInfo["SuperTypes"], ["Class"]) and "Neuron" not in termInfo["SuperTypes"]:
q = PartsOf_to_schema(termInfo["Name"], {"short_form": vfbTerm.term.core.short_form})
queries.append(q)

Expand Down Expand Up @@ -941,17 +950,72 @@ def term_info_parse_object(results, short_form):
queries.append(q)
q = UpstreamClassConnectivity_to_schema(termInfo["Name"], {"short_form": vfbTerm.term.core.short_form})
queries.append(q)


# Hierarchy entries — surfaced in RelatedTools, dispatched via the
# get_hierarchy MCP tool rather than run_query.
sf_for_hier = vfbTerm.term.core.short_form
# subclass_of hierarchy is meaningful for cell-type taxonomies.
# Gate: Class + Cell (matches the "Cell"-bounded ancestor filter inside
# get_hierarchy itself).
if termInfo["SuperTypes"] and contains_all_tags(termInfo["SuperTypes"], ["Class", "Cell"]):
termInfo["RelatedTools"].append({
"tool": "get_hierarchy",
"label": f"Cell-type hierarchy of {termInfo['Name']}",
"default_args": {
"id": sf_for_hier,
"relationship": "subclass_of",
"direction": "both",
"max_depth": 1,
},
})
# part_of hierarchy is meaningful for nervous-system regions
# (brain, neuropils, ganglia, tracts), but NOT for cells/neurons or
# non-neural anatomy. Special-case the nervous system root, which lacks
# the "Nervous_system" SuperType because it isn't part_of itself.
is_ns_region = (
termInfo["SuperTypes"]
and contains_all_tags(termInfo["SuperTypes"], ["Class", "Nervous_system"])
and "Cell" not in termInfo["SuperTypes"]
)
is_ns_root = sf_for_hier == "FBbt_00005093"
if is_ns_region or is_ns_root:
termInfo["RelatedTools"].append({
"tool": "get_hierarchy",
"label": f"Region containment hierarchy of {termInfo['Name']}",
"default_args": {
"id": sf_for_hier,
"relationship": "part_of",
"direction": "both",
"max_depth": 1,
},
})

# FlyBase stock finder — for Feature terms (FBgn/FBal/FBti/FBtp/FBco/FBst)
sf = vfbTerm.term.core.short_form
if sf.startswith(("FBgn", "FBal", "FBti", "FBtp", "FBco", "FBst")):
q = FindStocks_to_schema(termInfo["Name"], {"short_form": sf})
queries.append(q)
# Also surface the dedicated find_stocks MCP tool, which exposes
# the optional collection_filter parameter (Bloomington, Kyoto,
# VDRC, etc.) that the run_query/FindStocks path does not.
termInfo["RelatedTools"].append({
"tool": "find_stocks",
"label": f"Find fly stocks for {termInfo['Name']} (with optional stock-centre filter)",
"default_args": {"feature_id": sf},
})

# FlyBase combination publications — for FBco terms
if sf.startswith("FBco"):
q = FindComboPublications_to_schema(termInfo["Name"], {"short_form": sf})
queries.append(q)
# Also surface the dedicated find_combo_publications MCP tool,
# which returns full per-publication metadata (DOI, PMID, miniref,
# year) ready for citation rendering.
termInfo["RelatedTools"].append({
"tool": "find_combo_publications",
"label": f"Find publications for {termInfo['Name']} (with full citation metadata)",
"default_args": {"fbco_id": sf},
})

# For individuals that are painted domains of anatomical regions, add parent class queries
if termInfo["IsIndividual"] and termInfo["Technique"] and any('computer' in t.lower() for t in termInfo["Technique"]):
Expand Down
Loading