tooluniverse 0.1.4__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 (187) 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 +544 -168
  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 +82 -58
  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-0.1.4.dist-info → tooluniverse-1.0.0.dist-info}/WHEEL +1 -1
  183. tooluniverse-1.0.0.dist-info/entry_points.txt +9 -0
  184. tooluniverse-0.1.4.dist-info/METADATA +0 -141
  185. tooluniverse-0.1.4.dist-info/RECORD +0 -18
  186. {tooluniverse-0.1.4.dist-info → tooluniverse-1.0.0.dist-info}/licenses/LICENSE +0 -0
  187. {tooluniverse-0.1.4.dist-info → tooluniverse-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,975 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ SMCP Server Entry Point
4
+
5
+ This module provides the command-line entry point for running the SMCP (Scientific Model Context Protocol) server.
6
+ It creates a minimal SMCP server that exposes all ToolUniverse tools as MCP tools.
7
+ """
8
+
9
+ import argparse
10
+ import sys
11
+ from .smcp import SMCP
12
+
13
+
14
+ def run_http_server():
15
+ """
16
+ Run SMCP server with streamable-http transport on localhost:8000
17
+
18
+ This function provides compatibility with the original MCP server's run_server function.
19
+ """
20
+ parser = argparse.ArgumentParser(
21
+ description="Start SMCP (Scientific Model Context Protocol) Server with HTTP transport",
22
+ formatter_class=argparse.RawDescriptionHelpFormatter,
23
+ epilog="""
24
+ Examples:
25
+ # Start server with all tools using HTTP transport
26
+ tooluniverse-smcp-server
27
+
28
+ # Start with specific categories
29
+ tooluniverse-smcp-server --categories uniprot ChEMBL opentarget
30
+
31
+ # Start with hooks enabled
32
+ tooluniverse-smcp-server --hooks-enabled
33
+
34
+ # Start with specific hook type
35
+ tooluniverse-smcp-server --hook-type SummarizationHook
36
+
37
+ # Start with custom hook configuration
38
+ tooluniverse-smcp-server --hook-config-file /path/to/hook_config.json
39
+ """,
40
+ )
41
+
42
+ # Hook configuration options
43
+ hook_group = parser.add_argument_group("Hook Configuration")
44
+ hook_group.add_argument(
45
+ "--hooks-enabled",
46
+ action="store_true",
47
+ help="Enable output processing hooks (default: False)",
48
+ )
49
+ hook_group.add_argument(
50
+ "--hook-type",
51
+ choices=["SummarizationHook", "FileSaveHook"],
52
+ help="Simple hook type selection (SummarizationHook or FileSaveHook)",
53
+ )
54
+ hook_group.add_argument(
55
+ "--hook-config-file",
56
+ type=str,
57
+ help="Path to custom hook configuration JSON file",
58
+ )
59
+
60
+ # Server configuration options
61
+ server_group = parser.add_argument_group("Server Configuration")
62
+ server_group.add_argument(
63
+ "--host", default="127.0.0.1", help="Server host address (default: 127.0.0.1)"
64
+ )
65
+ server_group.add_argument(
66
+ "--port", type=int, default=8000, help="Server port (default: 8000)"
67
+ )
68
+ server_group.add_argument(
69
+ "--name",
70
+ default="ToolUniverse SMCP Server",
71
+ help="Server name (default: ToolUniverse SMCP Server)",
72
+ )
73
+
74
+ args = parser.parse_args()
75
+
76
+ try:
77
+ print("šŸš€ Starting ToolUniverse SMCP Server...")
78
+ print("šŸ“” Transport: streamable-http")
79
+ print(f"🌐 Address: http://{args.host}:{args.port}")
80
+ print(f"šŸ·ļø Name: {args.name}")
81
+
82
+ # Load hook configuration if specified
83
+ hook_config = None
84
+ if args.hook_config_file:
85
+ import json
86
+
87
+ with open(args.hook_config_file, "r") as f:
88
+ hook_config = json.load(f)
89
+ print(f"šŸ”— Hook config loaded from: {args.hook_config_file}")
90
+
91
+ # Determine hook settings
92
+ hooks_enabled = (
93
+ args.hooks_enabled or args.hook_type is not None or hook_config is not None
94
+ )
95
+ if hooks_enabled:
96
+ if args.hook_type:
97
+ print(f"šŸ”— Hooks enabled: {args.hook_type}")
98
+ elif hook_config:
99
+ hook_count = len(hook_config.get("hooks", []))
100
+ print(f"šŸ”— Hooks enabled: {hook_count} custom hooks")
101
+ else:
102
+ print("šŸ”— Hooks enabled: default configuration")
103
+ else:
104
+ print("šŸ”— Hooks disabled")
105
+
106
+ print()
107
+
108
+ # Create SMCP server with hook support
109
+ server = SMCP(
110
+ name=args.name,
111
+ auto_expose_tools=True,
112
+ search_enabled=True,
113
+ max_workers=5,
114
+ stateless_http=True, # Enable stateless mode for MCPAutoLoaderTool compatibility
115
+ hooks_enabled=hooks_enabled,
116
+ hook_config=hook_config,
117
+ hook_type=args.hook_type,
118
+ )
119
+
120
+ # Run server with streamable-http transport
121
+ server.run_simple(transport="http", host=args.host, port=args.port)
122
+
123
+ except KeyboardInterrupt:
124
+ print("\nšŸ›‘ Server stopped by user")
125
+ sys.exit(0)
126
+ except Exception as e:
127
+ print(f"āŒ Error starting server: {e}")
128
+ sys.exit(1)
129
+
130
+
131
+ def run_stdio_server():
132
+ """
133
+ Run SMCP server with stdio transport for Claude Desktop integration
134
+
135
+ This function provides compatibility with the original MCP server's run_claude_desktop function.
136
+ It accepts the same arguments as run_smcp_server but forces transport='stdio'.
137
+ """
138
+ parser = argparse.ArgumentParser(
139
+ description="Start SMCP (Scientific Model Context Protocol) Server with stdio transport",
140
+ formatter_class=argparse.RawDescriptionHelpFormatter,
141
+ epilog="""
142
+ Examples:
143
+ # Start server with all tools using stdio transport (hooks enabled by default)
144
+ tooluniverse-stdio
145
+
146
+ # Start with specific categories
147
+ tooluniverse-stdio --categories uniprot ChEMBL opentarget
148
+
149
+ # Disable hooks
150
+ tooluniverse-stdio --no-hooks
151
+
152
+ # Use FileSaveHook instead of SummarizationHook
153
+ tooluniverse-stdio --hook-type FileSaveHook
154
+
155
+ # Use custom hook configuration
156
+ tooluniverse-stdio --hook-config-file /path/to/hook_config.json
157
+
158
+ # Start with categories but exclude specific tools
159
+ tooluniverse-stdio --categories uniprot ChEMBL --exclude-tools "ChEMBL_get_molecule_by_chembl_id"
160
+
161
+ # Start with all tools but exclude entire categories
162
+ tooluniverse-stdio --exclude-categories mcp_auto_loader_boltz mcp_auto_loader_expert_feedback
163
+
164
+ # Load only specific tools by name
165
+ tooluniverse-stdio --include-tools "UniProt_get_entry_by_accession" "ChEMBL_get_molecule_by_chembl_id"
166
+
167
+ # Load tools from a file
168
+ tooluniverse-stdio --tools-file "/path/to/tool_names.txt"
169
+
170
+ # Load additional config files
171
+ tooluniverse-stdio --tool-config-files "custom:/path/to/custom_tools.json"
172
+
173
+ # Include/exclude specific tool types
174
+ tooluniverse-stdio --include-tool-types "OpenTarget" "ToolFinderEmbedding"
175
+ tooluniverse-stdio --exclude-tool-types "ToolFinderLLM" "Unknown"
176
+
177
+ # List available categories
178
+ tooluniverse-stdio --list-categories
179
+
180
+ # List all available tools
181
+ tooluniverse-stdio --list-tools
182
+
183
+ # Start minimal server with just search tools
184
+ tooluniverse-stdio --categories special_tools tool_finder
185
+ """,
186
+ )
187
+
188
+ # Tool selection options
189
+ tool_group = parser.add_mutually_exclusive_group()
190
+ tool_group.add_argument(
191
+ "--categories",
192
+ nargs="*",
193
+ metavar="CATEGORY",
194
+ help="Specific tool categories to load (e.g., uniprot ChEMBL opentarget). Use --list-categories to see available options.",
195
+ )
196
+ tool_group.add_argument(
197
+ "--list-categories",
198
+ action="store_true",
199
+ help="List all available tool categories and exit",
200
+ )
201
+
202
+ # Tool exclusion options
203
+ parser.add_argument(
204
+ "--exclude-tools",
205
+ nargs="*",
206
+ metavar="TOOL_NAME",
207
+ help='Specific tool names to exclude from loading (e.g., "tool1" "tool2"). Can be used with --categories.',
208
+ )
209
+ parser.add_argument(
210
+ "--exclude-categories",
211
+ nargs="*",
212
+ metavar="CATEGORY",
213
+ help="Tool categories to exclude from loading (e.g., mcp_auto_loader_boltz). Can be used with --categories.",
214
+ )
215
+
216
+ # Tool inclusion options
217
+ parser.add_argument(
218
+ "--include-tools",
219
+ nargs="*",
220
+ metavar="TOOL_NAME",
221
+ help="Specific tool names to include (only these tools will be loaded). Overrides category selection.",
222
+ )
223
+ parser.add_argument(
224
+ "--tools-file",
225
+ metavar="FILE_PATH",
226
+ help="Path to text file containing tool names to include (one per line). Overrides category selection.",
227
+ )
228
+ parser.add_argument(
229
+ "--tool-config-files",
230
+ nargs="*",
231
+ metavar="CATEGORY:PATH",
232
+ help='Additional tool config files to load. Format: "category:/path/to/config.json"',
233
+ )
234
+
235
+ # Tool type filtering options
236
+ parser.add_argument(
237
+ "--include-tool-types",
238
+ nargs="*",
239
+ metavar="TOOL_TYPE",
240
+ help="Specific tool types to include (e.g., OpenTarget ToolFinderEmbedding). Only tools of these types will be loaded.",
241
+ )
242
+ parser.add_argument(
243
+ "--exclude-tool-types",
244
+ nargs="*",
245
+ metavar="TOOL_TYPE",
246
+ help="Tool types to exclude from loading (e.g., ToolFinderLLM Unknown). Useful for excluding specific tool type classes.",
247
+ )
248
+
249
+ # Listing options
250
+ parser.add_argument(
251
+ "--list-tools", action="store_true", help="List all available tools and exit"
252
+ )
253
+
254
+ # Server configuration (stdio-specific)
255
+ parser.add_argument(
256
+ "--name",
257
+ default="SMCP ToolUniverse Server",
258
+ help="Server name (default: SMCP ToolUniverse Server)",
259
+ )
260
+ parser.add_argument(
261
+ "--no-search",
262
+ action="store_true",
263
+ help="Disable intelligent search functionality",
264
+ )
265
+ parser.add_argument(
266
+ "--max-workers",
267
+ type=int,
268
+ default=5,
269
+ help="Maximum worker threads for concurrent execution (default: 5)",
270
+ )
271
+ parser.add_argument(
272
+ "--verbose", "-v", action="store_true", help="Enable verbose logging"
273
+ )
274
+
275
+ # Hook configuration options (default enabled for stdio)
276
+ hook_group = parser.add_argument_group("Hook Configuration")
277
+ hook_group.add_argument(
278
+ "--no-hooks",
279
+ action="store_true",
280
+ help="Disable output processing hooks (default: enabled for stdio)",
281
+ )
282
+ hook_group.add_argument(
283
+ "--hook-type",
284
+ choices=["SummarizationHook", "FileSaveHook"],
285
+ default="SummarizationHook",
286
+ help="Hook type to use (default: SummarizationHook)",
287
+ )
288
+ hook_group.add_argument(
289
+ "--hook-config-file",
290
+ type=str,
291
+ help="Path to custom hook configuration JSON file",
292
+ )
293
+
294
+ args = parser.parse_args()
295
+
296
+ # Handle --list-categories
297
+ if args.list_categories:
298
+ try:
299
+ from .execute_function import ToolUniverse
300
+
301
+ tu = ToolUniverse()
302
+ tool_types = tu.get_tool_types()
303
+
304
+ print("Available tool categories:")
305
+ print("=" * 50)
306
+
307
+ # Group categories for better readability
308
+ scientific_db = []
309
+ literature = []
310
+ software = []
311
+ special = []
312
+ clinical = []
313
+ other = []
314
+
315
+ for category in sorted(tool_types):
316
+ if category in [
317
+ "uniprot",
318
+ "ChEMBL",
319
+ "opentarget",
320
+ "pubchem",
321
+ "hpa",
322
+ "rcsb_pdb",
323
+ "reactome",
324
+ "go",
325
+ ]:
326
+ scientific_db.append(category)
327
+ elif category in [
328
+ "EuropePMC",
329
+ "semantic_scholar",
330
+ "pubtator",
331
+ "OpenAlex",
332
+ ]:
333
+ literature.append(category)
334
+ elif category.startswith("software_"):
335
+ software.append(category)
336
+ elif category in [
337
+ "special_tools",
338
+ "tool_finder",
339
+ "tool_composition",
340
+ "agents",
341
+ ]:
342
+ special.append(category)
343
+ elif category in [
344
+ "clinical_trials",
345
+ "fda_drug_label",
346
+ "fda_drug_adverse_event",
347
+ "dailymed",
348
+ "medlineplus",
349
+ ]:
350
+ clinical.append(category)
351
+ else:
352
+ other.append(category)
353
+
354
+ if scientific_db:
355
+ print("\nšŸ”¬ Scientific Databases:")
356
+ for cat in scientific_db:
357
+ print(f" {cat}")
358
+
359
+ if literature:
360
+ print("\nšŸ“š Literature & Knowledge:")
361
+ for cat in literature:
362
+ print(f" {cat}")
363
+
364
+ if clinical:
365
+ print("\nšŸ„ Clinical & Drug Information:")
366
+ for cat in clinical:
367
+ print(f" {cat}")
368
+
369
+ if software:
370
+ print("\nšŸ’» Software Tools:")
371
+ for cat in software[:5]: # Show first 5
372
+ print(f" {cat}")
373
+ if len(software) > 5:
374
+ print(f" ... and {len(software) - 5} more software categories")
375
+
376
+ if special:
377
+ print("\nšŸ›  Special & Meta Tools:")
378
+ for cat in special:
379
+ print(f" {cat}")
380
+
381
+ if other:
382
+ print("\nšŸ“‚ Other Categories:")
383
+ for cat in other:
384
+ print(f" {cat}")
385
+
386
+ print(f"\nTotal: {len(tool_types)} categories available")
387
+ print("\nCommon combinations:")
388
+ print(" Scientific research: uniprot ChEMBL opentarget pubchem hpa")
389
+ print(" Drug discovery: ChEMBL fda_drug_label clinical_trials pubchem")
390
+ print(" Literature analysis: EuropePMC semantic_scholar pubtator")
391
+ print(" Minimal setup: special_tools tool_finder")
392
+
393
+ except Exception as e:
394
+ print(f"āŒ Error listing categories: {e}")
395
+ sys.exit(1)
396
+ return
397
+
398
+ # Handle --list-tools
399
+ if args.list_tools:
400
+ try:
401
+ from .execute_function import ToolUniverse
402
+
403
+ tu = ToolUniverse()
404
+ tu.load_tools() # Load all tools to list them
405
+
406
+ print("Available tools:")
407
+ print("=" * 50)
408
+
409
+ # Group tools by category
410
+ tools_by_category = {}
411
+ for tool in tu.all_tools:
412
+ tool_type = getattr(tool, "tool_type", "unknown")
413
+ if tool_type not in tools_by_category:
414
+ tools_by_category[tool_type] = []
415
+ tools_by_category[tool_type].append(tool.name)
416
+
417
+ total_tools = 0
418
+ for category in sorted(tools_by_category.keys()):
419
+ tools = sorted(tools_by_category[category])
420
+ print(f"\nšŸ“ {category} ({len(tools)} tools):")
421
+ for tool in tools[:10]: # Show first 10 tools per category
422
+ print(f" {tool}")
423
+ if len(tools) > 10:
424
+ print(f" ... and {len(tools) - 10} more tools")
425
+ total_tools += len(tools)
426
+
427
+ print(f"\nTotal: {total_tools} tools available")
428
+ print("\nNote: Use --exclude-tools to exclude specific tools by name")
429
+ print(" Use --exclude-categories to exclude entire categories")
430
+
431
+ except Exception as e:
432
+ print(f"āŒ Error listing tools: {e}")
433
+ sys.exit(1)
434
+ return
435
+
436
+ try:
437
+ print(f"šŸš€ Starting {args.name}...")
438
+ print("šŸ“” Transport: stdio (for Claude Desktop)")
439
+ print(f"šŸ” Search enabled: {not args.no_search}")
440
+
441
+ if args.categories is not None:
442
+ if len(args.categories) == 0:
443
+ print("šŸ“‚ No categories specified, loading all tools")
444
+ tool_categories = None
445
+ else:
446
+ print(f"šŸ“‚ Tool categories: {', '.join(args.categories)}")
447
+ tool_categories = args.categories
448
+ else:
449
+ print("šŸ“‚ Loading all tool categories")
450
+ tool_categories = None
451
+
452
+ # Handle exclusions and inclusions
453
+ exclude_tools = args.exclude_tools or []
454
+ exclude_categories = args.exclude_categories or []
455
+ include_tools = args.include_tools or []
456
+ tools_file = args.tools_file
457
+ include_tool_types = args.include_tool_types or []
458
+ exclude_tool_types = args.exclude_tool_types or []
459
+
460
+ # Parse tool config files
461
+ tool_config_files = {}
462
+ if args.tool_config_files:
463
+ for config_spec in args.tool_config_files:
464
+ if ":" in config_spec:
465
+ category, path = config_spec.split(":", 1)
466
+ tool_config_files[category] = path
467
+ else:
468
+ print(f"āŒ Invalid tool config file format: {config_spec}")
469
+ print(" Expected format: 'category:/path/to/config.json'")
470
+ sys.exit(1)
471
+
472
+ if exclude_tools:
473
+ print(f"🚫 Excluding tools: {', '.join(exclude_tools)}")
474
+ if exclude_categories:
475
+ print(f"🚫 Excluding categories: {', '.join(exclude_categories)}")
476
+ if include_tools:
477
+ print(f"āœ… Including only specific tools: {len(include_tools)} tools")
478
+ if tools_file:
479
+ print(f"šŸ“„ Loading tools from file: {tools_file}")
480
+ if tool_config_files:
481
+ print(f"šŸ“¦ Additional config files: {', '.join(tool_config_files.keys())}")
482
+ if include_tool_types:
483
+ print(f"šŸŽÆ Including tool types: {', '.join(include_tool_types)}")
484
+ if exclude_tool_types:
485
+ print(f"🚫 Excluding tool types: {', '.join(exclude_tool_types)}")
486
+
487
+ # Load hook configuration if specified
488
+ hook_config = None
489
+ if args.hook_config_file:
490
+ import json
491
+
492
+ with open(args.hook_config_file, "r") as f:
493
+ hook_config = json.load(f)
494
+ print(f"šŸ”— Hook config loaded from: {args.hook_config_file}")
495
+
496
+ # Determine hook settings (default enabled for stdio)
497
+ hooks_enabled = not args.no_hooks
498
+ if hooks_enabled:
499
+ if args.hook_type:
500
+ print(f"šŸ”— Hooks enabled: {args.hook_type}")
501
+ elif hook_config:
502
+ hook_count = len(hook_config.get("hooks", []))
503
+ print(f"šŸ”— Hooks enabled: {hook_count} custom hooks")
504
+ else:
505
+ print(f"šŸ”— Hooks enabled: {args.hook_type} (default)")
506
+ else:
507
+ print("šŸ”— Hooks disabled")
508
+
509
+ print(f"⚔ Max workers: {args.max_workers}")
510
+ print()
511
+
512
+ # Create SMCP server with hook support
513
+ server = SMCP(
514
+ name=args.name,
515
+ tool_categories=tool_categories,
516
+ exclude_tools=exclude_tools,
517
+ exclude_categories=exclude_categories,
518
+ include_tools=include_tools,
519
+ tools_file=tools_file,
520
+ tool_config_files=tool_config_files,
521
+ include_tool_types=include_tool_types,
522
+ exclude_tool_types=exclude_tool_types,
523
+ search_enabled=not args.no_search,
524
+ max_workers=args.max_workers,
525
+ stateless_http=True, # Enable stateless mode for MCPAutoLoaderTool compatibility
526
+ hooks_enabled=hooks_enabled,
527
+ hook_config=hook_config,
528
+ hook_type=args.hook_type,
529
+ )
530
+
531
+ # Run server with stdio transport (forced)
532
+ server.run_simple(transport="stdio")
533
+
534
+ except KeyboardInterrupt:
535
+ print("\nšŸ›‘ Server stopped by user")
536
+ sys.exit(0)
537
+ except Exception as e:
538
+ print(f"āŒ Error starting server: {e}")
539
+ if args.verbose:
540
+ import traceback
541
+
542
+ traceback.print_exc()
543
+ sys.exit(1)
544
+
545
+
546
+ def run_smcp_server():
547
+ """
548
+ Main entry point for the SMCP server command.
549
+
550
+ This function is called when running `tooluniverse-smcp` from the command line.
551
+ """
552
+ parser = argparse.ArgumentParser(
553
+ description="Start SMCP (Scientific Model Context Protocol) Server",
554
+ formatter_class=argparse.RawDescriptionHelpFormatter,
555
+ epilog="""
556
+ Examples:
557
+ # Start server with all tools on port 7890
558
+ tooluniverse-smcp --port 7890
559
+
560
+ # Start with specific categories
561
+ tooluniverse-smcp --categories uniprot ChEMBL opentarget --port 8000
562
+
563
+ # Start with categories but exclude specific tools
564
+ tooluniverse-smcp --categories uniprot ChEMBL --exclude-tools "ChEMBL_get_molecule_by_chembl_id" --port 8000
565
+
566
+ # Start with all tools but exclude entire categories
567
+ tooluniverse-smcp --exclude-categories mcp_auto_loader_boltz mcp_auto_loader_expert_feedback --port 8000
568
+
569
+ # Load only specific tools by name
570
+ tooluniverse-smcp --include-tools "UniProt_get_entry_by_accession" "ChEMBL_get_molecule_by_chembl_id" --port 8000
571
+
572
+ # Load tools from a file
573
+ tooluniverse-smcp --tools-file "/path/to/tool_names.txt" --port 8000
574
+
575
+ # Load additional config files
576
+ tooluniverse-smcp --tool-config-files "custom:/path/to/custom_tools.json" --port 8000
577
+
578
+ # Include/exclude specific tool types
579
+ tooluniverse-smcp --include-tool-types "OpenTarget" "ToolFinderEmbedding" --port 8000
580
+ tooluniverse-smcp --exclude-tool-types "ToolFinderLLM" "Unknown" --port 8000
581
+
582
+ # List available categories
583
+ tooluniverse-smcp --list-categories
584
+
585
+ # List all available tools
586
+ tooluniverse-smcp --list-tools
587
+
588
+ # Start minimal server with just search tools
589
+ tooluniverse-smcp --categories special_tools tool_finder --port 7000
590
+
591
+ # Start server for Claude Desktop (stdio transport)
592
+ tooluniverse-smcp --transport stdio
593
+ """,
594
+ )
595
+
596
+ # Tool selection options
597
+ tool_group = parser.add_mutually_exclusive_group()
598
+ tool_group.add_argument(
599
+ "--categories",
600
+ nargs="*",
601
+ metavar="CATEGORY",
602
+ help="Specific tool categories to load (e.g., uniprot ChEMBL opentarget). Use --list-categories to see available options.",
603
+ )
604
+ tool_group.add_argument(
605
+ "--list-categories",
606
+ action="store_true",
607
+ help="List all available tool categories and exit",
608
+ )
609
+
610
+ # Tool exclusion options
611
+ parser.add_argument(
612
+ "--exclude-tools",
613
+ nargs="*",
614
+ metavar="TOOL_NAME",
615
+ help='Specific tool names to exclude from loading (e.g., "tool1" "tool2"). Can be used with --categories.',
616
+ )
617
+ parser.add_argument(
618
+ "--exclude-categories",
619
+ nargs="*",
620
+ metavar="CATEGORY",
621
+ help="Tool categories to exclude from loading (e.g., mcp_auto_loader_boltz). Can be used with --categories.",
622
+ )
623
+
624
+ # Tool inclusion options
625
+ parser.add_argument(
626
+ "--include-tools",
627
+ nargs="*",
628
+ metavar="TOOL_NAME",
629
+ help="Specific tool names to include (only these tools will be loaded). Overrides category selection.",
630
+ )
631
+ parser.add_argument(
632
+ "--tools-file",
633
+ metavar="FILE_PATH",
634
+ help="Path to text file containing tool names to include (one per line). Overrides category selection.",
635
+ )
636
+ parser.add_argument(
637
+ "--tool-config-files",
638
+ nargs="*",
639
+ metavar="CATEGORY:PATH",
640
+ help='Additional tool config files to load. Format: "category:/path/to/config.json"',
641
+ )
642
+
643
+ # Tool type filtering options
644
+ parser.add_argument(
645
+ "--include-tool-types",
646
+ nargs="*",
647
+ metavar="TOOL_TYPE",
648
+ help="Specific tool types to include (e.g., OpenTarget ToolFinderEmbedding). Only tools of these types will be loaded.",
649
+ )
650
+ parser.add_argument(
651
+ "--exclude-tool-types",
652
+ nargs="*",
653
+ metavar="TOOL_TYPE",
654
+ help="Tool types to exclude from loading (e.g., ToolFinderLLM Unknown). Useful for excluding specific tool type classes.",
655
+ )
656
+
657
+ # Listing options
658
+ parser.add_argument(
659
+ "--list-tools", action="store_true", help="List all available tools and exit"
660
+ )
661
+
662
+ # Server configuration
663
+ parser.add_argument(
664
+ "--transport",
665
+ choices=["stdio", "http", "sse"],
666
+ default="http",
667
+ help="Transport protocol to use (default: http)",
668
+ )
669
+ parser.add_argument(
670
+ "--host",
671
+ default="0.0.0.0",
672
+ help="Host to bind to for HTTP/SSE transport (default: 0.0.0.0)",
673
+ )
674
+ parser.add_argument(
675
+ "--port",
676
+ type=int,
677
+ default=7000,
678
+ help="Port to bind to for HTTP/SSE transport (default: 7000)",
679
+ )
680
+ parser.add_argument(
681
+ "--name",
682
+ default="SMCP ToolUniverse Server",
683
+ help="Server name (default: SMCP ToolUniverse Server)",
684
+ )
685
+ parser.add_argument(
686
+ "--no-search",
687
+ action="store_true",
688
+ help="Disable intelligent search functionality",
689
+ )
690
+ parser.add_argument(
691
+ "--max-workers",
692
+ type=int,
693
+ default=5,
694
+ help="Maximum worker threads for concurrent execution (default: 5)",
695
+ )
696
+ parser.add_argument(
697
+ "--verbose", "-v", action="store_true", help="Enable verbose logging"
698
+ )
699
+
700
+ # Hook configuration options
701
+ hook_group = parser.add_argument_group("Hook Configuration")
702
+ hook_group.add_argument(
703
+ "--hooks-enabled",
704
+ action="store_true",
705
+ help="Enable output processing hooks (default: False)",
706
+ )
707
+ hook_group.add_argument(
708
+ "--hook-type",
709
+ choices=["SummarizationHook", "FileSaveHook"],
710
+ help="Simple hook type selection (SummarizationHook or FileSaveHook)",
711
+ )
712
+ hook_group.add_argument(
713
+ "--hook-config-file",
714
+ type=str,
715
+ help="Path to custom hook configuration JSON file",
716
+ )
717
+
718
+ args = parser.parse_args()
719
+
720
+ # Handle --list-categories
721
+ if args.list_categories:
722
+ try:
723
+ from .execute_function import ToolUniverse
724
+
725
+ tu = ToolUniverse()
726
+ tool_types = tu.get_tool_types()
727
+
728
+ print("Available tool categories:")
729
+ print("=" * 50)
730
+
731
+ # Group categories for better readability
732
+ scientific_db = []
733
+ literature = []
734
+ software = []
735
+ special = []
736
+ clinical = []
737
+ other = []
738
+
739
+ for category in sorted(tool_types):
740
+ if category in [
741
+ "uniprot",
742
+ "ChEMBL",
743
+ "opentarget",
744
+ "pubchem",
745
+ "hpa",
746
+ "rcsb_pdb",
747
+ "reactome",
748
+ "go",
749
+ ]:
750
+ scientific_db.append(category)
751
+ elif category in [
752
+ "EuropePMC",
753
+ "semantic_scholar",
754
+ "pubtator",
755
+ "OpenAlex",
756
+ ]:
757
+ literature.append(category)
758
+ elif category.startswith("software_"):
759
+ software.append(category)
760
+ elif category in [
761
+ "special_tools",
762
+ "tool_finder",
763
+ "tool_composition",
764
+ "agents",
765
+ ]:
766
+ special.append(category)
767
+ elif category in [
768
+ "clinical_trials",
769
+ "fda_drug_label",
770
+ "fda_drug_adverse_event",
771
+ "dailymed",
772
+ "medlineplus",
773
+ ]:
774
+ clinical.append(category)
775
+ else:
776
+ other.append(category)
777
+
778
+ if scientific_db:
779
+ print("\nšŸ”¬ Scientific Databases:")
780
+ for cat in scientific_db:
781
+ print(f" {cat}")
782
+
783
+ if literature:
784
+ print("\nšŸ“š Literature & Knowledge:")
785
+ for cat in literature:
786
+ print(f" {cat}")
787
+
788
+ if clinical:
789
+ print("\nšŸ„ Clinical & Drug Information:")
790
+ for cat in clinical:
791
+ print(f" {cat}")
792
+
793
+ if software:
794
+ print("\nšŸ’» Software Tools:")
795
+ for cat in software[:5]: # Show first 5
796
+ print(f" {cat}")
797
+ if len(software) > 5:
798
+ print(f" ... and {len(software) - 5} more software categories")
799
+
800
+ if special:
801
+ print("\nšŸ›  Special & Meta Tools:")
802
+ for cat in special:
803
+ print(f" {cat}")
804
+
805
+ if other:
806
+ print("\nšŸ“‚ Other Categories:")
807
+ for cat in other:
808
+ print(f" {cat}")
809
+
810
+ print(f"\nTotal: {len(tool_types)} categories available")
811
+ print("\nCommon combinations:")
812
+ print(" Scientific research: uniprot ChEMBL opentarget pubchem hpa")
813
+ print(" Drug discovery: ChEMBL fda_drug_label clinical_trials pubchem")
814
+ print(" Literature analysis: EuropePMC semantic_scholar pubtator")
815
+ print(" Minimal setup: special_tools tool_finder")
816
+
817
+ except Exception as e:
818
+ print(f"āŒ Error listing categories: {e}")
819
+ sys.exit(1)
820
+ return
821
+
822
+ # Handle --list-tools
823
+ if args.list_tools:
824
+ try:
825
+ from .execute_function import ToolUniverse
826
+
827
+ tu = ToolUniverse()
828
+ tu.load_tools() # Load all tools to list them
829
+
830
+ print("Available tools:")
831
+ print("=" * 50)
832
+
833
+ # Group tools by category
834
+ tools_by_category = {}
835
+ for tool in tu.all_tools:
836
+ tool_type = getattr(tool, "tool_type", "unknown")
837
+ if tool_type not in tools_by_category:
838
+ tools_by_category[tool_type] = []
839
+ tools_by_category[tool_type].append(tool.name)
840
+
841
+ total_tools = 0
842
+ for category in sorted(tools_by_category.keys()):
843
+ tools = sorted(tools_by_category[category])
844
+ print(f"\nšŸ“ {category} ({len(tools)} tools):")
845
+ for tool in tools[:10]: # Show first 10 tools per category
846
+ print(f" {tool}")
847
+ if len(tools) > 10:
848
+ print(f" ... and {len(tools) - 10} more tools")
849
+ total_tools += len(tools)
850
+
851
+ print(f"\nTotal: {total_tools} tools available")
852
+ print("\nNote: Use --exclude-tools to exclude specific tools by name")
853
+ print(" Use --exclude-categories to exclude entire categories")
854
+
855
+ except Exception as e:
856
+ print(f"āŒ Error listing tools: {e}")
857
+ sys.exit(1)
858
+ return
859
+
860
+ try:
861
+ print(f"šŸš€ Starting {args.name}...")
862
+ print(f"šŸ“” Transport: {args.transport}")
863
+ if args.transport in ["http", "sse"]:
864
+ print(f"🌐 Address: http://{args.host}:{args.port}")
865
+ print(f"šŸ” Search enabled: {not args.no_search}")
866
+
867
+ if args.categories is not None:
868
+ if len(args.categories) == 0:
869
+ print("šŸ“‚ No categories specified, loading all tools")
870
+ tool_categories = None
871
+ else:
872
+ print(f"šŸ“‚ Tool categories: {', '.join(args.categories)}")
873
+ tool_categories = args.categories
874
+ else:
875
+ print("šŸ“‚ Loading all tool categories")
876
+ tool_categories = None
877
+
878
+ # Handle exclusions and inclusions
879
+ exclude_tools = args.exclude_tools or []
880
+ exclude_categories = args.exclude_categories or []
881
+ include_tools = args.include_tools or []
882
+ tools_file = args.tools_file
883
+ include_tool_types = args.include_tool_types or []
884
+ exclude_tool_types = args.exclude_tool_types or []
885
+
886
+ # Parse tool config files
887
+ tool_config_files = {}
888
+ if args.tool_config_files:
889
+ for config_spec in args.tool_config_files:
890
+ if ":" in config_spec:
891
+ category, path = config_spec.split(":", 1)
892
+ tool_config_files[category] = path
893
+ else:
894
+ print(f"āŒ Invalid tool config file format: {config_spec}")
895
+ print(" Expected format: 'category:/path/to/config.json'")
896
+ sys.exit(1)
897
+
898
+ if exclude_tools:
899
+ print(f"🚫 Excluding tools: {', '.join(exclude_tools)}")
900
+ if exclude_categories:
901
+ print(f"🚫 Excluding categories: {', '.join(exclude_categories)}")
902
+ if include_tools:
903
+ print(f"āœ… Including only specific tools: {len(include_tools)} tools")
904
+ if tools_file:
905
+ print(f"šŸ“„ Loading tools from file: {tools_file}")
906
+ if tool_config_files:
907
+ print(f"šŸ“¦ Additional config files: {', '.join(tool_config_files.keys())}")
908
+ if include_tool_types:
909
+ print(f"šŸŽÆ Including tool types: {', '.join(include_tool_types)}")
910
+ if exclude_tool_types:
911
+ print(f"🚫 Excluding tool types: {', '.join(exclude_tool_types)}")
912
+
913
+ # Load hook configuration if specified
914
+ hook_config = None
915
+ if args.hook_config_file:
916
+ import json
917
+
918
+ with open(args.hook_config_file, "r") as f:
919
+ hook_config = json.load(f)
920
+ print(f"šŸ”— Hook config loaded from: {args.hook_config_file}")
921
+
922
+ # Determine hook settings
923
+ hooks_enabled = (
924
+ args.hooks_enabled or args.hook_type is not None or hook_config is not None
925
+ )
926
+ if hooks_enabled:
927
+ if args.hook_type:
928
+ print(f"šŸ”— Hooks enabled: {args.hook_type}")
929
+ elif hook_config:
930
+ hook_count = len(hook_config.get("hooks", []))
931
+ print(f"šŸ”— Hooks enabled: {hook_count} custom hooks")
932
+ else:
933
+ print("šŸ”— Hooks enabled: default configuration")
934
+ else:
935
+ print("šŸ”— Hooks disabled")
936
+
937
+ print(f"⚔ Max workers: {args.max_workers}")
938
+ print()
939
+
940
+ # Create SMCP server with hook support
941
+ server = SMCP(
942
+ name=args.name,
943
+ tool_categories=tool_categories,
944
+ exclude_tools=exclude_tools,
945
+ exclude_categories=exclude_categories,
946
+ include_tools=include_tools,
947
+ tools_file=tools_file,
948
+ tool_config_files=tool_config_files,
949
+ include_tool_types=include_tool_types,
950
+ exclude_tool_types=exclude_tool_types,
951
+ search_enabled=not args.no_search,
952
+ max_workers=args.max_workers,
953
+ stateless_http=True, # Enable stateless mode for MCPAutoLoaderTool compatibility
954
+ hooks_enabled=hooks_enabled,
955
+ hook_config=hook_config,
956
+ hook_type=args.hook_type,
957
+ )
958
+
959
+ # Run server
960
+ server.run_simple(transport=args.transport, host=args.host, port=args.port)
961
+
962
+ except KeyboardInterrupt:
963
+ print("\nšŸ›‘ Server stopped by user")
964
+ sys.exit(0)
965
+ except Exception as e:
966
+ print(f"āŒ Error starting server: {e}")
967
+ if args.verbose:
968
+ import traceback
969
+
970
+ traceback.print_exc()
971
+ sys.exit(1)
972
+
973
+
974
+ if __name__ == "__main__":
975
+ run_smcp_server()