Troubleshooting Tutorial#
Quick Diagnostic#
Run this diagnostic script to check your ToolUniverse installation:
from tooluniverse import ToolUniverse
# Run basic system check
try:
tu = ToolUniverse()
tu.load_tools()
print(f"✅ ToolUniverse working correctly! {len(tu.all_tools)} tools loaded.")
except Exception as e:
print(f"❌ Issue detected: {e}")
Most common installation problems and solutions.
Errors that occur during tool execution.
Slow queries and optimization tips.
Network and API-related problems.
Installation Issues#
Tip
Install with uv rather than the system
pip. It sidesteps the two errors below entirely, because it downloads and
manages its own Python:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv venv --python 3.12 && source .venv/bin/activate
uv pip install tooluniverse
error: externally-managed-environment#
Symptom: pip install tooluniverse refuses to run:
error: externally-managed-environment
× This environment is externally managed
Cause: PEP 668. Homebrew, Debian/Ubuntu, and Fedora mark their system
Python as externally managed so that pip cannot modify packages the OS
package manager owns.
Solution: install into a virtual environment managed by uv:
uv venv --python 3.12
source .venv/bin/activate # Windows: .venv\Scripts\activate
uv pip install tooluniverse
Do not use sudo pip install or pip install --break-system-packages;
both can leave the system Python in a broken state.
python3 -m venv fails at ensurepip#
Symptom: creating a virtual environment fails:
Error: Command '[.../bin/python3.14', '-m', 'ensurepip', '--upgrade',
'--default-pip']' returned non-zero exit status 1.
Cause: some Homebrew Python builds (3.13/3.14) ship without a working
ensurepip, so the standard library venv module cannot bootstrap pip.
Solution: let uv supply the interpreter instead of the system one:
uv venv --python 3.12
source .venv/bin/activate
uv pip install tooluniverse
If you only need the tu command line and not the Python API:
uv tool install tooluniverse
ImportError: No module named ‘tooluniverse’#
Symptom: Python can’t find the ToolUniverse module.
Diagnosis:
python -c "import tooluniverse; print(tooluniverse.__version__)"
Solutions:
pip install tooluniverse
git clone https://github.com/mims-harvard/ToolUniverse
cd tooluniverse
pip install -e .
python -m venv tooluniverse_env
source tooluniverse_env/bin/activate # Linux/Mac
# tooluniverse_env\Scripts\activate # Windows
pip install tooluniverse
Dependency conflicts#
Symptom: Conflicting package versions during installation.
Check dependencies:
pip check
Solutions:
Create clean environment:
conda create -n tooluniverse python=3.10 conda activate tooluniverse pip install tooluniverse
Update conflicting packages:
pip install --upgrade requests urllib3 certifiInstall specific versions:
pip install 'requests>=2.25.0,<3.0.0'
Runtime Errors#
A tool loads but fails when run#
Symptom: the tool appears in tu list and tu status counts it, but
running it raises something like ADMETModel requires 'admet-ai' package or
RDKit is required.
Cause: loading a tool registers its JSON config; it does not install the tool’s dependencies. Tools backed by an optional extra load fine on a base install and only fail at call time.
Diagnosis:
tooluniverse-doctor
The report lists every optional dependency group that is not installed.
Solution: install the group it names:
uv pip install 'tooluniverse[ml]' # ADMET-AI, embeddings
uv pip install 'tooluniverse[visualization]' # RDKit, py3Dmol, plotting
uv pip install 'tooluniverse[bioinformatics]' # Biopython, freesasa
uv pip install 'tooluniverse[all]' # everything except the four below
[all] excludes singlecell, smolagents, client, and build —
install those by name.
PyTorch / Lightning warnings during ML tool calls#
Symptom: ADMET-AI or other local-model tools print warnings about a missing
GPU, PyTorch Lightning Trainer settings, or TypedStorage is deprecated.
These are normal. They come from PyTorch and PyTorch Lightning during
ordinary CPU inference and do not affect results. Treat the call as failed only
if the tool returns an error field or no predictions.
To silence them in scripts:
import warnings
warnings.filterwarnings("ignore", category=UserWarning)
Tool not found errors#
Symptom: ToolNotFoundError: Tool 'XYZ' not found
Diagnosis:
from tooluniverse import ToolUniverse
tu = ToolUniverse()
tu.load_tools()
# List available tools
print("Available tools:")
for tool_name in tu.list_built_in_tools(mode='list_name'):
print(f" - {tool_name}")
Solutions:
Check tool name spelling:
# Correct tool names correct_names = [ "OpenTargets_get_associated_targets_by_disease_efoId", "PubChem_get_compound_info", "UniProt_get_function_by_accession" ]
Verify tool is loaded:
if "OpenTargets_tool" not in tu.all_tool_dict: print("OpenTargets tool not loaded") # Check for missing dependencies
Manual tool loading:
from tooluniverse.opentargets_tool import OpenTargetsTool tool = OpenTargetsTool() tu.register_custom_tool(tool_instance=tool)
API Authentication errors#
Symptom: 401 Unauthorized, 403 Forbidden, or “API key required” errors.
Common API Error Codes#
When you encounter API errors, check this reference table:
Error Code |
Meaning |
Solution |
|---|---|---|
401 Unauthorized |
Invalid or missing API key |
Verify API key is correct and environment variable is set correctly. Check with |
403 Forbidden |
Valid key but insufficient permissions |
Check account subscription level. May need to upgrade or request additional access from API provider. |
429 Too Many Requests |
Rate limit exceeded |
Add API key for higher limits, enable caching with |
404 Not Found |
Invalid resource identifier |
Verify ID format (e.g., UniProt accession “P05067”, ChEMBL ID “CHEMBL25”). Check Glossary for ID formats. |
502 Bad Gateway |
Service temporarily unavailable |
Wait 30-60 seconds and retry. Check API provider’s status page if error persists. |
503 Service Unavailable |
API endpoint overloaded or maintenance |
Retry with exponential backoff (wait 2s, 4s, 8s…). Check provider status page. |
Diagnosis:
import os
# Check if API keys are set (environment variables)
api_keys = {
'NVIDIA_API_KEY': os.getenv('NVIDIA_API_KEY'),
'NCBI_API_KEY': os.getenv('NCBI_API_KEY'),
'SEMANTIC_SCHOLAR_API_KEY': os.getenv('SEMANTIC_SCHOLAR_API_KEY'),
'DISGENET_API_KEY': os.getenv('DISGENET_API_KEY'),
'USPTO_API_KEY': os.getenv('USPTO_API_KEY'),
'FDA_API_KEY': os.getenv('FDA_API_KEY'),
'OMIM_API_KEY': os.getenv('OMIM_API_KEY'),
}
for key, value in api_keys.items():
status = "✓ Set" if value else "✗ Missing"
print(f"{key}: {status}")
Solutions:
Identify which API key you need:
See API Keys and Authentication for a complete list of which tools require which API keys and how to obtain them.
Set API keys in environment:
# Linux/macOS export NVIDIA_API_KEY="your_key_here" export NCBI_API_KEY="your_ncbi_key" # Windows (Command Prompt) set NVIDIA_API_KEY=your_key_here # Windows (PowerShell) $env:NVIDIA_API_KEY="your_key_here"
Use .env file (recommended):
# Copy template and fill in your keys cp docs/.env.template .env nano .env # or use any text editor
from dotenv import load_dotenv load_dotenv() # Load from .env file
In Python code:
import os os.environ['NVIDIA_API_KEY'] = 'your_key_here' os.environ['NCBI_API_KEY'] = 'your_ncbi_key'
Common API key issues:
Key is expired or invalid: Regenerate the key from the provider’s dashboard
Key has insufficient permissions: Check that the key has the necessary scopes/permissions
Key not loaded: Ensure environment variables are set before importing ToolUniverse
Typo in variable name: Double-check spelling (e.g.,
NVIDIA_API_KEYnotNVIDIA_KEY)
Rate limiting errors#
Symptom: 429 Too Many Requests or RateLimitExceeded
Understanding Rate Limits:
Many services limit how many requests you can make per second/minute. API keys typically provide much higher limits:
Service |
Without API Key |
With API Key |
|---|---|---|
NCBI E-utilities |
3 req/sec |
10 req/sec (3x faster, set NCBI_API_KEY) |
Semantic Scholar |
1 req/sec |
100 req/sec (100x faster, set SEMANTIC_SCHOLAR_API_KEY) |
OpenFDA |
40 req/min |
240 req/min (6x faster, set FDA_API_KEY) |
Solutions:
Get an API key for higher limits:
See API Keys and Authentication for how to obtain API keys for each service.
Add delays between requests:
import time for query in queries: result = tu.run(query) time.sleep(1) # Wait 1 second between requests
Use batch processing:
# Process in smaller batches batch_size = 5 for i in range(0, len(queries), batch_size): batch = queries[i:i+batch_size] results = tu.run(batch, max_workers=4) time.sleep(2) # Pause between batches
Enable caching to avoid redundant requests:
import os os.environ["TOOLUNIVERSE_CACHE_DEFAULT_TTL"] = "3600" # 1 hour result = tu.run(query, use_cache=True)
Network & Connectivity#
Connection timeouts#
Symptom: ConnectionTimeout or ReadTimeout errors.
Diagnosis:
import requests
# Test basic connectivity
try:
response = requests.get('https://httpbin.org/delay/1', timeout=5)
print(f"Network OK: {response.status_code}")
except requests.exceptions.Timeout:
print("Network timeout - check connection")
Solutions:
Increase timeout:
tu = ToolUniverse() # timeout is configured per-tool at the HTTP request levelCheck network connection:
ping google.com curl -I https://platform-api.opentargets.org
Configure proxy (if needed):
import os os.environ['HTTP_PROXY'] = 'http://proxy.company.com:8080' os.environ['HTTPS_PROXY'] = 'http://proxy.company.com:8080'
SSL Certificate errors#
Symptom: SSLError or certificate verification failures.
Solutions:
Update certificates:
pip install --upgrade certifi requests urllib3Temporary bypass (not recommended for production):
import ssl ssl._create_default_https_context = ssl._create_unverified_context
Corporate firewall:
Contact your IT department for proper certificate configuration.
Performance Issues#
Slow query performance#
Diagnosis:
import time
from tooluniverse import ToolUniverse
tu = ToolUniverse()
# Time a query
start_time = time.time()
result = tu.run({"name": "tool_name", "arguments": {}})
elapsed = time.time() - start_time
print(f"Query took {elapsed:.2f} seconds")
Optimization strategies:
Enable caching:
import os os.environ["TOOLUNIVERSE_CACHE_DEFAULT_TTL"] = "3600" # 1 hour tu = ToolUniverse() result = tu.run(..., use_cache=True)
Use async operations:
import asyncio async def run_queries(): tasks = [tu.run(query) for query in queries] # run() is context-aware results = await asyncio.gather(*tasks) return results
Batch similar queries:
# Instead of individual queries by gene symbol, prefer accession-based queries accession_info = [] for accession in accessions: info = tu.run({ "name": "UniProt_get_function_by_accession", "arguments": {"accession": accession} }) accession_info.append(info) # Use batch processing queries = [ {"name": "UniProt_get_function_by_accession", "arguments": {"accession": accession}} for accession in accessions ] results = tu.run(queries, max_workers=4)
Memory usage issues#
Symptom: High memory consumption or MemoryError.
Diagnosis:
import psutil
import os
process = psutil.Process(os.getpid())
memory_mb = process.memory_info().rss / 1024 / 1024
print(f"Memory usage: {memory_mb:.1f} MB")
Solutions:
Process data in chunks:
def process_large_dataset(data, chunk_size=100): for i in range(0, len(data), chunk_size): chunk = data[i:i+chunk_size] yield tu.run(chunk, max_workers=4)
Clear cache periodically:
tu.clear_cache()Use generators for large results:
def yield_results(queries): for query in queries: yield tu.run(query)
MCP Server Issues#
MCP server won’t start#
Symptom: Server fails to start or crashes immediately.
Diagnosis:
# Test server directly tooluniverse-smcp --debug
Solutions:
Check port availability:
# Check if port is in use lsof -i :3000 # Linux/Mac netstat -an | findstr :3000 # Windows
Use different port:
tooluniverse-smcp --port 3001Check permissions:
# Ensure user can bind to port sudo tooluniverse-smcp # If needed
Claude/AI assistant not finding tools#
Diagnosis:
Verify MCP server is running:
curl http://localhost:3000/healthCheck Claude configuration:
Ensure MCP server is properly configured in Claude Desktop settings.
Solutions:
Restart both server and Claude
Check Claude logs for connection errors
Verify tool registration:
# In MCP server tools = tu.return_all_loaded_tools() print(f"Registered {len(tools)} tools")
Claude Desktop reports spawn uv ENOENT or spawn uvx ENOENT#
Diagnosis:
Claude Desktop starts MCP servers from a GUI process and may not inherit the
same PATH as your terminal. A config that works in a shell can still fail in
Claude Desktop if uv or uvx is not available by absolute path.
Solutions:
Verify the command in a normal terminal:
uvx --version uvx tooluniverse --help
Find the absolute path to ``uvx``:
which uvx # macOS/Linux where uvx # Windows
Use that absolute path in Claude Desktop config. For example:
{ "mcpServers": { "tooluniverse": { "command": "/Users/yourname/.local/bin/uvx", "args": ["--refresh", "tooluniverse"] } } }
Restart Claude Desktop completely after saving the config. First startup can take longer while
uvxresolves and installs the package.
Advanced Debugging#
Enable debug logging#
import logging
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('tooluniverse')
logger.setLevel(logging.DEBUG)
# Now run your code
tu = ToolUniverse()
Capture detailed error information#
import traceback
try:
result = tu.run(query)
except Exception as e:
print(f"Error: {e}")
print(f"Type: {type(e).__name__}")
print("Traceback:")
traceback.print_exc()
Profile performance bottlenecks#
import cProfile
import pstats
# Profile your code
profiler = cProfile.Profile()
profiler.enable()
# Your ToolUniverse code here
tu = ToolUniverse()
result = tu.run(query)
profiler.disable()
# Analyze results
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats(10)
Getting Help#
If none of these solutions work:
Check the FAQ: Comprehensive FAQ
Search GitHub issues: Issues
Create a bug report with:
ToolUniverse version:
tooluniverse.__version__Python version:
python --versionOperating system
Full error message and traceback
Minimal code example that reproduces the issue
Join our community: Discord server link
Note
When reporting issues, please run the diagnostic script first:
from tooluniverse.diagnostics import run_diagnostic
print(run_diagnostic())
Include this output in your bug report.