Release tooluniverse 1.4.0 (#385)

This commit is contained in:
Shanghua Gao
2026-07-24 06:47:31 -04:00
committed by GitHub
parent cd3e3debfb
commit 95ea136ff9
12 changed files with 227 additions and 90 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
},
"metadata": {
"description": "ToolUniverse marketplace \u2014 1000+ scientific research tools and 115 specialized skills for biology, chemistry, medicine, and data science.",
"version": "1.3.2"
"version": "1.4.0"
},
"plugins": [
{
+1 -1
View File
@@ -10,7 +10,7 @@ sys.path.insert(0, os.path.abspath("../src"))
project = "ToolUniverse"
copyright = "2025, Shanghua Gao"
author = "Shanghua Gao"
release = "1.3.1"
release = "1.4.0"
# -- General configuration ---------------------------------------------------
extensions = [
+28 -40
View File
@@ -99,24 +99,18 @@ def example_get_variant_details():
print(f"Status: {result.get('status')}")
if result.get('status') == 'success':
if 'formatted_data' in result:
formatted = result['formatted_data']
print(f"Variant ID: {formatted['variant_id']}")
print(f"Accession: {formatted['accession']}")
print(f"Title: {formatted['title']}")
print(f"Object Type: {formatted['obj_type']}")
print(f"Genes: {formatted['genes'][:5]}") # Show first 5 genes
print(f"Clinical Significance: {formatted['clinical_significance']}")
print(f"Review Status: {formatted['review_status']}")
print(f"Chromosome: {formatted['chromosome']}")
print(f"Location: {formatted['location']}")
else:
# Fallback to original format
data = result.get('data', {})
print(f"Variant ID: {result.get('variant_id')}")
print(f"Data type: {type(data)}")
if isinstance(data, dict):
print(f"Data keys: {list(data.keys())}")
# The formatted payload is returned under 'data'; the unprocessed
# esummary record stays available at data['raw_data'].
formatted = result.get('data', {})
print(f"Variant ID: {formatted['variant_id']}")
print(f"Accession: {formatted['accession']}")
print(f"Title: {formatted['title']}")
print(f"Object Type: {formatted['obj_type']}")
print(f"Genes: {formatted['genes'][:5]}") # Show first 5 genes
print(f"Clinical Significance: {formatted['clinical_significance']}")
print(f"Review Status: {formatted['review_status']}")
print(f"Chromosome: {formatted['chromosome']}")
print(f"Location: {formatted['location']}")
else:
print(f"Error: {result.get('error')}")
@@ -138,28 +132,22 @@ def example_get_clinical_significance():
print(f"Status: {result.get('status')}")
if result.get('status') == 'success':
if 'formatted_data' in result:
formatted = result['formatted_data']
print(f"Variant ID: {formatted['variant_id']}")
print("Germline Classification:")
print(f" Description: {formatted['germline_classification']['description']}")
print(f" Review Status: {formatted['germline_classification']['review_status']}")
print(f" Last Evaluated: {formatted['germline_classification']['last_evaluated']}")
print(f" FDA Recognized: {formatted['germline_classification']['fda_recognized']}")
print(f" Traits: {formatted['germline_classification']['traits']}")
print("Clinical Impact:")
print(f" Description: {formatted['clinical_impact']['description']}")
print(f" Review Status: {formatted['clinical_impact']['review_status']}")
print("Oncogenicity:")
print(f" Description: {formatted['oncogenicity']['description']}")
print(f" Review Status: {formatted['oncogenicity']['review_status']}")
else:
# Fallback to original format
data = result.get('data', {})
print(f"Variant ID: {result.get('variant_id')}")
print(f"Data type: {type(data)}")
if isinstance(data, dict):
print(f"Data keys: {list(data.keys())}")
# The formatted payload is returned under 'data'; the unprocessed
# esummary record stays available at data['raw_data'].
formatted = result.get('data', {})
print(f"Variant ID: {formatted['variant_id']}")
print("Germline Classification:")
print(f" Description: {formatted['germline_classification']['description']}")
print(f" Review Status: {formatted['germline_classification']['review_status']}")
print(f" Last Evaluated: {formatted['germline_classification']['last_evaluated']}")
print(f" FDA Recognized: {formatted['germline_classification']['fda_recognized']}")
print(f" Traits: {formatted['germline_classification']['traits']}")
print("Clinical Impact:")
print(f" Description: {formatted['clinical_impact']['description']}")
print(f" Review Status: {formatted['clinical_impact']['review_status']}")
print("Oncogenicity:")
print(f" Description: {formatted['oncogenicity']['description']}")
print(f" Review Status: {formatted['oncogenicity']['review_status']}")
else:
print(f"Error: {result.get('error')}")
+1 -1
View File
@@ -2,7 +2,7 @@
"manifest_version": "0.4",
"name": "tooluniverse",
"display_name": "ToolUniverse",
"version": "1.3.1",
"version": "1.4.0",
"description": "ToolUniverse: an ecosystem for democratizing AI scientists with 2,500+ scientific tools.",
"long_description": "ToolUniverse is an ecosystem for creating AI scientist systems from any large language model (LLM). It standardizes how LLMs interact with tools, integrating thousands of machine learning models, datasets, APIs, and scientific packages for data analysis, knowledge retrieval, and experimental design. This bundle exposes ToolUniverse capability via the Model Context Protocol (MCP), enabling AI assistants to perform complex scientific tasks.",
"author": {
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "tooluniverse-mcpb-native"
version = "1.3.1"
version = "1.4.0"
description = "ToolUniverse MCP Server (Native MCPB bundle)"
requires-python = ">=3.10,<3.14"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "tooluniverse",
"description": "1000+ scientific research tools for biology, chemistry, medicine, and data science. Access PubMed, UniProt, PubChem, TCGA, NHANES, GWAS Catalog, and hundreds more databases through a unified MCP interface. Includes 120+ specialized research skills for genomics, drug discovery, clinical analysis, protein variant interpretation, and more.",
"version": "1.3.2",
"version": "1.4.0",
"author": {
"name": "Shanghua Gao",
"url": "https://shgao.site"
@@ -1,7 +1,7 @@
{
"name": "tooluniverse",
"description": "1000+ scientific research tools for biology, chemistry, medicine, and data science. Access PubMed, UniProt, PubChem, TCGA, NHANES, GWAS Catalog, and hundreds more databases through a unified MCP interface. Includes specialized research skills for genomics, drug discovery, clinical analysis, protein variant interpretation, and more.",
"version": "1.3.2",
"version": "1.4.0",
"author": {
"name": "Shanghua Gao",
"url": "https://shgao.site"
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "tooluniverse"
version = "1.3.1"
version = "1.4.0"
description = "A comprehensive collection of scientific tools for Agentic AI, offering integration with the ToolUniverse SDK and MCP Server to support advanced scientific workflows."
authors = [
{ name = "Shanghua Gao", email = "shanghuagao@gmail.com" }
+2 -2
View File
@@ -7,13 +7,13 @@
"url": "https://github.com/mims-harvard/ToolUniverse",
"source": "github"
},
"version": "1.3.1",
"version": "1.4.0",
"packages": [
{
"registryType": "pypi",
"registryBaseUrl": "https://pypi.org",
"identifier": "tooluniverse",
"version": "1.3.1",
"version": "1.4.0",
"runtimeHint": "uvx",
"transport": {
"type": "stdio"
+15 -14
View File
@@ -559,7 +559,16 @@ class ClinVarGetVariantDetails(ClinVarRESTTool):
variant_data = fetch["variant_data"]
result = fetch["result"]
result["variant_id"] = variant_id
result["formatted_data"] = {
# Fix-R8E-1/R6C-2: assign the formatted payload *over* `result["data"]`
# (the full, unprocessed esummary envelope from _make_request) instead
# of publishing it alongside under a second key. Keeping both roughly
# tripled payload size for no informational gain and made this tool's
# output nearly indistinguishable from ClinVarGetClinicalSignificance's,
# which had the identical duplication. Overwriting preserves that
# de-duplication -- raw access remains at data["raw_data"] -- while
# delivering the payload under the `data` key the return_schema
# declares and every other tool in the registry uses.
result["data"] = {
"variant_id": variant_id,
"accession": variant_data.get("accession", ""),
"obj_type": variant_data.get("obj_type", ""),
@@ -573,15 +582,6 @@ class ClinVarGetVariantDetails(ClinVarRESTTool):
**self._parse_variant_summary(variant_data),
"raw_data": variant_data,
}
# Fix-R8E-1/R6C-2: `result["data"]` (the full, unprocessed esummary
# envelope from _make_request) duplicates the same content already
# exposed at formatted_data["raw_data"] -- keeping both roughly
# tripled payload size for no informational gain and made this
# tool's output nearly indistinguishable from
# ClinVarGetClinicalSignificance's, which has the identical
# duplication. Drop the redundant top-level copy; raw access is
# still available via formatted_data.raw_data.
result.pop("data", None)
return result
@@ -613,7 +613,11 @@ class ClinVarGetClinicalSignificance(ClinVarRESTTool):
clinical_impact = variant_data.get("clinical_impact_classification", {})
oncogenicity = variant_data.get("oncogenicity_classification", {})
result["formatted_data"] = {
# Fix-R8E-1/R6C-2: see the matching fix in ClinVarGetVariantDetails --
# assigning over `result["data"]` (the raw esummary envelope) both
# de-duplicates the payload and honours the `data` key declared in the
# return_schema. Raw access remains at data["raw_data"].
result["data"] = {
"variant_id": variant_id,
"germline_classification": {
"description": germline_class.get("description", ""),
@@ -637,8 +641,5 @@ class ClinVarGetClinicalSignificance(ClinVarRESTTool):
},
"raw_data": variant_data,
}
# Fix-R8E-1/R6C-2: see the matching fix in ClinVarGetVariantDetails
# -- result["data"] duplicates formatted_data["raw_data"].
result.pop("data", None)
return result
+118 -6
View File
@@ -186,11 +186,54 @@
},
"data": {
"type": "object",
"description": "Detailed variant information from ClinVar",
"description": "Formatted variant details. Replaces the raw esummary envelope; the unprocessed record for this variant is preserved at raw_data.",
"properties": {
"result": {
"variant_id": {
"type": "string",
"description": "ClinVar Variation ID that was requested"
},
"accession": {
"type": "string",
"description": "ClinVar accession (e.g. VCV000012345)"
},
"obj_type": {
"type": "string",
"description": "Variant object type (e.g. single nucleotide variant, Deletion)"
},
"chromosome": {
"type": "string",
"description": "Chromosome the variant maps to"
},
"location": {
"type": "string",
"description": "Cytogenetic band of the variant's first variation location"
},
"variation_name": {
"type": "string",
"description": "HGVS-style variation name (e.g. NM_000059.4(BRCA2):c.1234A>G)"
},
"title": {
"type": "string",
"description": "ClinVar record title"
},
"genes": {
"type": "array",
"items": {
"type": "string"
},
"description": "HGNC symbols of genes overlapping the variant"
},
"clinical_significance": {
"type": "string",
"description": "Germline classification description (e.g. Pathogenic, Likely benign, Uncertain significance)"
},
"review_status": {
"type": "string",
"description": "ClinVar review status for the germline classification"
},
"raw_data": {
"type": "object",
"description": "Variant data structure keyed by variant ID"
"description": "Unprocessed NCBI esummary record for this variant"
}
}
},
@@ -257,11 +300,80 @@
},
"data": {
"type": "object",
"description": "Clinical significance information from ClinVar",
"description": "Formatted clinical significance. Replaces the raw esummary envelope; the unprocessed record for this variant is preserved at raw_data.",
"properties": {
"result": {
"variant_id": {
"type": "string",
"description": "ClinVar Variation ID that was requested"
},
"germline_classification": {
"type": "object",
"description": "Summary results for the variant"
"description": "Germline classification for the variant",
"properties": {
"description": {
"type": "string",
"description": "Classification text"
},
"review_status": {
"type": "string",
"description": "ClinVar review status for this classification"
},
"last_evaluated": {
"type": "string",
"description": "Date the classification was last evaluated"
},
"fda_recognized": {
"type": "string",
"description": "FDA-recognized database designation, when present"
},
"traits": {
"type": "array",
"items": {
"type": "string"
},
"description": "Trait/condition names associated with the classification"
}
}
},
"clinical_impact": {
"type": "object",
"description": "Clinical impact classification (somatic variants)",
"properties": {
"description": {
"type": "string",
"description": "Classification text"
},
"review_status": {
"type": "string",
"description": "ClinVar review status for this classification"
},
"last_evaluated": {
"type": "string",
"description": "Date the classification was last evaluated"
}
}
},
"oncogenicity": {
"type": "object",
"description": "Oncogenicity classification",
"properties": {
"description": {
"type": "string",
"description": "Classification text"
},
"review_status": {
"type": "string",
"description": "ClinVar review status for this classification"
},
"last_evaluated": {
"type": "string",
"description": "Date the classification was last evaluated"
}
}
},
"raw_data": {
"type": "object",
"description": "Unprocessed NCBI esummary record for this variant"
}
}
},
@@ -1,11 +1,16 @@
"""Regression guard for Fix-R8E-1/R6C-2: ClinVarGetVariantDetails and
ClinVarGetClinicalSignificance both returned the full raw esummary blob
twice -- once as the top-level "data" key (from the underlying
_make_request envelope) and again inside formatted_data["raw_data"] --
roughly tripling payload size for no informational gain and making the
two tools' outputs nearly indistinguishable from each other. The
top-level duplicate is now dropped; raw access remains available via
formatted_data.raw_data.
_make_request envelope) and again inside the formatted payload's
"raw_data" -- roughly tripling payload size for no informational gain and
making the two tools' outputs nearly indistinguishable from each other.
The formatted payload is now assigned *over* the raw envelope, so the
duplication stays gone and the payload is delivered under the "data" key
that both tools' return_schema declares (it was previously published under
a non-standard "formatted_data" key that no other tool in the registry
uses, so callers reading result["data"] by convention silently saw
nothing). Raw access remains available at data["raw_data"].
"""
from unittest.mock import patch
@@ -23,10 +28,18 @@ _VARIANT_DATA = {
"accession": "VCV000000009",
"obj_type": "single nucleotide variant",
"chr_sort": "13",
"variation_set": [{"variation_loc": [{"band": "13q13.1"}], "variation_name": "NM_007294.4(BRCA1):c.68_69delAG"}],
"variation_set": [
{
"variation_loc": [{"band": "13q13.1"}],
"variation_name": "NM_007294.4(BRCA1):c.68_69delAG",
}
],
"title": "NM_007294.4(BRCA1):c.68_69delAG",
"genes": [{"symbol": "BRCA1"}],
"germline_classification": {"description": "Pathogenic", "review_status": "reviewed"},
"germline_classification": {
"description": "Pathogenic",
"review_status": "reviewed",
},
"clinical_impact_classification": {},
"oncogenicity_classification": {},
}
@@ -38,31 +51,54 @@ _FETCH_RESULT = {
}
def _fetch_variant_patch():
def _count_variant_copies(node):
"""Count how many times the raw variant record appears anywhere in the
response, so duplication is caught no matter where it is reintroduced."""
if isinstance(node, dict):
found = 1 if node == _VARIANT_DATA else 0
return found + sum(_count_variant_copies(v) for v in node.values())
if isinstance(node, list):
return sum(_count_variant_copies(v) for v in node)
return 0
def _fetch_variant_patch(tool_cls):
return patch.object(
ClinVarGetVariantDetails.__bases__[0],
tool_cls.__bases__[0],
"_fetch_variant",
return_value={"variant_data": _VARIANT_DATA, "result": dict(_FETCH_RESULT)},
)
def test_get_variant_details_has_no_duplicate_top_level_data():
def _assert_payload_contract(result):
# Payload is delivered under the schema-declared key, not the old one.
assert "data" in result
assert "formatted_data" not in result
# Raw access is preserved...
assert result["data"]["raw_data"] == _VARIANT_DATA
# ...but the raw esummary envelope no longer sits at the top level, and
# the variant record is present exactly once in the whole response.
assert "result" not in result["data"]
assert result["data"] != _FETCH_RESULT["data"]
assert _count_variant_copies(result) == 1
def test_get_variant_details_has_no_duplicate_raw_data():
tool = ClinVarGetVariantDetails({"name": "ClinVar_get_variant_details"})
with _fetch_variant_patch():
with _fetch_variant_patch(ClinVarGetVariantDetails):
result = tool.run({"variant_id": "9"})
assert "data" not in result
assert result["formatted_data"]["raw_data"] == _VARIANT_DATA
_assert_payload_contract(result)
assert result["data"]["variant_id"] == "9"
assert result["data"]["accession"] == "VCV000000009"
assert result["data"]["clinical_significance"] == "Pathogenic"
def test_get_clinical_significance_has_no_duplicate_top_level_data():
def test_get_clinical_significance_has_no_duplicate_raw_data():
tool = ClinVarGetClinicalSignificance({"name": "ClinVar_get_clinical_significance"})
with patch.object(
ClinVarGetClinicalSignificance.__bases__[0],
"_fetch_variant",
return_value={"variant_data": _VARIANT_DATA, "result": dict(_FETCH_RESULT)},
):
with _fetch_variant_patch(ClinVarGetClinicalSignificance):
result = tool.run({"variant_id": "9"})
assert "data" not in result
assert result["formatted_data"]["raw_data"] == _VARIANT_DATA
_assert_payload_contract(result)
assert result["data"]["variant_id"] == "9"
assert result["data"]["germline_classification"]["description"] == "Pathogenic"