
Tooluniverse Clinical Trial Matching
- 351 installs
- 1.6k repo stars
- Updated August 4, 2026
- mims-harvard/tooluniverse
tooluniverse-clinical-trial-matching is a ToolUniverse agent skill that matches indications, inclusion-exclusion criteria, and study profiles against registered clinical trials for developers building feasibility, recrui
About
tooluniverse-clinical-trial-matching is a ToolUniverse skill that helps coding agents match patient indications, inclusion-exclusion criteria, and study profiles against registered clinical trials. The skill supports feasibility assessment, recruitment targeting, and portfolio analysis workflows in Claude Code by calling ToolUniverse clinical-trial tools instead of manual registry searches. Developers in health-tech, biostatistics, and research engineering reach for tooluniverse-clinical-trial-matching when agents must programmatically evaluate trial fit against structured eligibility rules.
- Trial eligibility matching
- ClinicalTrials.gov style search
- Inclusion-exclusion parsing
- Recruitment feasibility analysis
- Agent-driven protocol alignment
Tooluniverse Clinical Trial Matching by the numbers
- 351 all-time installs (skills.sh)
- +5 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #2,122 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 tooluniverse-clinical-trial-matchingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 351 |
|---|---|
| repo stars | ★ 1.6k |
| Last updated | August 4, 2026 |
| Repository | mims-harvard/tooluniverse ↗ |
How do agents match patients to clinical trials?
Help agents match indications, inclusion-exclusion criteria, and study profiles against registered clinical trials for feasibility, recruitment, and portfolio analysis.
Who is it for?
Health-tech and research engineers building agent workflows that evaluate clinical-trial eligibility against structured study profiles.
Skip if: General-purpose web search or non-clinical recommendation systems without trial registry data requirements.
When should I use this skill?
An agent must evaluate trial feasibility, recruitment fit, or portfolio overlap using inclusion-exclusion criteria.
What you get
Ranked trial match lists, eligibility assessments, and feasibility reports against registered study criteria.
- trial match rankings
- eligibility feasibility reports
Files
Clinical Trial Matching for Precision Medicine
Transform patient molecular profiles and clinical characteristics into prioritized clinical trial recommendations. Searches ClinicalTrials.gov and cross-references with molecular databases (CIViC, OpenTargets, ChEMBL, FDA) to produce evidence-graded, scored trial matches.
KEY PRINCIPLES: 1. Report-first approach - Create report file FIRST, then populate progressively 2. Patient-centric - Every recommendation considers the individual patient's profile 3. Molecular-first matching - Prioritize trials targeting patient's specific biomarkers
Molecular Matching Priority
Match patients to trials by molecular profile FIRST (specific mutations), then by disease stage, then by prior treatments. A patient with EGFR L858R should match to EGFR-targeted trials regardless of other factors. 4. Evidence-graded - Every recommendation has an evidence tier (T1-T4) 5. Quantitative scoring - Trial Match Score (0-100) for every trial 6. Eligibility-aware - Parse and evaluate inclusion/exclusion criteria 7. Actionable output - Clear next steps, contact info, enrollment status 8. Source-referenced - Every statement cites the tool/database source 9. Completeness checklist - Mandatory section showing analysis coverage 10. English-first queries - Always use English terms in tool calls. Respond in user's language
LOOK UP, DON'T GUESS
When uncertain about any scientific fact, SEARCH databases first rather than reasoning from memory. A database-verified answer is always more reliable than a guess.
---
COMPUTE, DON'T DESCRIBE
When analysis requires computation (statistics, data processing, scoring, enrichment), write and run Python code via Bash. Don't describe what you would do — execute it and report actual results. Use ToolUniverse tools to retrieve data, then Python (pandas, scipy, statsmodels, matplotlib) to analyze it.
When to Use
Apply when user asks:
- "What clinical trials are available for my NSCLC with EGFR L858R?"
- "Patient has BRAF V600E melanoma, failed ipilimumab - what trials?"
- "Find basket trials for NTRK fusion"
- "Breast cancer with HER2 amplification, post-CDK4/6 inhibitor trials"
- "KRAS G12C colorectal cancer clinical trials"
- "Immunotherapy trials for TMB-high solid tumors"
- "Clinical trials near Boston for lung cancer"
- "What are my options after failing osimertinib for EGFR+ NSCLC?"
NOT for (use other skills instead):
- Single variant interpretation without trial focus -> Use
tooluniverse-cancer-variant-interpretation - Drug safety profiling -> Use
tooluniverse-adverse-event-detection - Target validation -> Use
tooluniverse-drug-target-validation - General disease research -> Use
tooluniverse-disease-research
---
Input Parsing
Required Input
- Disease/cancer type: Free-text disease name (e.g., "non-small cell lung cancer", "melanoma")
Strongly Recommended
- Molecular alterations: One or more biomarkers (e.g., "EGFR L858R", "KRAS G12C", "PD-L1 50%", "TMB-high")
- Stage/grade: Disease stage (e.g., "Stage IV", "metastatic", "locally advanced")
- Prior treatments: Previous therapies and outcomes (e.g., "failed platinum chemotherapy", "progressed on osimertinib")
Optional
- Performance status: ECOG or Karnofsky score
- Geographic location: City/state for proximity filtering
- Trial phase preference: I, II, III, IV, or "any"
- Intervention type: drug, biological, device, etc.
- Recruiting status preference: recruiting, not yet recruiting, active
For biomarker parsing rules and gene symbol normalization, see MATCHING_ALGORITHMS.md.
---
Workflow Overview
Input: Patient profile (disease + biomarkers + stage + prior treatments)
Phase 1: Patient Profile Standardization
- Resolve disease to EFO/ontology IDs (OpenTargets, OLS)
- Parse molecular alterations to gene + variant
- Resolve gene symbols to Ensembl/Entrez IDs (MyGene)
- Classify biomarker actionability (FDA-approved vs investigational)
Phase 2: Broad Trial Discovery
- Disease-based trial search (ClinicalTrials.gov)
- Biomarker-specific trial search
- Intervention-based search (for known drugs targeting patient's biomarkers)
- Deduplicate and collect NCT IDs
Phase 3: Trial Characterization (batch, groups of 10)
- Eligibility criteria, conditions/interventions, locations, status, descriptions
Phase 4: Molecular Eligibility Matching
- Parse eligibility text for biomarker requirements
- Match patient's molecular profile to trial requirements
- Score molecular eligibility (0-40 points)
Phase 5: Drug-Biomarker Alignment
- Identify trial intervention drugs and mechanisms (OpenTargets, ChEMBL)
- FDA approval status for biomarker-drug combinations
- Classify drugs (targeted therapy, immunotherapy, chemotherapy)
Phase 6: Evidence Assessment
- FDA-approved biomarker-drug combinations
- Clinical trial results (PubMed), CIViC evidence, PharmGKB
- Evidence tier classification (T1-T4)
Phase 7: Geographic & Feasibility Analysis
- Trial site locations, enrollment status, proximity scoring
Phase 8: Alternative Options
- Basket trials, expanded access, related studies
Phase 9: Scoring & Ranking (0-100 composite score)
- Tier classification: Optimal (80-100) / Good (60-79) / Possible (40-59) / Exploratory (0-39)
Phase 10: Report Synthesis
- Executive summary, ranked trial list, evidence grading, completeness checklist---
Critical Tool Parameters
Clinical Trial Search Tools
| Tool | Key Parameters | Notes |
|---|---|---|
search_clinical_trials | query_term (REQ), condition, intervention, pageSize | Main search (ClinicalTrials.gov, U.S./global) |
search_clinical_trials | action="search_studies" (REQ), condition, intervention, limit | Alternative search |
get_clinical_trial_descriptions | action="get_study_details" (REQ), nct_id (REQ) | Full trial details |
CTIS_search_trials | query (REQ), limit, page | EU/EEA trials (EU CTIS register, since 2022) — complements ClinicalTrials.gov |
CTIS_get_trial | ct_number (REQ, e.g. 2022-503001-38-01) | Full EU trial detail (Part I/II, member states, results) |
ISRCTN_search_trials | query (REQ), limit | ISRCTN registry (UK-based, WHO-primary, international) — a third source |
ISRCTN_get_trial | isrctn_id (REQ, e.g. ISRCTN12336055) | Full ISRCTN trial detail + cross-ref ids (DOI/EudraCT/NCT) |
Geographic coverage: ClinicalTrials.gov is U.S.-centric but global; many EU/EEA-only trials appear only in the EU CTIS register, and UK/international trials in ISRCTN. For a comprehensive search — or any patient who could enroll outside the U.S. — query search_clinical_trials, CTIS_search_trials, and ISRCTN_search_trials, then merge (the three registers list largely disjoint trials; ISRCTN records carry DOI/EudraCT/NCT cross-refs you can use to dedupe against the others). Each register has its own id namespace and detail tool: NCT→get_clinical_trial_*, CT number→CTIS_get_trial, ISRCTN id→ISRCTN_get_trial.
Batch Trial Detail Tools (all take nct_ids array)
| Tool | Second Required Param | Returns |
|---|---|---|
get_clinical_trial_eligibility_criteria | eligibility_criteria="all" | Eligibility text |
get_clinical_trial_locations | location="all" | Site locations |
get_clinical_trial_conditions_and_interventions | condition_and_intervention="all" | Arms/interventions |
get_clinical_trial_status_and_dates | status_and_date="all" | Status/dates |
get_clinical_trial_descriptions | description_type="brief" or "full" | Titles/summaries |
get_clinical_trial_outcome_measures | outcome_measures="all" | Outcomes |
Gene/Disease Resolution
| Tool | Key Parameters |
|---|---|
MyGene_query_genes | query, species |
OpenTargets_get_disease_id_description_by_name | diseaseName |
OpenTargets_get_target_id_description_by_name | targetName |
ols_search_efo_terms | query, limit |
Drug Information
| Tool | Key Parameters | Notes |
|---|---|---|
OpenTargets_get_drug_id_description_by_name | drugName | Resolve drug to ChEMBL ID |
OpenTargets_get_drug_mechanisms_of_action_by_chemblId | chemblId | Drug MoA and targets |
OpenTargets_get_associated_drugs_by_target_ensemblID | ensemblId, size | Drugs for a target |
drugbank_get_targets_by_drug_name_or_drugbank_id | query, case_sensitive, exact_match, limit (ALL REQ) | Drug targets |
fda_pharmacogenomic_biomarkers | (none) | FDA biomarker-drug list |
FDA_get_indications_by_drug_name | drug_name, limit | FDA indications |
Evidence Tools
| Tool | Key Parameters |
|---|---|
PubMed_search_articles | query, max_results |
civic_get_variants_by_gene | gene_id (CIViC int ID), limit |
PharmGKB_search_genes | query |
Known CIViC Gene IDs
EGFR=19, BRAF=5, ALK=1, ABL1=4, KRAS=30, TP53=45, ERBB2=20, NTRK1=197, NTRK2=560, NTRK3=561, PIK3CA=37, MET=52, ROS1=118, RET=122, BRCA1=2370, BRCA2=2371
Critical Parameter Notes
1. DrugBank tools: ALL 4 parameters (query, case_sensitive, exact_match, limit) are REQUIRED 2. `search_clinical_trials`: query_term is REQUIRED even for disease-only searches 3. `search_clinical_trials`: action must be exactly "search_studies" 4. CIViC `civic_search_variants`: Does NOT filter by query - returns alphabetically 5. CIViC `civic_get_variants_by_gene`: Takes CIViC gene ID (integer), NOT gene symbol 6. Batch clinical trial tools: Accept arrays of NCT IDs, process in batches of 10
---
Scoring Summary
Trial Match Score (0-100):
- Molecular Match: 0-40 pts (exact variant=40, gene-level=30, pathway=20, none=10, excluded=0)
- Clinical Eligibility: 0-25 pts (all met=25, most=18, some=10, ineligible=0)
- Evidence Strength: 0-20 pts (FDA-approved=20, Phase III=15, Phase II=10, Phase I=5)
- Trial Phase: 0-10 pts (III=10, II=8, I/II=6, I=4)
- Geographic: 0-5 pts (local=5, same country=3, international=1)
Recommendation Tiers: Optimal (80-100), Good (60-79), Possible (40-59), Exploratory (0-39)
Evidence Tiers: T1 (FDA/guideline), T2 (Phase III), T3 (Phase I/II), T4 (computational)
For detailed scoring logic, see SCORING_CRITERIA.md.
---
Parallelization Strategy
Group 1 (Phase 1 - simultaneous):
MyGene_query_genesper gene,OpenTargetsdisease search,ols_search_efo_terms,fda_pharmacogenomic_biomarkers
Group 2 (Phase 2 - simultaneous):
search_clinical_trialsby disease, biomarker, and intervention;search_clinical_trialsalternative
Group 3 (Phase 3 - simultaneous):
- All batch detail tools (eligibility, interventions, locations, status, descriptions)
Group 4 (Phases 5-6 - per drug):
- Drug resolution, MoA, FDA indications, PubMed evidence
---
Error Handling
1. Wrap every tool call in try/except 2. Check for empty results and string error responses 3. Use fallback tools when primary fails (e.g., OLS if OpenTargets fails) 4. Document failures in completeness checklist 5. Never let one failure block the entire analysis
---
Reference Files
| File | Contents |
|---|---|
| TOOLS_REFERENCE.md | Full tool inventory with parameters and response structures |
| MATCHING_ALGORITHMS.md | Patient profile standardization, biomarker parsing, molecular eligibility matching, drug-biomarker alignment code |
| SCORING_CRITERIA.md | Detailed scoring tables, molecular match logic, drug-biomarker alignment scoring |
| REPORT_TEMPLATE.md | Full markdown report template with all sections |
| TRIAL_SEARCH_PATTERNS.md | Search functions, batch retrieval, parallelization, common use patterns, edge cases |
| EXAMPLES.md | Worked examples for different matching scenarios |
| QUICK_START.md | Quick-start guide for common workflows |
Clinical Trial Matching - Examples
Example 1: NSCLC with EGFR L858R
User Query: "Find clinical trials for Stage IV non-small cell lung cancer with EGFR L858R mutation, failed platinum chemotherapy, ECOG 0-1"
Skill Execution:
Phase 1: Patient Profile Standardization
- Disease: "non-small cell lung cancer" -> EFO_0003060 (non-small cell lung carcinoma)
- Gene: EGFR -> Ensembl: ENSG00000146648, Entrez: 1956
- Biomarker: EGFR L858R -> type: mutation, gene: EGFR, alteration: L858R
- FDA Biomarker: EGFR is FDA-recognized biomarker (afatinib, osimertinib, erlotinib, gefitinib)
Phase 2: Trial Discovery
- Disease search: ~200+ trials for NSCLC
- Biomarker search: "EGFR L858R" -> 10-30 targeted trials
- Intervention search: "osimertinib" for NSCLC -> 20+ trials
- Combined and deduplicated: ~50-80 unique candidate trials
Phase 3-6: Analysis
- Parse eligibility criteria for EGFR L858R requirements
- Check drug mechanisms for EGFR inhibitors
- Cross-reference FDA approvals (osimertinib for EGFR L858R NSCLC)
- Score molecular match: 40/40 for trials requiring EGFR L858R
Expected Top Matches
Trials testing:
- EGFR TKIs (osimertinib, amivantamab-vmjw)
- EGFR TKI combinations (TKI + chemotherapy)
- Next-generation EGFR inhibitors
- EGFR-targeted ADCs or bispecific antibodies
---
Example 2: Melanoma with BRAF V600E
User Query: "Patient has BRAF V600E melanoma, failed ipilimumab, looking for trials"
Skill Execution:
Phase 1: Standardization
- Disease: melanoma -> EFO_0000756
- Gene: BRAF -> ENSG00000157764, CIViC ID: 5
- Biomarker: BRAF V600E -> type: mutation
- FDA: BRAF is FDA-recognized (dabrafenib, vemurafenib, encorafenib)
Phase 2: Trial Discovery
- "melanoma BRAF V600E" -> BRAF-targeted trials
- "melanoma immunotherapy" -> post-ipilimumab options
- Combined targeted + immunotherapy options
Expected Top Matches
- BRAF/MEK inhibitor combinations (dabrafenib + trametinib)
- Novel BRAF inhibitors
- Combination of BRAF inhibition + immunotherapy
- Anti-PD-1 after ipilimumab
- Novel immunotherapy combinations
---
Example 3: Colorectal Cancer with KRAS G12C
User Query: "KRAS G12C colorectal cancer, Stage IV, failed FOLFOX + bevacizumab, microsatellite stable"
Skill Execution:
Phase 1: Standardization
- Disease: colorectal cancer -> EFO_0005842
- Gene: KRAS -> ENSG00000133703, CIViC ID: 30
- Biomarkers: KRAS G12C (mutation), MSS (status)
- FDA: KRAS G12C targeted (sotorasib approved for NSCLC, under investigation for CRC)
Phase 2: Trial Discovery
- "colorectal cancer KRAS G12C" -> specific trials
- "KRAS G12C" without disease filter -> cross-tumor trials
- "sotorasib" or "adagrasib" for CRC
Expected Top Matches
- KRAS G12C inhibitor trials (sotorasib, adagrasib) for CRC
- KRAS G12C + anti-EGFR combinations
- Novel KRAS inhibitors
- Basket trials for KRAS G12C any solid tumor
---
Example 4: Breast Cancer with HER2+
User Query: "HER2-positive breast cancer, failed trastuzumab + pertuzumab, brain metastases"
Skill Execution:
Phase 1: Standardization
- Disease: breast cancer -> EFO_0000305
- Gene: HER2/ERBB2 -> ENSG00000141736, CIViC ID: 20
- Biomarker: HER2 amplification
- FDA: HER2 recognized (trastuzumab, pertuzumab, T-DXd, tucatinib)
Phase 2: Trial Discovery
- "breast cancer HER2" + "brain metastases"
- "trastuzumab deruxtecan" (T-DXd) trials
- "tucatinib" trials (CNS-penetrant)
- Post-pertuzumab HER2+ trials
Special Considerations
- Brain metastases eligibility (many trials exclude CNS disease)
- CNS-active agents prioritized (tucatinib, T-DXd)
---
Example 5: NTRK Fusion (Basket Trial)
User Query: "Find trials for NTRK1 fusion, any solid tumor type"
Skill Execution:
Phase 1: Standardization
- Gene: NTRK1 -> ENSG00000198400, CIViC ID: 197
- Biomarker: NTRK1 fusion
- FDA: NTRK recognized (larotrectinib, entrectinib - tissue-agnostic)
Phase 2: Trial Discovery
- "NTRK fusion" + "basket" or "tumor agnostic"
- "larotrectinib" and "entrectinib" trials
- "NTRK" across all cancer types
Expected Top Matches
- Larotrectinib/entrectinib expansion studies
- Next-generation TRK inhibitors (selitrectinib, repotrectinib)
- Basket trials requiring NTRK fusion
- Combination approaches with TRK inhibition
---
Example 6: Geographic Search
User Query: "Find Phase II or III lung cancer trials near Boston"
Skill Execution:
Phase 1: Standardization
- Disease: lung cancer -> EFO_0001071
- Geographic preference: Boston, Massachusetts
Phase 2: Trial Discovery
- "lung cancer" Phase II/III
- Filter by Massachusetts locations
Phase 3: Location Analysis
- Sites in Massachusetts: Dana-Farber, Massachusetts General, Beth Israel
- Nearby states: Connecticut, Rhode Island, New Hampshire
Scoring
- Geographic feasibility weighted more heavily
- Massachusetts sites: 5/5 points
- Nearby New England sites: 3/5 points
---
Output Format
Each example produces a comprehensive markdown report saved to a file with the following key sections:
1. Executive Summary with top 3 trials and scores 2. Patient Profile with standardized identifiers 3. Ranked Trial List with scoring breakdown 4. Trial Categories (targeted, immunotherapy, combination, basket) 5. Alternative Options (expanded access, off-label) 6. Evidence Grading and completeness checklist
Matching Algorithms
Detailed algorithms for patient profile standardization, biomarker parsing, molecular eligibility matching, and drug-biomarker alignment.
Phase 1: Patient Profile Standardization
1.1 Disease Resolution
def resolve_disease(tu, disease_name):
"""Resolve disease name to EFO ID and standard terminology."""
# OpenTargets disease search
result = tu.tools.OpenTargets_get_disease_id_description_by_name(diseaseName=disease_name)
hits = result.get('data', {}).get('search', {}).get('hits', [])
if hits:
disease_info = hits[0]
return {
'efo_id': disease_info.get('id'),
'name': disease_info.get('name'),
'description': disease_info.get('description'),
'original_input': disease_name
}
# Fallback: OLS EFO search
ols_result = tu.tools.ols_search_efo_terms(query=disease_name, limit=5)
ols_terms = ols_result.get('data', {}).get('terms', [])
if ols_terms:
term = ols_terms[0]
return {
'efo_id': term.get('short_form'),
'name': term.get('label'),
'description': term.get('description', [''])[0] if term.get('description') else '',
'original_input': disease_name
}
return {'efo_id': None, 'name': disease_name, 'description': '', 'original_input': disease_name}1.2 Gene/Biomarker Resolution
def resolve_gene(tu, gene_symbol):
"""Resolve gene symbol to cross-database IDs."""
alias_map = {
'HER2': 'ERBB2', 'HER-2': 'ERBB2',
'PD-L1': 'CD274', 'PDL1': 'CD274',
'PD-1': 'PDCD1', 'PD1': 'PDCD1',
'VEGF': 'VEGFA',
}
normalized = alias_map.get(gene_symbol.upper(), gene_symbol)
result = tu.tools.MyGene_query_genes(query=normalized, species='human')
hits = result.get('hits', [])
gene_hit = None
for hit in hits:
if hit.get('symbol', '').upper() == normalized.upper():
gene_hit = hit
break
if not gene_hit and hits:
gene_hit = hits[0]
if gene_hit:
ensembl = gene_hit.get('ensembl', {})
ensembl_id = ensembl.get('gene') if isinstance(ensembl, dict) else (ensembl[0].get('gene') if isinstance(ensembl, list) and ensembl else None)
return {
'symbol': gene_hit.get('symbol'),
'entrez_id': gene_hit.get('entrezgene'),
'ensembl_id': ensembl_id,
'name': gene_hit.get('name'),
'original_input': gene_symbol
}
return {'symbol': gene_symbol, 'entrez_id': None, 'ensembl_id': None, 'name': None, 'original_input': gene_symbol}1.3 Biomarker Actionability Classification
def classify_biomarker_actionability(tu, gene_symbol, alteration):
"""Classify biomarker as FDA-approved, guideline, or investigational."""
fda_result = tu.tools.fda_pharmacogenomic_biomarkers()
fda_biomarkers = fda_result.get('results', [])
fda_match = [b for b in fda_biomarkers if gene_symbol.upper() in str(b.get('Biomarker', '')).upper()]
if fda_match:
return {
'level': 'FDA-approved',
'drugs': [b.get('Drug') for b in fda_match],
'labeling_sections': [b.get('LabelingSection') for b in fda_match]
}
return {'level': 'investigational', 'drugs': [], 'labeling_sections': []}1.4 Biomarker Parsing
def parse_biomarker(biomarker_text):
"""Parse free-text biomarker into structured components."""
import re
# Pattern: "GENE VARIANT" (e.g., "EGFR L858R")
mutation_match = re.match(r'(\w+)\s+([A-Z]\d+[A-Z])', biomarker_text, re.IGNORECASE)
if mutation_match:
return {'gene': mutation_match.group(1), 'alteration': mutation_match.group(2), 'type': 'mutation'}
# Pattern: "GENE exon N deletion/insertion"
exon_match = re.match(r'(\w+)\s+exon\s+(\d+)\s+(\w+)', biomarker_text, re.IGNORECASE)
if exon_match:
return {'gene': exon_match.group(1), 'alteration': f'exon {exon_match.group(2)} {exon_match.group(3)}', 'type': 'exon_alteration'}
# Pattern: "GENE1-GENE2 fusion" or "GENE1/GENE2"
fusion_match = re.match(r'(\w+)[-/](\w+)\s*(fusion)?', biomarker_text, re.IGNORECASE)
if fusion_match:
return {'gene': fusion_match.group(2), 'alteration': f'{fusion_match.group(1)}-{fusion_match.group(2)}', 'type': 'fusion', 'partner': fusion_match.group(1)}
# Pattern: "GENE amplification"
amp_match = re.match(r'(\w+)\s+amplification', biomarker_text, re.IGNORECASE)
if amp_match:
return {'gene': amp_match.group(1), 'alteration': 'amplification', 'type': 'amplification'}
# Pattern: "PD-L1 XX%"
expression_match = re.match(r'([\w-]+)\s+(\d+%|high|low|positive|negative)', biomarker_text, re.IGNORECASE)
if expression_match:
return {'gene': expression_match.group(1), 'alteration': expression_match.group(2), 'type': 'expression'}
# Pattern: "MSI-high", "TMB-high"
status_match = re.match(r'(MSI|TMB|dMMR|MMR)[-\s]*(high|low|stable|deficient|proficient)', biomarker_text, re.IGNORECASE)
if status_match:
return {'gene': status_match.group(1), 'alteration': status_match.group(2), 'type': 'status'}
# Fallback
return {'gene': biomarker_text.split()[0], 'alteration': ' '.join(biomarker_text.split()[1:]), 'type': 'unknown'}Gene Symbol Normalization
| Common Alias | Official Symbol | Notes |
|---|---|---|
| HER2 | ERBB2 | Search both in trials |
| PD-L1 | CD274 | Often searched as "PD-L1" in trials |
| ALK | ALK | EML4-ALK is a fusion |
| VEGF | VEGFA | Often searched as "VEGF" |
| PD-1 | PDCD1 | Search as "PD-1" in trials |
| BRCA | BRCA1/BRCA2 | Specify which BRCA gene |
Biomarker Parsing Rules
| Input Format | Parsed As | Example |
|---|---|---|
| Gene + amino acid change | Specific mutation | EGFR L858R |
| Gene + exon notation | Exon-level alteration | EGFR exon 19 deletion |
| Gene + fusion partner | Fusion | EML4-ALK fusion |
| Gene + amplification | Copy number gain | HER2 amplification |
| Gene + expression level | Expression biomarker | PD-L1 50% |
| Gene + status | Status biomarker | MSI-high, TMB-high |
| Gene + resistance | Resistance mutation | EGFR T790M |
Phase 4: Molecular Eligibility Matching
Extract Biomarker Requirements from Eligibility Text
def extract_biomarker_requirements(eligibility_text):
"""Extract biomarker requirements from eligibility criteria text."""
import re
requirements = {
'required_biomarkers': [],
'excluded_biomarkers': [],
'biomarker_agnostic': False
}
if not eligibility_text:
return requirements
text_upper = eligibility_text.upper()
inclusion_section = eligibility_text.split('Exclusion Criteria')[0] if 'Exclusion Criteria' in eligibility_text else eligibility_text
exclusion_section = eligibility_text.split('Exclusion Criteria')[1] if 'Exclusion Criteria' in eligibility_text else ''
gene_patterns = [
r'(?:EGFR|KRAS|BRAF|ALK|ROS1|RET|MET|NTRK|HER2|ERBB2|PIK3CA|BRCA|PD-?L1|MSI|TMB|dMMR)',
]
for pattern in gene_patterns:
for match in re.finditer(pattern, inclusion_section, re.IGNORECASE):
gene = match.group(0).upper()
context = inclusion_section[max(0, match.start()-100):match.end()+100]
requirements['required_biomarkers'].append({
'gene': gene,
'context': context.strip()
})
for match in re.finditer(pattern, exclusion_section, re.IGNORECASE):
gene = match.group(0).upper()
context = exclusion_section[max(0, match.start()-100):match.end()+100]
requirements['excluded_biomarkers'].append({
'gene': gene,
'context': context.strip()
})
basket_terms = ['tumor-agnostic', 'histology-independent', 'basket', 'any solid tumor', 'all comers', 'biomarker-selected']
if any(term in text_upper.lower() for term in basket_terms):
requirements['biomarker_agnostic'] = True
return requirementsPhase 5: Drug-Biomarker Alignment
Get Drug Mechanism Info
def get_drug_mechanism_info(tu, drug_name):
"""Get drug mechanism, targets, and approval status."""
result = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName=drug_name)
hits = result.get('data', {}).get('search', {}).get('hits', [])
if not hits:
return {'drug_name': drug_name, 'chembl_id': None, 'mechanisms': [], 'is_approved': False}
drug_info = hits[0]
chembl_id = drug_info.get('id')
moa_result = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId=chembl_id)
moa_rows = moa_result.get('data', {}).get('drug', {}).get('mechanismsOfAction', {}).get('rows', [])
mechanisms = []
for row in moa_rows:
targets = row.get('targets', [])
mechanisms.append({
'mechanism': row.get('mechanismOfAction'),
'action_type': row.get('actionType'),
'target_name': row.get('targetName'),
'target_genes': [t.get('approvedSymbol') for t in targets]
})
return {
'drug_name': drug_name,
'chembl_id': chembl_id,
'description': drug_info.get('description'),
'mechanisms': mechanisms,
'is_approved': 'approved' in drug_info.get('description', '').lower()
}FDA Approval Evidence
def check_fda_approval(tu, drug_name, disease_name):
"""Check FDA approval status and labeled indications."""
result = tu.tools.FDA_get_indications_by_drug_name(drug_name=drug_name, limit=3)
indications = result.get('results', [])
for ind in indications:
ind_text = str(ind.get('indications_and_usage', ''))
if any(term.lower() in ind_text.lower() for term in disease_name.split()):
return {
'approved': True,
'indication_text': ind_text[:500],
'brand_name': ind.get('openfda.brand_name', []),
'evidence_tier': 'T1'
}
return {'approved': False, 'indication_text': '', 'brand_name': [], 'evidence_tier': 'T3'}Literature Evidence
def get_literature_evidence(tu, gene, alteration, drug_name, disease_name):
"""Search PubMed for evidence of drug efficacy for this biomarker."""
query = f'{gene} {alteration} {drug_name} {disease_name} clinical trial'
result = tu.tools.PubMed_search_articles(query=query, max_results=5)
articles = result if isinstance(result, list) else result.get('articles', [])
return articlesCIViC Evidence
def get_civic_evidence(tu, gene_symbol, civic_gene_id):
"""Get CIViC clinical evidence for gene variants."""
if not civic_gene_id:
return []
result = tu.tools.civic_get_variants_by_gene(gene_id=civic_gene_id, limit=100)
variants = result.get('data', {}).get('gene', {}).get('variants', {}).get('nodes', [])
return variantsClinical Trial Matching - Quick Start Guide
Overview
This skill matches patients to optimal clinical trials based on their molecular profile, disease characteristics, and eligibility criteria. It searches ClinicalTrials.gov and cross-references molecular databases to produce evidence-graded, scored trial recommendations.
Basic Usage
Simply describe your patient profile and ask for trial matches:
Find clinical trials for a patient with non-small cell lung cancer,
EGFR L858R mutation, Stage IV, who failed first-line osimertinib.Use Case Examples
1. Targeted Therapy Matching
Patient profile:
- Disease: Non-small cell lung cancer (adenocarcinoma)
- Biomarker: EGFR L858R mutation
- Stage: Stage IV, metastatic
- Prior treatment: Failed platinum-based chemotherapy
- ECOG: 0-1
Find the best clinical trial matches.2. Immunotherapy Selection
Match clinical trials for:
- Melanoma, advanced/metastatic
- TMB-high (>10 mut/Mb)
- PD-L1 positive (TPS >= 50%)
- Failed ipilimumab/nivolumab combination
- ECOG 03. Basket Trial Identification
Find basket/tumor-agnostic trials for a patient with:
- Solid tumor (colorectal cancer)
- NTRK1 fusion detected by NGS
- No prior TRK inhibitor therapy4. Post-Progression Options
Clinical trial options for:
- HR+/HER2- breast cancer
- Failed CDK4/6 inhibitor (palbociclib) + letrozole
- ESR1 Y537S mutation detected
- Bone and liver metastases5. Novel Biomarker Trials
Find trials for:
- Colorectal cancer, Stage IV
- KRAS G12C mutation
- Failed FOLFOX + bevacizumab
- MSS (microsatellite stable)6. Geographic Search
Find lung cancer clinical trials:
- Non-small cell lung cancer, any molecular subtype
- Prefer trials in Boston, Massachusetts area
- Currently recruiting
- Phase II or III onlyWhat You Get
For each patient, the skill produces:
1. Executive Summary - Top 3 trial recommendations with Trial Match Scores 2. Patient Profile - Standardized disease/biomarker information with EFO and gene IDs 3. Biomarker Actionability - FDA-approved vs investigational status 4. Ranked Trial List - Up to 10+ trials with detailed scoring breakdown:
- Molecular Match (0-40 points)
- Clinical Eligibility (0-25 points)
- Evidence Strength (0-20 points)
- Trial Phase (0-10 points)
- Geographic Feasibility (0-5 points)
5. Trial Details - NCT ID, phase, status, interventions, eligibility, locations 6. Drug-Biomarker Alignment - Whether trial drugs target the patient's biomarkers 7. Evidence Grading - T1 (FDA-approved) through T4 (computational) 8. Alternative Options - Basket trials, expanded access, off-label options 9. Additional Testing - Biomarker tests that would unlock more trials 10. Completeness Checklist - What analyses were performed
Trial Match Score Guide
| Score | Tier | Meaning |
|---|---|---|
| 80-100 | Optimal | Strongly recommend - patient's biomarker directly targeted |
| 60-79 | Good | Recommend - good disease and biomarker alignment |
| 40-59 | Possible | Consider - matches on some criteria, needs discussion |
| 0-39 | Exploratory | Backup - general disease trials or weak match |
Tips for Best Results
1. Be specific about biomarkers - Include variant-level detail (e.g., "EGFR L858R" not just "EGFR mutation") 2. Include prior treatments - Post-progression trials need to know what failed 3. Specify stage - Many trials require specific disease stages 4. Add geographic preference - If location matters, include city/state 5. Mention performance status - ECOG score helps filter eligibility 6. List multiple biomarkers - Complex profiles help find the best-matched trials
Data Sources
| Source | What It Provides |
|---|---|
| ClinicalTrials.gov | Trial search, eligibility, locations, status |
| OpenTargets | Drug-target associations, disease ontology |
| CIViC | Clinical variant interpretations |
| ChEMBL | Drug mechanisms and targets |
| FDA | Approved indications, biomarker labels |
| DrugBank | Drug targets and pharmacology |
| PharmGKB | Pharmacogenomics data |
| PubMed | Literature evidence |
| OLS/EFO | Disease ontology standardization |
| MyGene | Gene identifier resolution |
Limitations
- Trial availability changes frequently; always verify current status at ClinicalTrials.gov
- Eligibility assessment is approximate; final determination is by trial investigators
- Geographic distance calculations are approximate (state/city level, not exact)
- Report is for informational/research purposes only; discuss with healthcare team
- Some trials may have enrollment caps not reflected in public data
Report Template
Use the following markdown structure for the final clinical trial matching report.
File Naming Convention
clinical_trial_matching_[DISEASE]_[BIOMARKER]_[DATE].mdExample: clinical_trial_matching_NSCLC_EGFR_L858R_2026-02-15.md
Template
# Clinical Trial Matching Report
**Patient**: [Disease type] with [biomarker(s)]
**Date**: [Current date]
**Trials Analyzed**: [N total] | **Top Matches**: [N with score >= 60]
---
## Executive Summary
**Top 3 Trial Recommendations**:
1. **[NCT ID]** - [Brief title] (Score: XX/100, Tier N)
- Phase: [Phase], Status: [Status]
- Why: [Key reason for match]
2. **[NCT ID]** - [Brief title] (Score: XX/100, Tier N)
...
3. **[NCT ID]** - [Brief title] (Score: XX/100, Tier N)
...
---
## Patient Profile Summary
| Parameter | Value | Standardized |
|-----------|-------|-------------|
| Disease | [input] | [EFO name] (EFO_XXXX) |
| Biomarker(s) | [input] | [gene: variant, type] |
| Stage | [input] | [standardized] |
| Prior Treatment | [input] | [standardized] |
| Performance Status | [input] | [ECOG score] |
| Location | [input] | [city, state] |
### Biomarker Actionability
| Biomarker | Actionability Level | FDA-Approved Drugs | Evidence |
|-----------|--------------------|--------------------|----------|
| [gene variant] | [FDA-approved/investigational] | [drugs] | [T1/T2/T3/T4] |
---
## Ranked Trial Matches
### Trial 1: [NCT ID] - [Title]
**Trial Match Score: XX/100** (Tier N: [Label])
| Component | Score | Details |
|-----------|-------|---------|
| Molecular Match | XX/40 | [explanation] |
| Clinical Eligibility | XX/25 | [explanation] |
| Evidence Strength | XX/20 | [explanation] |
| Trial Phase | XX/10 | [phase] |
| Geographic | XX/5 | [location info] |
**Trial Details**:
- **Phase**: [Phase]
- **Status**: [Recruiting/Active/etc.]
- **Sponsor**: [Sponsor]
- **Start Date**: [Date]
- **Estimated Completion**: [Date]
**Interventions**:
- [Drug name]: [Mechanism] | [Dosing info if available]
- [Comparator]: [Description]
**Molecular Eligibility Match**:
- Required biomarkers: [list]
- Patient match: [Exact/Gene-level/Pathway/None]
- Notes: [details]
**Clinical Eligibility Assessment**:
- Disease type: [Match/Mismatch]
- Stage: [Match/Mismatch/Unclear]
- Prior treatment: [Match/Mismatch/Unclear]
- Performance status: [Match/Mismatch/Unclear]
**Evidence for Efficacy**:
- FDA approval: [Yes/No for this indication]
- Clinical results: [Phase III/II/I data if available]
- Mechanism alignment: [Drug targets patient's biomarker: Yes/No]
- Literature: [Key references]
**Trial Sites** (first 5):
- [City, State, Country]
- ...
**Next Steps**: [Contact info, enrollment instructions]
[Repeat for each matched trial]
---
## Trials by Category
### Targeted Therapy Trials
[List trials with targeted agents matching patient's biomarkers]
### Immunotherapy Trials
[List immunotherapy trials, noting PD-L1/TMB/MSI requirements]
### Combination Therapy Trials
[List trials with drug combinations]
### Basket/Platform Trials
[List biomarker-agnostic or multi-arm trials]
---
## Additional Testing Recommendations
If the patient has not been tested for certain biomarkers, these trials would become relevant:
| Biomarker | Test Needed | Trials Unlocked | Priority |
|-----------|-------------|----------------|----------|
| [e.g., TMB] | [NGS panel] | [NCT IDs] | [High/Medium/Low] |
---
## Alternative Options
### Expanded Access Programs
[List any expanded access or compassionate use programs]
### Off-Label Options
[FDA-approved drugs for other indications with same biomarker]
---
## Evidence Grading Summary
| Evidence Tier | Count | Description |
|--------------|-------|-------------|
| T1 (FDA/Guideline) | N | FDA-approved biomarker-drug, clinical guideline |
| T2 (Clinical) | N | Phase III data, robust clinical evidence |
| T3 (Emerging) | N | Phase I/II, preclinical evidence |
| T4 (Exploratory) | N | Computational, mechanism inference |
---
## Completeness Checklist
| Analysis Step | Status | Source |
|--------------|--------|--------|
| Disease standardization | [Done/Partial/Failed] | [OpenTargets/OLS] |
| Gene resolution | [Done/Partial/Failed] | [MyGene] |
| Biomarker actionability | [Done/Partial/Failed] | [FDA biomarkers] |
| Disease trial search | [Done/Partial/Failed] | [ClinicalTrials.gov] |
| Biomarker trial search | [Done/Partial/Failed] | [ClinicalTrials.gov] |
| Intervention trial search | [Done/Partial/Failed] | [ClinicalTrials.gov] |
| Eligibility parsing | [Done/Partial/Failed] | [ClinicalTrials.gov] |
| Drug mechanism analysis | [Done/Partial/Failed] | [OpenTargets/ChEMBL] |
| Evidence assessment | [Done/Partial/Failed] | [FDA/PubMed/CIViC] |
| Location analysis | [Done/Partial/Failed] | [ClinicalTrials.gov] |
| Basket trial search | [Done/Partial/Failed] | [ClinicalTrials.gov] |
| Expanded access search | [Done/Partial/Failed] | [ClinicalTrials.gov] |
| Scoring & ranking | [Done/Partial/Failed] | [Composite] |
---
## Disclaimer
This report is for informational and research purposes only. Clinical trial eligibility is ultimately determined by the trial investigators based on complete medical records. Patients should discuss all options with their healthcare team. Trial availability and status may change; verify current status at [ClinicalTrials.gov](https://clinicaltrials.gov).
## Sources
All data sourced from:
- ClinicalTrials.gov (trial search, eligibility, locations, status)
- OpenTargets Platform (drug-target associations, disease ontology)
- CIViC (clinical variant interpretations)
- ChEMBL (drug mechanisms, targets)
- FDA (approved indications, pharmacogenomic biomarkers, drug labels)
- DrugBank (drug targets, indications)
- PharmGKB (pharmacogenomics)
- PubMed/NCBI (literature evidence)
- OLS/EFO (disease ontology)
- MyGene (gene identifier resolution)Scoring Criteria & Recommendation Tiers
Trial Match Score Components (Total: 0-100)
Molecular Match (0-40 points)
| Criterion | Points | Description |
|---|---|---|
| Exact biomarker match | 40 | Trial requires patient's specific variant |
| Gene-level match | 30 | Trial requires gene mutation, patient has specific variant |
| Pathway match | 20 | Trial targets same pathway as patient's biomarker |
| No molecular criteria | 10 | General disease trial |
| Excluded biomarker | 0 | Patient's biomarker is in exclusion criteria |
Clinical Eligibility (0-25 points)
| Criterion | Points | Description |
|---|---|---|
| All criteria met | 25 | Disease, stage, prior treatment all match |
| Most criteria met | 18 | 1-2 criteria unclear |
| Some criteria met | 10 | Several criteria unclear |
| Clearly ineligible | 0 | Fails major criterion |
Evidence Strength (0-20 points)
| Criterion | Points | Description |
|---|---|---|
| FDA-approved combination | 20 | T1 evidence |
| Phase III positive | 15 | T2 evidence |
| Phase II promising | 10 | T3 evidence |
| Phase I or no results | 5 | T4 evidence |
Trial Phase (0-10 points)
| Phase | Points |
|---|---|
| Phase III | 10 |
| Phase II | 8 |
| Phase I/II | 6 |
| Phase I | 4 |
Geographic Feasibility (0-5 points)
| Criterion | Points |
|---|---|
| Patient's city/state | 5 |
| Same country | 3 |
| International only | 1 |
| Unknown | 0 |
Evidence Tier Classification
| Tier | Symbol | Criteria | Score Impact |
|---|---|---|---|
| T1 | [T1] | FDA-approved biomarker-drug, NCCN guideline | 20 points |
| T2 | [T2] | Phase III positive, clinical evidence | 15 points |
| T3 | [T3] | Phase I/II results, preclinical | 10 points |
| T4 | [T4] | Computational, mechanism inference | 5 points |
Recommendation Tiers
| Score | Tier | Label | Action |
|---|---|---|---|
| 80-100 | Tier 1 | Optimal Match | Strongly recommend - contact site immediately |
| 60-79 | Tier 2 | Good Match | Recommend - discuss with care team |
| 40-59 | Tier 3 | Possible Match | Consider - needs further eligibility review |
| 0-39 | Tier 4 | Exploratory | Backup option - consider if Tier 1-3 unavailable |
Molecular Match Scoring Logic
def score_molecular_match(patient_biomarkers, trial_requirements):
"""Score molecular match between patient and trial (0-40 points)."""
if not trial_requirements['required_biomarkers'] and not trial_requirements['excluded_biomarkers']:
return 10, 'No specific molecular criteria (general trial)'
patient_genes = {b['gene'].upper() for b in patient_biomarkers}
required_genes = {b['gene'].upper() for b in trial_requirements['required_biomarkers']}
excluded_genes = {b['gene'].upper() for b in trial_requirements['excluded_biomarkers']}
# Check exclusions first
excluded_match = patient_genes & excluded_genes
if excluded_match:
return 0, f'Patient biomarker(s) {excluded_match} are in exclusion criteria'
if not required_genes:
return 10, 'No specific biomarker requirements found'
# Check for exact gene match
matched_genes = patient_genes & required_genes
if matched_genes:
exact_variant_match = False
for req in trial_requirements['required_biomarkers']:
for pb in patient_biomarkers:
if pb['gene'].upper() == req['gene'].upper():
alt = pb.get('alteration', '').upper()
if alt and alt in req.get('context', '').upper():
exact_variant_match = True
break
if exact_variant_match:
return 40, f'Exact biomarker match: {matched_genes} with specific variant'
else:
return 30, f'Gene-level match: {matched_genes} (specific variant match unclear)'
return 5, 'No direct biomarker match found'Drug-Biomarker Alignment Scoring
def score_drug_biomarker_alignment(patient_gene_symbols, drug_mechanisms):
"""Check if trial drug targets patient's biomarkers."""
patient_genes_upper = {g.upper() for g in patient_gene_symbols}
for mech in drug_mechanisms:
target_genes = {g.upper() for g in mech.get('target_genes', [])}
if patient_genes_upper & target_genes:
return True, f"Drug targets {patient_genes_upper & target_genes} via {mech.get('mechanism')}"
return False, "No direct target overlap with patient biomarkers"#!/usr/bin/env python3
"""
Comprehensive Test Suite for Clinical Trial Matching Skill
Tests all phases with diverse patient profiles:
- NSCLC with EGFR L858R
- Melanoma with BRAF V600E
- Colorectal cancer with KRAS G12C
- Breast cancer with HER2+
- NTRK fusion basket trial
"""
import json
import sys
import time
import traceback
# ============================================================
# Test Infrastructure
# ============================================================
PASS = 0
FAIL = 0
TOTAL = 0
RESULTS = []
def run_test(name, func):
"""Run a single test and track results."""
global PASS, FAIL, TOTAL, RESULTS
TOTAL += 1
start = time.time()
try:
func()
elapsed = time.time() - start
PASS += 1
RESULTS.append({"name": name, "status": "PASS", "time": f"{elapsed:.1f}s", "error": None})
print(f" PASS [{elapsed:.1f}s] {name}")
except Exception as e:
elapsed = time.time() - start
FAIL += 1
err_msg = str(e)
RESULTS.append({"name": name, "status": "FAIL", "time": f"{elapsed:.1f}s", "error": err_msg})
print(f" FAIL [{elapsed:.1f}s] {name}")
print(f" Error: {err_msg}")
traceback.print_exc()
def load_tu():
"""Load ToolUniverse instance."""
from tooluniverse import ToolUniverse
tu = ToolUniverse()
tu.load_tools()
return tu
# ============================================================
# Phase 1: Patient Profile Standardization Tests
# ============================================================
def test_01_disease_resolution_nsclc():
"""Phase 1: Resolve 'non-small cell lung cancer' to EFO ID."""
tu = load_tu()
result = tu.tools.OpenTargets_get_disease_id_description_by_name(diseaseName='non-small cell lung cancer')
hits = result.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "No disease hits returned"
top = hits[0]
assert top.get('id') == 'EFO_0003060', f"Expected EFO_0003060, got {top.get('id')}"
assert 'non-small cell' in top.get('name', '').lower(), f"Name mismatch: {top.get('name')}"
print(f" Disease resolved: {top.get('name')} ({top.get('id')})")
def test_02_disease_resolution_melanoma():
"""Phase 1: Resolve 'melanoma' to EFO ID."""
tu = load_tu()
result = tu.tools.OpenTargets_get_disease_id_description_by_name(diseaseName='melanoma')
hits = result.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "No disease hits returned"
top = hits[0]
assert top.get('id') is not None, "No EFO ID for melanoma"
print(f" Disease resolved: {top.get('name')} ({top.get('id')})")
def test_03_disease_resolution_colorectal():
"""Phase 1: Resolve 'colorectal cancer' to EFO ID."""
tu = load_tu()
result = tu.tools.OpenTargets_get_disease_id_description_by_name(diseaseName='colorectal cancer')
hits = result.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "No disease hits returned"
print(f" Disease resolved: {hits[0].get('name')} ({hits[0].get('id')})")
def test_04_disease_resolution_breast_cancer():
"""Phase 1: Resolve 'breast cancer' to EFO ID."""
tu = load_tu()
result = tu.tools.OpenTargets_get_disease_id_description_by_name(diseaseName='breast cancer')
hits = result.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "No disease hits returned"
print(f" Disease resolved: {hits[0].get('name')} ({hits[0].get('id')})")
def test_05_disease_resolution_ols_fallback():
"""Phase 1: Resolve disease using OLS EFO search as fallback."""
tu = load_tu()
result = tu.tools.ols_search_efo_terms(query='non-small cell lung cancer', limit=5)
data = result.get('data', {})
terms = data.get('terms', [])
assert len(terms) > 0, "No OLS terms returned"
top = terms[0]
assert 'EFO_0003060' in top.get('short_form', ''), f"Expected EFO_0003060, got {top.get('short_form')}"
print(f" OLS resolved: {top.get('label')} ({top.get('short_form')})")
def test_06_gene_resolution_egfr():
"""Phase 1: Resolve EGFR to Ensembl/Entrez IDs."""
tu = load_tu()
result = tu.tools.MyGene_query_genes(query='EGFR', species='human')
hits = result.get('hits', [])
assert len(hits) > 0, "No gene hits returned"
top = hits[0]
assert top.get('symbol') == 'EGFR', f"Expected EGFR, got {top.get('symbol')}"
assert top.get('entrezgene') == 1956 or str(top.get('entrezgene')) == '1956', f"Wrong Entrez ID: {top.get('entrezgene')}"
ensembl = top.get('ensembl', {})
ensembl_id = ensembl.get('gene') if isinstance(ensembl, dict) else None
assert ensembl_id == 'ENSG00000146648', f"Wrong Ensembl ID: {ensembl_id}"
print(f" Gene resolved: EGFR -> {ensembl_id}, Entrez: {top.get('entrezgene')}")
def test_07_gene_resolution_braf():
"""Phase 1: Resolve BRAF to Ensembl/Entrez IDs."""
tu = load_tu()
result = tu.tools.MyGene_query_genes(query='BRAF', species='human')
hits = result.get('hits', [])
assert len(hits) > 0, "No gene hits returned"
top = hits[0]
assert top.get('symbol') == 'BRAF', f"Expected BRAF, got {top.get('symbol')}"
print(f" Gene resolved: BRAF -> Entrez: {top.get('entrezgene')}")
def test_08_gene_resolution_kras():
"""Phase 1: Resolve KRAS to Ensembl/Entrez IDs."""
tu = load_tu()
result = tu.tools.MyGene_query_genes(query='KRAS', species='human')
hits = result.get('hits', [])
assert len(hits) > 0, "No gene hits returned"
top = hits[0]
assert top.get('symbol') == 'KRAS', f"Expected KRAS, got {top.get('symbol')}"
print(f" Gene resolved: KRAS -> Entrez: {top.get('entrezgene')}")
def test_09_gene_resolution_erbb2_her2():
"""Phase 1: Resolve HER2/ERBB2 to Ensembl/Entrez IDs."""
tu = load_tu()
result = tu.tools.MyGene_query_genes(query='ERBB2', species='human')
hits = result.get('hits', [])
assert len(hits) > 0, "No gene hits returned"
top = hits[0]
assert top.get('symbol') == 'ERBB2', f"Expected ERBB2, got {top.get('symbol')}"
print(f" Gene resolved: ERBB2 (HER2) -> Entrez: {top.get('entrezgene')}")
def test_10_gene_resolution_ntrk1():
"""Phase 1: Resolve NTRK1 to Ensembl/Entrez IDs."""
tu = load_tu()
result = tu.tools.MyGene_query_genes(query='NTRK1', species='human')
hits = result.get('hits', [])
assert len(hits) > 0, "No gene hits returned"
top = hits[0]
assert top.get('symbol') == 'NTRK1', f"Expected NTRK1, got {top.get('symbol')}"
print(f" Gene resolved: NTRK1 -> Entrez: {top.get('entrezgene')}")
def test_11_fda_pharmacogenomic_biomarkers():
"""Phase 1: Get FDA pharmacogenomic biomarkers list."""
tu = load_tu()
# Use limit=1000 to get all biomarkers (default is 10)
result = tu.tools.fda_pharmacogenomic_biomarkers(limit=1000)
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert 'results' in result, f"Missing 'results' key. Keys: {list(result.keys())}"
total_count = result.get('count', 0)
assert total_count > 100, f"Expected >100 total biomarkers, got {total_count}"
biomarkers = result.get('results', [])
assert len(biomarkers) > 10, f"Expected >10 biomarkers returned, got {len(biomarkers)}"
# Check EGFR is in the list
egfr_entries = [b for b in biomarkers if 'EGFR' in str(b.get('Biomarker', ''))]
assert len(egfr_entries) > 0, "EGFR not found in FDA biomarkers"
print(f" FDA biomarkers: {total_count} total, {len(biomarkers)} returned, {len(egfr_entries)} EGFR entries")
# ============================================================
# Phase 2: Broad Trial Discovery Tests
# ============================================================
def test_12_search_trials_disease_nsclc():
"""Phase 2: Search trials by disease (NSCLC)."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
condition='non-small cell lung cancer',
query_term='EGFR mutation',
pageSize=5
)
assert not isinstance(result, str), f"Got string result (no trials): {result}"
studies = result.get('studies', [])
assert len(studies) > 0, "No studies returned"
# Verify study structure
study = studies[0]
assert 'NCT ID' in study, f"Missing 'NCT ID'. Keys: {list(study.keys())}"
assert 'brief_title' in study, f"Missing 'brief_title'"
assert 'overall_status' in study, f"Missing 'overall_status'"
print(f" Found {len(studies)} NSCLC/EGFR trials, total: {result.get('total_count', '?')}")
def test_13_search_trials_biomarker_kras_g12c():
"""Phase 2: Search trials by specific biomarker (KRAS G12C)."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
condition='colorectal cancer',
query_term='KRAS G12C',
pageSize=5
)
assert not isinstance(result, str), f"Got string result: {result}"
studies = result.get('studies', [])
assert len(studies) > 0, "No KRAS G12C CRC trials found"
print(f" Found {len(studies)} CRC/KRAS G12C trials")
def test_14_search_trials_biomarker_braf_v600e():
"""Phase 2: Search trials for BRAF V600E melanoma."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
condition='melanoma',
query_term='BRAF V600E',
pageSize=5
)
assert not isinstance(result, str), f"Got string result: {result}"
studies = result.get('studies', [])
assert len(studies) > 0, "No BRAF V600E melanoma trials found"
print(f" Found {len(studies)} melanoma/BRAF V600E trials")
def test_15_search_trials_her2_breast():
"""Phase 2: Search trials for HER2+ breast cancer."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
condition='breast cancer',
query_term='HER2 positive',
pageSize=5
)
assert not isinstance(result, str), f"Got string result: {result}"
studies = result.get('studies', [])
assert len(studies) > 0, "No HER2+ breast cancer trials found"
print(f" Found {len(studies)} HER2+ breast cancer trials")
def test_16_search_trials_ntrk_basket():
"""Phase 2: Search basket trials for NTRK fusion."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
query_term='NTRK fusion',
pageSize=5
)
assert not isinstance(result, str), f"Got string result: {result}"
studies = result.get('studies', [])
assert len(studies) > 0, "No NTRK fusion trials found"
print(f" Found {len(studies)} NTRK fusion trials")
def test_17_search_trials_intervention():
"""Phase 2: Search trials by intervention drug (osimertinib)."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
condition='non-small cell lung cancer',
intervention='osimertinib',
query_term='osimertinib',
pageSize=5
)
assert not isinstance(result, str), f"Got string result: {result}"
studies = result.get('studies', [])
assert len(studies) > 0, "No osimertinib NSCLC trials found"
print(f" Found {len(studies)} osimertinib NSCLC trials")
def test_18_alternative_search():
"""Phase 2: Test alternative trial search (ClinicalTrials_search_studies)."""
tu = load_tu()
result = tu.tools.ClinicalTrials_search_studies(
action='search_studies',
condition='non-small cell lung cancer',
intervention='pembrolizumab',
limit=5
)
studies = result.get('studies', [])
assert len(studies) > 0, "No studies from alternative search"
study = studies[0]
assert 'nctId' in study, f"Missing 'nctId'. Keys: {list(study.keys())}"
print(f" Alternative search found {len(studies)} trials")
def test_19_search_pagination():
"""Phase 2: Test trial search pagination."""
tu = load_tu()
# First page
result1 = tu.tools.search_clinical_trials(
condition='lung cancer',
query_term='EGFR',
pageSize=3
)
assert not isinstance(result1, str), "No results"
studies1 = result1.get('studies', [])
next_token = result1.get('nextPageToken')
assert len(studies1) > 0, "No first page results"
total = result1.get('total_count', 0)
print(f" First page: {len(studies1)} trials, total: {total}, has next: {next_token is not None}")
# ============================================================
# Phase 3: Trial Characterization Tests
# ============================================================
def test_20_get_trial_details():
"""Phase 3: Get full trial details by NCT ID."""
tu = load_tu()
result = tu.tools.ClinicalTrials_get_study(action='get_study_details', nct_id='NCT02841579')
assert isinstance(result, dict), f"Expected dict, got {type(result)}"
assert 'nctId' in result, f"Missing 'nctId'. Keys: {list(result.keys())}"
assert result.get('nctId') == 'NCT02841579', f"Wrong NCT ID: {result.get('nctId')}"
assert 'eligibility' in result, "Missing eligibility"
print(f" Got details for {result.get('nctId')}: {result.get('title', '')[:80]}...")
def test_21_get_eligibility_criteria():
"""Phase 3: Get eligibility criteria for trials."""
tu = load_tu()
result = tu.tools.get_clinical_trial_eligibility_criteria(
nct_ids=['NCT04765059', 'NCT02841579'],
eligibility_criteria='all'
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) >= 1, f"Expected >= 1 results, got {len(result)}"
for item in result:
assert 'NCT ID' in item, f"Missing 'NCT ID'"
assert 'eligibility_criteria' in item, f"Missing 'eligibility_criteria'"
criteria = item.get('eligibility_criteria', '')
assert len(criteria) > 50, f"Eligibility text too short: {len(criteria)} chars"
assert 'Inclusion Criteria' in criteria or 'inclusion' in criteria.lower(), "No inclusion criteria found"
print(f" Got eligibility for {len(result)} trials, avg length: {sum(len(r.get('eligibility_criteria','')) for r in result)//len(result)} chars")
def test_22_get_conditions_and_interventions():
"""Phase 3: Get conditions and interventions for trials."""
tu = load_tu()
result = tu.tools.get_clinical_trial_conditions_and_interventions(
nct_ids=['NCT04765059'],
condition_and_intervention='all'
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) > 0, "No results"
item = result[0]
assert 'condition' in item, f"Missing 'condition'. Keys: {list(item.keys())}"
assert 'interventions' in item, f"Missing 'interventions'"
interventions = item.get('interventions', [])
assert len(interventions) > 0, "No interventions found"
for inv in interventions:
assert 'name' in inv, f"Missing intervention name"
assert 'type' in inv, f"Missing intervention type"
print(f" Trial has {len(interventions)} interventions, {len(item.get('arm_groups', []))} arm groups")
def test_23_get_trial_locations():
"""Phase 3: Get trial site locations."""
tu = load_tu()
result = tu.tools.get_clinical_trial_locations(
nct_ids=['NCT04765059'],
location='all'
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) > 0, "No results"
item = result[0]
locations = item.get('locations', [])
assert len(locations) > 0, "No locations found"
loc = locations[0]
assert 'country' in loc, f"Missing 'country' in location"
countries = set(l.get('country', '') for l in locations)
print(f" {len(locations)} sites across {len(countries)} countries: {list(countries)[:5]}")
def test_24_get_trial_status():
"""Phase 3: Get trial status and dates."""
tu = load_tu()
result = tu.tools.get_clinical_trial_status_and_dates(
nct_ids=['NCT04765059'],
status_and_date='all'
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) > 0, "No results"
item = result[0]
assert 'overall_status' in item, f"Missing 'overall_status'"
assert item.get('overall_status') is not None, "Status is None"
print(f" Status: {item.get('overall_status')}, Start: {item.get('start_date')}, Completion: {item.get('completion_date')}")
def test_25_get_trial_descriptions():
"""Phase 3: Get trial descriptions."""
tu = load_tu()
result = tu.tools.get_clinical_trial_descriptions(
nct_ids=['NCT04765059'],
description_type='full'
)
assert isinstance(result, list), f"Expected list, got {type(result)}"
assert len(result) > 0, "No results"
item = result[0]
assert 'brief_title' in item, f"Missing 'brief_title'"
assert 'official_title' in item, f"Missing 'official_title'"
assert 'brief_summary' in item, f"Missing 'brief_summary'"
print(f" Title: {item.get('brief_title', '')[:80]}...")
# ============================================================
# Phase 4: Molecular Eligibility Matching Tests
# ============================================================
def test_26_eligibility_biomarker_parsing():
"""Phase 4: Parse eligibility criteria for biomarker requirements."""
# Test with real eligibility text
sample_text = """Inclusion Criteria:
* Patients with histological confirmation of NSCLC with an activating EGFR mutation
(exon 19 deletion or L858R) and concomitant T790M mutation
* ECOG performance status less than or equal to 2
Exclusion Criteria:
* Prior treatment with a third-generation EGFR TKI
* Known ALK rearrangement"""
# Check that we can find EGFR in inclusion
assert 'EGFR' in sample_text, "EGFR not in text"
assert 'L858R' in sample_text, "L858R not in text"
assert 'T790M' in sample_text, "T790M not in text"
# Split inclusion/exclusion
inclusion = sample_text.split('Exclusion Criteria')[0]
exclusion = sample_text.split('Exclusion Criteria')[1] if 'Exclusion Criteria' in sample_text else ''
assert 'EGFR' in inclusion, "EGFR should be in inclusion"
assert 'ALK' in exclusion, "ALK should be in exclusion"
print(" Biomarker parsing: EGFR/L858R/T790M in inclusion, ALK in exclusion")
def test_27_molecular_match_scoring():
"""Phase 4: Test molecular match scoring logic."""
# Test exact match
patient_biomarkers = [{'gene': 'EGFR', 'alteration': 'L858R', 'type': 'mutation'}]
# Trial requiring exact match
trial_with_exact = {
'required_biomarkers': [{'gene': 'EGFR', 'context': 'EGFR mutation L858R or exon 19 deletion'}],
'excluded_biomarkers': []
}
# Check exact match logic
patient_genes = {b['gene'].upper() for b in patient_biomarkers}
required_genes = {b['gene'].upper() for b in trial_with_exact['required_biomarkers']}
matched = patient_genes & required_genes
assert matched == {'EGFR'}, f"Expected EGFR match, got {matched}"
# Check variant in context
for req in trial_with_exact['required_biomarkers']:
for pb in patient_biomarkers:
if pb['gene'].upper() == req['gene'].upper():
alt = pb.get('alteration', '').upper()
assert alt in req.get('context', '').upper(), f"{alt} not in context"
print(" Molecular scoring: exact match correctly identified")
# ============================================================
# Phase 5: Drug-Biomarker Alignment Tests
# ============================================================
def test_28_drug_resolution_osimertinib():
"""Phase 5: Resolve osimertinib in OpenTargets."""
tu = load_tu()
result = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName='osimertinib')
hits = result.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "No drug hits returned"
top = hits[0]
assert top.get('id') == 'CHEMBL3353410', f"Expected CHEMBL3353410, got {top.get('id')}"
assert 'approved' in top.get('description', '').lower(), "Drug not described as approved"
print(f" Drug resolved: {top.get('name')} ({top.get('id')})")
def test_29_drug_mechanism_egfr():
"""Phase 5: Get drug mechanism for osimertinib (should target EGFR)."""
tu = load_tu()
result = tu.tools.OpenTargets_get_drug_mechanisms_of_action_by_chemblId(chemblId='CHEMBL3353410')
moa = result.get('data', {}).get('drug', {}).get('mechanismsOfAction', {}).get('rows', [])
assert len(moa) > 0, "No mechanisms returned"
# Check EGFR is a target
all_targets = []
for row in moa:
for t in row.get('targets', []):
all_targets.append(t.get('approvedSymbol'))
assert 'EGFR' in all_targets, f"EGFR not in targets: {all_targets}"
print(f" Osimertinib targets: {list(set(all_targets))}, MoA: {moa[0].get('mechanismOfAction')}")
def test_30_drug_resolution_sotorasib():
"""Phase 5: Resolve sotorasib (KRAS G12C inhibitor)."""
tu = load_tu()
result = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName='sotorasib')
hits = result.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "No drug hits for sotorasib"
top = hits[0]
assert top.get('id') is not None, "No ChEMBL ID"
print(f" Drug resolved: {top.get('name')} ({top.get('id')})")
def test_31_drug_resolution_dabrafenib():
"""Phase 5: Resolve dabrafenib (BRAF inhibitor)."""
tu = load_tu()
result = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName='dabrafenib')
hits = result.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "No drug hits for dabrafenib"
print(f" Drug resolved: {hits[0].get('name')} ({hits[0].get('id')})")
def test_32_drug_resolution_larotrectinib():
"""Phase 5: Resolve larotrectinib (NTRK inhibitor)."""
tu = load_tu()
result = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName='larotrectinib')
hits = result.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "No drug hits for larotrectinib"
print(f" Drug resolved: {hits[0].get('name')} ({hits[0].get('id')})")
def test_33_drugs_by_target_egfr():
"""Phase 5: Get drugs targeting EGFR from OpenTargets."""
tu = load_tu()
result = tu.tools.OpenTargets_get_associated_drugs_by_target_ensemblID(
ensemblId='ENSG00000146648', size=10
)
drugs = result.get('data', {}).get('target', {}).get('knownDrugs', {})
rows = drugs.get('rows', [])
assert len(rows) > 0, "No drugs found for EGFR"
# Find approved drugs
approved = [r for r in rows if r.get('drug', {}).get('isApproved')]
print(f" EGFR drugs: {len(rows)} total (showing first 10), {len(approved)} approved")
def test_34_drugs_by_disease_nsclc():
"""Phase 5: Get drugs for NSCLC from OpenTargets."""
tu = load_tu()
result = tu.tools.OpenTargets_get_associated_drugs_by_disease_efoId(
efoId='EFO_0003060', size=5
)
drugs = result.get('data', {}).get('disease', {}).get('knownDrugs', {})
count = drugs.get('count', 0)
rows = drugs.get('rows', [])
assert count > 0, "No drug count for NSCLC"
assert len(rows) > 0, "No drug rows for NSCLC"
print(f" NSCLC drugs: {count} total, showing {len(rows)}")
def test_35_drugbank_targets():
"""Phase 5: Get drug targets from DrugBank."""
tu = load_tu()
result = tu.tools.drugbank_get_targets_by_drug_name_or_drugbank_id(
query='osimertinib', case_sensitive=False, exact_match=False, limit=3
)
matches = result.get('total_matches', 0)
assert matches > 0, "No DrugBank matches for osimertinib"
results = result.get('results', [])
targets = results[0].get('targets', [])
assert len(targets) > 0, "No targets found"
target_names = [t.get('name') for t in targets]
assert any('growth factor receptor' in n.lower() for n in target_names), f"EGFR not in targets: {target_names}"
print(f" DrugBank: osimertinib targets {target_names}")
# ============================================================
# Phase 6: Evidence Assessment Tests
# ============================================================
def test_36_fda_indications_osimertinib():
"""Phase 6: Get FDA indications for osimertinib."""
tu = load_tu()
result = tu.tools.FDA_get_indications_by_drug_name(drug_name='osimertinib', limit=3)
results = result.get('results', [])
assert len(results) > 0, "No FDA results for osimertinib"
ind_text = str(results[0].get('indications_and_usage', ''))
assert 'NSCLC' in ind_text or 'non-small cell' in ind_text.lower(), f"NSCLC not in indication text"
assert 'EGFR' in ind_text, "EGFR not in indication text"
print(f" FDA indication confirmed for osimertinib in EGFR+ NSCLC")
def test_37_pubmed_evidence():
"""Phase 6: Search PubMed for clinical evidence."""
tu = load_tu()
result = tu.tools.PubMed_search_articles(
query='EGFR L858R osimertinib NSCLC clinical trial',
max_results=3
)
articles = result if isinstance(result, list) else result.get('articles', [])
assert len(articles) > 0, "No PubMed articles found"
article = articles[0]
assert 'pmid' in article or 'PMID' in article, f"Missing PMID. Keys: {list(article.keys())}"
assert 'title' in article or 'Title' in article, f"Missing title"
print(f" Found {len(articles)} PubMed articles for EGFR L858R osimertinib")
def test_38_civic_gene_variants():
"""Phase 6: Get CIViC variants for EGFR (gene_id=19)."""
tu = load_tu()
result = tu.tools.civic_get_variants_by_gene(gene_id=19, limit=100)
gene_data = result.get('data', {}).get('gene', {})
assert gene_data.get('name') == 'EGFR', f"Wrong gene: {gene_data.get('name')}"
variants = gene_data.get('variants', {}).get('nodes', [])
assert len(variants) > 0, "No CIViC variants for EGFR"
variant_names = [v.get('name', '') for v in variants]
# L858R should be in the list
l858r_found = any('L858R' in name for name in variant_names)
print(f" CIViC EGFR: {len(variants)} variants, L858R found: {l858r_found}")
def test_39_civic_braf_variants():
"""Phase 6: Get CIViC variants for BRAF (gene_id=5)."""
tu = load_tu()
result = tu.tools.civic_get_variants_by_gene(gene_id=5, limit=100)
gene_data = result.get('data', {}).get('gene', {})
assert gene_data.get('name') == 'BRAF', f"Wrong gene: {gene_data.get('name')}"
variants = gene_data.get('variants', {}).get('nodes', [])
assert len(variants) > 0, "No CIViC variants for BRAF"
v600e_found = any('V600E' in v.get('name', '') for v in variants)
print(f" CIViC BRAF: {len(variants)} variants, V600E found: {v600e_found}")
def test_40_pharmgkb_egfr():
"""Phase 6: Get PharmGKB data for EGFR."""
tu = load_tu()
result = tu.tools.PharmGKB_search_genes(query='EGFR')
data = result.get('data', [])
assert len(data) > 0, "No PharmGKB results for EGFR"
gene = data[0]
assert gene.get('symbol') == 'EGFR', f"Wrong gene: {gene.get('symbol')}"
print(f" PharmGKB: EGFR ({gene.get('id')}), chr{gene.get('chr', {}).get('name', '?')}")
# ============================================================
# Phase 7: Geographic & Feasibility Tests
# ============================================================
def test_41_location_analysis():
"""Phase 7: Analyze trial site locations."""
tu = load_tu()
result = tu.tools.get_clinical_trial_locations(
nct_ids=['NCT04765059'],
location='all'
)
assert isinstance(result, list), f"Expected list"
item = result[0]
locations = item.get('locations', [])
assert len(locations) > 0, "No locations"
countries = set(l.get('country', '') for l in locations)
us_sites = [l for l in locations if l.get('country') == 'United States']
us_states = set(l.get('state', '') for l in us_sites)
assert len(countries) > 0, "No countries found"
print(f" {len(locations)} sites, {len(countries)} countries, {len(us_sites)} US sites in {len(us_states)} states")
def test_42_trial_status_check():
"""Phase 7: Verify trial enrollment feasibility."""
tu = load_tu()
result = tu.tools.get_clinical_trial_status_and_dates(
nct_ids=['NCT04765059'],
status_and_date='all'
)
assert isinstance(result, list)
item = result[0]
status = item.get('overall_status')
assert status is not None, "No status"
# Check that we can determine recruiting vs non-recruiting
recruiting_statuses = ['RECRUITING', 'NOT_YET_RECRUITING', 'ENROLLING_BY_INVITATION']
active_statuses = ['ACTIVE_NOT_RECRUITING', 'COMPLETED', 'TERMINATED', 'SUSPENDED']
assert status in recruiting_statuses + active_statuses, f"Unknown status: {status}"
print(f" Status: {status}, is recruiting: {status in recruiting_statuses}")
# ============================================================
# Phase 8: Alternative Options Tests
# ============================================================
def test_43_basket_trial_search():
"""Phase 8: Search for NTRK basket/tumor-agnostic trials."""
tu = load_tu()
# Note: "NTRK fusion tumor agnostic" returns no results because the search
# is too specific. Use broader queries and combine results.
result = tu.tools.search_clinical_trials(
query_term='NTRK solid tumor',
pageSize=5
)
if isinstance(result, str):
# Fallback: try even simpler query
result = tu.tools.search_clinical_trials(
query_term='NTRK',
pageSize=5
)
assert not isinstance(result, str), f"No results for any NTRK query"
studies = result.get('studies', [])
assert len(studies) > 0, "No NTRK trials found"
print(f" Found {len(studies)} NTRK basket/solid tumor trials")
def test_44_expanded_access_search():
"""Phase 8: Search for expanded access programs."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
query_term='expanded access osimertinib',
pageSize=5
)
# May return string if no results, that's OK for expanded access
if isinstance(result, str):
print(f" No expanded access found (expected for some drugs)")
else:
studies = result.get('studies', [])
print(f" Found {len(studies)} expanded access programs")
# This test passes regardless - expanded access may not exist
# ============================================================
# Phase 9: Scoring & Integration Tests
# ============================================================
def test_45_scoring_system():
"""Phase 9: Verify scoring system calculations."""
# Molecular Match (0-40)
assert 0 <= 40 <= 40, "Molecular max out of range"
# Clinical Eligibility (0-25)
assert 0 <= 25 <= 25, "Clinical max out of range"
# Evidence Strength (0-20)
assert 0 <= 20 <= 20, "Evidence max out of range"
# Trial Phase (0-10)
assert 0 <= 10 <= 10, "Phase max out of range"
# Geographic (0-5)
assert 0 <= 5 <= 5, "Geographic max out of range"
# Total
assert 40 + 25 + 20 + 10 + 5 == 100, "Scoring components don't sum to 100"
# Test tier classification
def get_tier(score):
if score >= 80: return 1
if score >= 60: return 2
if score >= 40: return 3
return 4
assert get_tier(85) == 1, "Tier 1 wrong"
assert get_tier(70) == 2, "Tier 2 wrong"
assert get_tier(50) == 3, "Tier 3 wrong"
assert get_tier(30) == 4, "Tier 4 wrong"
assert get_tier(80) == 1, "Tier 1 boundary wrong"
assert get_tier(60) == 2, "Tier 2 boundary wrong"
assert get_tier(40) == 3, "Tier 3 boundary wrong"
print(" Scoring system verified: 40+25+20+10+5=100, tiers correct")
def test_46_end_to_end_egfr_nsclc():
"""Phase 9: End-to-end integration test (EGFR NSCLC)."""
tu = load_tu()
# Step 1: Resolve disease
disease = tu.tools.OpenTargets_get_disease_id_description_by_name(diseaseName='non-small cell lung cancer')
efo_id = disease.get('data', {}).get('search', {}).get('hits', [{}])[0].get('id')
assert efo_id == 'EFO_0003060', f"Disease resolution failed: {efo_id}"
# Step 2: Resolve gene
gene = tu.tools.MyGene_query_genes(query='EGFR', species='human')
ensembl_id = gene.get('hits', [{}])[0].get('ensembl', {}).get('gene')
assert ensembl_id == 'ENSG00000146648', f"Gene resolution failed: {ensembl_id}"
# Step 3: Search trials
trials = tu.tools.search_clinical_trials(
condition='non-small cell lung cancer',
query_term='EGFR mutation',
pageSize=5
)
assert not isinstance(trials, str), "No trials found"
studies = trials.get('studies', [])
assert len(studies) > 0, "No studies"
# Step 4: Get first trial details
nct_id = studies[0].get('NCT ID')
assert nct_id is not None, "No NCT ID"
# Step 5: Get eligibility
elig = tu.tools.get_clinical_trial_eligibility_criteria(
nct_ids=[nct_id],
eligibility_criteria='all'
)
assert isinstance(elig, list) and len(elig) > 0, "No eligibility"
print(f" End-to-end: {efo_id} + {ensembl_id} -> {len(studies)} trials -> {nct_id} with eligibility")
def test_47_end_to_end_braf_melanoma():
"""Phase 9: End-to-end integration test (BRAF melanoma)."""
tu = load_tu()
# Resolve
disease = tu.tools.OpenTargets_get_disease_id_description_by_name(diseaseName='melanoma')
efo_id = disease.get('data', {}).get('search', {}).get('hits', [{}])[0].get('id')
assert efo_id is not None, "Disease resolution failed"
gene = tu.tools.MyGene_query_genes(query='BRAF', species='human')
symbol = gene.get('hits', [{}])[0].get('symbol')
assert symbol == 'BRAF', f"Gene resolution failed: {symbol}"
# Search
trials = tu.tools.search_clinical_trials(
condition='melanoma',
query_term='BRAF V600E',
pageSize=5
)
assert not isinstance(trials, str), "No trials"
studies = trials.get('studies', [])
assert len(studies) > 0, "No BRAF melanoma trials"
print(f" End-to-end: melanoma({efo_id}) + BRAF -> {len(studies)} trials")
def test_48_end_to_end_kras_crc():
"""Phase 9: End-to-end integration test (KRAS G12C CRC)."""
tu = load_tu()
# Search
trials = tu.tools.search_clinical_trials(
condition='colorectal cancer',
query_term='KRAS G12C',
pageSize=5
)
assert not isinstance(trials, str), "No trials"
studies = trials.get('studies', [])
assert len(studies) > 0, "No KRAS G12C CRC trials"
# Check drug alignment
drug = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName='sotorasib')
hits = drug.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "Sotorasib not found"
print(f" End-to-end: CRC + KRAS G12C -> {len(studies)} trials, sotorasib: {hits[0].get('id')}")
def test_49_end_to_end_her2_breast():
"""Phase 9: End-to-end integration test (HER2+ breast)."""
tu = load_tu()
# Search
trials = tu.tools.search_clinical_trials(
condition='breast cancer',
query_term='HER2 positive amplification',
pageSize=5
)
assert not isinstance(trials, str), "No trials"
studies = trials.get('studies', [])
assert len(studies) > 0, "No HER2+ breast trials"
# Drug check
drug = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName='trastuzumab')
hits = drug.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "Trastuzumab not found"
print(f" End-to-end: breast + HER2 -> {len(studies)} trials, trastuzumab: {hits[0].get('id')}")
def test_50_end_to_end_ntrk_fusion():
"""Phase 9: End-to-end integration test (NTRK fusion)."""
tu = load_tu()
# Search
trials = tu.tools.search_clinical_trials(
query_term='NTRK fusion',
pageSize=5
)
assert not isinstance(trials, str), "No trials"
studies = trials.get('studies', [])
assert len(studies) > 0, "No NTRK fusion trials"
# Drug check
drug = tu.tools.OpenTargets_get_drug_id_description_by_name(drugName='larotrectinib')
hits = drug.get('data', {}).get('search', {}).get('hits', [])
assert len(hits) > 0, "Larotrectinib not found"
print(f" End-to-end: NTRK fusion -> {len(studies)} trials, larotrectinib: {hits[0].get('id')}")
# ============================================================
# Edge Case Tests
# ============================================================
def test_51_rare_biomarker():
"""Edge case: Rare biomarker with few trials."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
query_term='FGFR3 S249C bladder cancer',
pageSize=5
)
# May or may not find trials - that's OK
if isinstance(result, str):
print(f" Rare biomarker: No trials found (expected)")
else:
studies = result.get('studies', [])
print(f" Rare biomarker: Found {len(studies)} trials")
def test_52_no_molecular_criteria():
"""Edge case: Disease-only search (no biomarker)."""
tu = load_tu()
result = tu.tools.search_clinical_trials(
condition='pancreatic cancer',
query_term='pancreatic cancer',
pageSize=5
)
assert not isinstance(result, str), "No trials for pancreatic cancer"
studies = result.get('studies', [])
assert len(studies) > 0, "No pancreatic cancer trials"
print(f" Disease-only search: {len(studies)} pancreatic cancer trials")
def test_53_multiple_biomarkers():
"""Edge case: Patient with multiple biomarkers."""
tu = load_tu()
# Patient has both EGFR mutation and PD-L1 expression
result1 = tu.tools.search_clinical_trials(
condition='non-small cell lung cancer',
query_term='EGFR mutation PD-L1',
pageSize=5
)
assert not isinstance(result1, str), "No trials"
studies = result1.get('studies', [])
assert len(studies) > 0, "No multi-biomarker trials"
print(f" Multiple biomarkers (EGFR + PD-L1): {len(studies)} trials")
# ============================================================
# Main Test Runner
# ============================================================
def main():
print("=" * 70)
print("Clinical Trial Matching Skill - Comprehensive Test Suite")
print("=" * 70)
# Phase 1: Patient Profile Standardization (11 tests)
print("\n--- Phase 1: Patient Profile Standardization ---")
run_test("01. Disease resolution: NSCLC", test_01_disease_resolution_nsclc)
run_test("02. Disease resolution: melanoma", test_02_disease_resolution_melanoma)
run_test("03. Disease resolution: colorectal", test_03_disease_resolution_colorectal)
run_test("04. Disease resolution: breast cancer", test_04_disease_resolution_breast_cancer)
run_test("05. Disease resolution: OLS fallback", test_05_disease_resolution_ols_fallback)
run_test("06. Gene resolution: EGFR", test_06_gene_resolution_egfr)
run_test("07. Gene resolution: BRAF", test_07_gene_resolution_braf)
run_test("08. Gene resolution: KRAS", test_08_gene_resolution_kras)
run_test("09. Gene resolution: ERBB2/HER2", test_09_gene_resolution_erbb2_her2)
run_test("10. Gene resolution: NTRK1", test_10_gene_resolution_ntrk1)
run_test("11. FDA pharmacogenomic biomarkers", test_11_fda_pharmacogenomic_biomarkers)
# Phase 2: Broad Trial Discovery (8 tests)
print("\n--- Phase 2: Broad Trial Discovery ---")
run_test("12. Search trials: NSCLC/EGFR", test_12_search_trials_disease_nsclc)
run_test("13. Search trials: CRC/KRAS G12C", test_13_search_trials_biomarker_kras_g12c)
run_test("14. Search trials: melanoma/BRAF V600E", test_14_search_trials_biomarker_braf_v600e)
run_test("15. Search trials: HER2+ breast", test_15_search_trials_her2_breast)
run_test("16. Search trials: NTRK fusion basket", test_16_search_trials_ntrk_basket)
run_test("17. Search trials: intervention (osimertinib)", test_17_search_trials_intervention)
run_test("18. Alternative search (ClinicalTrials_search_studies)", test_18_alternative_search)
run_test("19. Search pagination", test_19_search_pagination)
# Phase 3: Trial Characterization (6 tests)
print("\n--- Phase 3: Trial Characterization ---")
run_test("20. Get trial details", test_20_get_trial_details)
run_test("21. Get eligibility criteria", test_21_get_eligibility_criteria)
run_test("22. Get conditions and interventions", test_22_get_conditions_and_interventions)
run_test("23. Get trial locations", test_23_get_trial_locations)
run_test("24. Get trial status", test_24_get_trial_status)
run_test("25. Get trial descriptions", test_25_get_trial_descriptions)
# Phase 4: Molecular Eligibility Matching (2 tests)
print("\n--- Phase 4: Molecular Eligibility Matching ---")
run_test("26. Eligibility biomarker parsing", test_26_eligibility_biomarker_parsing)
run_test("27. Molecular match scoring", test_27_molecular_match_scoring)
# Phase 5: Drug-Biomarker Alignment (8 tests)
print("\n--- Phase 5: Drug-Biomarker Alignment ---")
run_test("28. Drug resolution: osimertinib", test_28_drug_resolution_osimertinib)
run_test("29. Drug mechanism: EGFR inhibitor", test_29_drug_mechanism_egfr)
run_test("30. Drug resolution: sotorasib", test_30_drug_resolution_sotorasib)
run_test("31. Drug resolution: dabrafenib", test_31_drug_resolution_dabrafenib)
run_test("32. Drug resolution: larotrectinib", test_32_drug_resolution_larotrectinib)
run_test("33. Drugs by target: EGFR", test_33_drugs_by_target_egfr)
run_test("34. Drugs by disease: NSCLC", test_34_drugs_by_disease_nsclc)
run_test("35. DrugBank targets", test_35_drugbank_targets)
# Phase 6: Evidence Assessment (5 tests)
print("\n--- Phase 6: Evidence Assessment ---")
run_test("36. FDA indications: osimertinib", test_36_fda_indications_osimertinib)
run_test("37. PubMed evidence", test_37_pubmed_evidence)
run_test("38. CIViC EGFR variants", test_38_civic_gene_variants)
run_test("39. CIViC BRAF variants", test_39_civic_braf_variants)
run_test("40. PharmGKB EGFR", test_40_pharmgkb_egfr)
# Phase 7: Geographic & Feasibility (2 tests)
print("\n--- Phase 7: Geographic & Feasibility ---")
run_test("41. Location analysis", test_41_location_analysis)
run_test("42. Trial status check", test_42_trial_status_check)
# Phase 8: Alternative Options (2 tests)
print("\n--- Phase 8: Alternative Options ---")
run_test("43. Basket trial search", test_43_basket_trial_search)
run_test("44. Expanded access search", test_44_expanded_access_search)
# Phase 9: Scoring & Integration (6 tests)
print("\n--- Phase 9: Scoring & Integration ---")
run_test("45. Scoring system verification", test_45_scoring_system)
run_test("46. End-to-end: EGFR NSCLC", test_46_end_to_end_egfr_nsclc)
run_test("47. End-to-end: BRAF melanoma", test_47_end_to_end_braf_melanoma)
run_test("48. End-to-end: KRAS G12C CRC", test_48_end_to_end_kras_crc)
run_test("49. End-to-end: HER2+ breast", test_49_end_to_end_her2_breast)
run_test("50. End-to-end: NTRK fusion", test_50_end_to_end_ntrk_fusion)
# Edge Cases (3 tests)
print("\n--- Edge Cases ---")
run_test("51. Rare biomarker", test_51_rare_biomarker)
run_test("52. No molecular criteria", test_52_no_molecular_criteria)
run_test("53. Multiple biomarkers", test_53_multiple_biomarkers)
# Summary
print("\n" + "=" * 70)
print(f"RESULTS: {PASS}/{TOTAL} passed, {FAIL} failed")
print("=" * 70)
if FAIL > 0:
print("\nFailed tests:")
for r in RESULTS:
if r['status'] == 'FAIL':
print(f" - {r['name']}: {r['error']}")
print(f"\nOverall: {'ALL TESTS PASSED' if FAIL == 0 else f'{FAIL} TESTS FAILED'}")
return 0 if FAIL == 0 else 1
if __name__ == '__main__':
sys.exit(main())
Clinical Trial Matching - Tools Reference
Summary
This skill uses 40+ ToolUniverse tools across 10 categories to perform comprehensive clinical trial matching.
Tool Inventory
Clinical Trial Tools (13 total, 11 primary)
| Tool | Purpose | Key Parameters | Response |
|---|---|---|---|
search_clinical_trials | Main trial search | query_term (REQ), condition, intervention, pageSize | {studies, nextPageToken, total_count} |
ClinicalTrials_search_studies | Alternative search | action="search_studies" (REQ), condition, intervention, limit | {total_count, studies} |
ClinicalTrials_get_study | Full trial details | action="get_study_details" (REQ), nct_id (REQ) | Full study object |
get_clinical_trial_eligibility_criteria | Eligibility text | nct_ids (REQ array), eligibility_criteria (REQ) | [{NCT ID, eligibility_criteria}] |
get_clinical_trial_locations | Site locations | nct_ids (REQ array), location (REQ) | [{NCT ID, locations}] |
get_clinical_trial_descriptions | Title/summary | nct_ids (REQ array), description_type (REQ) | [{NCT ID, brief_title, ...}] |
get_clinical_trial_status_and_dates | Status/dates | nct_ids (REQ array), status_and_date (REQ) | [{NCT ID, overall_status, dates}] |
get_clinical_trial_conditions_and_interventions | Interventions | nct_ids (REQ array), condition_and_intervention (REQ) | [{NCT ID, condition, arm_groups, interventions}] |
get_clinical_trial_outcome_measures | Outcomes | nct_ids (REQ array), outcome_measures | [{NCT ID, outcomes}] |
extract_clinical_trial_outcomes | Result data | nct_ids (REQ array) | Outcome result data |
extract_clinical_trial_adverse_events | AE data | nct_ids (REQ array) | Adverse event data |
OpenTargets Tools (10 primary)
| Tool | Purpose | Key Parameters |
|---|---|---|
OpenTargets_get_disease_id_description_by_name | Resolve disease | diseaseName |
OpenTargets_get_target_id_description_by_name | Resolve target | targetName |
OpenTargets_get_drug_id_description_by_name | Resolve drug | drugName |
OpenTargets_get_drug_mechanisms_of_action_by_chemblId | Drug MoA | chemblId |
OpenTargets_get_associated_drugs_by_target_ensemblID | Drugs for target | ensemblId, size |
OpenTargets_get_associated_drugs_by_disease_efoId | Drugs for disease | efoId, size |
OpenTargets_get_approved_indications_by_drug_chemblId | Approved indications | chemblId |
OpenTargets_get_drug_approval_status_by_chemblId | Approval status | chemblId |
OpenTargets_target_disease_evidence | Target-disease evidence | ensemblId, efoId, size |
OpenTargets_get_drug_description_by_chemblId | Drug description | chemblId |
CIViC Tools (6 primary)
| Tool | Purpose | Key Parameters |
|---|---|---|
civic_get_variants_by_gene | Gene variants | gene_id (CIViC int), limit |
civic_get_variant | Variant details | variant_id |
civic_search_evidence_items | Evidence search | query, limit |
civic_search_therapies | Therapy search | query, limit |
civic_search_diseases | Disease search | query, limit |
civic_get_molecular_profile | Molecular profile | molecular_profile_id |
FDA Tools (5 primary)
| Tool | Purpose | Key Parameters |
|---|---|---|
fda_pharmacogenomic_biomarkers | Biomarker-drug list | (none) |
FDA_get_indications_by_drug_name | Drug indications | drug_name, limit |
FDA_get_mechanism_of_action_by_drug_name | Drug MoA | drug_name, limit |
FDA_get_clinical_studies_info_by_drug_name | Clinical studies | drug_name, limit |
FDA_get_adverse_reactions_by_drug_name | Drug safety | drug_name, limit |
Gene/Disease Resolution Tools (4 primary)
| Tool | Purpose | Key Parameters |
|---|---|---|
MyGene_query_genes | Gene ID resolution | query, species |
ols_search_efo_terms | Disease ontology | query, limit |
ols_get_efo_term | Term details | term_id |
ols_get_efo_term_children | Sub-diseases | term_id |
Drug Information Tools (4 primary)
| Tool | Purpose | Key Parameters |
|---|---|---|
drugbank_get_targets_by_drug_name_or_drugbank_id | Drug targets | query, case_sensitive, exact_match, limit (ALL REQ) |
drugbank_get_indications_by_drug_name_or_drugbank_id | Drug indications | query, case_sensitive, exact_match, limit (ALL REQ) |
ChEMBL_search_drugs | Drug search | query, limit |
ChEMBL_get_drug_mechanisms | Drug mechanisms | drug_chembl_id__exact |
Literature Tools (2 primary)
| Tool | Purpose | Key Parameters |
|---|---|---|
PubMed_search_articles | Literature search | query, max_results |
openalex_literature_search | Literature search | query, limit |
PharmGKB Tools (2 primary)
| Tool | Purpose | Key Parameters |
|---|---|---|
PharmGKB_search_genes | Gene pharmacogenomics | query |
PharmGKB_get_clinical_annotations | Clinical annotations | query |
Critical Parameter Notes
1. DrugBank tools: ALL 4 parameters (query, case_sensitive, exact_match, limit) are REQUIRED 2. `search_clinical_trials`: query_term is REQUIRED even for disease-only searches 3. `ClinicalTrials_search_studies`: action must be exactly "search_studies" 4. `ClinicalTrials_get_study`: action must be exactly "get_study_details" 5. CIViC `civic_search_variants`: Does NOT filter by query - returns alphabetically 6. CIViC `civic_get_variants_by_gene`: Takes CIViC gene ID (integer), NOT gene symbol 7. OpenTargets drug lookup: Use drugName (NOT genericName) 8. Batch clinical trial tools: Accept arrays of NCT IDs 9. `fda_pharmacogenomic_biomarkers`: Takes no parameters
Response Structure Cheat Sheet
search_clinical_trials:
{studies: [{NCT ID, brief_title, brief_summary, overall_status, condition: [], phase: []}], nextPageToken, total_count}
ClinicalTrials_search_studies:
{total_count, studies: [{nctId, title, status, conditions: []}]}
get_clinical_trial_eligibility_criteria:
[{NCT ID, eligibility_criteria: "Inclusion Criteria:\n...\nExclusion Criteria:\n..."}]
get_clinical_trial_locations:
[{NCT ID, locations: [{facility, city, state, country}]}]
get_clinical_trial_conditions_and_interventions:
[{NCT ID, condition: [], arm_groups: [{label, type, description, interventionNames}], interventions: [{type, name, description}]}]
get_clinical_trial_status_and_dates:
[{NCT ID, overall_status, start_date, primary_completion_date, completion_date}]
OpenTargets_get_drug_mechanisms_of_action_by_chemblId:
{data: {drug: {id, name, mechanismsOfAction: {rows: [{mechanismOfAction, actionType, targetName, targets: [{id, approvedSymbol}]}]}}}}
OpenTargets_get_associated_drugs_by_target_ensemblID:
{data: {target: {id, approvedSymbol, knownDrugs: {count, rows: [{drug: {id, name, isApproved}, phase, mechanismOfAction, disease: {id, name}}]}}}}
fda_pharmacogenomic_biomarkers:
{count, shown, results: [{Drug, TherapeuticArea, Biomarker, LabelingSection}]}
MyGene_query_genes:
{hits: [{symbol, entrezgene, ensembl: {gene}, name}]}
PubMed_search_articles:
[{pmid, title, abstract, authors, journal, pub_date}]Trial Search Patterns & Execution Strategy
Trial Search Functions
Disease-Based Search
def search_trials_by_disease(tu, disease_name, status_filter=None, phase_filter=None, page_size=20):
"""Search ClinicalTrials.gov by disease/condition."""
query_parts = []
if status_filter:
query_parts.append(f'AREA[OverallStatus]{status_filter}')
if phase_filter:
query_parts.append(phase_filter)
query_term = ' AND '.join(query_parts) if query_parts else disease_name
result = tu.tools.search_clinical_trials(
condition=disease_name,
query_term=query_term if query_parts else disease_name,
pageSize=page_size
)
if isinstance(result, str):
return []
return result.get('studies', [])Biomarker-Specific Search
def search_trials_by_biomarker(tu, gene_symbol, alteration, disease_name=None, page_size=15):
"""Search trials mentioning specific biomarkers."""
biomarker_query = f'{gene_symbol} {alteration}' if alteration else gene_symbol
result = tu.tools.search_clinical_trials(
condition=disease_name if disease_name else '',
query_term=biomarker_query,
pageSize=page_size
)
if isinstance(result, str):
return []
return result.get('studies', [])Intervention-Based Search
def search_trials_by_intervention(tu, drug_name, disease_name=None, page_size=10):
"""Search trials by intervention/drug name."""
result = tu.tools.search_clinical_trials(
condition=disease_name if disease_name else '',
intervention=drug_name,
query_term=drug_name,
pageSize=page_size
)
if isinstance(result, str):
return []
return result.get('studies', [])Alternative Search (ClinicalTrials_search_studies)
def search_trials_alternative(tu, condition, intervention=None, limit=10):
"""Alternative trial search with different API endpoint."""
params = {
'action': 'search_studies',
'condition': condition,
'limit': limit
}
if intervention:
params['intervention'] = intervention
result = tu.tools.ClinicalTrials_search_studies(**params)
return result.get('studies', [])Deduplication
def deduplicate_trials(trial_lists):
"""Merge and deduplicate trials from multiple searches."""
seen_ncts = set()
unique_trials = []
for trials in trial_lists:
for trial in trials:
nct = trial.get('NCT ID') or trial.get('nctId', '')
if nct and nct not in seen_ncts:
seen_ncts.add(nct)
unique_trials.append(trial)
return unique_trialsBatch Trial Detail Retrieval (Phase 3)
All batch functions process NCT IDs in groups of 10:
def get_trial_eligibility(tu, nct_ids):
all_criteria = []
for i in range(0, len(nct_ids), 10):
batch = nct_ids[i:i+10]
result = tu.tools.get_clinical_trial_eligibility_criteria(nct_ids=batch, eligibility_criteria='all')
if isinstance(result, list):
all_criteria.extend(result)
return all_criteria
def get_trial_interventions(tu, nct_ids):
all_interventions = []
for i in range(0, len(nct_ids), 10):
batch = nct_ids[i:i+10]
result = tu.tools.get_clinical_trial_conditions_and_interventions(nct_ids=batch, condition_and_intervention='all')
if isinstance(result, list):
all_interventions.extend(result)
return all_interventions
def get_trial_locations(tu, nct_ids):
all_locations = []
for i in range(0, len(nct_ids), 10):
batch = nct_ids[i:i+10]
result = tu.tools.get_clinical_trial_locations(nct_ids=batch, location='all')
if isinstance(result, list):
all_locations.extend(result)
return all_locations
def get_trial_status(tu, nct_ids):
all_status = []
for i in range(0, len(nct_ids), 10):
batch = nct_ids[i:i+10]
result = tu.tools.get_clinical_trial_status_and_dates(nct_ids=batch, status_and_date='all')
if isinstance(result, list):
all_status.extend(result)
return all_status
def get_trial_descriptions(tu, nct_ids):
all_descriptions = []
for i in range(0, len(nct_ids), 10):
batch = nct_ids[i:i+10]
result = tu.tools.get_clinical_trial_descriptions(nct_ids=batch, description_type='full')
if isinstance(result, list):
all_descriptions.extend(result)
return all_descriptionsGeographic & Feasibility Analysis (Phase 7)
def analyze_trial_locations(locations_data, patient_location=None):
"""Analyze trial site locations and proximity."""
if not locations_data:
return {'total_sites': 0, 'countries': [], 'us_states': [], 'nearest': None}
locations = locations_data.get('locations', [])
countries = list(set(loc.get('country', '') for loc in locations if loc.get('country')))
us_states = list(set(loc.get('state', '') for loc in locations if loc.get('country') == 'United States' and loc.get('state')))
return {
'total_sites': len(locations),
'countries': countries,
'us_states': us_states,
'has_us_sites': 'United States' in countries,
'locations': locations[:10]
}Alternative Options (Phase 8)
Basket Trial Search
IMPORTANT: ClinicalTrials.gov search is sensitive to query complexity. Overly specific queries like "NTRK fusion tumor agnostic" may return zero results. Use simpler queries and combine results.
def search_basket_trials(tu, biomarker, page_size=10):
query_terms = [
f'{biomarker} solid tumor',
f'{biomarker}',
f'{biomarker} basket',
]
all_trials = []
for query in query_terms:
result = tu.tools.search_clinical_trials(query_term=query, pageSize=page_size)
if not isinstance(result, str):
all_trials.extend(result.get('studies', []))
return deduplicate_trials([all_trials])Expanded Access Search
def search_expanded_access(tu, drug_name):
result = tu.tools.search_clinical_trials(query_term=f'{drug_name} expanded access', pageSize=5)
if isinstance(result, str):
return []
return result.get('studies', [])Parallelization Opportunities
Parallel Group 1 (Phase 1 - all simultaneous):
MyGene_query_genesfor each geneOpenTargets_get_disease_id_description_by_namefor diseaseols_search_efo_termsfor diseasefda_pharmacogenomic_biomarkers(no params)
Parallel Group 2 (Phase 2 - all simultaneous):
search_clinical_trialswith disease conditionsearch_clinical_trialswith biomarker querysearch_clinical_trialswith intervention queryClinicalTrials_search_studiesas alternative
Parallel Group 3 (Phase 3 - all simultaneous):
get_clinical_trial_eligibility_criteriafor all NCT IDsget_clinical_trial_conditions_and_interventionsfor all NCT IDsget_clinical_trial_locationsfor all NCT IDsget_clinical_trial_status_and_datesfor all NCT IDsget_clinical_trial_descriptionsfor all NCT IDs
Parallel Group 4 (Phases 5-6 - for each drug):
OpenTargets_get_drug_id_description_by_namefor drugOpenTargets_get_drug_mechanisms_of_action_by_chemblIdfor drugFDA_get_indications_by_drug_namefor drugPubMed_search_articlesfor evidence
Performance Optimization
- Batch NCT IDs in groups of 10 for detail tools
- Limit initial search to 20-30 trials per search strategy
- Focus detailed analysis on top 15-20 candidates after initial filtering
- Cache gene/disease resolution results for reuse across phases
Error Handling
For each tool call: 1. Wrap in try/except 2. Check for empty results 3. Use fallback tools when primary fails 4. Document what failed in completeness checklist 5. Never let one failure block the entire analysis
Common Use Patterns
Pattern 1: Targeted Therapy Matching (Most Common)
Input: "NSCLC patient with EGFR L858R, failed platinum chemotherapy" 1. Resolve: NSCLC -> EFO_0003060, EGFR -> ENSG00000146648 2. Search: "non-small cell lung cancer" + "EGFR mutation" + "EGFR L858R" 3. Filter: Recruiting trials with EGFR molecular requirements 4. Match: Score trials by EGFR L858R specificity 5. Drugs: Identify TKIs (osimertinib, erlotinib, etc.) in trial arms 6. Evidence: Check FDA approval of EGFR TKIs for NSCLC 7. Report: Prioritize targeted therapy trials, include immunotherapy options
Pattern 2: Immunotherapy Selection
Input: "Melanoma, TMB-high, PD-L1 positive, failed ipilimumab" 1. Resolve: Melanoma -> EFO_0000756 2. Search: "melanoma" + "TMB" + "PD-L1" + "immunotherapy" 3. Filter: Trials requiring PD-L1 or TMB testing 4. Match: Score by TMB/PD-L1 requirements 5. Drugs: Identify checkpoint inhibitors (pembrolizumab, nivolumab) 6. Evidence: Check FDA approval for TMB-high indications 7. Report: Focus on anti-PD-1/PD-L1 trials, combination immunotherapy
Pattern 3: Basket Trial Identification
Input: "Any solid tumor with NTRK fusion" 1. Resolve: NTRK genes (NTRK1, NTRK2, NTRK3) 2. Search: "NTRK fusion" + "tumor agnostic" + "basket" 3. Filter: Biomarker-agnostic trials 4. Match: Score by NTRK-specific inclusion criteria 5. Drugs: Identify larotrectinib, entrectinib 6. Evidence: FDA tissue-agnostic approval for larotrectinib 7. Report: Highlight tumor-agnostic approval, broad eligibility
Pattern 4: Post-Progression Options
Input: "Breast cancer, failed CDK4/6 inhibitors, ESR1 mutation" 1. Resolve: Breast cancer -> EFO_0000305, ESR1 -> ENSG00000091831 2. Search: "breast cancer" + "ESR1" + "CDK4/6 resistance" 3. Filter: Trials for post-CDK4/6 setting 4. Match: Score by ESR1 mutation and prior treatment requirements 5. Drugs: Identify novel endocrine agents, SERDs, ESR1-targeting drugs 6. Evidence: Check clinical data for post-CDK4/6 options 7. Report: Focus on resistance-overcoming strategies
Pattern 5: Geographic Search
Input: "Lung cancer trials within 100 miles of Boston" 1. Search: "lung cancer" (broad) 2. Get locations for all candidate trials 3. Filter: Sites in Massachusetts and nearby states 4. Score: High geographic feasibility for Boston-area sites 5. Report: Prioritize by proximity, include contact info
Edge Case Handling
No Matching Trials Found
1. Broaden search to gene-level (remove specific variant) 2. Search for pathway-level trials 3. Search basket trials 4. Suggest additional biomarker testing 5. Report alternative options (off-label, compassionate use)
Rare Biomarkers
1. Search gene-level trials (any EGFR mutation) 2. Search mechanism-level trials (TKI trials) 3. Check CIViC for any evidence on this specific variant 4. Note variant rarity in report 5. Suggest discussion with molecular tumor board
Multiple Biomarkers
1. Search for each biomarker independently 2. Search for combination biomarker trials 3. Identify trials that require multiple biomarkers 4. Score based on most actionable biomarker 5. Flag potential synergistic drug targets
Conflicting Eligibility
1. Score partial match transparently 2. Highlight which criteria are met/unmet 3. Note if unmet criteria are waivable 4. Suggest contacting PI for edge cases 5. Provide alternative trials without conflicting criteria
Related skills
How it compares
Use tooluniverse-clinical-trial-matching when structured eligibility scoring against registries matters more than generic biomedical literature summarization.
FAQ
What does tooluniverse-clinical-trial-matching evaluate?
tooluniverse-clinical-trial-matching compares indications, inclusion-exclusion criteria, and study profiles against registered clinical trials. Outputs support feasibility checks, recruitment targeting, and portfolio analysis inside ToolUniverse agent workflows.
Which environment runs tooluniverse-clinical-trial-matching?
tooluniverse-clinical-trial-matching runs as a ToolUniverse skill inside Claude Code. Agents call bundled clinical-trial tools to query registry data rather than performing manual trial searches.