mcpField
C
clinicaltrials-database-search

clinicaltrials-database-search

github.com/jaechang-hitsデータ・データベース38473 回インストール6 日前に更新
リポジトリ

clinicaltrials-database-search について

clinicaltrials-database-searchは、ClinicalTrials.gov API v2で世界の臨床試験データを検索・取得するスキルです。研究者や臨床チーム、レビュー作業向けに、条件検索、試験詳細の取得、CSV出力を提供します。

主な機能

  1. 01疾患、介入、地域による検索
  2. 02スポンサー、状態、フェーズによる絞り込み
  3. 03NCT IDによる試験詳細の取得
  4. 04大量の検索結果のページング
  5. 05試験データのCSV出力

活用シーン

  1. 01疾患に関する募集中試験の検索
  2. 02特定薬剤の臨床研究の特定
  3. 03地域内で紹介可能な試験の調査
  4. 04システマティックレビュー用データの集約

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill clinicaltrials-database-search

インストール後、Claude Code で/clinicaltrials-database-searchを入力して読み込まれたか確認します。

詳細

ClinicalTrials.gov データベース — 臨床試験の検索

概要

ClinicalTrials.gov API v2(公開、認証不要)に問い合わせ、世界中の臨床試験データを検索・取得します。疾患、介入、場所、スポンサー、ステータスでの検索、NCT ID による詳細な試験情報の取得、大規模な検索結果のページネーション、CSV へのエクスポートに対応しています。

使用する場面

  • 特定の疾患や病気を対象に、参加者を募集している臨床試験を検索する
  • 特定の薬剤、医療機器、介入を試験する臨床試験を探す
  • 患者紹介のため、特定の地域で実施されている臨床試験を探す
  • スポンサーや機関が実施する臨床試験のポートフォリオを把握する
  • 特定の試験の詳細な適格基準、評価項目、連絡先を取得する
  • 治療領域全体の臨床試験の傾向(フェーズ、登録数、期間)を分析する
  • システマティックレビューやメタアナリシス用に試験データをエクスポートする
  • 試験のステータス変更や結果の掲載状況を監視する
  • 化合物の生物活性データには代わりに chembl-database-bioactivity を、公開済み文献には pubmed-database を使用してください

前提条件

uv pip install requests pandas

API の詳細:

  • ベース URL:https://clinicaltrials.gov/api/v2
  • 認証:不要(公開 API)
  • レート制限:IP あたり約 50 リクエスト/分
  • レスポンス形式:JSON(デフォルト)、CSV
  • 最大ページサイズ:1 リクエストあたり 1000 件の試験
  • 日付形式:ISO 8601。テキストフィールドでは CommonMark Markdown を使用

クイックスタート

import requests
import time

CT_API = "https://clinicaltrials.gov/api/v2"

def ct_search(params):
    """Reusable helper for ClinicalTrials.gov searches."""
    response = requests.get(f"{CT_API}/studies", params=params, timeout=30)
    response.raise_for_status()
    return response.json()

# Search for recruiting breast cancer trials
results = ct_search({
    "query.cond": "breast cancer",
    "filter.overallStatus": "RECRUITING",
    "pageSize": 10,
    "sort": "LastUpdatePostDate:desc"
})
print(f"Found {results['totalCount']} trials")
for study in results['studies'][:3]:
    nct = study['protocolSection']['identificationModule']['nctId']
    title = study['protocolSection']['identificationModule']['briefTitle']
    print(f"  {nct}: {title}")

主な概念

レスポンスデータの構造

ClinicalTrials.gov は、深くネストされた JSON を返します。主なデータへのアクセスパスは次のとおりです。

データパス
NCT IDstudy['protocolSection']['identificationModule']['nctId']
タイトルstudy['protocolSection']['identificationModule']['briefTitle']
ステータスstudy['protocolSection']['statusModule']['overallStatus']
フェーズstudy['protocolSection']['designModule']['phases']
登録数study['protocolSection']['designModule']['enrollmentInfo']['count']
適格基準study['protocolSection']['eligibilityModule']
実施施設study['protocolSection']['contactsLocationsModule']['locations']
介入study['protocolSection']['armsInterventionsModule']['interventions']
結果study.get('resultsSection')(結果が掲載されていない場合は None)
試験ステータスの値
ステータス説明
RECRUITING現在、参加者を募集しています
NOT_YET_RECRUITING承認済みですが、まだ募集を開始していません
ENROLLING_BY_INVITATION招待者のみを登録しています
ACTIVE_NOT_RECRUITING試験は進行中ですが、登録は終了しています
SUSPENDED一時的に中断しています
TERMINATED予定より早く中止されました
COMPLETED試験が終了しました
WITHDRAWN登録開始前に取り下げられました
試験フェーズの値
フェーズ説明
EARLY_PHASE1初期フェーズ 1(旧フェーズ 0)
PHASE1フェーズ 1 — 安全性と用量
PHASE2フェーズ 2 — 有効性と副作用
PHASE3フェーズ 3 — 大規模な有効性評価
PHASE4フェーズ 4 — 市販後調査
NA該当なし(医薬品以外の試験)
クエリパラメーターのリファレンス
パラメーター型説明例
query.condstring疾患・病気lung cancer
query.intrstring介入・薬剤Pembrolizumab
query.locnstring地理的な場所New York
query.sponsstringスポンサー名National Cancer Institute
query.termstring一般的な全文検索immunotherapy
filter.overallStatusstringステータスフィルター(カンマ区切り)RECRUITING,COMPLETED
filter.phasestringフェーズフィルターPHASE2,PHASE3
filter.idsstringNCT ID フィルターNCT04852770
sortstring並べ替え順LastUpdatePostDate:desc
pageSizeint1ページあたりの結果数(最大 1000)100
pageTokenstringページネーショントークン(前回のレスポンスから取得)
formatstringレスポンス形式json または csv

並べ替えオプション:LastUpdatePostDate、EnrollmentCount、StartDate、StudyFirstPostDate。それぞれに :asc または :desc を指定できます。

コア API

1. 疾患で検索
results = ct_search({
    "query.cond": "type 2 diabetes",
    "filter.overallStatus": "RECRUITING",
    "pageSize": 20,
    "sort": "LastUpdatePostDate:desc"
})
print(f"Found {results['totalCount']} recruiting diabetes trials")
for study in results['studies'][:5]:
    proto = study['protocolSection']
    nct = proto['identificationModule']['nctId']
    title = proto['identificationModule']['briefTitle']
    print(f"  {nct}: {title}")
2. 介入・薬剤で検索
# Find Phase 3 trials testing Pembrolizumab
results = ct_search({
    "query.intr": "Pembrolizumab",
    "filter.overallStatus": "RECRUITING,ACTIVE_NOT_RECRUITING",
    "filter.phase": "PHASE3",
    "pageSize": 50
})
print(f"Phase 3 Pembrolizumab trials: {results['totalCount']}")
3. 場所で検索
results = ct_search({
    "query.cond": "cancer",
    "query.locn": "New York",
    "filter.overallStatus": "RECRUITING",
    "pageSize": 20
})

# Extract location details
for study in results['studies'][:3]:
    locs = study['protocolSection'].get('contactsLocationsModule', {}).get('locations', [])
    for loc in locs:
        if 'New York' in loc.get('city', ''):
            print(f"  {loc.get('facility')}: {loc['city']}, {loc.get('state', '')}")
4. スポンサーで検索
results = ct_search({
    "query.spons": "National Cancer Institute",
    "pageSize": 20
})

for study in results['studies'][:5]:
    sponsor_mod = study['protocolSection']['sponsorCollaboratorsModule']
    lead = sponsor_mod['leadSponsor']['name']
    collabs = [c['name'] for c in sponsor_mod.get('collaborators', [])]
    print(f"  Lead: {lead}, Collaborators: {collabs}")
5. NCT ID で試験の詳細を取得
nct_id = "NCT04852770"
response = requests.get(f"{CT_API}/studies/{nct_id}", timeout=30)
response.raise_for_status()
study = response.json()

# Extract key information
proto = study['protocolSection']
print(f"Title: {proto['identificationModule']['briefTitle']}")
print(f"Status: {proto['statusModule']['overallStatus']}")

# Eligibility criteria
elig = proto.get('eligibilityModule', {})
print(f"Ages: {elig.get('minimumAge')} - {elig.get('maximumAge')}")
print(f"Sex: {elig.get('sex')}")
print(f"Criteria:\n{elig.get('eligibilityCriteria', 'N/A')[:300]}")
6. 大規模な結果セットのページネーション
all_studies = []
page_token = None
max_pages = 10

for page in range(max_pages):
    params = {
        "query.cond": "cancer",
        "filter.overallStatus": "RECRUITING",
        "pageSize": 1000,
    }
    if page_token:
        params["pageToken"] = page_token

    results = ct_search(params)
    all_studies.extend(results['studies'])
    page_token = results.get('nextPageToken')

    if not page_token:
        break
    time.sleep(1.5)  # respect rate limits

print(f"Retrieved {len(all_studies)} studies across {page + 1} pages")
7. CSV へのエクスポート
response = requests.get(f"{CT_API}/studies", params={
    "query.cond": "heart disease",
    "filter.overallStatus": "RECRUITING",
    "format": "csv",
    "pageSize": 1000
}, timeout=60)

with open("heart_disease_trials.csv", "w") as f:
    f.write(response.text)
print("Exported to heart_disease_trials.csv")

よくあるワークフロー

ワークフロー 1:複数条件による試験の検索
import requests, time

CT_API = "https://clinicaltrials.gov/api/v2"

def ct_search(params):
    response = requests.get(f"{CT_API}/studies", params=params, timeout=30)
    response.raise_for_status()
    return response.json()

# Step 1: Search with multiple filters
results = ct_search({
    "query.cond": "lung cancer",
    "query.intr": "immunotherapy",
    "query.locn": "California",
    "filter.overallStatus": "RECRUITING,NOT_YET_RECRUITING",
    "pageSize": 100,
    "sort": "LastUpdatePostDate:desc"
})
print(f"Total matches: {results['totalCount']}")

# Step 2: Filter by phase
phase23 = [
    s for s in results['studies']
    if any(p in ['PHASE2', 'PHASE3']
           for p in s['protocolSection'].get('designModule', {}).get('phases', []))
]
print(f"Phase 2/3 trials: {len(phase23)}")

# Step 3: Extract summaries
for study in phase23[:5]:
    proto = study['protocolSection']
    nct = proto['identificationModule']['nctId']
    title = proto['identificationModule']['briefTitle']
    enrollment = proto.get('designModule', {}).get('enrollmentInfo', {}).get('count', 'N/A')
    print(f"  {nct}: {title} (n={enrollment})")
ワークフロー 2:完了した試験の結果分析
# Step 1: Find completed trials with posted results
results = ct_search({
    "query.cond": "alzheimer disease",
    "filter.overallStatus": "COMPLETED",
    "pageSize": 100,
    "sort": "LastUpdatePostDate:desc"
})

with_results = [s for s in results['studies'] if s.get('hasResults', False)]
print(f"Completed with results: {len(with_results)} / {len(results['studies'])}")

# Step 2: Get detailed results for top trial
if with_results:
    nct = with_results[0]['protocolSection']['identificationModule']['nctId']
    detail = requests.get(f"{CT_API}/studies/{nct}", timeout=30).json()

    if 'resultsSection' in detail:
        outcomes = detail['resultsSection'].get('outcomeMeasuresModule', {})
        measures = outcomes.get('outcomeMeasures', [])
        for m in measures[:3]:
            print(f"  Outcome: {m.get('title')}")
            print(f"  Type: {m.get('type')}")
ワークフロー 3:スポンサーのポートフォリオ比較
sponsors = ["Pfizer", "Novartis", "Roche"]
for sponsor in sponsors:
    results = ct_search({
        "query.spons": sponsor,
        "filter.overallStatus": "RECRUITING",
        "pageSize": 1
    })
    print(f"{sponsor}: {results['totalCount']} recruiting trials")
    time.sleep(1.5)

よく使うレシピ

レシピ:レート制限に対応した一括検索
def ct_search_with_retry(params, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = requests.get(f"{CT_API}/studies", params=params, timeout=30)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 429:
                wait = 60
                print(f"Rate limited. Waiting {wait}s...")
                time.sleep(wait)
            else:
                raise
        except requests.exceptions.RequestException:
            if attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)
    raise Exception("Max retries exceeded")
レシピ:試験の概要を抽出
def extract_summary(study):
    proto = study.get('protocolSection', {})
    ident = proto.get('identificationModule', {})
    status = proto.get('statusModule', {})
    design = proto.get('designModule', {})
    return {
        'nct_id': ident.get('nctId'),
        'title': ident.get('officialTitle') or ident.get('briefTitle'),
        'status': status.get('overallStatus'),
        'phases': design.get('phases', []),
        'enrollment': design.get('enrollmentInfo', {}).get('count'),
        'last_update': status.get('lastUpdatePostDateStruct', {}).get('date')
    }

# Usage
for study in results['studies'][:3]:
    s = extract_summary(study)
    print(f"{s['nct_id']}: {s['status']} | Phase: {s['phases']} | n={s['enrollment']}")
レシピ:安全なフィールドの参照
def safe_get(study, *keys, default='N/A'):
    """Navigate nested study JSON safely."""
    current = study
    for key in keys:
        if isinstance(current, dict):
            current = current.get(key)
        else:
            return default
        if current is None:
            return default
    return current

# Usage — handles missing fields gracefully
nct = safe_get(study, 'protocolSection', 'identificationModule', 'nctId')
phases = safe_get(study, 'protocolSection', 'designModule', 'phases', default=[])
enrollment = safe_get(study, 'protocolSection', 'designModule', 'enrollmentInfo', 'count')

主なパラメーター

パラメーターエンドポイントデフォルト説明
query.cond検索—疾患・病気の検索語
query.intr検索—介入・薬剤の検索語
query.locn検索—地理的な場所のフィルター
query.spons検索—スポンサー・組織のフィルター
query.term検索—一般的な全文検索
filter.overallStatus検索allカンマ区切りのステータス値
filter.phase検索allカンマ区切りのフェーズ値
pageSize検索101ページあたりの結果数(最大 1000)
sort検索relevance{field}:{asc|desc}
format両方jsonjson または csv
timeout(クライアント)30srequests の呼び出しで設定

トラブルシューティング

問題原因解決策
429 Too Many Requestsレート制限を超過しました(約 50 回/分)60 秒待機します。pageSize=1000 を指定し、指数バックオフを実装します
空の studies 配列フィルターに一致する試験がありません検索範囲を広げます(ステータス・フェーズのフィルターを削除します)。スペルを確認します
400 Bad Requestパラメーター値が無効ですステータス・フェーズの値が列挙型と完全に一致していることを確認します(例:recruiting ではなく RECRUITING)
resultsSection がない試験に投稿済みの結果がありません結果にアクセスする前に study['hasResults'] を確認します
ネストされたフィールドで KeyErrorすべての試験にすべてのモジュールがあるとは限りませんデフォルト値を指定して .get() を使うか、safe_get ヘルパーを使います(レシピを参照)
ページネーションが早期に終了するnextPageToken がありませんすべての結果を取得済みです。totalCount と収集した件数を確認します
CSV 形式が JSON と異なるフィールド構造が異なりますCSV ではネストされた構造がフラット化されます。プログラムからアクセスする場合は JSON を使います
大規模なエクスポートでタイムアウトする多数の結果を含む CSVタイムアウトを延長します。または pageSize=1000 を指定してページネーションします

ベストプラクティス

  • 一括取得では最大ページサイズ(1000)を使用して、レート制限に対するリクエスト数を最小限に抑えます
  • resultsSection にアクセスする前に、必ず hasResults を確認します。ほとんどの試験には公開済みの結果がありません
  • .get() チェーンで安全に移動します。すべての試験ですべてのモジュール(特に contactsLocationsModule、armsInterventionsModule)にデータが含まれているとは限りません
  • 複数のステータス値はカンマ区切りで指定します(例:RECRUITING,NOT_YET_RECRUITING)。ステータスごとに個別のリクエストを送信しないでください
  • デフォルトで sort=LastUpdatePostDate:desc を使用します。これにより、最終更新日が新しい試験から順に返されます
  • 日付の解釈:lastUpdatePostDateStruct.date は ISO 8601 文字列です。type フィールドは ACTUAL と ESTIMATED のどちらかを示します

関連スキル

  • pubmed-database — 試験登録データを補完する、公開済み文献の検索
  • chembl-database-bioactivity — 調査中の医薬品の化合物生物活性データ
  • bioservices-multi-database — 統合 Python インターフェースによる別のデータベースアクセス方法

参考資料

同梱リソース

自己完結型のエントリーです。元の合計:866 行(SKILL.md 507 行 + api_reference.md 359 行)。スクリプト:216 行(query_clinicaltrials.py)。

元ファイルの扱い:

  • SKILL.md(507 行)→ コア API モジュール 1~7(condition、intervention、location、sponsor、details、pagination、CSV export)。「Core Capabilities」セクション 1~10 を統合:Search by Condition → Module 1、Search by Intervention → Module 2、Geographic Search → Module 3、Search by Sponsor → Module 4、Retrieve Detailed Study → Module 5、Pagination → Module 6、Data Export → Module 7、Combined Query → Workflow 1、Extract Summary → Recipe。「Resources」セクションの仮置き部分 → 削除し、内容をインラインに統合。ユースケース別の扱い:Patient Matching → When to Use の箇条書き + Workflow 1、Research Analysis → When to Use + Workflow 2、Drug Tracking → When to Use + Module 2、Geographic Search → Module 3、Sponsor Tracking → Module 4 + Workflow 3、Data Export → Module 7、Trial Monitoring → When to Use の箇条書き、Eligibility Screening → Module 5
  • references/api_reference.md(359 行)→ 全内容をインラインに統合:エンドポイントパラメーター → Key Concepts の「Query Parameters Reference」テーブル、ステータス/フェーズの値 → Key Concepts のテーブル、レスポンス構造 → Key Concepts の「Response Data Structure」テーブル、HTTP エラーコード → Troubleshooting のテーブル、レート制限のガイダンス → Prerequisites + Best Practices、ユースケース → メインの SKILL.md の例と重複するため、Core API に統合。データ標準(ISO 8601、CommonMark)→ Prerequisites の注記。エラー処理パターン → Recipes の「Rate-Limited Bulk Search」
  • scripts/query_clinicaltrials.py(216 行)→ ヘルパー関数のパターン:search_studies() → Quick Start の ct_search() ヘルパー、get_study_details() → Module 5 にインラインで記載、search_with_all_results() → Module 6 のページネーションパターン、extract_study_summary() → Recipe の「Extract Study Summary」。薄いラッパーのショートカットを適用 — 各関数は requests.get() を呼び出す薄いラッパーでした

保持率:元の866行(スクリプトを除く)のうち約465行(約54%)。

コメント