tooluniverse 0.2.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tooluniverse might be problematic. Click here for more details.

Files changed (190) hide show
  1. tooluniverse/__init__.py +340 -4
  2. tooluniverse/admetai_tool.py +84 -0
  3. tooluniverse/agentic_tool.py +563 -0
  4. tooluniverse/alphafold_tool.py +96 -0
  5. tooluniverse/base_tool.py +129 -6
  6. tooluniverse/boltz_tool.py +207 -0
  7. tooluniverse/chem_tool.py +192 -0
  8. tooluniverse/compose_scripts/__init__.py +1 -0
  9. tooluniverse/compose_scripts/biomarker_discovery.py +293 -0
  10. tooluniverse/compose_scripts/comprehensive_drug_discovery.py +186 -0
  11. tooluniverse/compose_scripts/drug_safety_analyzer.py +89 -0
  12. tooluniverse/compose_scripts/literature_tool.py +34 -0
  13. tooluniverse/compose_scripts/output_summarizer.py +279 -0
  14. tooluniverse/compose_scripts/tool_description_optimizer.py +681 -0
  15. tooluniverse/compose_scripts/tool_discover.py +705 -0
  16. tooluniverse/compose_scripts/tool_graph_composer.py +448 -0
  17. tooluniverse/compose_tool.py +371 -0
  18. tooluniverse/ctg_tool.py +1002 -0
  19. tooluniverse/custom_tool.py +81 -0
  20. tooluniverse/dailymed_tool.py +108 -0
  21. tooluniverse/data/admetai_tools.json +155 -0
  22. tooluniverse/data/agentic_tools.json +1156 -0
  23. tooluniverse/data/alphafold_tools.json +87 -0
  24. tooluniverse/data/boltz_tools.json +9 -0
  25. tooluniverse/data/chembl_tools.json +16 -0
  26. tooluniverse/data/clait_tools.json +108 -0
  27. tooluniverse/data/clinicaltrials_gov_tools.json +326 -0
  28. tooluniverse/data/compose_tools.json +202 -0
  29. tooluniverse/data/dailymed_tools.json +70 -0
  30. tooluniverse/data/dataset_tools.json +646 -0
  31. tooluniverse/data/disease_target_score_tools.json +712 -0
  32. tooluniverse/data/efo_tools.json +17 -0
  33. tooluniverse/data/embedding_tools.json +319 -0
  34. tooluniverse/data/enrichr_tools.json +31 -0
  35. tooluniverse/data/europe_pmc_tools.json +22 -0
  36. tooluniverse/data/expert_feedback_tools.json +10 -0
  37. tooluniverse/data/fda_drug_adverse_event_tools.json +491 -0
  38. tooluniverse/data/fda_drug_labeling_tools.json +1 -1
  39. tooluniverse/data/fda_drugs_with_brand_generic_names_for_tool.py +76929 -148860
  40. tooluniverse/data/finder_tools.json +209 -0
  41. tooluniverse/data/gene_ontology_tools.json +113 -0
  42. tooluniverse/data/gwas_tools.json +1082 -0
  43. tooluniverse/data/hpa_tools.json +333 -0
  44. tooluniverse/data/humanbase_tools.json +47 -0
  45. tooluniverse/data/idmap_tools.json +74 -0
  46. tooluniverse/data/mcp_client_tools_example.json +113 -0
  47. tooluniverse/data/mcpautoloadertool_defaults.json +28 -0
  48. tooluniverse/data/medlineplus_tools.json +141 -0
  49. tooluniverse/data/monarch_tools.json +1 -1
  50. tooluniverse/data/openalex_tools.json +36 -0
  51. tooluniverse/data/opentarget_tools.json +1 -1
  52. tooluniverse/data/output_summarization_tools.json +101 -0
  53. tooluniverse/data/packages/bioinformatics_core_tools.json +1756 -0
  54. tooluniverse/data/packages/categorized_tools.txt +206 -0
  55. tooluniverse/data/packages/cheminformatics_tools.json +347 -0
  56. tooluniverse/data/packages/earth_sciences_tools.json +74 -0
  57. tooluniverse/data/packages/genomics_tools.json +776 -0
  58. tooluniverse/data/packages/image_processing_tools.json +38 -0
  59. tooluniverse/data/packages/machine_learning_tools.json +789 -0
  60. tooluniverse/data/packages/neuroscience_tools.json +62 -0
  61. tooluniverse/data/packages/original_tools.txt +0 -0
  62. tooluniverse/data/packages/physics_astronomy_tools.json +62 -0
  63. tooluniverse/data/packages/scientific_computing_tools.json +560 -0
  64. tooluniverse/data/packages/single_cell_tools.json +453 -0
  65. tooluniverse/data/packages/software_tools.json +4954 -0
  66. tooluniverse/data/packages/structural_biology_tools.json +396 -0
  67. tooluniverse/data/packages/visualization_tools.json +399 -0
  68. tooluniverse/data/pubchem_tools.json +215 -0
  69. tooluniverse/data/pubtator_tools.json +68 -0
  70. tooluniverse/data/rcsb_pdb_tools.json +1332 -0
  71. tooluniverse/data/reactome_tools.json +19 -0
  72. tooluniverse/data/semantic_scholar_tools.json +26 -0
  73. tooluniverse/data/special_tools.json +2 -25
  74. tooluniverse/data/tool_composition_tools.json +88 -0
  75. tooluniverse/data/toolfinderkeyword_defaults.json +34 -0
  76. tooluniverse/data/txagent_client_tools.json +9 -0
  77. tooluniverse/data/uniprot_tools.json +211 -0
  78. tooluniverse/data/url_fetch_tools.json +94 -0
  79. tooluniverse/data/uspto_downloader_tools.json +9 -0
  80. tooluniverse/data/uspto_tools.json +811 -0
  81. tooluniverse/data/xml_tools.json +3275 -0
  82. tooluniverse/dataset_tool.py +296 -0
  83. tooluniverse/default_config.py +165 -0
  84. tooluniverse/efo_tool.py +42 -0
  85. tooluniverse/embedding_database.py +630 -0
  86. tooluniverse/embedding_sync.py +396 -0
  87. tooluniverse/enrichr_tool.py +266 -0
  88. tooluniverse/europe_pmc_tool.py +52 -0
  89. tooluniverse/execute_function.py +1775 -95
  90. tooluniverse/extended_hooks.py +444 -0
  91. tooluniverse/gene_ontology_tool.py +194 -0
  92. tooluniverse/graphql_tool.py +158 -36
  93. tooluniverse/gwas_tool.py +358 -0
  94. tooluniverse/hpa_tool.py +1645 -0
  95. tooluniverse/humanbase_tool.py +389 -0
  96. tooluniverse/logging_config.py +254 -0
  97. tooluniverse/mcp_client_tool.py +764 -0
  98. tooluniverse/mcp_integration.py +413 -0
  99. tooluniverse/mcp_tool_registry.py +925 -0
  100. tooluniverse/medlineplus_tool.py +337 -0
  101. tooluniverse/openalex_tool.py +228 -0
  102. tooluniverse/openfda_adv_tool.py +283 -0
  103. tooluniverse/openfda_tool.py +393 -160
  104. tooluniverse/output_hook.py +1122 -0
  105. tooluniverse/package_tool.py +195 -0
  106. tooluniverse/pubchem_tool.py +158 -0
  107. tooluniverse/pubtator_tool.py +168 -0
  108. tooluniverse/rcsb_pdb_tool.py +38 -0
  109. tooluniverse/reactome_tool.py +108 -0
  110. tooluniverse/remote/boltz/boltz_mcp_server.py +50 -0
  111. tooluniverse/remote/depmap_24q2/depmap_24q2_mcp_tool.py +442 -0
  112. tooluniverse/remote/expert_feedback/human_expert_mcp_tools.py +2013 -0
  113. tooluniverse/remote/expert_feedback/simple_test.py +23 -0
  114. tooluniverse/remote/expert_feedback/start_web_interface.py +188 -0
  115. tooluniverse/remote/expert_feedback/web_only_interface.py +0 -0
  116. tooluniverse/remote/expert_feedback_mcp/human_expert_mcp_server.py +1611 -0
  117. tooluniverse/remote/expert_feedback_mcp/simple_test.py +34 -0
  118. tooluniverse/remote/expert_feedback_mcp/start_web_interface.py +91 -0
  119. tooluniverse/remote/immune_compass/compass_tool.py +327 -0
  120. tooluniverse/remote/pinnacle/pinnacle_tool.py +328 -0
  121. tooluniverse/remote/transcriptformer/transcriptformer_tool.py +586 -0
  122. tooluniverse/remote/uspto_downloader/uspto_downloader_mcp_server.py +61 -0
  123. tooluniverse/remote/uspto_downloader/uspto_downloader_tool.py +120 -0
  124. tooluniverse/remote_tool.py +99 -0
  125. tooluniverse/restful_tool.py +53 -30
  126. tooluniverse/scripts/generate_tool_graph.py +408 -0
  127. tooluniverse/scripts/visualize_tool_graph.py +829 -0
  128. tooluniverse/semantic_scholar_tool.py +62 -0
  129. tooluniverse/smcp.py +2452 -0
  130. tooluniverse/smcp_server.py +975 -0
  131. tooluniverse/test/mcp_server_test.py +0 -0
  132. tooluniverse/test/test_admetai_tool.py +370 -0
  133. tooluniverse/test/test_agentic_tool.py +129 -0
  134. tooluniverse/test/test_alphafold_tool.py +71 -0
  135. tooluniverse/test/test_chem_tool.py +37 -0
  136. tooluniverse/test/test_compose_lieraturereview.py +63 -0
  137. tooluniverse/test/test_compose_tool.py +448 -0
  138. tooluniverse/test/test_dailymed.py +69 -0
  139. tooluniverse/test/test_dataset_tool.py +200 -0
  140. tooluniverse/test/test_disease_target_score.py +56 -0
  141. tooluniverse/test/test_drugbank_filter_examples.py +179 -0
  142. tooluniverse/test/test_efo.py +31 -0
  143. tooluniverse/test/test_enrichr_tool.py +21 -0
  144. tooluniverse/test/test_europe_pmc_tool.py +20 -0
  145. tooluniverse/test/test_fda_adv.py +95 -0
  146. tooluniverse/test/test_fda_drug_labeling.py +91 -0
  147. tooluniverse/test/test_gene_ontology_tools.py +66 -0
  148. tooluniverse/test/test_gwas_tool.py +139 -0
  149. tooluniverse/test/test_hpa.py +625 -0
  150. tooluniverse/test/test_humanbase_tool.py +20 -0
  151. tooluniverse/test/test_idmap_tools.py +61 -0
  152. tooluniverse/test/test_mcp_server.py +211 -0
  153. tooluniverse/test/test_mcp_tool.py +247 -0
  154. tooluniverse/test/test_medlineplus.py +220 -0
  155. tooluniverse/test/test_openalex_tool.py +32 -0
  156. tooluniverse/test/test_opentargets.py +28 -0
  157. tooluniverse/test/test_pubchem_tool.py +116 -0
  158. tooluniverse/test/test_pubtator_tool.py +37 -0
  159. tooluniverse/test/test_rcsb_pdb_tool.py +86 -0
  160. tooluniverse/test/test_reactome.py +54 -0
  161. tooluniverse/test/test_semantic_scholar_tool.py +24 -0
  162. tooluniverse/test/test_software_tools.py +147 -0
  163. tooluniverse/test/test_tool_description_optimizer.py +49 -0
  164. tooluniverse/test/test_tool_finder.py +26 -0
  165. tooluniverse/test/test_tool_finder_llm.py +252 -0
  166. tooluniverse/test/test_tools_find.py +195 -0
  167. tooluniverse/test/test_uniprot_tools.py +74 -0
  168. tooluniverse/test/test_uspto_tool.py +72 -0
  169. tooluniverse/test/test_xml_tool.py +113 -0
  170. tooluniverse/tool_finder_embedding.py +267 -0
  171. tooluniverse/tool_finder_keyword.py +693 -0
  172. tooluniverse/tool_finder_llm.py +699 -0
  173. tooluniverse/tool_graph_web_ui.py +955 -0
  174. tooluniverse/tool_registry.py +416 -0
  175. tooluniverse/uniprot_tool.py +155 -0
  176. tooluniverse/url_tool.py +253 -0
  177. tooluniverse/uspto_tool.py +240 -0
  178. tooluniverse/utils.py +369 -41
  179. tooluniverse/xml_tool.py +369 -0
  180. tooluniverse-1.0.0.dist-info/METADATA +377 -0
  181. tooluniverse-1.0.0.dist-info/RECORD +186 -0
  182. tooluniverse-1.0.0.dist-info/entry_points.txt +9 -0
  183. tooluniverse/generate_mcp_tools.py +0 -113
  184. tooluniverse/mcp_server.py +0 -3340
  185. tooluniverse-0.2.0.dist-info/METADATA +0 -139
  186. tooluniverse-0.2.0.dist-info/RECORD +0 -21
  187. tooluniverse-0.2.0.dist-info/entry_points.txt +0 -4
  188. {tooluniverse-0.2.0.dist-info → tooluniverse-1.0.0.dist-info}/WHEEL +0 -0
  189. {tooluniverse-0.2.0.dist-info → tooluniverse-1.0.0.dist-info}/licenses/LICENSE +0 -0
  190. {tooluniverse-0.2.0.dist-info → tooluniverse-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple Test for Human Expert System
4
+ """
5
+
6
+ from tooluniverse import ToolUniverse
7
+
8
+ # Initialize tool universe
9
+ tooluni = ToolUniverse()
10
+ tooluni.load_tools()
11
+
12
+ # Test queries for expert feedback tools
13
+ test_queries = [
14
+ {"name": "expert_get_expert_status", "arguments": {}},
15
+ {
16
+ "name": "expert_consult_human_expert",
17
+ "arguments": {
18
+ "question": "What is aspirin used for?",
19
+ "specialty": "general",
20
+ "priority": "normal",
21
+ "timeout_minutes": 1,
22
+ },
23
+ },
24
+ ]
25
+
26
+ print(tooluni.tool_specification("expert_consult_human_expert"))
27
+
28
+ for idx, query in enumerate(test_queries):
29
+ print(
30
+ f"\n[{idx+1}] Running tool: {query['name']} with arguments: {query['arguments']}"
31
+ )
32
+ result = tooluni.run(query)
33
+ print("✅ Success. Example output snippet:")
34
+ print(result if isinstance(result, dict) else str(result))
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Start Web Interface Only
4
+
5
+ This script starts only the web interface for expert consultations.
6
+ The MCP server must be running separately.
7
+ """
8
+
9
+ import subprocess
10
+ import sys
11
+ import requests
12
+ from pathlib import Path
13
+
14
+
15
+ def check_mcp_server():
16
+ """Check if MCP server is running"""
17
+ try:
18
+ # Try the MCP endpoint first (follows redirects)
19
+ response = requests.get("http://localhost:7002/mcp/", timeout=10)
20
+ if response.status_code in [200, 405, 406]: # 405/406 means endpoint exists
21
+ return True
22
+
23
+ # Fallback: try without trailing slash
24
+ response = requests.get("http://localhost:7002/mcp", timeout=10)
25
+ if response.status_code in [200, 405, 406, 307]: # 307 redirect is also OK
26
+ return True
27
+
28
+ # Last resort: try the tool endpoint
29
+ response = requests.post(
30
+ "http://localhost:7002/tools/get_expert_status", json={}, timeout=10
31
+ )
32
+ return response.status_code == 200
33
+ except Exception:
34
+ return False
35
+
36
+
37
+ def main():
38
+ print("🌐 Starting Human Expert Web Interface")
39
+ print("=" * 50)
40
+
41
+ # Check if MCP server is running
42
+ print("🔍 Checking for MCP server...")
43
+ if not check_mcp_server():
44
+ print("❌ MCP Server not detected!")
45
+ print("📡 Please start MCP server first:")
46
+ print(" python start_mcp_server.py")
47
+ print(" or")
48
+ print(" python human_expert_mcp_server.py")
49
+ print()
50
+ choice = input("Continue anyway? (y/N): ").strip().lower()
51
+ if choice != "y":
52
+ return 1
53
+ else:
54
+ print("✅ MCP Server is running")
55
+
56
+ print("🌐 Web Interface will run on port 8080")
57
+ print("🖥️ Browser should open automatically")
58
+ print("👨‍⚕️ Expert dashboard will be available at http://localhost:8080")
59
+ print("\nPress Ctrl+C to stop")
60
+ print("=" * 50)
61
+
62
+ # Find the main server script
63
+ script_path = Path(__file__).parent / "human_expert_mcp_server.py"
64
+
65
+ if not script_path.exists():
66
+ print(f"❌ Server script not found: {script_path}")
67
+ return 1
68
+
69
+ try:
70
+ # Check Flask availability
71
+ try:
72
+ pass
73
+
74
+ print("✅ Flask is available")
75
+ except ImportError:
76
+ print("❌ Flask not found. Install with: pip install flask")
77
+ return 1
78
+
79
+ # Start web interface only
80
+ subprocess.run([sys.executable, str(script_path), "--web-only"])
81
+ return 0
82
+ except KeyboardInterrupt:
83
+ print("\n👋 Web Interface stopped")
84
+ return 0
85
+ except Exception as e:
86
+ print(f"❌ Error starting web interface: {str(e)}")
87
+ return 1
88
+
89
+
90
+ if __name__ == "__main__":
91
+ sys.exit(main())
@@ -0,0 +1,327 @@
1
+ """
2
+ COMPASS Prediction Tool - MCP Server
3
+
4
+ This module provides an MCP (Model Context Protocol) server for running immune checkpoint
5
+ inhibitor (ICI) response predictions using the COMPASS (COMprehensive Pathway Analysis
6
+ for Single-cell Sequencing) model. The tool processes tumor gene expression data to
7
+ predict patient responsiveness to immunotherapy.
8
+
9
+ The COMPASS model analyzes gene expression profiles to identify key immune cell
10
+ populations and pathways that contribute to treatment response prediction.
11
+ """
12
+
13
+ import os
14
+ import sys
15
+ import torch
16
+ import pandas as pd
17
+ import asyncio
18
+ import uuid
19
+ from fastmcp import FastMCP
20
+ from typing import List, Tuple, Optional
21
+
22
+ sys.path.insert(
23
+ 0, f'{os.getenv("COMPASS_MODEL_PATH")}/immune-compass/COMPASS'
24
+ ) # noqa: E402
25
+
26
+ from compass import loadcompass # noqa: E402
27
+
28
+ # Initialize MCP Server for COMPASS predictions
29
+ server = FastMCP("COMPASS Prediction SMCP Server")
30
+
31
+
32
+ class CompassTool:
33
+ """
34
+ A comprehensive tool for running immune checkpoint inhibitor (ICI) response predictions
35
+ using the COMPASS model.
36
+
37
+ This class provides functionality to:
38
+ - Load pre-trained COMPASS model checkpoints
39
+ - Process gene expression data (TPM format)
40
+ - Predict ICI treatment response
41
+ - Extract key immune cell populations contributing to predictions
42
+
43
+ The COMPASS model is trained to identify immune cell concepts and pathways
44
+ that are predictive of patient response to checkpoint inhibitor therapy.
45
+ """
46
+
47
+ def __init__(
48
+ self,
49
+ root_path: Optional[str] = None,
50
+ ckp_path: str = "pft_leave_IMVigor210.pt",
51
+ device: str = "cpu",
52
+ ):
53
+ """
54
+ Initializes the COMPASS tool by loading the pre-trained model checkpoint.
55
+
56
+ Args:
57
+ root_path (str, optional): Path to the directory containing model checkpoints.
58
+ If None, uses COMPASS_MODEL_PATH/immune-compass/checkpoint.
59
+ ckp_path (str): Name of the checkpoint file to load.
60
+ Defaults to "pft_leave_IMVigor210.pt" (IMVigor210 cohort).
61
+ device (str): Device for model inference ("cuda" or "cpu"). Defaults to "cuda".
62
+
63
+ Raises:
64
+ FileNotFoundError: If the specified checkpoint file cannot be found.
65
+ Exception: If model loading fails due to compatibility or corruption issues.
66
+ """
67
+ # Construct model checkpoint path
68
+ if root_path is None:
69
+ compass_model_path = os.getenv("COMPASS_MODEL_PATH")
70
+ if compass_model_path is None:
71
+ raise ValueError("COMPASS_MODEL_PATH environment variable is not set")
72
+ if not os.path.exists(
73
+ os.path.join(compass_model_path, "immune-compass", "checkpoint")
74
+ ):
75
+ checkpoint_path = os.path.join(
76
+ compass_model_path, "immune-compass", "checkpoint"
77
+ )
78
+ raise FileNotFoundError(
79
+ f"COMPASS model checkpoint not found at {checkpoint_path}. Please check your COMPASS_MODEL_PATH."
80
+ )
81
+ root_path = os.path.join(compass_model_path, "immune-compass", "checkpoint")
82
+
83
+ self.model_path = os.path.join(root_path, ckp_path)
84
+ self.device = device
85
+
86
+ # Load the pre-trained COMPASS model
87
+ print(f"🛠️ Initializing COMPASS tool from checkpoint: {self.model_path}...")
88
+ self.finetuner = loadcompass(
89
+ self.model_path, weights_only=False, map_location=torch.device(self.device)
90
+ )
91
+
92
+ # Configure device settings for CPU inference if needed
93
+ if self.device == "cpu":
94
+ self.finetuner.device = "cpu"
95
+
96
+ # Display model parameter count for transparency
97
+ self.finetuner.count_parameters()
98
+ print(
99
+ "[COMPASS] Tool initialized successfully (model loaded and ready for predictions)."
100
+ )
101
+
102
+ def _get_top_columns_per_row(
103
+ self,
104
+ df: pd.DataFrame,
105
+ top_n: int = 44,
106
+ exclude: Optional[List[str]] = None,
107
+ ) -> List[List[Tuple[str, float]]]:
108
+ """
109
+ Extracts the top-scoring immune cell concepts for each sample from COMPASS output.
110
+
111
+ This method processes the COMPASS cell concept matrix to identify the most
112
+ influential immune cell populations contributing to the prediction for each sample.
113
+
114
+ Args:
115
+ df (pd.DataFrame): DataFrame containing cell concept scores from COMPASS analysis.
116
+ Rows represent samples, columns represent immune cell concepts.
117
+ top_n (int): Maximum number of top concepts to return per sample. Defaults to 44.
118
+ exclude (List[str]): List of column names to exclude from results.
119
+ Defaults to ['CANCER', 'Reference'].
120
+
121
+ Returns:
122
+ List[List[Tuple[str, float]]]: For each sample, a list of tuples containing
123
+ (concept_name, concept_score) sorted by score descending.
124
+ """
125
+ # Set default excludes safely to avoid mutable default argument
126
+ if exclude is None:
127
+ exclude = ["CANCER", "Reference"]
128
+ # Sort concepts by score (descending) for each sample
129
+ sorted_concepts_indices = [
130
+ row.sort_values(ascending=False).index[:top_n] for _, row in df.iterrows()
131
+ ]
132
+
133
+ results = []
134
+ for i, (_, row) in enumerate(df.iterrows()):
135
+ row_concepts = []
136
+ for col in sorted_concepts_indices[i]:
137
+ # Skip excluded columns (e.g., metadata columns)
138
+ if col not in exclude:
139
+ row_concepts.append((col, row[col]))
140
+ results.append(row_concepts)
141
+ return results
142
+
143
+ def predict(
144
+ self,
145
+ gene_expression_data_path: str,
146
+ threshold: float = 0.5,
147
+ batch_size: int = 128,
148
+ ) -> Tuple[bool, List[Tuple[str, float]]]:
149
+ """
150
+ Performs immune checkpoint inhibitor response prediction on gene expression data.
151
+
152
+ This method processes single-sample tumor gene expression data (in TPM format)
153
+ through the COMPASS model to predict treatment response and identify key
154
+ immune cell populations contributing to the prediction.
155
+
156
+ Args:
157
+ gene_expression_data_path (str): Path to the TPM expression data file.
158
+ to their expression levels in Transcripts Per Million (TPM).
159
+ threshold (float): Prediction probability threshold for classifying samples as responders.
160
+ Values ≥ threshold are classified as responders. Defaults to 0.5.
161
+ batch_size (int): Batch size for model inference. Larger values may improve speed
162
+ but require more memory. Defaults to 128.
163
+
164
+ Returns:
165
+ Tuple[bool, List[Tuple[str, float]]]: A tuple containing:
166
+ - bool: True if predicted as responder (probability ≥ threshold), False otherwise
167
+ - List[Tuple[str, float]]: Top immune cell concepts ranked by importance,
168
+ where each tuple contains (concept_name, concept_score)
169
+
170
+ Raises:
171
+ ValueError: If gene_expression_data is empty or contains invalid values.
172
+ RuntimeError: If model inference fails.
173
+ """
174
+ # Convert gene expression dictionary to DataFrame format expected by COMPASS
175
+ df_tpm = pd.read_pickle(gene_expression_data_path)
176
+ df_tpm.index.name = "Index" # Required by COMPASS for gene indexing
177
+
178
+ # Extract immune cell concepts and generate predictions using COMPASS model
179
+ # dfct contains cell concept scores, dfpred contains response probabilities
180
+ _, _, dfct = self.finetuner.extract(
181
+ df_tpm, batch_size=batch_size, with_gene_level=True
182
+ )
183
+ _, dfpred = self.finetuner.predict(df_tpm)
184
+
185
+ # Extract and rank the most influential immune cell concepts
186
+ sorted_cell_concepts = self._get_top_columns_per_row(dfct)
187
+
188
+ # Classify sample as responder based on prediction probability threshold
189
+ # dfpred.iloc[:, 1] contains the responder probability (column 1)
190
+ responder = dfpred.iloc[:, 1].max() >= threshold
191
+
192
+ return responder, sorted_cell_concepts[0] if sorted_cell_concepts else []
193
+
194
+
195
+ @server.tool()
196
+ async def run_compass_prediction(
197
+ gene_expression_data_path: str,
198
+ threshold: float = 0.5,
199
+ root_path: Optional[str] = None,
200
+ ):
201
+ """
202
+ MCP Tool: Predicts immune checkpoint inhibitor (ICI) response using COMPASS model.
203
+
204
+ This tool analyzes single-sample tumor gene expression data to predict patient
205
+ responsiveness to immune checkpoint inhibitor therapy. The COMPASS model leverages
206
+ immune cell concept analysis to provide both a binary prediction and interpretable
207
+ insights into the immune microenvironment factors driving the prediction.
208
+
209
+ Clinical Context:
210
+ - Designed for precision oncology applications
211
+ - Helps identify patients likely to benefit from ICI therapy
212
+ - Provides mechanistic insights through immune cell population analysis
213
+ - Based on validated cohorts including IMVigor210 (urothelial carcinoma)
214
+
215
+ Args:
216
+ gene_expression_data_path (str): Path to the TPM expression data file.
217
+ Keys should be standard gene symbols (e.g., "CD274", "PDCD1", "CTLA4")
218
+ Values should be normalized expression in TPM (Transcripts Per Million).
219
+ Minimum ~100 genes recommended for reliable predictions.
220
+ threshold (float): Probability threshold for responder classification (0.0-1.0).
221
+ Values ≥ threshold classify sample as likely responder.
222
+ Default 0.5 provides balanced sensitivity/specificity.
223
+ Consider lower thresholds (~0.3) for higher sensitivity.
224
+
225
+ Returns:
226
+ dict: Structured prediction results containing:
227
+ - 'prediction' (dict): Core prediction results with:
228
+ * 'is_responder' (bool): True if predicted responder (probability ≥ threshold)
229
+ * 'top_concepts' (list): Ranked immune cell concepts as dicts with:
230
+ - 'concept' (str): Name of immune cell population/concept
231
+ - 'score' (float): Importance score for this concept
232
+ - 'context_info' (list): Human-readable analysis summary and status messages
233
+ - 'error' (str, optional): Error description if prediction failed
234
+ """
235
+ # Generate unique request ID for tracking and logging
236
+ request_id = str(uuid.uuid4())[:8]
237
+ print(f"[{request_id}] Received COMPASS ICI response prediction request")
238
+
239
+ # Initialize global COMPASS tool instance for MCP server
240
+ # This instance will be used by the MCP tool function to serve predictions
241
+ try:
242
+ compass_tool = CompassTool(root_path=root_path)
243
+ print("✅ COMPASS Prediction tool instance created and ready for MCP server")
244
+ except Exception as e:
245
+ print(f"❌ Error creating COMPASS Prediction tool: {str(e)}")
246
+ print(
247
+ "Please ensure COMPASS_MODEL_PATH is correctly set and model checkpoint exists."
248
+ )
249
+ raise e
250
+
251
+ try:
252
+ # Brief async pause to allow for proper request handling
253
+ await asyncio.sleep(0.1)
254
+
255
+ # Validate input parameters
256
+ if (
257
+ not isinstance(gene_expression_data_path, str)
258
+ or not gene_expression_data_path
259
+ ):
260
+ raise ValueError(
261
+ "Input 'gene_expression_data' must be a non-empty dictionary mapping gene symbols to TPM values."
262
+ )
263
+
264
+ if not (0.0 <= threshold <= 1.0):
265
+ raise ValueError(f"Threshold must be between 0.0 and 1.0, got {threshold}")
266
+
267
+ print(
268
+ f"[{request_id}] Processing {len(gene_expression_data_path)} genes with threshold {threshold}"
269
+ )
270
+
271
+ # Execute COMPASS model prediction
272
+ is_responder, top_concepts = compass_tool.predict(
273
+ gene_expression_data_path, threshold=threshold
274
+ )
275
+
276
+ # Convert concept tuples to JSON-serializable format
277
+ serializable_concepts = [
278
+ {"concept": concept, "score": float(score)}
279
+ for concept, score in top_concepts
280
+ ]
281
+
282
+ # Log successful completion
283
+ response_status = "RESPONDER" if is_responder else "NON-RESPONDER"
284
+ print(
285
+ f"[{request_id}] ✅ COMPASS prediction completed: {response_status} ({len(serializable_concepts)} concepts)"
286
+ )
287
+
288
+ return {
289
+ "prediction": {
290
+ "is_responder": is_responder,
291
+ "top_concepts": serializable_concepts,
292
+ },
293
+ "context_info": [
294
+ "COMPASS prediction completed successfully.",
295
+ f"Sample classified as: {'RESPONDER' if is_responder else 'NON-RESPONDER'}",
296
+ f"Analysis based on {len(gene_expression_data_path)} input genes.",
297
+ f"Top {len(serializable_concepts)} immune cell concepts identified.",
298
+ ],
299
+ }
300
+
301
+ except (ValueError, FileNotFoundError) as e:
302
+ error_message = f"COMPASS prediction validation error: {str(e)}"
303
+ print(f"[{request_id}] {error_message}")
304
+ return {
305
+ "error": error_message,
306
+ "context_info": ["Please check input data format and model availability."],
307
+ }
308
+ except Exception as e:
309
+ error_message = f"Unexpected error during COMPASS prediction: {str(e)}"
310
+ print(f"[{request_id}] {error_message}")
311
+ return {
312
+ "error": error_message,
313
+ "context_info": ["Internal server error occurred during prediction."],
314
+ }
315
+
316
+
317
+ if __name__ == "__main__":
318
+ print("Starting MCP server for COMPASS Immune Response Prediction Tool...")
319
+ print("Model: COMPASS (COMprehensive Pathway Analysis for Single-cell Sequencing)")
320
+ print("Application: Immune Checkpoint Inhibitor Response Prediction")
321
+ print("Server: FastMCP with streamable HTTP transport")
322
+ print("Port: 7003 (configured to avoid conflicts with other biomedical tools)")
323
+
324
+ # Launch the MCP server with COMPASS prediction capabilities
325
+ server.run(
326
+ transport="streamable-http", host="0.0.0.0", port=7003, stateless_http=True
327
+ )