ToolUniverse Tool Creator
Create new scientific tools following established patterns.
Top 7 Mistakes (90% of Failures)
- Missing
default_config.pyEntry — tools silently won't load - Non-nullable Mutually Exclusive Parameters — validation errors (#1 issue in 2026)
- Fake test_examples — tests fail, agents get bad examples
- Single-level Testing — misses registration bugs
- Skipping
test_new_tools.py— misses schema/API issues - Tool Names > 55 chars — breaks MCP compatibility
- Raising Exceptions — should return error dicts instead
Two-Stage Architecture
Stage 1: Tool Class Stage 2: Wrappers (Auto-Generated)
@register_tool("MyTool") MyAPI_list_items()
class MyTool(BaseTool): MyAPI_search()
def run(arguments): MyAPI_get_details()
One class handles multiple operations. JSON defines individual wrappers. Need BOTH.
Three-Step Registration
Step 1: Class registration via @register_tool("MyAPITool")
Step 2 (MOST COMMONLY MISSED): Config registration in default_config.py:
python1TOOLS_CONFIGS = { 2 "my_category": os.path.join(current_dir, "data", "my_category_tools.json"), 3}
Step 3: Automatic wrapper generation on tu.load_tools()
Implementation Guide
Files to Create
src/tooluniverse/my_api_tool.py— implementationsrc/tooluniverse/data/my_api_tools.json— tool definitionstests/tools/test_my_api_tool.py— tests
Python Tool Class (Multi-Operation Pattern)
python1from typing import Dict, Any 2from tooluniverse.tool import BaseTool 3from tooluniverse.tool_utils import register_tool 4import requests 5 6@register_tool("MyAPITool") 7class MyAPITool(BaseTool): 8 BASE_URL = "https://api.example.com/v1" 9 10 def __init__(self, tool_config): 11 super().__init__(tool_config) 12 self.parameter = tool_config.get("parameter", {}) 13 self.required = self.parameter.get("required", []) 14 15 def run(self, arguments: Dict[str, Any]) -> Dict[str, Any]: 16 operation = arguments.get("operation") 17 if not operation: 18 return {"status": "error", "error": "Missing: operation"} 19 if operation == "search": 20 return self._search(arguments) 21 return {"status": "error", "error": f"Unknown: {operation}"} 22 23 def _search(self, arguments: Dict[str, Any]) -> Dict[str, Any]: 24 query = arguments.get("query") 25 if not query: 26 return {"status": "error", "error": "Missing: query"} 27 try: 28 response = requests.get( 29 f"{self.BASE_URL}/search", 30 params={"q": query}, timeout=30 31 ) 32 response.raise_for_status() 33 data = response.json() 34 return {"status": "success", "data": data.get("results", [])} 35 except requests.exceptions.Timeout: 36 return {"status": "error", "error": "Timeout after 30s"} 37 except requests.exceptions.HTTPError as e: 38 return {"status": "error", "error": f"HTTP {e.response.status_code}"} 39 except Exception as e: 40 return {"status": "error", "error": str(e)}
JSON Configuration
json1[ 2 { 3 "name": "MyAPI_search", 4 "class": "MyAPITool", 5 "description": "Search items. Returns array of results. Supports Boolean operators. Example: 'protein AND membrane'.", 6 "parameter": { 7 "type": "object", 8 "required": ["operation", "query"], 9 "properties": { 10 "operation": {"const": "search", "description": "Operation (fixed)"}, 11 "query": {"type": "string", "description": "Search term"}, 12 "limit": {"type": ["integer", "null"], "description": "Max results (1-100)"} 13 } 14 }, 15 "return_schema": { 16 "oneOf": [ 17 {"type": "object", "properties": {"data": {"type": "array"}}}, 18 {"type": "object", "properties": {"error": {"type": "string"}}, "required": ["error"]} 19 ] 20 }, 21 "test_examples": [{"operation": "search", "query": "protein", "limit": 10}] 22 } 23]
Critical Requirements
- return_schema MUST have oneOf: success + error schemas
- test_examples MUST use real IDs: NO "TEST", "DUMMY", "PLACEHOLDER"
- Tool name <= 55 chars:
{API}_{action}_{target}template - Description 150-250 chars: what, format, example, notes
- NEVER raise in run(): return
{"status": "error", "error": "..."} - Set timeout on all HTTP requests (30s)
- Standard response:
{"status": "success|error", "data": {...}}
Parameter Design
Mutually Exclusive Parameters (CRITICAL — #1 issue)
When tool accepts EITHER id OR name, BOTH must be nullable:
json1{ 2 "id": {"type": ["integer", "null"], "description": "Numeric ID"}, 3 "name": {"type": ["string", "null"], "description": "Name (alternative to id)"} 4}
Without "null", validation fails when user provides only one parameter.
Common cases: id OR name, gene_id OR gene_symbol, any optional filters.
API Key Configuration
Optional keys (tool works without, better with):
json1{"optional_api_keys": ["NCBI_API_KEY"]}
python1self.api_key = os.environ.get("NCBI_API_KEY", "") # Read from env only
Required keys (tool won't work without):
json1{"required_api_keys": ["NVIDIA_API_KEY"]}
Rules: Never add api_key as tool parameter for optional keys. Use env vars only.
Testing (MANDATORY)
Full guide: references/testing-guide.md
Quick Testing Checklist
- Level 1 — Direct class test: import class, call
run(), check response - Level 2 — ToolUniverse test:
tu.tools.YourTool_op1(...), check registration - Level 3 — Real API test: use real IDs, verify actual responses
- MANDATORY — Run
python scripts/test_new_tools.py your_tool -v→ 0 failures
Verification Script
bash1# Check all 3 registration steps 2python3 -c " 3import sys; sys.path.insert(0, 'src') 4from tooluniverse.tool_registry import get_tool_registry 5import tooluniverse.your_tool_module 6assert 'YourToolClass' in get_tool_registry(), 'Step 1 FAILED' 7from tooluniverse.default_config import TOOLS_CONFIGS 8assert 'your_category' in TOOLS_CONFIGS, 'Step 2 FAILED' 9from tooluniverse import ToolUniverse 10tu = ToolUniverse(); tu.load_tools() 11assert hasattr(tu.tools, 'YourCategory_op1'), 'Step 3 FAILED' 12print('All 3 steps verified!') 13"
Quick Commands
bash1python3 -m json.tool src/tooluniverse/data/your_tools.json # Validate JSON 2python3 -m py_compile src/tooluniverse/your_tool.py # Check syntax 3grep "your_category" src/tooluniverse/default_config.py # Verify config 4python scripts/test_new_tools.py your_tool -v # MANDATORY test
References
- Testing guide: references/testing-guide.md
- Advanced patterns (async, SOAP, pagination): references/advanced-patterns.md
- Implementation guide (full checklist): references/implementation-guide.md
- Tool improvement checklist: references/tool-improvement-checklist.md