clinical-trial-matching

SkillDev tools

ToolUniverse workflow — Clinical Trial Matching

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the clinical-trial-matching skill

What this skill tells your AI

The instructions your AI receives, as published by lamm-mit/scienceclaw in skills/clinical-trial-matching/SKILL.md and read by ahel’s review.


name: tooluniverse-clinical-trial-matching description: AI-driven patient-to-trial matching for precision medicine and oncology. Given a patient profile (disease, molecular alterations, stage, prior treatments), discovers and ranks clinical trials from ClinicalTrials.gov using multi-dimensional matching across molecular eligibility, clinical criteria, drug-biomarker alignment, evidence strength, and geographic feasibility. Produces a quantitative Trial Match Score (0-100) per trial with tiered recommendations and a comprehensive markdown report. Use when oncologists, molecular tumor boards, or patients ask about clinical trial options for specific cancer types, biomarker profiles, or post-progression scenarios.

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
  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

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 (e.g., "ECOG 0-1")
  • Geographic location: City/state for proximity filtering (e.g., "Boston, MA")
  • Trial phase preference: I, II, III, IV, or "any"
  • Intervention type: drug, biological, device, etc.
  • Recruiting status preference: recruiting, not yet recruiting, active

Biomarker Parsing Rules

Input FormatParsed AsExample
Gene + amino acid changeSpecific mutationEGFR L858R
Gene + exon notationExon-level alterationEGFR exon 19 deletion
Gene + fusion partnerFusionEML4-ALK fusion
Gene + amplificationCopy number gainHER2 amplification
Gene + expression levelExpression biomarkerPD-L1 50%
Gene + statusStatus biomarkerMSI-high, TMB-high
Gene + resistanceResistance mutationEGFR T790M

Gene Symbol Normalization

Common AliasOfficial SymbolNotes
HER2ERBB2Search both in trials
PD-L1CD274Often searched as "PD-L1" in trials
ALKALKEML4-ALK is a fusion
VEGFVEGFAOften searched as "VEGF"
PD-1PDCD1Search as "PD-1" in trials
BRCABRCA1/BRCA2Specify which BRCA gene

Phase 0: Tool Parameter Reference (CRITICAL)

BEFORE calling ANY tool, verify its parameters from this reference table.

Clinical Trial Tools

ToolParametersNotes
search_clinical_trialsquery_term (REQUIRED str), condition (str), intervention (str), pageSize (int, default 10), pageToken (str)Main search. Returns {studies: [{NCT ID, brief_title, brief_summary, overall_status, condition, phase}], nextPageToken, total_count}
clinical_trials_searchaction (REQUIRED, must be "search_studies"), condition (str), intervention (str), limit (int)Alternative search. Returns {total_count, studies: [{nctId, title, status, conditions}]}
clinical_trials_get_detailsaction (REQUIRED, must be "get_study_details"), nct_id (REQUIRED str)Full trial details. Returns {nctId, title, summary, eligibility: {eligibilityCriteria}, ...}
get_clinical_trial_eligibility_criterianct_ids (REQUIRED array), eligibility_criteria (REQUIRED str, use "all")Returns [{NCT ID, eligibility_criteria}]
get_clinical_trial_locationsnct_ids (REQUIRED array), location (REQUIRED str, use "all")Returns [{NCT ID, locations: [{facility, city, state, country}]}]
get_clinical_trial_descriptionsnct_ids (REQUIRED array), description_type (REQUIRED str: "brief" or "full")Returns [{NCT ID, brief_title, official_title, brief_summary, detailed_description}]
get_clinical_trial_status_and_datesnct_ids (REQUIRED array), status_and_date (REQUIRED str, use "all")Returns [{NCT ID, overall_status, start_date, primary_completion_date, completion_date}]
get_clinical_trial_conditions_and_interventionsnct_ids (REQUIRED array), condition_and_intervention (REQUIRED str, use "all")Returns [{NCT ID, condition, arm_groups, interventions}]
get_clinical_trial_outcome_measuresnct_ids (REQUIRED array), outcome_measures (str: "primary", "secondary", "all")Returns [{NCT ID, primary_outcomes, secondary_outcomes}]
extract_clinical_trial_outcomesnct_ids (REQUIRED array), outcome_measure (str)Returns trial outcome results
extract_clinical_trial_adverse_eventsnct_ids (REQUIRED array), adverse_event_type (str)Returns adverse event data

Molecular/Disease Tools

ToolParametersNotes
MyGene_query_genesquery (str), species (str)Returns {hits: [{symbol, entrezgene, ensembl: {gene}, name}]}
OpenTargets_get_target_id_description_by_nametargetName (str)Returns {data: {search: {hits: [{id, name, description}]}}}
OpenTargets_get_disease_id_description_by_namediseaseName (str)Returns {data: {search: {hits: [{id, name, description}]}}}
OpenTargets_get_associated_drugs_by_target_ensemblIDensemblId (str), size (int)Returns {data: {target: {knownDrugs: {count, rows: [{drug: {id, name, isApproved}, phase, mechanismOfAction, disease: {id, name}}]}}}}
OpenTargets_get_associated_drugs_by_disease_efoIdefoId (str), size (int)Returns {data: {disease: {knownDrugs: {count, rows: [...]}}}}
OpenTargets_get_drug_id_description_by_namedrugName (str)Returns {data: {search: {hits: [{id, name, description}]}}}
OpenTargets_get_drug_mechanisms_of_action_by_chemblIdchemblId (str)Returns {data: {drug: {mechanismsOfAction: {rows: [{mechanismOfAction, actionType, targetName, targets}]}}}}
OpenTargets_get_approved_indications_by_drug_chemblIdchemblId (str)Returns {data: {drug: {approvedIndications: [efoIds]}}}
OpenTargets_target_disease_evidenceensemblId (str), efoId (str), size (int)Returns target-disease evidence rows

CIViC Tools

ToolParametersNotes
civic_search_variantsquery (str), limit (int)Does NOT filter by query. Returns alphabetically sorted variants
civic_get_variants_by_genegene_id (int, CIViC gene ID), limit (int)Returns {data: {gene: {variants: {nodes: [{id, name}]}}}}. Max 100 per call
civic_search_evidence_itemsquery (str), limit (int)Does NOT filter by query. Returns evidence alphabetically
civic_get_variantvariant_id (int)Returns {data: {variant: {id, name}}}
civic_search_therapiesquery (str), limit (int)Search therapies
civic_search_diseasesquery (str), limit (int)Search diseases

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

Drug Information Tools

ToolParametersNotes
drugbank_get_targets_by_drug_name_or_drugbank_idquery, case_sensitive, exact_match, limit (ALL REQUIRED)Returns {results: [{drug_name, drugbank_id, targets: [{name, organism, actions}]}]}
drugbank_get_indications_by_drug_name_or_drugbank_idquery, case_sensitive, exact_match, limit (ALL REQUIRED)Returns drug indications
ChEMBL_search_drugsquery (str), limit (int)Returns {status, data: {drugs: [...]}}
ChEMBL_get_drug_mechanismsdrug_chembl_id__exact (str)Returns drug mechanisms
fda_pharmacogenomic_biomarkersdrug_name (opt str), biomarker (opt str), limit (opt int, default 10)Returns {count, shown, results: [{Drug, TherapeuticArea, Biomarker, LabelingSection}]}. Use limit=1000 to get all.
FDA_get_indications_by_drug_namedrug_name (str), limit (int)Returns FDA indications text
FDA_get_mechanism_of_action_by_drug_namedrug_name (str), limit (int)Returns FDA MoA text
FDA_get_clinical_studies_info_by_drug_namedrug_name (str), limit (int)Returns FDA clinical study info
FDA_get_adverse_reactions_by_drug_namedrug_name (str), limit (int)Returns adverse reactions

Disease Ontology Tools

ToolParametersNotes
ols_search_efo_termsquery (str), limit (int)Returns {data: {terms: [{iri, obo_id, short_form, label, description}]}}
ols_get_efo_termterm_id (str)Get specific EFO term details
ols_get_efo_term_childrenterm_id (str)Get child terms

Literature Tools

ToolParametersNotes
PubMed_search_articlesquery (str), max_results (int)Returns list of {pmid, title, abstract, authors, journal, pub_date}
openalex_literature_searchquery (str), limit (int)Returns literature results

PharmGKB Tools

ToolParametersNotes
PharmGKB_search_genesquery (str)Returns gene pharmacogenomics data
PharmGKB_get_clinical_annotationsquery (str)Returns clinical annotations

Workflow Overview

Input: Patient profile (disease + biomarkers + stage + prior treatments)

Phase 1: Patient Profile Standardization
  - Resolve disease to EFO/ontology IDs
  - Parse molecular alterations to gene + variant
  - Resolve gene symbols to Ensembl/Entrez IDs
  - 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)
  - Collect NCT IDs for detailed analysis

Phase 3: Trial Characterization
  - Get eligibility criteria for top candidate trials
  - Get conditions and interventions
  - Get locations and status
  - Get trial descriptions and phase information

Phase 4: Molecular Eligibility Matching
  - Parse eligibility criteria text for biomarker requirements
  - Match patient's molecular profile to trial requirements
  - Score molecular eligibility

Phase 5: Drug-Biomarker Alignment
  - Identify trial intervention drugs
  - Check drug mechanisms against patient biomarkers (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 for similar patients (PubMed)
  - CIViC clinical evidence
  - PharmGKB pharmacogenomics
  - Drug safety profiles

Phase 7: Geographic & Feasibility Analysis
  - Trial site locations
  - Enrollment status and dates
  - Distance from patient location (if provided)

Phase 8: Alternative Options
  - Basket trials (biomarker-driven, tumor-agnostic)
  - Expanded access and compassionate use
  - Related trials with different study designs

Phase 9: Scoring & Ranking
  - Calculate Trial Match Score (0-100) for each trial
  - Tier classification (Optimal/Good/Possible/Exploratory)
  - Rank by composite score
  - Generate recommendations

Phase 10: Report Synthesis
  - Executive summary (top 3 trials)
  - Patient profile summary
  - Ranked trial list with detailed analysis
  - Alternative options
  - Evidence grading
  - Completeness checklist

Phase 1: Patient Profile Standardization

Goal: Resolve all patient inputs to standardized identifiers for cross-database queries.

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}

Response: {efo_id: "EFO_0003060", name: "non-small cell lung carcinoma", description: "...", original_input: "..."}

1.2 Gene/Biomarker Resolution

def resolve_gene(tu, gene_symbol):
    """Resolve gene symbol to cross-database IDs."""
    # Normalize common aliases
    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)

    # MyGene resolution
    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

Classify each biomarker using FDA pharmacogenomic biomarkers list:

def classify_biomarker_actionability(tu, gene_symbol, alteration):
    """Classify biomarker as FDA-approved, guideline, or investigational."""
    # Check FDA pharmacogenomic biomarkers
    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]
        }

    # Check OpenTargets for drugs targeting this gene
    # (done in Phase 5)

    return {'level': 'investigational', 'drugs': [], 'labeling_sections': []}

1.4 Parse Molecular Alterations

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: treat as gene name
    return {'gene': biomarker_text.split()[0], 'alteration': ' '.join(biomarker_text.split()[1:]), 'type': 'unknown'}

Phase 2: Broad Trial Discovery

Goal: Cast a wide net to find all potentially relevant clinical trials.

2.1 Disease-Based Trial 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
    )

    # Response: {studies: [{NCT ID, brief_title, brief_summary, overall_status, condition, phase}], nextPageToken, total_count}
    if isinstance(result, str):
        return []  # No studies found

    return result.get('studies', [])

2.2 Biomarker-Specific Trial Search

def search_trials_by_biomarker(tu, gene_symbol, alteration, disease_name=None, page_size=15):
    """Search trials mentioning specific biomarkers."""
    # Search 1: Gene + alteration
    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', [])

2.3 Intervention-Based Trial 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', [])

2.4 Alternative Search (clinical_trials_search)

Use as a complement to the main search:

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.clinical_trials_search(**params)

    return result.get('studies', [])

2.5 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_trials

Phase 3: Trial Characterization

Goal: Get detailed information for the top candidate trials.

3.1 Get Eligibility Criteria (Batch)

def get_trial_eligibility(tu, nct_ids):
    """Get eligibility criteria for multiple trials."""
    # Process in batches of 10
    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
    # Returns: [{NCT ID, eligibility_criteria: "Inclusion Criteria:\n...\nExclusion Criteria:\n..."}]

3.2 Get Conditions and Interventions (Batch)

def get_trial_interventions(tu, nct_ids):
    """Get conditions, arm groups, and interventions for multiple trials."""
    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
    # Returns: [{NCT ID, condition, arm_groups: [{label, type, description, interventionNames}], interventions: [{type, name, description}]}]

3.3 Get Locations (Batch)

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
242
Forks
42
Last commit
Aug 2026
Advanced
Catalog kind
skill
Gateway key
clinical-trial-matching
Source
github.com/lamm-mit/scienceclaw