tooluniverse 1.0.2__py3-none-any.whl → 1.0.3__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.

@@ -17,6 +17,7 @@ def compose(arguments, tooluniverse, call_tool):
17
17
  """
18
18
  import json
19
19
  import warnings
20
+ import uuid
20
21
  from collections import Counter
21
22
 
22
23
  def _parse_agent_output(output, tool_name="Unknown Tool"):
@@ -365,5 +366,10 @@ def compose(arguments, tooluniverse, call_tool):
365
366
 
366
367
  except Exception as e:
367
368
  print(f"An error occurred during single-occurrence tag removal: {e}")
369
+
370
+ # Step 6: Manually set the UUID 'id' field to ensure true randomness
371
+ for tool_metadata in all_tool_metadata:
372
+ if 'error' not in tool_metadata:
373
+ tool_metadata['id'] = str(uuid.uuid4())
368
374
 
369
375
  return all_tool_metadata
@@ -1162,7 +1162,7 @@
1162
1162
  "type": "AgenticTool",
1163
1163
  "name": "ToolMetadataGenerator",
1164
1164
  "description": "Generates a JSON structure with the metadata of a tool in ToolUniverse, given the JSON configuration of the tool.",
1165
- "prompt": "You are an expert in processing ToolUniverse tool configurations. Your task is to extract and generate key metadata from a given tool's JSON configuration and return it as a new, structured JSON object.\n\n**Input Tool Configuration:**\n```json\n{tool_config}\n```\n\n**Tool Type Mappings (for simplifying toolType):**\n```json\n{tool_type_mappings}\n```\n\n**Instructions:**\nFrom the input configuration, generate a new JSON object with the specified structure. All fields enclosed in '<','>' are placeholders for instructions; you should generate a specific value for the tool based on its configuration. Fields not in brackets should use the default values provided.\n\n**Output JSON Structure:**\n```json\n{\n \"id\": \"<generate a new uuid>\",\n \"name\": \"<extract from tool_config.name>\",\n \"description\": \"<extract and tool_config.description and slightly summarize it if it is too long>\",\n \"detailed_description\": \"<extract from tool_config.description>\",\n \"toolType\": \"<if tool_config.type or tool_config.name appears in tool_type_mappings dict in one of the lists (among the dict's values), extract the corresponding key and set it as the simplified toolType. otherwise, set toolType to be 'API' (the default)>\",\n \"tags\": [],\n \"category\": \"<extract from tool_config.type>\",\n \"lab\": \"Zitnik Lab\",\n \"source\": \"<extract the name of the database, package, model, or write 'Agentic'>\",\n \"version\": \"v1.0.0\",\n \"reviewed\": true,\n \"isValidated\": true,\n \"usageStats\": \"100+ uses\",\n \"capabilities\": [\n \"<list capabilities strictly derivable from tool_config>\"\n ],\n \"limitations\": [\n \"May require refinement\"\n ],\n \"parameters\": {<for each parameter key include an object with type and description>},\n \"inputSchema\": <echo tool_config.parameter exactly>,\n \"exampleInput\": <JSON object with example values for each parameter>,\n \"apiEndpoints\": [\n {\n \"method\": \"MCP\",\n \"url\": \"https://tooluniversemcpserver.onrender.com/mcp/\"\n }\n ]\n}\n```\n\nReturn ONLY the final JSON object with no extra commentary.",
1165
+ "prompt": "You are an expert in processing ToolUniverse tool configurations. Your task is to extract and generate key metadata from a given tool's JSON configuration and return it as a new, structured JSON object.\n\n**Input Tool Configuration:**\n```json\n{tool_config}\n```\n\n**Tool Type Mappings (for simplifying toolType):**\n```json\n{tool_type_mappings}\n```\n\n**Instructions:**\nFrom the input configuration, generate a new JSON object with the specified structure. All fields enclosed in '<','>' are placeholders for instructions; you should generate a specific value for the tool based on its configuration. Fields not in brackets should use the default values provided.\n\n**Output JSON Structure:**\n```json\n{\n \"id\": \"<generate a new uuid>\",\n \"name\": \"<extract from tool_config.name>\",\n \"description\": \"<extract and tool_config.description and slightly summarize it if it is too long>\",\n \"detailed_description\": \"<extract from tool_config.description>\",\n \"toolType\": \"<if tool_config.type or tool_config.name appears in tool_type_mappings dict in one of the lists (among the dict's values), extract the corresponding key and set it as the simplified toolType. otherwise, set toolType to be 'API' (the default)>\",\n \"tags\": [],\n \"category\": \"<extract from tool_config.type>\",\n \"lab\": \"Zitnik Lab\",\n \"source\": \"<extract the name of the database, package, model, or write 'Agentic'>\",\n \"version\": \"v1.0.0\",\n \"reviewed\": true,\n \"isValidated\": true,\n \"usageStats\": \"100+ uses\",\n \"capabilities\": [\n \"<list capabilities strictly derivable from tool_config>\"\n ],\n \"limitations\": [\n \"None for now\"\n ],\n \"parameters\": {<for each parameter key include an object with type and description>},\n \"inputSchema\": <echo tool_config.parameter exactly>,\n \"exampleInput\": <JSON object with example values for each parameter>,\n \"apiEndpoints\": [\n {\n \"method\": \"MCP\",\n \"url\": \"https://tooluniversemcpserver.onrender.com/mcp/\"\n }\n ]\n}\n```\n\nReturn ONLY the final JSON object with no extra commentary.",
1166
1166
  "input_arguments": [
1167
1167
  "tool_config",
1168
1168
  "tool_type_mappings"
@@ -1000,9 +1000,9 @@ class ToolUniverse:
1000
1000
  """
1001
1001
  return copy.deepcopy(self.all_tools)
1002
1002
 
1003
- def list_built_in_tools(self, mode="config"):
1003
+ def list_built_in_tools(self, mode="config", scan_all=False):
1004
1004
  """
1005
- List all built-in tool categories and their statistics with two different modes.
1005
+ List all built-in tool categories and their statistics with different modes.
1006
1006
 
1007
1007
  This method provides a comprehensive overview of all available tools in the ToolUniverse,
1008
1008
  organized by categories. It reads directly from the default tool files to gather statistics,
@@ -1012,39 +1012,62 @@ class ToolUniverse:
1012
1012
  mode (str, optional): Organization mode for tools. Defaults to 'config'.
1013
1013
  - 'config': Organize by config file categories (original behavior)
1014
1014
  - 'type': Organize by tool types (implementation classes)
1015
+ - 'list_name': Return a list of all tool names
1016
+ - 'list_spec': Return a list of all tool specifications
1017
+ scan_all (bool, optional): Whether to scan all JSON files in data directory recursively.
1018
+ If True, scans all JSON files in data/ and its subdirectories.
1019
+ If False (default), uses predefined tool file mappings.
1015
1020
 
1016
1021
  Returns:
1017
- dict: A dictionary containing tool statistics with the following structure:
1018
-
1019
- {
1020
- 'categories': {
1021
- 'category_name': {
1022
- 'count': int, # Number of tools in this category
1023
- 'tools': list # List of tool names (only when mode='type')
1024
- },
1025
- ...
1026
- },
1027
- 'total_categories': int, # Total number of tool categories
1028
- 'total_tools': int, # Total number of unique tools
1029
- 'mode': str, # The mode used for organization
1030
- 'summary': str # Human-readable summary of statistics
1031
- }
1022
+ dict or list:
1023
+ - For 'config' and 'type' modes: A dictionary containing tool statistics
1024
+ - For 'list_name' mode: A list of all tool names
1025
+ - For 'list_spec' mode: A list of all tool specifications
1032
1026
 
1033
1027
  Example:
1034
1028
  >>> tool_universe = ToolUniverse()
1035
- >>> # Group by config file categories
1029
+ >>> # Group by config file categories (predefined files only)
1036
1030
  >>> stats = tool_universe.list_built_in_tools(mode='config')
1037
- >>> # Group by tool types
1038
- >>> stats = tool_universe.list_built_in_tools(mode='type')
1031
+ >>> # Scan all JSON files in data directory recursively
1032
+ >>> stats = tool_universe.list_built_in_tools(mode='config', scan_all=True)
1033
+ >>> # Get all tool names from all JSON files
1034
+ >>> tool_names = tool_universe.list_built_in_tools(mode='list_name', scan_all=True)
1039
1035
 
1040
1036
  Note:
1041
1037
  - This method reads directly from tool files and works without calling load_tools()
1042
1038
  - Tools are deduplicated across categories, so the same tool won't be counted multiple times
1043
- - The summary is automatically printed to console when this method is called
1039
+ - The summary is automatically printed to console when this method is called (except for list_name and list_spec modes)
1040
+ - When scan_all=True, all JSON files in data/ and subdirectories are scanned
1044
1041
  """
1045
- if mode not in ["config", "type"]:
1046
- raise ValueError("Mode must be either 'config' or 'type'")
1042
+ if mode not in ["config", "type", "list_name", "list_spec"]:
1043
+ raise ValueError(
1044
+ "Mode must be one of: 'config', 'type', 'list_name', 'list_spec'"
1045
+ )
1047
1046
 
1047
+ # For list_name and list_spec modes, we can return early with just the data
1048
+ if mode in ["list_name", "list_spec"]:
1049
+ all_tools = []
1050
+ all_tool_names = set() # For deduplication across categories
1051
+
1052
+ if scan_all:
1053
+ # Scan all JSON files in data directory recursively
1054
+ all_tools, all_tool_names = self._scan_all_json_files()
1055
+ else:
1056
+ # Use predefined tool files (original behavior)
1057
+ all_tools, all_tool_names = self._scan_predefined_files()
1058
+
1059
+ # Deduplicate tools by name
1060
+ unique_tools = {}
1061
+ for tool in all_tools:
1062
+ if tool["name"] not in unique_tools:
1063
+ unique_tools[tool["name"]] = tool
1064
+
1065
+ if mode == "list_name":
1066
+ return sorted(list(unique_tools.keys()))
1067
+ elif mode == "list_spec":
1068
+ return list(unique_tools.values())
1069
+
1070
+ # Original logic for config and type modes
1048
1071
  result = {
1049
1072
  "categories": {},
1050
1073
  "total_categories": 0,
@@ -1053,58 +1076,43 @@ class ToolUniverse:
1053
1076
  "summary": "",
1054
1077
  }
1055
1078
 
1056
- all_tool_names = set() # For deduplication across categories
1057
- all_tools = [] # Store all tools for type-based grouping
1058
-
1059
- # Read tools from each category file
1060
- for category, file_path in self.tool_files.items():
1061
- try:
1062
- # Read the JSON file for this category
1063
- tools_in_category = read_json_list(file_path)
1064
- all_tools.extend(tools_in_category)
1079
+ if scan_all:
1080
+ # Scan all JSON files in data directory recursively
1081
+ all_tools, all_tool_names = self._scan_all_json_files()
1065
1082
 
1066
- if mode == "config":
1067
- tool_names = [tool["name"] for tool in tools_in_category]
1068
- result["categories"][category] = {"count": len(tool_names)}
1083
+ # For config mode with scan_all, organize by file names
1084
+ if mode == "config":
1085
+ file_tools_map = {}
1086
+ for tool in all_tools:
1087
+ # Get the source file for this tool (we need to track this)
1088
+ # For now, we'll organize by tool type as a fallback
1089
+ tool_type = tool.get("type", "Unknown")
1090
+ if tool_type not in file_tools_map:
1091
+ file_tools_map[tool_type] = []
1092
+ file_tools_map[tool_type].append(tool)
1093
+
1094
+ for category, tools in file_tools_map.items():
1095
+ result["categories"][category] = {"count": len(tools)}
1096
+ else:
1097
+ # Use predefined tool files (original behavior)
1098
+ all_tools, all_tool_names = self._scan_predefined_files()
1069
1099
 
1070
- # Add to global set for deduplication
1071
- all_tool_names.update([tool["name"] for tool in tools_in_category])
1100
+ # Read tools from each category file
1101
+ for category, file_path in self.tool_files.items():
1102
+ try:
1103
+ # Read the JSON file for this category
1104
+ tools_in_category = read_json_list(file_path)
1072
1105
 
1073
- except Exception as e:
1074
- warning(
1075
- f"Warning: Could not read tools from {category} ({file_path}): {e}"
1076
- )
1077
- if mode == "config":
1078
- result["categories"][category] = {"count": 0}
1106
+ if mode == "config":
1107
+ tool_names = [tool["name"] for tool in tools_in_category]
1108
+ result["categories"][category] = {"count": len(tool_names)}
1079
1109
 
1080
- # Also include remote tools for listing purposes (not auto-loaded elsewhere)
1081
- try:
1082
- remote_dir = os.path.join(current_dir, "data", "remote_tools")
1083
- if os.path.isdir(remote_dir):
1084
- remote_tools = []
1085
- for fname in os.listdir(remote_dir):
1086
- if not fname.lower().endswith(".json"):
1087
- continue
1088
- fpath = os.path.join(remote_dir, fname)
1089
- try:
1090
- tools_in_file = read_json_list(fpath)
1091
- if isinstance(tools_in_file, dict):
1092
- tools_in_file = list(tools_in_file.values())
1093
- if isinstance(tools_in_file, list):
1094
- remote_tools.extend(tools_in_file)
1095
- except Exception as e:
1096
- warning(
1097
- f"Warning: Could not read remote tools from {fpath}: {e}"
1098
- )
1099
- if remote_tools:
1100
- all_tools.extend(remote_tools)
1101
- all_tool_names.update([tool["name"] for tool in remote_tools])
1110
+ except Exception as e:
1111
+ warning(
1112
+ f"Warning: Could not read tools from {category} ({file_path}): {e}"
1113
+ )
1102
1114
  if mode == "config":
1103
- result["categories"]["remote_tools"] = {
1104
- "count": len(remote_tools)
1105
- }
1106
- except Exception as e:
1107
- warning(f"Warning: Failed to scan remote tools directory: {e}")
1115
+ result["categories"][category] = {"count": 0}
1108
1116
 
1109
1117
  # If mode is 'type', organize by tool types instead
1110
1118
  if mode == "type":
@@ -1202,6 +1210,113 @@ class ToolUniverse:
1202
1210
 
1203
1211
  return result
1204
1212
 
1213
+ def _scan_predefined_files(self):
1214
+ """
1215
+ Scan predefined tool files (original behavior).
1216
+
1217
+ Returns:
1218
+ tuple: (all_tools, all_tool_names) where all_tools is a list of tool configs
1219
+ and all_tool_names is a set of tool names for deduplication
1220
+ """
1221
+ all_tools = []
1222
+ all_tool_names = set()
1223
+
1224
+ # Read tools from each category file
1225
+ for category, file_path in self.tool_files.items():
1226
+ try:
1227
+ # Read the JSON file for this category
1228
+ tools_in_category = read_json_list(file_path)
1229
+ all_tools.extend(tools_in_category)
1230
+ all_tool_names.update([tool["name"] for tool in tools_in_category])
1231
+ except Exception as e:
1232
+ warning(
1233
+ f"Warning: Could not read tools from {category} ({file_path}): {e}"
1234
+ )
1235
+
1236
+ # Also include remote tools
1237
+ try:
1238
+ remote_dir = os.path.join(current_dir, "data", "remote_tools")
1239
+ if os.path.isdir(remote_dir):
1240
+ remote_tools = []
1241
+ for fname in os.listdir(remote_dir):
1242
+ if not fname.lower().endswith(".json"):
1243
+ continue
1244
+ fpath = os.path.join(remote_dir, fname)
1245
+ try:
1246
+ tools_in_file = read_json_list(fpath)
1247
+ if isinstance(tools_in_file, dict):
1248
+ tools_in_file = list(tools_in_file.values())
1249
+ if isinstance(tools_in_file, list):
1250
+ remote_tools.extend(tools_in_file)
1251
+ except Exception as e:
1252
+ warning(
1253
+ f"Warning: Could not read remote tools from {fpath}: {e}"
1254
+ )
1255
+ if remote_tools:
1256
+ all_tools.extend(remote_tools)
1257
+ all_tool_names.update([tool["name"] for tool in remote_tools])
1258
+ except Exception as e:
1259
+ warning(f"Warning: Failed to scan remote tools directory: {e}")
1260
+
1261
+ return all_tools, all_tool_names
1262
+
1263
+ def _scan_all_json_files(self):
1264
+ """
1265
+ Recursively scan all JSON files in the data directory and its subdirectories.
1266
+
1267
+ Returns:
1268
+ tuple: (all_tools, all_tool_names) where all_tools is a list of tool configs
1269
+ and all_tool_names is a set of tool names for deduplication
1270
+ """
1271
+ all_tools = []
1272
+ all_tool_names = set()
1273
+
1274
+ # Get the data directory path
1275
+ data_dir = os.path.join(current_dir, "data")
1276
+
1277
+ if not os.path.exists(data_dir):
1278
+ warning(f"Warning: Data directory not found: {data_dir}")
1279
+ return all_tools, all_tool_names
1280
+
1281
+ # Recursively find all JSON files
1282
+ json_files = []
1283
+ for root, _dirs, files in os.walk(data_dir):
1284
+ for file in files:
1285
+ if file.lower().endswith(".json"):
1286
+ json_files.append(os.path.join(root, file))
1287
+
1288
+ self.logger.debug(f"Found {len(json_files)} JSON files to scan")
1289
+
1290
+ # Read tools from each JSON file
1291
+ for json_file in json_files:
1292
+ try:
1293
+ tools_in_file = read_json_list(json_file)
1294
+
1295
+ # Handle different data formats
1296
+ if isinstance(tools_in_file, dict):
1297
+ # Convert dict of tools to list of tools
1298
+ tools_in_file = list(tools_in_file.values())
1299
+ elif not isinstance(tools_in_file, list):
1300
+ # Skip files that don't contain tool configurations
1301
+ continue
1302
+
1303
+ # Add tools to our collection
1304
+ for tool in tools_in_file:
1305
+ if isinstance(tool, dict) and "name" in tool:
1306
+ all_tools.append(tool)
1307
+ all_tool_names.add(tool["name"])
1308
+
1309
+ self.logger.debug(f"Loaded {len(tools_in_file)} tools from {json_file}")
1310
+
1311
+ except Exception as e:
1312
+ warning(f"Warning: Could not read tools from {json_file}: {e}")
1313
+ continue
1314
+
1315
+ self.logger.info(
1316
+ f"Scanned {len(json_files)} JSON files, found {len(all_tools)} tools"
1317
+ )
1318
+ return all_tools, all_tool_names
1319
+
1205
1320
  def refresh_tool_name_desc(
1206
1321
  self,
1207
1322
  enable_full_desc=False,
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Script to filter tool files by removing tools that don't exist in the current tool universe.
4
+
5
+ This script:
6
+ 1. Gets all valid tool names from ToolUniverse using scan_all=True
7
+ 2. Filters tool_relationship_graph_FINAL.json to keep only valid tools
8
+ 3. Filters v4_all_tools_final.json to keep only valid tools
9
+ 4. Preserves all other data structure and content
10
+ """
11
+
12
+ import json
13
+ from pathlib import Path
14
+
15
+ # Import after modifying sys.path
16
+ from tooluniverse import ToolUniverse
17
+
18
+
19
+ def load_json_file(file_path):
20
+ """Load JSON file and return the data."""
21
+ try:
22
+ with open(file_path, "r", encoding="utf-8") as f:
23
+ return json.load(f)
24
+ except Exception as e:
25
+ print(f"Error loading {file_path}: {e}")
26
+ return None
27
+
28
+
29
+ def save_json_file(file_path, data):
30
+ """Save data to JSON file."""
31
+ try:
32
+ with open(file_path, "w", encoding="utf-8") as f:
33
+ json.dump(data, f, ensure_ascii=False, indent=2)
34
+ print(f"Successfully saved filtered data to {file_path}")
35
+ return True
36
+ except Exception as e:
37
+ print(f"Error saving {file_path}: {e}")
38
+ return False
39
+
40
+
41
+ def filter_tool_relationship_graph(data, valid_tool_names):
42
+ """
43
+ Filter tool_relationship_graph_FINAL.json to keep only valid tools.
44
+
45
+ Args:
46
+ data: The loaded JSON data
47
+ valid_tool_names: Set of valid tool names
48
+
49
+ Returns:
50
+ Filtered data
51
+ """
52
+ if not isinstance(data, dict):
53
+ print("Warning: tool_relationship_graph data is not a dict")
54
+ return data
55
+
56
+ filtered_data = {}
57
+
58
+ # Handle nodes array
59
+ if "nodes" in data and isinstance(data["nodes"], list):
60
+ filtered_nodes = []
61
+ for node in data["nodes"]:
62
+ if isinstance(node, dict) and "name" in node:
63
+ if node["name"] in valid_tool_names:
64
+ filtered_nodes.append(node)
65
+ else:
66
+ print(f"Removing node from relationship graph: {node['name']}")
67
+ else:
68
+ # Keep non-tool nodes (if any)
69
+ filtered_nodes.append(node)
70
+ filtered_data["nodes"] = filtered_nodes
71
+ print(
72
+ f"Nodes: {len(data['nodes'])} -> {len(filtered_nodes)} ({len(data['nodes']) - len(filtered_nodes)} removed)"
73
+ )
74
+
75
+ # Handle edges array
76
+ if "edges" in data and isinstance(data["edges"], list):
77
+ filtered_edges = []
78
+ for edge in data["edges"]:
79
+ if isinstance(edge, dict) and "source" in edge and "target" in edge:
80
+ # Keep edge if both source and target are valid tools
81
+ if (
82
+ edge["source"] in valid_tool_names
83
+ and edge["target"] in valid_tool_names
84
+ ):
85
+ filtered_edges.append(edge)
86
+ else:
87
+ print(
88
+ f"Removing edge from relationship graph: {edge.get('source', 'unknown')} -> {edge.get('target', 'unknown')}"
89
+ )
90
+ else:
91
+ # Keep non-tool edges (if any)
92
+ filtered_edges.append(edge)
93
+ filtered_data["edges"] = filtered_edges
94
+ print(
95
+ f"Edges: {len(data['edges'])} -> {len(filtered_edges)} ({len(data['edges']) - len(filtered_edges)} removed)"
96
+ )
97
+
98
+ # Keep other fields as-is (like stats, metadata, etc.)
99
+ for key, value in data.items():
100
+ if key not in ["nodes", "edges"]:
101
+ filtered_data[key] = value
102
+
103
+ return filtered_data
104
+
105
+
106
+ def filter_v4_all_tools(data, valid_tool_names):
107
+ """
108
+ Filter v4_all_tools_final.json to keep only valid tools.
109
+
110
+ Args:
111
+ data: The loaded JSON data
112
+ valid_tool_names: Set of valid tool names
113
+
114
+ Returns:
115
+ Filtered data
116
+ """
117
+ if not isinstance(data, list):
118
+ print("Warning: v4_all_tools data is not a list")
119
+ return data
120
+
121
+ filtered_data = []
122
+
123
+ for tool in data:
124
+ if isinstance(tool, dict) and "name" in tool:
125
+ if tool["name"] in valid_tool_names:
126
+ filtered_data.append(tool)
127
+ else:
128
+ print(f"Removing tool from v4_all_tools: {tool['name']}")
129
+ else:
130
+ # Keep non-tool entries (if any)
131
+ filtered_data.append(tool)
132
+
133
+ return filtered_data
134
+
135
+
136
+ def main():
137
+ """Main function to filter the tool files."""
138
+ print("Starting tool file filtering process...")
139
+
140
+ # Initialize ToolUniverse and get all valid tool names
141
+ print("Getting all valid tool names from ToolUniverse...")
142
+ tu = ToolUniverse()
143
+ all_tool_names = tu.list_built_in_tools(mode="list_name", scan_all=True)
144
+ valid_tool_names = set(all_tool_names)
145
+ print(f"Found {len(valid_tool_names)} valid tools")
146
+
147
+ # Define file paths
148
+ project_root = Path(__file__).parent.parent.parent.parent
149
+ web_dir = project_root / "web"
150
+
151
+ relationship_graph_file = web_dir / "tool_relationship_graph_FINAL.json"
152
+ v4_tools_file = web_dir / "v4_all_tools_final.json"
153
+
154
+ # Check if files exist
155
+ if not relationship_graph_file.exists():
156
+ print(f"Error: {relationship_graph_file} not found")
157
+ return
158
+
159
+ if not v4_tools_file.exists():
160
+ print(f"Error: {v4_tools_file} not found")
161
+ return
162
+
163
+ # Process tool_relationship_graph_FINAL.json
164
+ print(f"\nProcessing {relationship_graph_file.name}...")
165
+ relationship_data = load_json_file(relationship_graph_file)
166
+ if relationship_data is not None:
167
+ len(relationship_data.get("nodes", []))
168
+ len(relationship_data.get("edges", []))
169
+ filtered_relationship_data = filter_tool_relationship_graph(
170
+ relationship_data, valid_tool_names
171
+ )
172
+
173
+ # Save filtered data
174
+ save_json_file(relationship_graph_file, filtered_relationship_data)
175
+
176
+ # Process v4_all_tools_final.json
177
+ print(f"\nProcessing {v4_tools_file.name}...")
178
+ v4_data = load_json_file(v4_tools_file)
179
+ if v4_data is not None:
180
+ original_count = len(v4_data)
181
+ filtered_v4_data = filter_v4_all_tools(v4_data, valid_tool_names)
182
+ filtered_count = len(filtered_v4_data)
183
+ print(
184
+ f"V4 tools: {original_count} -> {filtered_count} tools ({original_count - filtered_count} removed)"
185
+ )
186
+
187
+ # Save filtered data
188
+ save_json_file(v4_tools_file, filtered_v4_data)
189
+
190
+ print("\nTool file filtering completed!")
191
+
192
+
193
+ if __name__ == "__main__":
194
+ main()
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Basic test for list_built_in_tools including scan_all option.
4
+ Run directly: python tests/test_list_built_in_tools.py
5
+ """
6
+
7
+ from tooluniverse import ToolUniverse # noqa: E402
8
+
9
+
10
+ def main():
11
+ tu = ToolUniverse()
12
+
13
+ # Use predefined files (original behavior)
14
+ tool_names = tu.list_built_in_tools(mode="list_name", scan_all=False)
15
+ print(f"predefined tool names: {len(tool_names)}")
16
+
17
+ # Scan all JSON files
18
+ all_tool_names = tu.list_built_in_tools(mode="list_name", scan_all=True)
19
+ print(f"all tool names (scan_all): {len(all_tool_names)}")
20
+
21
+ # Get all tool specifications
22
+ all_tool_specs = tu.list_built_in_tools(mode="list_spec", scan_all=True)
23
+ print(f"all tool specs (scan_all): {len(all_tool_specs)}")
24
+
25
+ # Organize all tools by type
26
+ type_stats = tu.list_built_in_tools(mode="type", scan_all=True)
27
+ print(
28
+ f"type stats -> total_categories: {type_stats['total_categories']}, total_tools: {type_stats['total_tools']}"
29
+ )
30
+
31
+
32
+ if __name__ == "__main__":
33
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tooluniverse
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: A comprehensive collection of scientific tools for Agentic AI, offering integration with the ToolUniverse SDK and MCP Server to support advanced scientific workflows.
5
5
  Author-email: Shanghua Gao <shanghuagao@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/mims-harvard/TxAgent
@@ -63,7 +63,7 @@ Provides-Extra: all
63
63
  Requires-Dist: tooluniverse[dev,docs,graph]; extra == "all"
64
64
  Dynamic: license-file
65
65
 
66
- # ToolUniverse: Democratizing AI scientists
66
+ # <img src="docs/_static/logo.png" alt="ToolUniverse Logo" height="28" style="vertical-align: middle; margin-right: 8px;" /> ToolUniverse: Democratizing AI scientists
67
67
 
68
68
  [![ToolUniverse-PIP](https://img.shields.io/badge/PyPI-ToolUniverse-blue)](https://pypi.org/project/tooluniverse/)
69
69
  [![ToolUniverse](https://img.shields.io/badge/Code-ToolUniverse-purple)](https://github.com/mims-harvard/ToolUniverse)
@@ -96,7 +96,7 @@ ToolUniverse is an ecosystem for creating AI scientist systems from any open or
96
96
 
97
97
 
98
98
  ## 🤖 Building AI Scientists with ToolUniverse in 5 minutes
99
- - **[Overview](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/index.html)**: Create AI research assistants from any LLM
99
+ - **[Overview](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/index.html)**: Create AI scientists from any LLM
100
100
  - **[Claude Desktop Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/claude_desktop.html)**: Native MCP integration with Claude Desktop App
101
101
  - **[Claude Code Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/claude_code.html)**: AI scientist development in Claude Code environment
102
102
  - **[Gemini CLI Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/gemini_cli.html)**: Command-line scientific research with Google Gemini
@@ -109,7 +109,7 @@ ToolUniverse is an ecosystem for creating AI scientist systems from any open or
109
109
 
110
110
  AI scientists are emerging computational systems that serve as collaborative partners in discovery. However, these systems remain difficult to build because they are bespoke, tied to rigid workflows, and lack shared environments that unify tools, data, and analysts into a common ecosystem.
111
111
 
112
- ToolUniverse addresses this challenge by providing a standardized ecosystem that transforms any AI model into a powerful research scientist. By abstracting capabilities behind a unified interface, ToolUniverse wraps around any AI model (LLM, AI agent, or large reasoning model) and enables users to create and refine entirely custom AI research assistants without additional training or finetuning.
112
+ ToolUniverse addresses this challenge by providing a standardized ecosystem that transforms any AI model into a powerful research scientist. By abstracting capabilities behind a unified interface, ToolUniverse wraps around any AI model (LLM, AI agent, or large reasoning model) and enables users to create and refine entirely custom AI scientists without additional training or finetuning.
113
113
 
114
114
  **Key Features:**
115
115
 
@@ -212,7 +212,7 @@ tooluniverse-smcp
212
212
 
213
213
 
214
214
  ---
215
- **Hypercholesterolemia Drug Discovery** [[Tutorial]]((https://zitniklab.hms.harvard.edu/bioagent/tutorials/tooluniverse_case_study.html)) [[Code]](https://colab.research.google.com/drive/1UwJ6RwyUoqI5risKQ365EeFdDQWOeOCv?usp=sharing)
215
+ **Hypercholesterolemia Drug Discovery** [[Tutorial]](https://zitniklab.hms.harvard.edu/bioagent/tutorials/tooluniverse_case_study.html) [[Code]](https://colab.research.google.com/drive/1UwJ6RwyUoqI5risKQ365EeFdDQWOeOCv?usp=sharing)
216
216
 
217
217
  ---
218
218
 
@@ -316,4 +316,4 @@ ToolUniverse is developed by the [Zitnik Lab](https://zitniklab.hms.harvard.edu/
316
316
 
317
317
  ---
318
318
 
319
- *Democratizing AI agents for science - one tooluniverse at a time.*
319
+ *Democratizing AI agents for science with ToolUniverse.*
@@ -16,7 +16,7 @@ tooluniverse/embedding_database.py,sha256=ua-yIyv3dPq2GHdyZmiwZyaUlkKYyV3nslB4H9
16
16
  tooluniverse/embedding_sync.py,sha256=Cq3g64MjNXXpiImrzPMNsCTvGZeEI9009OlHOD3EloI,14669
17
17
  tooluniverse/enrichr_tool.py,sha256=YXuHwoG_Hi-kUWK7cY_X21b5pbqoREdG7fJxKq050Jg,9472
18
18
  tooluniverse/europe_pmc_tool.py,sha256=aYTkDE_KMnpkYYMG5ojYZQ1QYsYB_Msf3pvNwY1Zz6w,1645
19
- tooluniverse/execute_function.py,sha256=kYmS-Efv1O-RtmeZaYL6TOq1yGtXRHX8KaYuQ8JYpyI,78094
19
+ tooluniverse/execute_function.py,sha256=T5y0OQdsPyz0hvrl9pqIfWjPi2OBJ1fhpuDPzjlKPac,82979
20
20
  tooluniverse/extended_hooks.py,sha256=1Cg0rETh6_D21YFVqD-FhL7y4inZAGyQ6vMtHyugueg,15376
21
21
  tooluniverse/gene_ontology_tool.py,sha256=l89XYdBEBm3eN3dG0lR4oV01H16_9IUB6a2c7W_c7Lg,7180
22
22
  tooluniverse/graphql_tool.py,sha256=y7ztNyb9hn4OdSGKhLsHev_snkNgbAwJZL8ZGHm0LEk,8980
@@ -62,11 +62,11 @@ tooluniverse/compose_scripts/tool_description_optimizer.py,sha256=L6Kd8e6Af8pD6d
62
62
  tooluniverse/compose_scripts/tool_discover.py,sha256=RKzCmhDWzDCsAl2KCcEQRNH96mOf_2bTQOH2EVGvpC8,24976
63
63
  tooluniverse/compose_scripts/tool_graph_composer.py,sha256=lmGeN94Mc9SZCeYo8ez55tKYLS1-dI3JDm-onKB5DZA,15566
64
64
  tooluniverse/compose_scripts/tool_graph_generation.py,sha256=zPVN58GufflVCo8Ak3V9_DpNjS9oLrhQ5A_JCN9TCzY,10052
65
- tooluniverse/compose_scripts/tool_metadata_generator.py,sha256=GCd_zKxN5n7vz_5Zlj7nP2KpGzfi0baRfOnKt9ZQsRI,19004
65
+ tooluniverse/compose_scripts/tool_metadata_generator.py,sha256=JAcLhXnmsZMzRjNRFM1fsxBtkjgY92LTzMGpgEq4s2k,19235
66
66
  tooluniverse/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
67
  tooluniverse/data/admetai_tools.json,sha256=08LCNSCZJxL6ZuPF1mVfbdlh9f2X5tnEwIYU7RcLLSM,6497
68
68
  tooluniverse/data/adverse_event_tools.json,sha256=EDU_X8r4YtMnVt7SJpyimdo3FlcSNFSdvckWLShDODA,12752
69
- tooluniverse/data/agentic_tools.json,sha256=2LVwit3QqoHQRja2f_8KvRh9evyIV62fpYqo4WyiIeM,103309
69
+ tooluniverse/data/agentic_tools.json,sha256=ABlMWxS06u-xqGHEDDEizUpprQ4MfDg1NXSKSWTo8x4,103299
70
70
  tooluniverse/data/alphafold_tools.json,sha256=--JO9z9fQM95t5dphrBoKKr28HKWg7iQOUm1hQYhEOk,10628
71
71
  tooluniverse/data/boltz_tools.json,sha256=_pMSRBkO3uj4vjYnQ7H6qbZFa4rzc9Looj6zHRyIkMg,303
72
72
  tooluniverse/data/chembl_tools.json,sha256=aQfFyFlUNz_y9ZTO3dNje4QGhqt7rLv6j5YdMJR2-x8,747
@@ -135,6 +135,7 @@ tooluniverse/remote/pinnacle/pinnacle_tool.py,sha256=Oprd2RyMCZF-NsMjkZMpPOB2apC
135
135
  tooluniverse/remote/transcriptformer/transcriptformer_tool.py,sha256=ZU_TT340LBwjTuowTaQV6QcANcsjYimA4CVSJHdU0c0,25229
136
136
  tooluniverse/remote/uspto_downloader/uspto_downloader_mcp_server.py,sha256=gsfs-a3RZ7kwauO3uWNJjEOEi2ctHva3iRUtEYqjS6U,2352
137
137
  tooluniverse/remote/uspto_downloader/uspto_downloader_tool.py,sha256=hkDmSVh2-KEc56YhmgtwrgIwkYMFXR2fN1jrIL9vVlk,4460
138
+ tooluniverse/scripts/filter_tool_files.py,sha256=IZZIeSmBfHKntno790er1hQHSDcrxfdIHZwZ-Lvq7I4,6415
138
139
  tooluniverse/scripts/generate_tool_graph.py,sha256=X_TpGcJ29CFH49Djzsz4gs8VvjDe0CcF6KFWU_hJHDI,12727
139
140
  tooluniverse/scripts/visualize_tool_graph.py,sha256=4USGnUVdffJfOxourBKELldGhhk5HV7ZeHHREhyKqOc,25812
140
141
  tooluniverse/test/mcp_server_test.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -158,6 +159,7 @@ tooluniverse/test/test_gwas_tool.py,sha256=_VXaf8K_1uO-oKlacJP6VTt4u5cjtX3w92G_e
158
159
  tooluniverse/test/test_hpa.py,sha256=53lx0rXx0-aT1LRm9IEeBbV5cXaYlp3Ol5bmeRz10Hc,30006
159
160
  tooluniverse/test/test_humanbase_tool.py,sha256=4KQnaehJwGtePEwb5my3yldZnMWQHfvCGEpzm-AWLH0,564
160
161
  tooluniverse/test/test_idmap_tools.py,sha256=bP_AKhZG1lX9vQKrrrzuZ1iXIMWIWjAriB66pbFCE5M,1743
162
+ tooluniverse/test/test_list_built_in_tools.py,sha256=fLnCHCwoF1kWd28joMPwrYXi0b1e8EqfuIVq6GzZCvA,1035
161
163
  tooluniverse/test/test_mcp_server.py,sha256=n3C3UuU9D-E6m6PbooWUgWwshLzncXDEmn0U9ugapaY,6631
162
164
  tooluniverse/test/test_mcp_tool.py,sha256=q6a9MCjFggK6KRy8F9h14tV2BezNVj63v6tDFaST-ys,8290
163
165
  tooluniverse/test/test_medlineplus.py,sha256=hQ2QyeHmF8BkCXMn9vW0SNImAcqd5jkZz_FhaZbZS7A,7332
@@ -176,9 +178,9 @@ tooluniverse/test/test_tools_find.py,sha256=vP9EV04iXXmejoY2rDhEM0Yc9uHEO2qEmd8s
176
178
  tooluniverse/test/test_uniprot_tools.py,sha256=V0x7aQjjlE-wS5alFWviHNwC3saHiEE5pMMqAcbokaw,2582
177
179
  tooluniverse/test/test_uspto_tool.py,sha256=y2VQBMbiHCTAmvXhBosHrc1innstUl9UMWNoPaBv-RY,2311
178
180
  tooluniverse/test/test_xml_tool.py,sha256=tx-w_psZ801C2kM6aDbBzR8QV3Vgu1p2zP6zExhjpVs,3602
179
- tooluniverse-1.0.2.dist-info/licenses/LICENSE,sha256=Unq9Y3buGL9J0vFop03FYZiVweytt1xdgLIh2n9ck2c,1115
180
- tooluniverse-1.0.2.dist-info/METADATA,sha256=SEmSfNFe6gFqwsmk5IyQbtAF2EVknHL2nm7Y7McWUUM,18270
181
- tooluniverse-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
182
- tooluniverse-1.0.2.dist-info/entry_points.txt,sha256=Z_znqzUc_XR9ZIXViavqNAwBkwM4XYfC9h7LoBbAAog,600
183
- tooluniverse-1.0.2.dist-info/top_level.txt,sha256=zZ8YeCJ5FAkEwdd_mxsFtSCQMBDgBdxrrmHo3RNBiWs,13
184
- tooluniverse-1.0.2.dist-info/RECORD,,
181
+ tooluniverse-1.0.3.dist-info/licenses/LICENSE,sha256=Unq9Y3buGL9J0vFop03FYZiVweytt1xdgLIh2n9ck2c,1115
182
+ tooluniverse-1.0.3.dist-info/METADATA,sha256=QijTmbm2I8zn-L7IVLeRuuDUcmpk8l-ZkWUQOhNK200,18362
183
+ tooluniverse-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
184
+ tooluniverse-1.0.3.dist-info/entry_points.txt,sha256=Z_znqzUc_XR9ZIXViavqNAwBkwM4XYfC9h7LoBbAAog,600
185
+ tooluniverse-1.0.3.dist-info/top_level.txt,sha256=zZ8YeCJ5FAkEwdd_mxsFtSCQMBDgBdxrrmHo3RNBiWs,13
186
+ tooluniverse-1.0.3.dist-info/RECORD,,