clinicaltrialsgov-mcp-server
Search ClinicalTrials.gov studies, retrieve records/results, explore field metadata, and match patients to candidate trials via MCP.
Search 600K+ studies with full-text and field-specific queries, status/phase/geography/advanced filters, sorting, pagination, and field selection.
Fetch a full study record by NCT ID, with optional caps for locations, outcomes, and references, plus near-location sorting.
Count matching studies quickly without fetching study data.
Discover valid field values and resolve field names/definitions via search, drill-down, or overview modes.
Fetch posted results for completed studies: outcomes, adverse events, participant flow, baseline characteristics, and results metadata, with summary/paging options.
Find candidate recruiting trials for a patient profile using age, sex, conditions, location, and healthy-volunteer status; inclusion/exclusion criteria are not checked.
Access a study as JSON through the
clinicaltrials://{nctId}resource.Use the
analyze_trial_landscapeprompt andclinicaltrialsgov-guideskill for guided landscape analysis and query-syntax help.Run over STDIO or Streamable HTTP, use a public hosted instance, and require no API key.
Provides the project repository on GitHub for access to source code and documentation.
Uses Hono as a high-performance HTTP server featuring session management, CORS, and IP-based rate limiting.
Supports returning clinical trial data in markdown format for improved readability in AI responses.
Requires Node.js (>=18.0.0) as the runtime environment for the MCP server.
Distributes the server via npm package management system, allowing for easy installation.
Uses Prettier for consistent code formatting during development.
Built with TypeScript for type safety and robust error handling when interacting with the ClinicalTrials.gov database.
Implements input validation and sanitization using Zod schema validation for secure API interactions.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@clinicaltrialsgov-mcp-server find Phase 3 diabetes studies currently recruiting"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Public Hosted Server: https://clinicaltrials.caseyjhand.com/mcp
Overview
Search 600K+ studies on ClinicalTrials.gov, the US National Library of Medicine's trial registry. Fetch full study records and posted results, discover fields and valid values, and match a patient's profile to recruiting trials. No API key required.
Tools
Tool | Description |
| Search studies with full-text and field-specific queries, status/phase/geographic filters, pagination, sorting, and field selection |
| Fetch a single study by NCT ID — full protocol record with optional location/outcome/reference caps |
| Fast total study count for a query, without fetching data |
| Discover valid values for API fields, with per-value study counts |
| Resolve valid field names — keyword search, path drill-down, or top-level overview |
| Fetch posted results — outcomes, adverse events, participant flow, baseline — for completed studies |
| Match patient demographics and conditions to candidate recruiting trials, without checking inclusion/exclusion criteria |
Resources
Resource | Description |
| Fetch a single clinical study by NCT ID as JSON, with capped lists and results replaced by counts |
Prompts
Prompt | Description |
| Guides a data-driven clinical trial landscape analysis using the count and search tools |
Skills
Skill | Description |
Gates and query syntax the tool descriptions don't carry: study versus site recruiting status, overlapping phase counts, |
Plugin installs load it from skills/; MCP clients that support the Skills extension read it as skill://clinicaltrialsgov-guide/SKILL.md. It also ships in the npm package and Docker image.
Related MCP server: ClinicalTrials.gov MCP Server
Capability reference
clinicaltrials_search_studies tool
Free-text
queryplus field-specificconditionQuery/interventionQuery/locationQuery/sponsorQuery/titleQuery/outcomeQuery,statusFilter/phaseFilterenums (case and spacing variants accepted),advancedFilter(AREA[FieldName]value/RANGE[min, max]), andgeoFilter(distance(lat,lon,radius)with ami/kmsuffix);pageSize(default 10, no upper bound; above the server's per-page cap, 200 by default, a page is served at the cap withtruncated/shown/cap),pageTokencursor,sorton up to 2 date or numeric fields (or@relevance)Each query and filter parameter also accepts its ClinicalTrials.gov REST name (
query.condorquery_cond,filter.overallStatus,filter.geo, …), andcondition,intervention,sponsor,phase,limit, andmax_resultswork as aliases tooReturns a compact per-study index by default (
nctId,briefTitle,overallStatus,phases,enrollmentCount,hasResults, …);fields(PascalCase piece names, or full paths asclinicaltrials_get_field_definitionsprints them) selects a full-fidelity projection — full records run ~70KBnctIds(up to 1000 entries, repeats collapsed) is served up to what fits one ClinicalTrials.gov request beside the other parameters — about 560 IDs on their own — with the rest returned unqueried indeferredNctIds, the same on every page of a walkTyped errors:
blank_value,ids_not_found,field_invalid,enum_invalid,query_parse_error,geo_invalid,sort_invalid,page_token_invalid,request_too_long,rate_limited,retry_deadline_exceeded,upstream_timeout
clinicaltrials_get_study_record tool
Full protocol record by
nctId, with optionallocationLimit,outcomeLimit/referenceLimit, andnearLocation(lat,lon,radiusMi) to bound and sort locations — none has an upper bound; a value past the list returns all of it, and0leaves that list outfiltersAppliedreports upstream totals when a cap trims a list;resultsSectionis replaced byresultsSummarycounts. Typed errors:study_not_found,rate_limited,retry_deadline_exceeded,upstream_timeout
clinicaltrials_get_study_count tool
Same query and filter surface as
clinicaltrials_search_studies, returning onlytotalCount— no study data fetchedTyped errors:
blank_value,field_invalid,enum_invalid,query_parse_error,request_too_long,rate_limited,retry_deadline_exceeded,upstream_timeout
clinicaltrials_get_field_values tool
One or more PascalCase leaf
fields(e.g.OverallStatus,Phase; a group such asLeadSponsoris refused with its leaf pieces named) — returns each field's type and top values with study counts (capped at 250 by the API), ormin/max/avgfor numeric and date fields andtrueCount/falseCountfor booleansSTRING fields add their
longestvalue; past 2,000 characters it is a preview flaggedtruncated, with the fulllengthand a notice naming theclinicaltrials_search_studiescall that returns the whole text. Free-text fields the API does not count distinct values on carryuniqueValuesCounted: false, marking theiruniqueValuesCountof 0 as no countmultiValuedflags fields whose per-value counts can sum above the study total. Typed errors:blank_value,field_invalid,request_too_long,rate_limited,retry_deadline_exceeded,upstream_timeout
clinicaltrials_get_field_definitions tool
mode:search(keywordquery,limitdefault 20, no upper bound),drill(dot-notationpath), oroverview— resolves the PascalCase names accepted byfields,advancedFilter,sort(date and numeric fields), andclinicaltrials_get_field_values(leaf fields)Typed errors:
blank_value,mode_mismatch,mode_requires,path_not_found,rate_limited,retry_deadline_exceeded,upstream_timeout
clinicaltrials_get_study_results tool
Up to 20 distinct
nctIdsfetched per call, with any further IDs (up to 500 entries) returned indeferredNctIds;sections(outcomes,adverseEvents,participantFlow,baseline,moreInfo),summaryto condense a result set that can exceed 500KB per study, andoutcomeLimit/adverseEventLimitwith resumableoutcomeOffset/seriousEventOffset/otherEventOffsetPer-study
fetchErrors/studiesWithoutResultsfor IDs that fail on their own, while a failed batch request, a rate limit, the call's time budget, or a cancellation ends the call;canonicalNctIdwhen an alias ID resolves to another study. Typed errors:blank_value,offset_not_applicable,rate_limited,retry_deadline_exceeded,upstream_timeout
clinicaltrials_find_eligible tool
age(whole number 0–120) with optionalageUnit(yearsdefault,months,weeks,days) for infants and young children,sex(FEMALE/MALE/ALL),conditions[],location(countryrequired,state/cityoptional; country synonyms such asUSA,UK, andCzech Republic, and US state abbreviations, rank sites as the registry's spelling does),healthyVolunteer,recruitingOnly(default true),maxResults(orlimit; no upper bound, each call returns at most 50, disclosing the rest viatruncated/shown/capand a replayablesearchCriteria),locationLimit(no upper bound; a value past the matched sites returns them all)Matches on age, sex, location, recruitment status, and a fuzzy condition search;
funnelreports match counts per filter stage, and anoticesays inclusion/exclusion criteria were not checked (useclinicaltrials_get_study_recordto review them)Typed errors:
blank_value,field_invalid,enum_invalid,query_parse_error,request_too_long,rate_limited,retry_deadline_exceeded,upstream_timeout
clinicaltrials://{nctId} resource
Full protocol record as
application/json, with locations, secondary/other outcomes, and references each capped at 50 — fixed server-side, no argumentsResults data is replaced by
resultsSummarycounts;truncatedandfiltersApplieddisclose what was capped, withretrievalnaming the tools that fetch the full dataTyped errors:
study_not_found,rate_limited,retry_deadline_exceeded,upstream_timeout
analyze_trial_landscape prompt
Arguments:
topicrequired;focusAreas(comma-separated) optionalReturns one user message pointing the agent at the count, search, field-discovery, and results tools for a data-driven landscape analysis
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
ClinicalTrials.gov-specific:
Type-safe client for the ClinicalTrials.gov REST API v2 — public, no authentication or API keys required
Serialized request queue enforcing ClinicalTrials.gov's ~1 req/sec rate limit, with retry and exponential backoff on 429/5xx responses and HTML error pages, all inside one 50-second budget per tool call or resource read — a hung upstream ends the call with a typed
retry_deadline_exceedederror before an MCP client's 60-second request timeoutAuto-corrects field names passed to
fields/sort(case, whitespace, legacy aliases likeRecruitmentStatus→OverallStatus, a full path such asprotocolSection.statusModule.overallStatus→OverallStatusinfields, a barerelevance→@relevanceinsort) and the case ofAREA[…]field names inadvancedFilterand the query parameters (AREA[overallstatus]→AREA[OverallStatus]), index-only pieces and search areas included (AREA[numlocations],AREA[conditionsearch];sorttakes the index-only counts such asNumLocationstoo), and logs each correctionGeographic proximity search and nearest-site re-ranking, with no geocoding dependency
Excludes the "unknown" enrollment sentinel (
99999999) from searches and counts by default;includeUnknownEnrollmentbrings it back
Agent-friendly output:
Provenance — search, count, and eligibility tools echo
searchCriteria;clinicaltrials_get_study_resultsnamescanonicalNctIdwhen an alias ID resolves to another studyGraceful partial failure —
clinicaltrials_get_study_resultsreturns per-studyfetchErrors/studiesWithoutResultsfor IDs that fail on their own instead of failing the whole batchTyped errors — each tool returns
reasoncodes (study_not_found,blank_value, …), and bounded lists carry anext*Offsetonly when more remainsResponse shaping — search and eligibility return a compact index by default instead of the ~70KB full record
Getting started
Public Hosted Instance
A public instance is available at https://clinicaltrials.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"clinicaltrialsgov-mcp-server": {
"type": "streamable-http",
"url": "https://clinicaltrials.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"clinicaltrialsgov-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["clinicaltrialsgov-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"clinicaltrialsgov-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "clinicaltrialsgov-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with Docker:
{
"mcpServers": {
"clinicaltrialsgov-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/clinicaltrialsgov-mcp-server:latest"]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.4 or higher (or Node.js v24+).
Installation
Clone the repository:
git clone https://github.com/cyanheads/clinicaltrialsgov-mcp-server.gitNavigate into the directory:
cd clinicaltrialsgov-mcp-serverInstall dependencies:
bun installConfiguration
All configuration is optional — the server works with defaults and no API keys.
Variable | Description | Default |
| ClinicalTrials.gov API base URL. |
|
| Timeout in milliseconds for each attempt at a ClinicalTrials.gov request; a positive integer. An attempt ends sooner when its tool call or resource read runs out of its 50-second budget, which covers every request, retry, and wait the call makes. |
|
| Most studies one |
|
| Transport: |
|
| Port for HTTP server. |
|
| HTTP session mode: |
|
| Auth mode: |
|
| Log level (RFC 5424). |
|
| Directory for log files (Node.js only). |
|
| Enable OpenTelemetry tracing. |
|
| OTLP base URL for traces and metrics. | — |
| OTLP endpoint for log export. | — |
| Log each failed tool call's arguments and result, redacted by key name and capped at |
|
See .env.example for the full list of optional overrides.
Running the server
Local development
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:http # or bun run start:stdioRun checks and tests:
bun run devcheck # Lint, format, typecheck, and security audit bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t clinicaltrialsgov-mcp-server .
docker run --rm -p 3010:3010 clinicaltrialsgov-mcp-serverThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/clinicaltrialsgov-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Directory | Purpose |
|
|
| Server-specific environment variable parsing and validation with Zod. |
| Tool definitions ( |
| Resource definitions ( |
| Prompt definitions ( |
| ClinicalTrials.gov REST API v2 client — retry, rate limiting, field search, types. |
| The published |
| Unit and integration tests. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped logging, noconsolecallsRegister new tools and resources via the barrels in
src/mcp-server/*/definitions/index.tsValidate raw API responses, normalize to domain types, and never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
Available Tools
7 toolsclinicaltrials_find_eligibleClinicaltrials Find EligibleARead-onlyIdempotentInspect
Find candidate clinical trials for a patient profile. Provide age, sex, conditions, and location to get studies with their demographic limits, contact information, and recruiting locations. Candidates are matched on age, sex, location, recruitment status, and ClinicalTrials.gov's fuzzy condition search only — inclusion/exclusion criteria are not checked, and a candidate can exclude the requested condition, so review each one's criteria with clinicaltrials_get_study_record before treating it as a match. Results are re-ranked so studies whose own condition matches a requested condition surface above tangential matches from ClinicalTrials.gov's fuzzy condition search. Each candidate returns only the sites matching the requested location (capped by locationLimit), not the study's full registered site list — a large trial can register hundreds of sites worldwide. When none of a candidate's matched sites is recruiting, one recruiting site is added so an enrollable site is never hidden behind a closer closed one: the one nearest the matched sites by their published coordinates, in the requested country whenever a site there recruits, carrying distanceMi, or the first in match order when coordinates are missing. Fetch a study's complete record with clinicaltrials_get_study_record.
Inputs: age (required), sex (required), location (required), conditions (required), +5 more. Returns: cap, error, shown, funnel, +5 more
clinicaltrials_get_field_definitionsClinicaltrials Get Field DefinitionsARead-onlyIdempotentInspect
Resolve valid field names from the ClinicalTrials.gov data model — the canonical PascalCase identifiers (OverallStatus, EnrollmentCount, LeadSponsorName) the other tools take. Every piece works in clinicaltrials_search_studies fields; a leaf piece (sourceType other than STRUCT) also works in AREA[…] expressions in advancedFilter and the query parameters, and as input to clinicaltrials_get_field_values (LocationGeoPoint aside); a leaf of a date or integer type (NormalizedDate, PartialDate, DateTimeMinutes, integer, long, short) also works in sort. Select a mode: "search" — keyword search returning ranked matches (pass query, e.g. "enrollment", "sponsor", "adverse events"); "drill" — drill into a specific section by dot-notation path (pass path, e.g. "protocolSection.designModule"); "overview" — top-level summary of all sections (no additional args).
Inputs: mode (required), path, limit, query, +1 more. Returns: cap, error, shown, fields, +6 more
clinicaltrials_get_field_valuesClinicaltrials Get Field ValuesARead-onlyIdempotentInspect
Discover valid values for ClinicalTrials.gov fields with study counts per value. Use to explore available filter options before building a search — e.g., valid OverallStatus, Phase, InterventionType, StudyType, or LeadSponsorClass values.
Inputs: fields (required). Returns: error, notice, fieldStats
clinicaltrials_get_study_countClinicaltrials Get Study CountARead-onlyIdempotentInspect
Get total clinical trial study count from ClinicalTrials.gov matching a query, without fetching study data. Fast and lightweight. Use for quick statistics or to build breakdowns by calling multiple times with different filters (e.g., count by phase, count by status, count recruiting vs completed for a condition).
Inputs: query, titleQuery, phaseFilter, outcomeQuery, +7 more. Returns: error, notice, totalCount, searchCriteria
clinicaltrials_get_study_recordClinicaltrials Get Study RecordARead-onlyIdempotentInspect
Fetch a single clinical trial study by NCT ID from ClinicalTrials.gov. Returns the full study record including protocol details, eligibility criteria, outcomes, arms, interventions, contacts, and locations. Optional locationLimit / outcomeLimit / referenceLimit / nearLocation parameters trim locations, outcomes, and references — original totals are preserved in filtersApplied only when a cap actually trims the set.
Inputs: nctId (required), nearLocation, outcomeLimit, locationLimit, +1 more. Returns: error, study, filtersApplied, resultsSummary
clinicaltrials_get_study_resultsClinicaltrials Get Study ResultsARead-onlyIdempotentInspect
Fetch clinical trial results data from ClinicalTrials.gov for completed studies — outcome measures with statistics, adverse events, participant flow, baseline characteristics, and results metadata (limitations & caveats, certain-agreement disclosure restrictions, results point of contact). Only available for studies where hasResults is true. Use clinicaltrials_search_studies first to find studies with results. Each call fetches up to 20 distinct NCT IDs and returns any further IDs in deferredNctIds for a follow-up call. A results-rich record can exceed 500KB per study in full mode — bound it with summary=true, narrower sections, or the outcomeLimit / adverseEventLimit caps; outcomeLimit / outcomeOffset also page summarized outcomes. A bounded list is resumable: outcomeOffset / seriousEventOffset / otherEventOffset start the next window, and each study's filtersApplied reports what was trimmed and the next offset for every list left short. A previous (alias) NCT ID resolves to its canonical study, named in canonicalNctId.
Inputs: nctIds (required), summary, sections, outcomeLimit, +4 more. Returns: error, results, truncated, fetchErrors, +2 more
clinicaltrials_search_studiesClinicaltrials Search StudiesARead-onlyIdempotentInspect
Search for clinical trial studies from ClinicalTrials.gov. Supports full-text and field-specific queries, status/phase/geographic filters, pagination, sorting, and field selection. Returns a compact per-study index by default; pass the fields parameter to get specific leaves at full fidelity — full study records are ~70KB each.
Inputs: sort, query, fields, nctIds, +14 more. Returns: cap, error, shown, notice, +8 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 7 tools
Each tool targets a distinct operation: aggregate counts, search index retrieval, full single-record fetch, results fetch, valid field values, valid field names, and patient eligibility matching. Descriptions explicitly delineate near-neighbors such as get_study_count vs search_studies and get_field_values vs get_field_definitions.
All names share the clinicaltrials_ prefix and follow a consistent snake_case verb_noun pattern (get_study_count, search_studies, get_study_record, find_eligible). Predictable and uniform throughout the set.
Seven tools is well-scoped for a read-only data API wrapper, with each tool earning its place by covering a distinct query mode. No redundant or filler tools are present.
The surface covers discovery, full records, results data, field metadata introspection, and patient matching, which is the appropriate scope for a read-only public dataset with no write operations. A minor gap is batch full-record fetching—get_study_record accepts only a single NCT ID—though the search index partially mitigates this.
Maintenance
Related MCP Connectors
Clinical trial search and status from ClinicalTrials.gov
Search ClinicalTrials.gov by condition, drug, location and status, with full trial details
Search and read ClinicalTrials.gov studies, sites and outcomes.
Provide structured access to ClinicalTrials.gov data for searching, retrieving, and analyzing clin…
Related MCP Servers
- AlicenseAqualityAmaintenanceProvides LLMs with structured access to critical biomedical databases including PubTator3 (PubMed/PMC), ClinicalTrials.gov, and MyVariant.info through the Model Context Protocol.4635765 PyPI674MIT
- FlicenseNot gradedqualityDmaintenanceEnables searching and retrieving information from the ClinicalTrials.gov database of over 400,000 clinical studies, including trial details, eligibility criteria, locations, and results across 220+ countries.5-
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to search and analyze clinical trial data from ClinicalTrials.gov using both structured SQL queries for filtering trials by status, phase, and conditions, and semantic vector search for exploring detailed protocol information like exclusion criteria.-
- AlicenseAqualityDmaintenanceEnables conversational access to 400,000+ clinical trials on ClinicalTrials.gov, allowing users to search, compare, and retrieve trial details using plain English through MCP-compatible clients like Cursor and Claude Desktop.53MIT