Search ClinicalTrials.gov: find studies, retrieve results, match patients to candidate trials.
One address for any MCP client. No sign-in needed on this server.
7 read
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Search ClinicalTrials.gov trials, retrieve study details and results, and match patients to candidate trials via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://clinicaltrials.caseyjhand.com/mcp
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.
| Tool | Description |
|---|---|
clinicaltrials_search_studies | Search studies with full-text and field-specific queries, status/phase/geographic filters, pagination, sorting, and field selection |
clinicaltrials_get_study_record | Fetch a single study by NCT ID — full protocol record with optional location/outcome/reference caps |
clinicaltrials_get_study_count | Fast total study count for a query, without fetching data |
clinicaltrials_get_field_values | Discover valid values for API fields, with per-value study counts |
clinicaltrials_get_field_definitions | Resolve valid field names — keyword search, path drill-down, or top-level overview |
clinicaltrials_get_study_results | Fetch posted results — outcomes, adverse events, participant flow, baseline — for completed studies |
clinicaltrials_find_eligible | Match patient demographics and conditions to candidate recruiting trials, without checking inclusion/exclusion criteria |
| Resource | Description |
|---|---|
clinicaltrials://{nctId} | Fetch a single clinical study by NCT ID as JSON, with capped lists and results replaced by counts |
| Prompt | Description |
|---|---|
analyze_trial_landscape | Guides a data-driven clinical trial landscape analysis using the count and search tools |
clinicaltrials_search_studies toolquery plus field-specific conditionQuery / interventionQuery / locationQuery / sponsorQuery / titleQuery / outcomeQuery, statusFilter / phaseFilter enums (case and spacing variants accepted), advancedFilter (AREA[FieldName]value / RANGE[min, max]), and geoFilter (distance(lat,lon,radius) with a mi/km suffix); pageSize 1–1000 (default 10; above the server's per-page cap, 200 by default, a page is served at the cap with truncated / shown / cap), pageToken cursor, sort on up to 2 fieldsnctId, briefTitle, overallStatus, phases, enrollmentCount, hasResults, …); fields (PascalCase leaves) selects a full-fidelity projection — full records run ~70KBblank_value, ids_not_found, field_invalid, enum_invalid, query_parse_error, geo_invalid, sort_invalid, page_token_invalid, rate_limitedclinicaltrials_get_study_record toolnctId, with optional locationLimit, outcomeLimit / referenceLimit, and nearLocation (lat, lon, radiusMi) to bound and sort locations — none has an upper bound; a value past the list returns all of itfiltersApplied reports upstream totals when a cap trims a list; resultsSection is replaced by resultsSummary counts. Typed errors: study_not_found, rate_limitedclinicaltrials_get_study_count toolclinicaltrials_search_studies, returning only totalCount — no study data fetchedblank_value, field_invalid, enum_invalid, query_parse_error, rate_limitedclinicaltrials_get_field_values toolfields (e.g. OverallStatus, Phase) — returns each field's type and top values with study counts (capped at 250 by the API), or min / max / avg for numeric and date fields and trueCount / falseCount for booleanslongest value; past 2,000 characters it is a preview flagged truncated, with the full length and a notice naming the clinicaltrials_search_studies call that returns the whole text. Free-text fields the API does not count distinct values on carry uniqueValuesCounted: false, marking their uniqueValuesCount of 0 as no countmultiValued flags fields whose per-value counts can sum above the study total. Typed errors: blank_value, field_invalid, rate_limitedclinicaltrials_get_field_definitions toolmode: search (keyword query, limit default 20, no upper bound), drill (dot-notation path), or overview — resolves the PascalCase names accepted by fields, advancedFilter, sort, and clinicaltrials_get_field_valuesblank_value, mode_mismatch, mode_requires, path_not_found, rate_limitedclinicaltrials_get_study_results toolnctIds fetched per call, with any further IDs (up to 500 entries) returned in deferredNctIds; sections (outcomes, adverseEvents, participantFlow, baseline, moreInfo), summary to condense a result set that can exceed 500KB per study, and outcomeLimit / adverseEventLimit with resumable outcomeOffset / seriousEventOffset / otherEventOffsetfetchErrors / studiesWithoutResults instead of a whole-batch failure, and canonicalNctId when an alias ID resolves to another study. Typed errors: blank_value, offset_not_applicable, rate_limitedclinicaltrials_find_eligible toolage (whole number 0–120) with optional ageUnit (years default, months, weeks, days) for infants and young children, sex (FEMALE / MALE / ALL), conditions[], location (country required, state / city optional), healthyVolunteer, recruitingOnly (default true), maxResults (up to 1000; each call returns at most 50, disclosing the rest via truncated / shown / cap and a replayable searchCriteria), locationLimit (no upper bound; a value past the matched sites returns them all)funnel reports match counts per filter stage, and a notice says inclusion/exclusion criteria were not checked (use clinicaltrials_get_study_record to review them)blank_value, rate_limitedclinicaltrials://{nctId} resourceapplication/json, with locations, secondary/other outcomes, and references each capped at 50 — fixed server-side, no argumentsresultsSummary counts; truncated and filtersApplied disclose what was capped, with retrieval naming the tools that fetch the full datastudy_not_found, rate_limitedanalyze_trial_landscape prompttopic required; focusAreas (comma-separated) optionalBuilt 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:
fields/sort (case, whitespace, legacy aliases like RecruitmentStatus → OverallStatus) and logs each correction99999999) from searches and counts by default; includeUnknownEnrollment brings it backAgent-friendly output:
searchCriteria; clinicaltrials_get_study_results names canonicalNctId when an alias ID resolves to another studyclinicaltrials_get_study_results returns per-study fetchErrors / studiesWithoutResults instead of failing the whole batchreason codes (study_not_found, blank_value, …), and bounded lists carry a next*Offset only when more remainsA 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"
}
}
}
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/mcp
git clone https://github.com/cyanheads/clinicaltrialsgov-mcp-server.git
cd clinicaltrialsgov-mcp-server
bun install
All configuration is optional — the server works with defaults and no API keys.
| Variable | Description | Default |
|---|---|---|
CT_API_BASE_URL | ClinicalTrials.gov API base URL. | https://clinicaltrials.gov/api/v2 |
CT_REQUEST_TIMEOUT_MS | Per-request timeout in milliseconds; a positive integer. | 30000 |
CT_MAX_PAGE_SIZE | Most studies one clinicaltrials_search_studies page returns; an integer ≥ 1, values above 1000 clamp to 1000. | 200 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
OTEL_ENABLED | Enable OpenTelemetry tracing. | false |
OTEL_EXPORTER_OTLP_ENDPOINT | OTLP base URL for traces and metrics. | — |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | OTLP endpoint for log export. | — |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted. | false |
See .env.example for the full list of optional overrides.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:http
# or
bun run start:stdio
Run 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 build -t clinicaltrialsgov-mcp-server .
docker run --rm -p 3010:3010 clinicaltrialsgov-mcp-server
The 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.
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools/resources/prompts and inits the ClinicalTrials.gov service. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). |
src/mcp-server/resources | Resource definitions (*.resource.ts). |
src/mcp-server/prompts | Prompt definitions (*.prompt.ts). |
src/services/clinical-trials | ClinicalTrials.gov REST API v2 client — retry, rate limiting, field search, types. |
tests/ | Unit and integration tests. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, no console callssrc/mcp-server/*/definitions/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.