ClinicalTrials.Gov Server — OncoHub
БесплатноНе проверенA production-quality MCP server that wraps the ClinicalTrials.gov v2 API, providing structured, TTL-cached access to trial data worldwide and Turkey via 4 MCP t
Описание
A production-quality MCP server that wraps the ClinicalTrials.gov v2 API, providing structured, TTL-cached access to trial data worldwide and Turkey via 4 MCP tools.
README
A production-quality MCP (Model Context Protocol) server that wraps the ClinicalTrials.gov v2 API. Designed for the OncoHub Tumor Council as the data source for clinical_trial_matching_module v2 / A2.3.
What this server does: structured, TTL-cached access to CT.gov trial data (worldwide + Turkey) via 4 MCP tools. What it does not do: clinical eligibility assessment, scoring, or any form of clinical judgment — that is the consuming system's responsibility.
Why Python + FastMCP?
- CT.gov v2 is a plain JSON REST API — no special SDK needed
- FastMCP's HTTP transport satisfies claude.ai's remote connector requirement (STDIO won't work there)
httpx+asynciogive clean async retry/backoff- SQLite cache requires zero infrastructure beyond the process itself
Project Structure
CT-MSP/
├── server.py # FastMCP server + 4 MCP tools (entry point)
├── ctgov_client.py # Async httpx client, rate limiting, retry
├── normalize.py # Raw CT.gov JSON → flat OncoHub schema
├── cache.py # SQLite TTL cache (no external deps)
├── models.py # Pydantic models (schema documentation)
├── tests/
│ ├── conftest.py # Shared fixtures + sample API responses
│ ├── test_normalize.py
│ ├── test_client.py
│ ├── test_cache.py
│ ├── test_tools.py # MCP tool integration tests (mocked)
│ └── test_smoke.py # Live API smoke tests (opt-in)
├── Dockerfile
├── render.yaml # One-click Render deployment
├── pyproject.toml
└── .env.example
Local Setup
Requirements: Python 3.11+, pip
# 1. Clone / navigate to project directory
cd CT-MSP
# 2. Create virtual environment
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# 3. Install dependencies
pip install -e ".[dev]"
# 4. Configure environment
cp .env.example .env
# Edit .env if needed (defaults work for local development)
# 5. Run the server
python server.py
# Server starts on http://localhost:8000
# MCP endpoint: http://localhost:8000/mcp
Test with MCP Inspector
npx @modelcontextprotocol/inspector http://localhost:8000/mcp
This opens a browser UI where you can call tools interactively.
Run tests
pytest # all unit tests (no network)
pytest -m smoke # live CT.gov smoke tests (requires internet)
pytest tests/test_normalize.py # specific module
Public Deployment
Required for claude.ai connector — claude.ai only connects to public HTTPS endpoints.
Option A: Render (recommended — free tier, auto-HTTPS)
- Push this repo to GitHub.
- Go to render.com → New → Web Service → connect your repo.
- Render auto-detects
render.yaml— click Apply. - Wait ~3 min for the first build.
- Your URL:
https://ctgov-mcp-oncohub.onrender.com(or your custom name). - Free tier caveat: the service sleeps after 15 min of inactivity (first request takes ~30s to wake). The SQLite cache resets on restart since
/tmpis ephemeral. Upgrade to Starter ($7/mo) for a persistent disk (/data).
Persistent disk (Starter plan): uncomment the disk: section in render.yaml and set CACHE_PATH=/data/ctgov.db.
Option B: Railway
# Install Railway CLI
npm i -g @railway/cli
railway login
railway init
railway up
Set environment variables via railway variables set STATUS_TTL_DAYS=7 ...
Option C: Fly.io
fly launch # detects Dockerfile automatically
fly deploy
fly secrets set STATUS_TTL_DAYS=7 META_TTL_DAYS=30
Add a volume for persistent cache:
fly volumes create ctgov_cache --size 1
Then set CACHE_PATH=/data/ctgov.db in fly.toml.
Option D: Any Docker host
docker build -t ctgov-mcp .
docker run -p 8000:8000 \
-e STATUS_TTL_DAYS=7 \
-v ctgov_cache:/data \
-e CACHE_PATH=/data/ctgov.db \
ctgov-mcp
Adding to claude.ai as a Custom Connector
Prerequisites:
- Server running at a public HTTPS URL (e.g.
https://ctgov-mcp-oncohub.onrender.com)- claude.ai Pro, Team, or Enterprise plan
- OncoHub Project must be private
Steps (Individual / Pro)
- Go to claude.ai → [Your name] → Settings → Connectors.
- Click "+" → "Add custom connector".
- Name:
ClinicalTrials.gov (OncoHub) - URL:
https://<your-host>/mcp - Auth: leave OAuth fields blank (this server has no auth).
- Click Add.
Steps (Team / Enterprise)
- An Owner goes to Organization Settings → Connectors → Add connector (same fields as above).
- Members connect it via [+] → Connectors in any conversation.
Activating in the Tumor Council Project
- Open the OncoHub project.
- In a conversation, click "+" (bottom-left) → Connectors.
- Toggle ClinicalTrials.gov (OncoHub) ON.
- The system prompt should reference tool names (e.g.
dual_source_search) for the module to call them automatically during report generation.
Note:
claude_desktop_config.json(STDIO transport) does NOT work with claude.ai Projects — only remote HTTP/SSE connectors work there.
MCP Tools Reference
dual_source_search ← Primary council entry point
Searches worldwide AND Turkey in two sequential calls, merges by NCT ID, marks has_turkey_site.
| Parameter | Type | Default | Description |
|---|---|---|---|
condition |
str | required | Disease/condition (maps from A1 tumor field) |
term |
str? | null | Biomarker / keyword (maps from A1 biomarker field) |
intervention |
str? | null | Drug or target (maps from A1 treatment field) |
statuses |
list[str]? | RECRUITING, NOT_YET_RECRUITING | Overall status filter |
phases |
list[str]? | null | Phase filter (maps from A1 treatment line) |
page_size |
int | 20 | Results per sub-query (max 50) |
force_refresh |
bool | false | Skip cache |
Returns: {studies: [...], total_count, turkey_count, next_page_token, note}
search_trials
Parameterized worldwide search.
| Parameter | Type | Default | Description |
|---|---|---|---|
condition |
str | required | Disease/condition |
term |
str? | null | Free-text biomarker or keyword |
intervention |
str? | null | Drug or target |
statuses |
list[str]? | RECRUITING, NOT_YET_RECRUITING | See valid values below |
phases |
list[str]? | null | See valid values below |
country |
str? | null | Country name for location filter |
page_size |
int | 20 | Max 50 recommended |
page_token |
str? | null | Pagination cursor |
force_refresh |
bool | false | Skip cache |
Valid statuses: RECRUITING, NOT_YET_RECRUITING, ACTIVE_NOT_RECRUITING, COMPLETED, TERMINATED, WITHDRAWN, SUSPENDED, ENROLLING_BY_INVITATION, UNKNOWN
Valid phases: EARLY_PHASE1, PHASE1, PHASE2, PHASE3, PHASE4, NA
search_turkey_trials
Convenience wrapper: search_trials with country="Turkey". Same parameters (minus country).
get_trial
Fetch full details for a single study.
| Parameter | Type | Description |
|---|---|---|
nct_id |
str | NCT number, e.g. "NCT05678901" |
force_refresh |
bool | Skip cache |
Returns: single normalized study with full eligibility.criteria_text and all locations.
Normalized Study Schema
Every tool returns studies in this flat schema:
{
"nct_id": "NCT05678901",
"title": "A Study of Sotorasib...",
"status": "RECRUITING",
"status_unknown_flag": false,
"phases": ["PHASE2"],
"study_type": "INTERVENTIONAL",
"conditions": ["Non-Small Cell Lung Cancer"],
"interventions": [{"type": "DRUG", "name": "Sotorasib"}],
"eligibility": {
"criteria_text": "Inclusion Criteria:\n- KRAS G12C...",
"sex": "ALL",
"min_age": "18 Years",
"max_age": "N/A",
"healthy_volunteers": false
},
"locations": [
{"country": "Turkey", "city": "Istanbul", "facility": "IUH", "status": "RECRUITING"}
],
"has_turkey_site": true,
"turkey_sites": [{"city": "Istanbul", "facility": "IUH", "status": "RECRUITING"}],
"lead_sponsor": "Amgen",
"last_update_post_date": "2024-05-01",
"url": "https://clinicaltrials.gov/study/NCT05678901",
"retrieved_at": "2026-06-24T10:30:00+00:00",
"freshness": "live",
"cached_at": null
}
Key fields for clinicians:
status_unknown_flag: true→ status unverified for 2+ years, verify before actingretrieved_at→ when data was fetched from CT.gov (always surface this)freshness: "cached"→ data served from local cache;cached_atshows when it was stored
OncoHub Integration: A1 → A2.3 Field Mapping
| A1 Patient Profile Field | dual_source_search Parameter |
Notes |
|---|---|---|
| Tumor type / primary diagnosis | condition |
Required |
| Biomarker (e.g. KRAS G12C, PD-L1) | term |
Free text, forwarded to query.term |
| Current/target drug | intervention |
Forwarded to query.intr |
| Treatment line / phase preference | phases |
e.g. ["PHASE2","PHASE3"] |
| Eligibility status filter | statuses |
Default: RECRUITING + NOT_YET_RECRUITING |
Module flow:
A1 Patient Card
↓
A2.3 dual_source_search(condition, term, intervention, phases)
↓
Normalized studies[] with has_turkey_site, freshness, retrieved_at
↓
A3 Eligibility Matrix
↓
A5 Council Report Output
The server provides data only. A3 performs eligibility matching; A5 formats the output.
Cache & Freshness Configuration
| Variable | Default | Description |
|---|---|---|
STATUS_TTL_DAYS |
7 | Max age for cached recruiting/status data |
META_TTL_DAYS |
30 | Reserved for future eligibility-only caching |
CACHE_PATH |
.cache/ctgov.db |
SQLite file path |
Why TTL matters for patient safety: Trial status changes frequently. A "RECRUITING" status cached 30 days ago may no longer be accurate. STATUS_TTL_DAYS=7 ensures clinicians see data verified within one week, or the server fetches fresh data.
Cache behavior:
- Hit (fresh): returns stored data with
freshness: "cached"+cached_attimestamp - Miss or expired: fetches from CT.gov, updates cache, returns with
freshness: "live" force_refresh: true: always fetches live, updates cache
Cloud deployments: On free-tier hosts with ephemeral storage, the cache resets on each restart — all requests go live until the cache warms up again. Use a persistent volume for production.
Rate Limiting & CT.gov ToU Compliance
- Minimum 1 second between API requests (
CTGOV_RATE_INTERVAL=1.0) - Automatic exponential-backoff retry on network/5xx errors (max 3 attempts)
- No contact-harvesting: email, phone, and investigator PII are never included in output
- No bulk corpus download:
pageSizecapped at 50 per call User-Agentheader identifies this client to CT.gov
No Authentication Required
CT.gov v2 is a public API — no API key, no OAuth. This server also has no auth on its /mcp endpoint (it serves read-only public data). For production deployments where you want to restrict access, place a reverse proxy with IP allowlisting or a simple bearer token in front.
Установка ClinicalTrials.Gov Server — OncoHub
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/baris-varis/Initial-CT-MSP-MCP-serverFAQ
ClinicalTrials.Gov Server — OncoHub MCP бесплатный?
Да, ClinicalTrials.Gov Server — OncoHub MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для ClinicalTrials.Gov Server — OncoHub?
Нет, ClinicalTrials.Gov Server — OncoHub работает без API-ключей и переменных окружения.
ClinicalTrials.Gov Server — OncoHub — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить ClinicalTrials.Gov Server — OncoHub в Claude Desktop, Claude Code или Cursor?
Открой ClinicalTrials.Gov Server — OncoHub на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare ClinicalTrials.Gov Server — OncoHub with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
