
Devtu Self Evolve
- 265 installs
- 1.6k repo stars
- Updated August 4, 2026
- mims-harvard/tooluniverse
Evolve ToolUniverse skill and tool catalogs automatically from usage feedback, expanding agent capabilities without manual curation each release.
About
Explains devtu-self-evolve from mims-harvard/tooluniverse: mechanisms for ToolUniverse to refine and extend its agent skills from runtime signals, keeping scientific and developer tool packs current without hand-maintaining every variant.
- Automated skill regeneration
- Usage-driven tool expansion
- Harvard ToolUniverse integration
- Reduced manual skill upkeep
- Adaptive agent tool catalogs
Devtu Self Evolve by the numbers
- 265 all-time installs (skills.sh)
- +6 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #2,453 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mims-harvard/tooluniverse --skill devtu-self-evolveAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 265 |
|---|---|
| repo stars | ★ 1.6k |
| Last updated | August 4, 2026 |
| Repository | mims-harvard/tooluniverse ↗ |
What it does
Evolve ToolUniverse skill and tool catalogs automatically from usage feedback, expanding agent capabilities without manual curation each release.
Files
ToolUniverse Self-Evolution Orchestrator
Coordinates the full development lifecycle by dispatching to specialized devtu skills.
The Cycle
Discover → Create → Test → Fix → Optimize → Ship → RepeatEach phase maps to a dedicated skill:
| Phase | Skill | What it does |
|---|---|---|
| Discover | devtu-auto-discover-apis | Gap analysis, web search for APIs, batch discovery |
| Create | devtu-create-tool | Build tool class + JSON config + test examples |
| Test | (this skill) | Launch researcher persona agents to find issues |
| Fix | devtu-fix-tool | Diagnose failures, implement fixes, validate |
| Optimize | devtu-optimize-skills | Improve skill reports, evidence handling, UX |
| Optimize | devtu-optimize-descriptions | Improve tool JSON descriptions for clarity |
| Docs | devtu-docs-quality | Validate documentation accuracy |
| Ship | devtu-github | Branch, commit, push, create PR |
Quick Start
Pick an entry point based on what's needed:
- "Run a test round" → jump to Testing Phase
- "Expand coverage" → invoke
Skill(skill="devtu-auto-discover-apis") - "Create a new tool" → invoke
Skill(skill="devtu-create-tool") - "Fix a broken tool" → invoke
Skill(skill="devtu-fix-tool") - "Improve skills" → invoke
Skill(skill="devtu-optimize-skills") - "Full cycle" → follow all phases below in order
---
Phase 1: Discovery (optional)
Invoke Skill(skill="devtu-auto-discover-apis") to: 1. Run gap analysis on current tool categories 2. Search for life science APIs in underrepresented domains 3. Score and prioritize APIs by coverage, reliability, documentation
Phase 2: Tool Creation (optional)
Invoke Skill(skill="devtu-create-tool") for each new API: 1. Create Python tool class implementing the API 2. Create JSON config with parameters, descriptions, test examples 3. Register in _lazy_registry_static.py and default_config.py 4. Validate: python -m tooluniverse.cli test <ToolName>
Phase 3: Testing Phase
This is the core testing loop, run directly by this skill.
Setup
1. Check for open PRs: gh pr list --state open 2. If unmerged PR → use that branch; if merged → new branch from origin/main 3. Rebase: git fetch origin && git rebase origin/main
Researcher Persona Agents
Launch 2 agents per round (A + B) using the Agent tool with these parameters:
Each agent gets:
- Domain specialty (oncology, genomics, pharmacology, etc.)
- Research question (specific biological question)
- 5-7 test scenarios exercising different tools
- Instructions to report issues with severity (HIGH/MEDIUM/LOW)
- Issue IDs:
Feature-{round}{letter}-{num}(e.g.,Feature-59A-001)
Agent prompt template — see references/persona-template.md
Verification (CRITICAL)
Before implementing ANY agent-reported issue, verify via CLI:
python3 -m tooluniverse.cli run <ToolName> '<json_args>'50%+ of agent reports are false positives from MCP interface confusion. Only fix verified issues.
Fix Principles
1. Prevent, don't recover — fix root cause, not symptoms 2. Validate at input — reject bad params early with clear guidance 3. Distinguish "no data" from "bad query" — different messages for each 4. Fix the abstraction — don't add alias lists that grow forever
Anti-patterns: hint text instead of validation, parameter aliases instead of fixing naming, post-hoc probing instead of pre-validation.
Skill Usefulness Testing (NEW — beyond tool testing)
Standard testing verifies tools work. Usefulness testing verifies skills actually solve scientist problems. Run this after standard testing:
1. Pick a real research question that the skill claims to answer (not a tool-level test) 2. Launch an agent following the skill workflow on the real question 3. Assess honestly: Does the skill produce an actionable answer, or just a data dump?
Score 1-10 rubric:
- 1-3: Tool catalog — lists tools without interpretation
- 4-6: Data collector — gathers data but doesn't help combine/interpret
- 7-8: Reasoning framework — guides interpretation with tables/scoring/synthesis
- 9-10: Decision engine — produces concrete, defensible recommendations
Common failure patterns found in usefulness tests:
| Pattern | Score Impact | Fix |
|---|---|---|
| "Call A, then B, then C" without explaining what to DO with results | -3 | Add interpretation tables |
| Tool params wrong (tool works but skill documents wrong names) | -2 | Verify ALL tool params via get_tool_info() |
| Promises data the API can't deliver (e.g., DepMap CRISPR scores) | -2 | Be honest about limitations; add computational procedure workaround |
| No synthesis phase at the end | -2 | Add "so what?" phase that combines all evidence |
| No evidence grading | -1 | Add T1-T4 or similar confidence tiers |
| No computational procedures for things tools can't do | -1 | Add Python code blocks using scipy/pandas/numpy |
When tools can't help, add computational procedures: Some analyses need Python code, not API calls. Skills should include working code blocks for:
- Statistical testing (scipy.stats, FDR correction)
- Data analysis from downloaded files (pandas + CSV from DepMap, TCGA, etc.)
- Scoring algorithms (ACMG classification, viability scores)
- Sequence analysis (Biopython)
See devtu-optimize-skills Patterns 14-15 for full guidance.
Phase 3.5: Benchmark Evaluation
Quantify plugin performance after testing. Uses Skill(skill="devtu-benchmark-harness").
# Run lab-bench (20 MCQ)
python skills/devtu-benchmark-harness/scripts/run_eval.py --benchmark lab-bench --mode plugin-only --n 20
# Run BixBench (computational, use first 20)
python skills/devtu-benchmark-harness/scripts/run_eval.py --benchmark bixbench --mode plugin-only --n 20
# Analyze results
python skills/devtu-benchmark-harness/scripts/analyze_results.py --results <results-file>
# Generate report
python skills/devtu-benchmark-harness/scripts/generate_report.py --results <results-file> --output BENCHMARK_REPORT.mdCompare with previous round. If any category regresses, prioritize fixing that skill/tool in Phase 4.
Phase 4: Fix & Commit
1. Implement verified fixes (see references/bug-patterns.md for code-level patterns) 2. Run code-simplifier: Skill(skill="simplify") — always after writing or modifying code 3. Lint: ruff check src/tooluniverse/<file>.py 4. Verify syntax: python -c "from tooluniverse.<module> import <Class>" 5. Test: python -m tooluniverse.cli run <Tool> '<json>' 6. Pre-commit hook pattern: stage → commit (fails, reformats) → re-stage → commit 7. Push: git push origin <branch>
Also see Skill(skill="devtu-code-optimization") for reusable fix patterns and anti-patterns.Phase 5: Optimize (optional)
After fixes are stable:
Skill(skill="devtu-optimize-descriptions")— improve tool descriptionsSkill(skill="devtu-optimize-skills")— improve research skill qualitySkill(skill="devtu-docs-quality")— validate docs accuracy
Phase 6: Ship
Invoke Skill(skill="devtu-github") or manually: 1. Rebase: git fetch origin && git stash && git rebase origin/main && git stash pop 2. git push --force-with-lease origin <branch> 3. Create or update PR: gh pr create / verify with gh pr view <N> --json mergeable 4. Verify "mergeable": "MERGEABLE" before reporting done
GitHub repo: mims-harvard/ToolUniverse — always verify with git remote -v before pushing.
---
Git Rules (CRITICAL)
- NEVER push to main — all work on feature branches
- NEVER have multiple open fix PRs — keep adding to current branch
- Always rebase before push:
git fetch origin && git rebase origin/main - Commit message format: no "BUG" terminology, use "Feature" or "Fix"
- No AI attribution in commits
Common Issue Categories
| Category | Signal |
|---|---|
| Silent parameter miss | Wrong-field check; param ignored |
| Always-fires conditional | .get("field") on wrong type |
| Silent normalization | Auto-transform not disclosed |
| Wrong notation/case | Gene fusions, Title Case names |
| Substring match | Short symbol returns multiple targets |
| try/except indent | Mismatched → SyntaxError |
Full patterns → references/bug-patterns.md
Round Tracking
After each round: advance counter, update patterns file, keep this SKILL.md under 150 lines.
Current round: 127 (rounds completed: 52-126)
Bug Patterns Reference
Detailed code-level fix patterns discovered through role-play debug rounds.
Table of Contents
Rounds 52–57 (core patterns)
- Regex Overmatch: Mutation vs Fusion
- Silent Parameter Alias
- Always-Fires Conditional
- Silent Normalization
- Notation / Case Sensitivity
- Truncation Not Top-Level
- Gene Symbol Substring Match
- try/except Indentation
- Multi-Word Search Hints
- Expression Units Inference
- Auto-Selected vs Explicit Study
- CIViC Therapy + AND Logic
Rounds 68–78 (API-specific patterns)
- DGIdb Client-Side Filtering
- CPIC PostgREST Filter Prefix
- CPIC Recommendation Join
- KEGG Organism-Specific Gene Search
- HPA Deprecated Column
- PDBePISA Operation from Config
- GTEx Dataset Selection
- GxA Silent geneId Ignore
- ExpressionAtlas Schema Key Mismatch
- Proteins API Human Priority
- RCSB Null Type in Schema
- ClinVar Search Field Tags
- ENCODE Tissue+Organism Filter
- GEO Double-Adding Search Terms
- CPIC Warfarin Algorithm Guideline
- PharmGKB clinpgxid vs pharmgkbid
- ENCODE ChIP-seq Assay Title
- RegulomeDB Parameter Names
- BindingDB API Typo
- ProteomeXchange Title and Instruments
- MetabolomicsWorkbench moverz URL
- MetaboLights Pagination Ignored
- ChEBI Search Nulls and HTML
- HMDB No Open API
- PharmGKB relatedGenes Filter
---
Regex Overmatch: Mutation vs Fusion {#regex-overmatch}
Tools: civic_search_evidence_items, civic_search_variants Bug IDs: BUG-56A-001
A regex that normalizes GENE1-GENE2 fusion notation can accidentally match GENE-MutationCode (e.g., EGFR-T790M, BRAF-V600E, KRAS-G12C) because mutation codes start with an uppercase letter.
Fix: Use a lambda that checks if the second part matches HGVS protein-change format before applying the substitution:
def _maybe_fuse(m):
second = m.group(2)
# Protein-change: single letter + digits + letter/asterisk (e.g. T790M, V600E, G12C)
if re.match(r"^[A-Z]\d+[A-Z*]?$", second):
return m.group(0) # leave unchanged
return m.group(1) + "::" + second
normalized = re.sub(r"\b([A-Z][A-Z0-9]*)-([A-Z][A-Z0-9]+)\b", _maybe_fuse, mol_profile)When the input matches the mutation pattern and returns 0 results, add a hint:
elif re.search(r"\b[A-Z][A-Z0-9]*-[A-Z]\d+[A-Z*]?\b", mol_profile):
space_form = mol_profile.replace("-", " ", 1)
mp_warn += f" If '{mol_profile}' is a point mutation, try molecular_profile='{space_form}'..."---
Silent Parameter Alias
Tools: SYNERGxDB_search_combos Bug IDs: BUG-55A-011
Users pass cancer_type but tool only accepts sample, tissue_name, tissue. Parameter silently dropped → all-tissue results returned with status: "success".
Fix:
sample = (
arguments.get("sample")
or arguments.get("tissue_name")
or arguments.get("tissue")
or arguments.get("cancer_type") # ADD aliases here
)---
Always-Fires Conditional
Tools: GtoPdb_get_interactions Bug IDs: BUG-55A-002, BUG-55A-003, BUG-55B-001
item.get("approved") on interaction records returns None always (field only exists on /ligands objects). So has_approved was always False → conditional note always fired. Also used wrong category language ("kinase/enzyme") for GPCR targets.
Fix: Remove the conditional check; always emit the note as factual guidance. Use neutral language; embed the queried gene symbol:
if isinstance(data, list) and len(data) > 0:
_chembl_target = gene_symbol or result.get("queried_target", {}).get("name", "the target")
result["coverage_note"] = (
"GtoPdb interactions list pharmacological research compounds — approved "
"drugs for this target may not be represented. For approved drugs and "
"clinical compounds, use ChEMBL_get_drug_mechanisms or "
f"ChEMBL_search_molecules with target_name='{_chembl_target}'."
)---
Silent Normalization
Tools: civic_search_evidence_items, civic_search_variants Bug IDs: BUG-55A-008, BUG-55B-005
CIViC normalizes therapy (lowercase → Title Case) and molecular_profile (BCR-ABL1 → BCR::ABL1) without disclosing the transformation. Users debugging empty results can't see what was tried.
Fix: Track pre-normalization values, add a normalization_note field to the result:
_therapy_normalized_from = None
_mp_normalized_from = None
# When normalizing therapy:
_therapy_normalized_from = therapy
arguments["therapy"] = therapy.title()
# After result is built:
_norm_parts = []
if _therapy_normalized_from:
_norm_parts.append(f"therapy '{_therapy_normalized_from}' → '{arguments.get('therapy')}' (CIViC uses Title Case)")
if _mp_normalized_from:
_norm_parts.append(f"molecular_profile '{_mp_normalized_from}' → '{arguments.get('molecular_profile')}' ...")
if _norm_parts:
result["normalization_note"] = "Input auto-normalized: " + "; ".join(_norm_parts) + "."---
Notation / Case Sensitivity {#notation-case}
Tools: civic_search_evidence_items Bug IDs: BUG-55B-005 (fusion), BUG-53B-002 (therapy case)
CIViC uses :: for gene fusions (BCR::ABL1 Fusion, EML4::ALK Fusion). Users write hyphenated forms (BCR-ABL1) → 0 results. CIViC therapy names are Title Case ("Sotorasib" not "sotorasib").
Fix (fusion normalization):
import re as _re
mol_profile = arguments.get("molecular_profile")
if mol_profile and isinstance(mol_profile, str):
normalized_mp = _re.sub(
r"\b([A-Z][A-Z0-9]*)-([A-Z][A-Z0-9]+)\b",
r"\1::\2",
mol_profile,
)
if normalized_mp != mol_profile:
_mp_normalized_from = mol_profile
arguments = dict(arguments)
arguments["molecular_profile"] = normalized_mpFix (therapy case):
if therapy == therapy.lower() or therapy == therapy.upper():
_therapy_normalized_from = therapy
arguments["therapy"] = therapy.title()---
Truncation Not Top-Level {#truncation}
Tools: CancerPrognosis_get_gene_expression Bug IDs: BUG-55A-006
Truncation info (e.g., 500 of 1100 samples returned) buried inside data.note string. Researchers reading top-level fields miss it.
Fix: Add top-level flags:
response = {"status": "success", "data": result_data}
if len(values) > max_samples:
response["truncated"] = True
response["truncation_note"] = (
f"Returning {len(values[:max_samples])} of {len(values)} samples. "
f"Pass max_samples={len(values)} (up to 2000) to retrieve the full dataset."
)
return response---
Gene Symbol Substring Match {#gene-symbol}
Tools: GtoPdb_get_interactions Bug IDs: BUG-54B-001
?name=AR returns all targets containing "AR" (13+ targets). ?geneSymbol=AR is the unambiguous HGNC lookup.
Fix: Try ?geneSymbol= first; fall back to ?name= if nothing returned:
gs_url = f"{base_url}/targets?{urlencode({'geneSymbol': gene_symbol})}"
gs_resp = request_with_retry(...)
if gs_resp.status_code == 200:
gs_targets = gs_resp.json()
if gs_targets:
target_id = gs_targets[0]["targetId"]
# Fall back to ?name= only if geneSymbol returned nothing
if target_id is None:
lookup_url = f"{base_url}/targets?{urlencode({'name': gene_symbol})}"
...---
try/except Indentation {#try-except}
Every try: MUST have an except at the exact same indentation level. A common error is placing code after try: at the same level as the try: keyword — Python sees the try block as having no except.
# CORRECT
try: # 16 spaces
resp = ... # 20 spaces (inside try)
if resp.ok: # 20 spaces (still inside try)
...
except Exception: # 16 spaces — matches try
pass
# WRONG — SyntaxError
try: # 16 spaces
resp = ... # 20 spaces
if resp.ok: # 16 spaces — OUTSIDE try, before except!
...
except Exception: # 16 spaces — Python: "try without except"
pass---
Multi-Word Search Hints {#multi-word-search}
Tools: GtoPdb_search_targets, GtoPdb_search_ligands Bug IDs: BUG-54B-002
name="androgen receptor" returns empty — GtoPdb text search doesn't reliably match multi-word phrases.
Fix: When count=0 and query has a space, add a hint suggesting first word only:
if result["count"] == 0 and name_q and " " in str(name_q):
first_word = str(name_q).split()[0]
result["multi_word_hint"] = (
f"GtoPdb text search may not match multi-word phrases like '{name_q}'. "
f"Try a single keyword: name='{first_word}'."
)---
Expression Units Inference {#expression-units}
Tools: CancerPrognosis_get_gene_expression Bug IDs: BUG-54A-002
_get_expression_units(profile_id) infers units from the profile ID string. Some studies use misleading suffixes (e.g., aml_ohsu_2022 uses _rna_seq_v2_mrna but stores log2 RPKM).
Fix: Return (profile_id, profile_name) from _get_mrna_profile() and prefer the actual API description over inference:
def _get_expression_units(self, profile_id, profile_name=None):
if profile_name:
return profile_name # Prefer actual API name
# ... fallback inference from profile_id---
Auto-Selected vs Explicit Study {#auto-selected-study}
Tools: CancerPrognosis_get_gene_expression Bug IDs: BUG-54A-004
study_note always said "Auto-selected study_id=..." even when user explicitly passed a full study ID like brca_tcga.
Fix: Detect explicit specification and use a different message:
study_was_explicit = (
cancer == study_id_arg
and study_id_arg
and "_" in str(study_id_arg)
and str(study_id_arg).upper() not in TCGA_STUDY_MAP
and str(study_id_arg).upper() not in _CANCER_NAME_ALIASES
)
"study_note": (
f"Using explicitly specified study_id='{study_id}'."
if study_was_explicit
else f"Auto-selected study_id='{study_id}'. Use CancerPrognosis_search_studies "
"to find alternative cohorts for this cancer type."
),---
CIViC Therapy + AND Logic {#civic-therapy-and}
Tools: civic_search_evidence_items, civic_search_variants Bug IDs: BUG-54A-001, BUG-54A-005
1. When both query and variant_name are provided, one silently wins. Fix: apply AND logic. 2. When molecular_profile + therapy returns 0, silently fails. Fix: auto-probe without therapy to surface available therapy names.
Fix (AND logic):
raw_query = arguments.get("query")
raw_variant_name = arguments.get("variant_name") or arguments.get("variant")
_both = raw_query and raw_variant_name and raw_query != raw_variant_name
if _both:
query_term = raw_query
_secondary_term = raw_variant_name
else:
query_term = raw_query or raw_variant_name
_secondary_term = None
# Later, apply AND filter:
if _secondary_term:
filtered = [v for v in filtered if _secondary_term.lower() in v.get("name","").lower()]
result["filter_note"] = f"Applied AND logic for query+variant_name."Fix (therapy auto-probe):
if mol_profile and therapy and not disease and len(evidence_nodes) == 0:
probe_args = {k: v for k, v in arguments.items() if k != "therapy"}
probe_nodes = civic_query(probe_args)
available_therapies = sorted({
t.get("name") for node in probe_nodes
for t in node.get("therapies", []) if t.get("name")
})
result["therapy_warning"] = (
f"No evidence for '{mol_profile}' + therapy='{therapy}'. "
f"Available therapies: {available_therapies[:10]}"
)---
DGIdb Client-Side Filtering (Feature-68A-001/002)
Tools: DGIdb_get_drug_gene_interactions Pattern: API ignores interaction_types and sources filters server-side → returns everything.
Fix: Fetch all, filter client-side:
if interaction_types:
results = [r for r in results if r.get("interactionTypes") and
any(it in interaction_types for it in r["interactionTypes"])]
if sources:
results = [r for r in results if r.get("source") in sources]
# Always wrap with status
return {"status": "success", "count": len(results), "data": results}---
CPIC PostgREST Filter Prefix (Feature-68A-004)
Tools: CPICSearchPairsTool Pattern: PostgREST requires equality filter as eq.VALUE, not bare VALUE.
Fix: Auto-prepend eq. if not already present:
def _postgrest_eq(value):
v = str(value)
return v if v.startswith("eq.") else f"eq.{v}"
params["genesymbol"] = _postgrest_eq(gene_symbol)---
CPIC Recommendation Join (Feature-68A-006)
Tools: CPIC_get_recommendations Pattern: /recommendation endpoint doesn't include drug name — need to join /drug table.
Fix: Use ?select=*,drug(name) in the URL query string for PostgREST joins.
---
KEGG Organism-Specific Gene Search (Feature-68B-001)
Tools: kegg_find_genes Pattern: /find/genes/{keyword} ignores organism param → returns all organisms.
Fix: Use /find/{organism}/{keyword}:
url = f"https://rest.kegg.jp/find/{organism}/{keyword}" if organism else f"https://rest.kegg.jp/find/genes/{keyword}"---
HPA Deprecated Column (Feature-68B-002)
Tools: HPA_get_protein_expression Pattern: ppi column was deprecated; API returns error listing alternatives (enhanced, supported, approved, uncertain).
Fix: Remove ppi from select list; return error message with alternatives when API responds with column-not-found error.
---
PDBePISA Operation from Config (Feature-68B-006)
Tools: PDBePISA tools Pattern: operation param exposed as public parameter → users pass wrong values.
Fix: Read operation from the tool's JSON fields config, never expose as public argument.
---
GTEx Dataset Selection (Feature-69A-001/002)
Tools: GTEx_get_median_gene_expression Pattern: gtex_v10 returns empty for medianGeneExpression endpoint; only gtex_v8 works reliably.
Fix: Always default to gtex_v8. Add note in result:
dataset = arguments.get("dataset", "gtex_v8")
if dataset == "gtex_v10":
result["dataset_note"] = "gtex_v10 may return empty for this endpoint; gtex_v8 is recommended."---
GxA Silent geneId Ignore (Feature-69A-003)
Tools: ExpressionAtlas_get_experiment Pattern: /experiments/{accession} API ignores geneId parameter silently.
Fix: Apply geneId filter client-side on the returned data.
---
ExpressionAtlas Schema Key Mismatch (Feature-69A-004)
Tools: ExpressionAtlas tools Pattern: return_schema key experiments but code returns baseline_experiments.
Fix: Make key in return_schema match exactly what the code produces. Use baseline_experiments.
---
Proteins API Human Priority (Feature-69A-007)
Tools: proteins_api_search Pattern: Gene/name searches return non-human results first.
Fix: Add taxid=9606 default for gene and name searches to prioritize human entries.
---
RCSB Null Type in Schema (Feature-69B-002/010)
Tools: RCSB tools Pattern: return_schema has type: null which is invalid JSON Schema.
Fix: Use ["array", "null"] or ["string", "null"] for nullable types. Also avoid using protein-only PDB IDs as test examples — use IDs that represent the tested operation.
---
ClinVar Search Field Tags (Feature-70B-004/005)
Tools: ClinVar tools Pattern: [variant_id] is not a valid eSearch field → use [uid]. [disease/phenotype] not valid → use bare condition text (no brackets).
Fix:
# Wrong:
query = f"{variant_id}[variant_id]"
query = f"{condition}[disease/phenotype]"
# Right:
query = f"{variant_id}[uid]"
query = condition # bare text, NCBI searches all fields---
ENCODE Tissue+Organism Filter (Feature-70A-003)
Tools: ENCODE_search_experiments Pattern: Combining tissue + organism filter → HTTP 404.
Fix: Fall back without organism filter when 404 occurs:
try:
resp = requests.get(url_with_organism)
if resp.status_code == 404:
resp = requests.get(url_without_organism)
except Exception:
...---
GEO Double-Adding Search Terms (Feature-70A-008)
Tools: geo_search_datasets Pattern: Code adds "methylation" to query even when it's already present.
Fix: Check before appending:
if "methylation" not in query.lower():
query += " methylation"---
CPIC Warfarin Algorithm Guideline (Feature-70B-001)
Tools: CPIC_get_recommendations Pattern: Warfarin guideline (ID 100425) uses algorithm endpoint, not /recommendation. Returns 0 rows from recommendation endpoint.
Fix: Detect warfarin (or any guideline that uses algorithm-based dosing) and redirect to /algorithm endpoint or explain in the response.
---
PharmGKB clinpgxid vs pharmgkbid (Feature-70B-002/011)
Tools: PharmGKB tools Pattern: CPIC list_guidelines returns clinpgxid, but code uses pharmgkbid → 404 from PharmGKB.
Fix: Use clinpgxid from CPIC response when building PharmGKB URLs.
---
ENCODE ChIP-seq Assay Title (Feature-73B)
Tools: ENCODE_search_experiments Pattern: Querying assay_title=ChIP-seq returns 0; correct value is TF ChIP-seq. Also: organism filter uses wrong nested field name.
Fix: Map user-friendly aliases to ENCODE-specific values:
ASSAY_ALIASES = {"ChIP-seq": "TF ChIP-seq", "CHIP": "TF ChIP-seq"}
assay_title = ASSAY_ALIASES.get(assay_title, assay_title)---
RegulomeDB Parameter Names (Feature-73B)
Tools: RegulomeDB tools Pattern: assembly=hg19 is wrong → use genome=GRCh38. Test example rs123456 doesn't exist → use rs4994.
Fix: Update parameter names and test examples to match actual API.
---
BindingDB API Typo (Feature-73A)
Tools: BindingDB tools Pattern: API endpoint has typo: getLindsByXxx (not getLigandsByXxx). Also response key is bdb.affinities not affinities.
Fix: Use exact typo as in the real API URL. Access response["bdb.affinities"].
---
ProteomeXchange Title and Instruments (Feature-73A)
Tools: ProteomeXchange tools Pattern: title field is plain string not dict. instruments use {name, accession} not nested terms.
Fix: Access dataset.get("title") directly. For instruments: inst.get("name") not inst["terms"][0]["name"].
---
MetabolomicsWorkbench moverz URL (Feature-73A/74A)
Tools: MetabolomicsWorkbench tools Pattern: moverz endpoint needs database prefix: /moverz/{database}/{mz}/{adduct}/{tolerance}. Also exactmass endpoint is broken → use moverz/REFMET/{mass}/M/{tolerance}.
Fix:
url = f"https://www.metabolomicsworkbench.org/rest/moverz/REFMET/{mz}/M+H/{tolerance}"---
MetaboLights Pagination Ignored (Feature-74A)
Tools: MetaboLights tools Pattern: /ws/studies API silently ignores size and page params → always returns full list.
Fix: Apply pagination client-side after fetching all results:
all_studies = resp.json()
start = page * size
return all_studies[start:start + size]---
ChEBI Search Nulls and HTML (Feature-74A)
Tools: ChEBI_search_compounds Pattern: smiles and inchikey always null from search API. Names contain HTML entities (&, <).
Fix: Strip HTML from names using re.sub(r"<[^>]+>", "", name) and html.unescape(name). Don't return null smiles/inchikey — omit them or fetch from detail endpoint.
---
HMDB No Open API (Feature-74A)
Tools: HMDB tools Pattern: HMDB has no open REST API; tools were returning stub success responses.
Fix: Return {"status": "error", "message": "HMDB does not provide a public REST API. Use MetabolomicsWorkbench or ChEBI instead."}.
---
PharmGKB relatedGenes Filter (Feature-74B)
Tools: PharmGKB tools Pattern: relatedGenes.id and relatedGenes.symbol filters not supported by API — silently returns all results.
Fix: Return informative message: "relatedGenes filter is not supported by PharmGKB API. Filter results client-side or use gene-specific endpoints instead."
Researcher Persona Agent Template
Agent Prompt Structure
Use the Agent tool with subagent_type="research" and the following prompt structure:
You are a {specialty} researcher investigating: {research_question}
You have access to ToolUniverse MCP tools. Use them to answer your research question.
## Your Test Scenarios
{numbered list of 5-7 specific queries}
## Reporting Issues
For each tool call, evaluate the response:
- Did it return expected data?
- Are parameter names intuitive?
- Are error messages actionable?
- Is the output format useful?
Report issues using this format:
### Feature-{round}{letter}-{num}
- **Tool**: ToolName
- **Input**: `{json_args}`
- **Expected**: What should happen
- **Actual**: What actually happened
- **Severity**: HIGH | MEDIUM | LOW
- HIGH: Wrong data, crash, silent failure
- MEDIUM: Confusing output, poor error message
- LOW: Cosmetic, minor UXExample Persona Pairs
Round N: Oncology + Pharmacology
Persona A — Clinical Oncologist
- Specialty: precision oncology, tumor genomics
- Question: "What targeted therapies are available for EGFR-mutant NSCLC?"
- Scenarios:
1. Search CIViC for EGFR L858R evidence 2. Find clinical trials for osimertinib 3. Check EGFR drug interactions 4. Look up EGFR protein structure 5. Search for resistance mutations (T790M)
Persona B — Clinical Pharmacologist
- Specialty: drug safety, pharmacogenomics
- Question: "What are the safety considerations for warfarin therapy?"
- Scenarios:
1. Search FAERS for warfarin adverse events 2. Look up CYP2C9 pharmacogenomics 3. Check warfarin drug-drug interactions 4. Find CPIC guidelines for warfarin 5. Search PharmGKB for warfarin annotations
Round N+1: Genomics + Systems Biology
Persona A — Genomics Researcher
- Specialty: GWAS, variant interpretation
- Question: "What genetic variants are associated with type 2 diabetes?"
- Scenarios:
1. Search GWAS catalog for T2D associations 2. Interpret rs7903146 (TCF7L2) 3. Look up variant in ClinVar 4. Check gnomAD allele frequencies 5. Find gene expression in pancreas (GTEx) 6. OpenTargets disease associations
Persona B — Systems Biologist
- Specialty: pathway analysis, network biology
- Question: "How does the insulin signaling pathway relate to cancer?"
- Scenarios:
1. Search Reactome for insulin signaling 2. Find protein interactions for AKT1 3. KEGG pathway for PI3K-Akt 4. Gene enrichment for insulin pathway genes 5. Literature search for insulin-cancer link
Domain Rotation
Rotate through these domains to maximize coverage:
- Oncology, Pharmacology, Genomics, Systems Biology
- Immunology, Rare Disease, Metabolomics, Epigenomics
- Structural Biology, Drug Discovery, Clinical Trials
- Infectious Disease, Proteomics, Bioinformatics
Key Guidelines
1. Vary query complexity: mix simple lookups with multi-hop chains 2. Use natural language: "find drugs for EGFR" not just formal parameter names 3. Include edge cases: misspellings, aliases, ambiguous names 4. Test across databases: same entity in PubChem vs ChEMBL vs DrugBank 5. Chain tool calls: use output of one tool as input to another