search_isrctn
Find UK and European clinical trials from academic institutions and research centers not listed on ClinicalTrials.gov. Search by condition or terms like 'pediatric epilepsy'.
Instructions
Search the ISRCTN registry for UK and European clinical trials. Read-only operation. No authentication required. Complements ClinicalTrials.gov by covering trials at UK academic institutions and European research centers not listed on ClinicalTrials.gov. Returns up to 10 results per call, filtered for relevance. Returns 'No ISRCTN trials found.' if no results match. Use for: UK/European trials, academic institution studies, international coverage beyond ClinicalTrials.gov.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Condition or search terms e.g. 'pediatric epilepsy', 'type 2 diabetes' | |
| max_results | No | Number of trials to return, between 1 and 10 |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes | Formatted list of trials with ISRCTN ID, title, phase, status, sponsor, condition, outcomes, countries, and eligibility criteria. |
Implementation Reference
- aria_mcp_server/server.py:146-195 (handler)MCP tool decorator and handler function for 'search_isrctn'. Decorates the function with @mcp.tool, defines description, output_schema, and the function itself which delegates to the core implementation in tools.py.
@mcp.tool( description=( "Search the ISRCTN registry for UK and European clinical trials. " "Read-only operation. No authentication required. " "Complements ClinicalTrials.gov by covering trials at UK academic institutions " "and European research centers not listed on ClinicalTrials.gov. " "Returns up to 10 results per call, filtered for relevance. " "Returns 'No ISRCTN trials found.' if no results match. " "Use for: UK/European trials, academic institution studies, " "international coverage beyond ClinicalTrials.gov." ), output_schema={ "type": "object", "properties": { "result": { "type": "string", "description": "Formatted list of trials with ISRCTN ID, title, phase, status, sponsor, condition, outcomes, countries, and eligibility criteria." } }, "required": ["result"] } ) def search_isrctn( query: Annotated[str, "Condition or search terms e.g. 'pediatric epilepsy', 'type 2 diabetes'"], max_results: Annotated[int, "Number of trials to return, between 1 and 10"] = 5, ) -> str: """ Search the ISRCTN registry for UK and European clinical trials. Use for: UK/European trials, academic institution studies, and international coverage beyond ClinicalTrials.gov. Args: query: Condition or search terms (e.g. "pediatric epilepsy") max_results: Number of trials to return (1-10, default 5) Returns: Formatted string with ISRCTN ID, title, phase, status, sponsor, condition, primary/secondary outcomes, countries, age range, and eligibility criteria. Returns a "no results" message if nothing is found. Notes: - Results are filtered for relevance — query terms must appear in title or condition - Requires no API key; uses ISRCTN public WHO-format API - Covers UK, European, and some international academic institution trials """ from aria_mcp_server.tools import search_isrctn as _search, format_isrctn_for_claude as _fmt max_results = max(1, min(max_results, 10)) trials = _search(query=query, max_results=max_results) return _fmt(trials) - aria_mcp_server/tools.py:392-416 (handler)Core implementation of search_isrctn. Calls ISRCTN WHO-format API, parses XML response, filters results by relevance using _is_relevant_isrctn, and parses each trial using _parse_isrctn_trial.
def search_isrctn(query: str, max_results: int = 5) -> list[dict]: """Search ISRCTN registry for UK/European clinical trials. No API key required.""" query = (query or "").strip() if not query: return [] max_results = max(1, min(max_results, 20)) try: r = requests.get( ISRCTN_BASE, params={"q": query, "limit": max_results}, timeout=15, ) r.raise_for_status() data = xmltodict.parse(r.content) except Exception as e: raise RuntimeError(f"ISRCTN search failed: {e}") from e trials_el = (data.get("trials") or {}).get("trial") if not trials_el: return [] if isinstance(trials_el, dict): trials_el = [trials_el] relevant = [tr for tr in trials_el if _is_relevant_isrctn(tr, query)] return [t for t in (_parse_isrctn_trial(tr) for tr in relevant) if t] - aria_mcp_server/tools.py:345-389 (helper)_parse_isrctn_trial: Parses a raw ISRCTN trial XML dict into a structured dict with trial_id, title, status, phase, sponsor, condition, outcomes, countries, eligibility, age range, and URL.
def _parse_isrctn_trial(trial: dict) -> dict | None: try: main = trial.get("main") or {} criteria = trial.get("criteria") or {} countries_el = trial.get("countries") or {} trial_id = _get_text(main.get("trial_id")) if not trial_id: return None # Handle countries — could be string or list country_raw = countries_el.get("country2") if isinstance(country_raw, list): countries = [c for c in country_raw if c] elif country_raw: countries = [country_raw] else: countries = [] inclusion = _get_text(criteria.get("inclusion_criteria")) exclusion = _get_text(criteria.get("exclusion_criteria")) eligibility = "" if inclusion: eligibility += f"Inclusion: {inclusion[:300]}..." if exclusion: eligibility += f"\nExclusion: {exclusion[:300]}..." return { "trial_id": trial_id, "title": _get_text(main.get("public_title")), "status": _get_text(main.get("recruitment_status")), "phase": _get_text(main.get("phase")), "sponsor": _get_text(main.get("primary_sponsor")), "condition": _get_text(main.get("hc_freetext")), "primary_outcome": _get_text(trial.get("primary_outcome", {}).get("prim_outcome"))[:300] + "..." if _get_text(trial.get("primary_outcome", {}).get("prim_outcome")) else "", "secondary_outcomes": _get_text(trial.get("secondary_outcome", {}).get("sec_outcome"))[:300] + "..." if _get_text(trial.get("secondary_outcome", {}).get("sec_outcome")) else "", "countries": countries, "min_age": _get_text(criteria.get("agemin")), "max_age": _get_text(criteria.get("agemax")), "gender": _get_text(criteria.get("gender")), "eligibility_criteria": eligibility, "url": _get_text(main.get("url")), } except (KeyError, TypeError, AttributeError): return None - aria_mcp_server/tools.py:332-343 (helper)_is_relevant_isrctn: Filters ISRCTN results by checking if query terms (words > 3 chars) appear in the trial's public_title or hc_freetext fields.
def _is_relevant_isrctn(trial: dict, query: str) -> bool: """Check if query terms appear in title or condition — not full document.""" query_words = set(query.lower().split()) main = trial.get("main") or {} searchable = " ".join([ _get_text(main.get("public_title")), _get_text(main.get("hc_freetext")), ]).lower() significant_words = [w for w in query_words if len(w) > 3] if not significant_words: return True return any(word in searchable for word in significant_words) - aria_mcp_server/tools.py:419-443 (helper)format_isrctn_for_claude: Formats parsed ISRCTN trial results into a readable text string for display.
def format_isrctn_for_claude(trials: list[dict]) -> str: """Format ISRCTN results as readable text.""" if not trials: return "No ISRCTN trials found matching those criteria." lines = [] for i, t in enumerate(trials, 1): countries_str = "; ".join(t.get("countries") or []) or "N/A" lines.append("\n".join([ f"[ISRCTN Trial {i}]", f"ISRCTN ID: {t.get('trial_id') or 'N/A'}", f"Title: {t.get('title') or 'N/A'}", f"Status: {t.get('status') or 'N/A'}", f"Phase: {t.get('phase') or 'N/A'}", f"Condition: {t.get('condition') or 'N/A'}", f"Primary Outcome: {t.get('primary_outcome') or 'N/A'}", f"Secondary Outcomes: {t.get('secondary_outcomes') or 'N/A'}", f"Sponsor: {t.get('sponsor') or 'N/A'}", f"Countries: {countries_str}", f"Age Range: {t.get('min_age') or 'N/A'} – {t.get('max_age') or 'N/A'}", f"Gender: {t.get('gender') or 'N/A'}", f"Eligibility: {t.get('eligibility_criteria') or 'N/A'}", f"URL: {t.get('url') or 'N/A'}", "", ])) return "\n".join(lines).strip()