ClinicalTrials.gov MCP Server — OncoHub

ClinicalTrials.gov MCP 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 tools.

Category
访问服务器

README

ClinicalTrials.gov MCP Server — OncoHub

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 + asyncio give 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)

  1. Push this repo to GitHub.
  2. Go to render.comNewWeb Service → connect your repo.
  3. Render auto-detects render.yaml — click Apply.
  4. Wait ~3 min for the first build.
  5. Your URL: https://ctgov-mcp-oncohub.onrender.com (or your custom name).
  6. Free tier caveat: the service sleeps after 15 min of inactivity (first request takes ~30s to wake). The SQLite cache resets on restart since /tmp is 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)

  1. Go to claude.ai → [Your name] → Settings → Connectors.
  2. Click "+""Add custom connector".
  3. Name: ClinicalTrials.gov (OncoHub)
  4. URL: https://<your-host>/mcp
  5. Auth: leave OAuth fields blank (this server has no auth).
  6. Click Add.

Steps (Team / Enterprise)

  1. An Owner goes to Organization Settings → Connectors → Add connector (same fields as above).
  2. Members connect it via [+] → Connectors in any conversation.

Activating in the Tumor Council Project

  1. Open the OncoHub project.
  2. In a conversation, click "+" (bottom-left) → Connectors.
  3. Toggle ClinicalTrials.gov (OncoHub) ON.
  4. 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 acting
  • retrieved_at → when data was fetched from CT.gov (always surface this)
  • freshness: "cached" → data served from local cache; cached_at shows 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_at timestamp
  • 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: pageSize capped at 50 per call
  • User-Agent header 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.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选