Skip to main content
Glama

ChiCTR MCP Server

npm version License: MIT

基于 Model Context Protocol (MCP) 的中国临床试验注册中心 (ChiCTR) 查询服务。

站点对 /searchproj.html(搜索)与 /showproj.html(详情)施加了阿里盾(阿里云 WAF)保护: 直连返回 405,换出口 IP 则变成一页需要执行 JavaScript 才能通过的挑战页。 本项目用「Node MCP 服务器 + Python sidecar 子进程」的两段式架构解决它 —— sidecar 过一次挑战 拿到 cookie,之后全程走纯 HTTP。

当前版本: v3.0.2 · MCP 工具: 10 个 · 详情字段: 76 个 · 默认数据通道: ChiCTR 原站直连


📍 目录


Related MCP server: Clinical Trials MCP Server

🧩 Scrapling 是什么

Scrapling 是一个第三方 Python 爬虫库(pip install scrapling,本项目用的 scrapling[fetchers] 实测版本 0.4.15)。它提供 Fetcher / StealthyFetcher / StealthySession 等一把好用的 抓取 API,把「伪装 TLS 指纹」「跑无头浏览器」「过挑战」收敛成几行调用。

⚠️ 关键结论:解决阿里盾 405 的不是 Scrapling 这个「框架」

这是本项目最重要的一条认知,也是「开箱即用」代价的来源:

真正过盾的是 Scrapling 捆绑的两个底层组件 —— curl_cffi 和 patchright。 Scrapling 只是把它们包成了好用的 API。

组件

角色

在过盾中的实际作用

curl_cffi

TLS 指纹伪装(impersonate)

让 Python HTTP 请求的 TLS 指纹(JA3/JA4)看起来像真 Chrome。它只把「硬 405」降级成「挑战页」,本身解不了挑战。

patchright

Chromium 反自动化补丁(Playwright 的 fork)

让阿里盾的挑战脚本能真正跑完并算出 acw_sc__v2 cookie。这才是拿到真实页面的那一步。

Scrapling(Fetcher / StealthyFetcher)

调用这两个组件的封装层

提供 API。它自带的 Cloudflare solver(solve_cloudflare=True)对阿里盾不起作用,只会打印 No Cloudflare challenge found.

三层证据(dev/browser-removal/SCRAPLING_SOLUTION.md,2026-10-04 本机实测):

层级

手段

结果

0

裸 curl / 裸 Node 直连

❌ 405 硬拦截

1

Fetcher(curl_cffi TLS 指纹伪装)

⚠️ 200,但只返回 17,136 字节的挑战页(含 acw_sc__v2 / aliyunwaf)

2

StealthyFetcher(patchright + 真实导航)

✅ 200,42,178 字节真实页面(共 13125、retrieve.js),挑战通过

2'

同样的活交给原生 Playwright + page.setContent()

❌ 挑战脚本死循环,跑不完

推论:StealthyFetcher 与原生 Playwright 的差别就在 patchright 的指纹补丁; 正是这个补丁让挑战脚本得以走完。Cloudflare solver 与本case无关。

代价:依赖很重(约 1 GB)

「框架能力很强」的反面是依赖体积。因为要同时带上「TLS 伪装库」和「带补丁的 Chromium 内核」:

项目

实测体积

.venv(Python 依赖,其中 playwright 134 MB + patchright 134 MB)

325 MB

~/Library/Caches/ms-playwright(浏览器内核)

557 MB

Node 侧 node_modules

数百 MB

合计

≈ 1 GB

而且会存在两套 Chromium 内核:Node 侧 Playwright 要 chromium-1234,Python 侧 patchright 要 chromium-1243。这正是本项目不能做成「一个安装包全搞定」、必须把 Python 侧做成 运行时按需自举的根本原因(见 部署)。


🏗️ 架构与部署形态

运行时架构

MCP 客户端(Cherry Studio / Claude Desktop / …)
   │  stdio(MCP 协议,stdout 专属通道)
   ▼
Node MCP server(dist/index.js)          ← 对外协议、缓存、状态机、工具编排
   │  CHICTR_USE_SIDECAR=1 时优先走这里
   │  HTTP JSON → 127.0.0.1:8848
   ▼
Python sidecar(sidecar/chictr_sidecar.py) ← 过盾 + 抓取 + 解析
   ├─ curl_cffi 纯 HTTP + TLS 指纹伪装 ─────── 常规请求路径(约 1.5s/页)
   └─ patchright StealthySession 无头浏览器 ── 仅 cookie 过期时过盾(约每小时 1 次)
   ▼
https://www.chictr.org.cn

职责边界很清楚:sidecar 只负责「把 HTML 拿回来并解析成 JSON」,Node 侧负责一切对外契约。

三种部署形态

形态

组成

浏览器依赖

适用

A. sidecar 形态(推荐)

Node MCP server + Python sidecar

仅过盾时拉一次 Chromium

本机开发、桌面 App、长期驻留服务

B. 纯 Playwright 形态(默认/回退)

只有 Node MCP server

每次查询都要 Chromium

已有浏览器环境、sidecar 不可用时的兜底

C. ICTRP 形态(规划中)

只有 Node MCP server,走 WHO ICTRP

无

轻量部署、批量检索;见 dev/browser-removal/DUAL_CHANNEL_ARCHITECTURE.md

当前实现落地的是 A + B:sidecar 可用则走 A,任一环节失败自动回退 B,对外契约不变。 形态 C 仍是设计稿,尚未实现。

打包形态(形态 A 打成 dmg/exe 时):

你的 App.app / your-app.exe
├── node_modules/              # Node 依赖,打包器收集(几十 MB)
├── dist/                      # TS 编译产物
├── sidecar/chictr_sidecar.py  # 随包分发(package.json 的 files 已含 "sidecar")
└── (首次运行时在用户目录创建)
    └── .venv/                 # 325 MB,由 setup 创建,不进安装包

关键约束:打成 dmg/exe 后安装器不会执行 package.json 的 postinstall(打包器只收集文件, 钩子被丢掉)。所以依赖准备必须由主程序在运行时驱动 —— 这就是 env-probe.ts + bootstrap.ts 存在的原因。


⚙️ sidecar 设计

接口(极小的 HTTP JSON)

sidecar 用标准库 ThreadingHTTPServer 暴露三个只读端点,没有框架依赖:

方法

路径

参数

返回

GET

/health

—

{ok, solver:{fresh, age_seconds, solve_count, last_error, cookie_names}}

GET

/search

title、regno、createyear、page、pages

{ok, source, query, total, total_pages, returned, pages_fetched, results[]}

GET

/detail

proj(project id)、raw(1/true/yes 时额外返回 raw_html)

{ok, source, project_id, url, registration_number, fields{}, field_count, raw_text, raw_text_length}

错误语义:过盾后仍拿到挑战页 → 503 {"error":"challenge_unsolved"};缺 proj → 400; 其他异常 → 502。默认监听 127.0.0.1:8848(--host / --port / CHICTR_SIDECAR_HOST / CHICTR_SIDECAR_PORT 可覆盖,另有 CHICTR_SIDECAR_LOG 控制日志级别)。

/search 的 results[] 元素:

{
  "project_id": "245719",
  "registration_number": "ChiCTR2600133371",
  "title": "阿得贝利单抗联合NALIRIFOX用于可切除胰腺癌新辅助治疗",
  "institution": "浙江大学医学院附属第一医院",
  "study_type": "干预性研究",
  "registration_date": "2026/09/24",
  "detail_url": "https://www.chictr.org.cn/showproj.html?proj=245719"
}

这是整个设计的地基 —— 挑战解出的 cookie 可以脱离浏览器复用。

  1. 过一次挑战:ChallengeSolver._solve() 启动 StealthySession(headless=True), 导航到 /searchproj.html,从 session.context.cookies() 取出 acw_sc__v2 / acw_tc / ssxmod_itna / ssxmod_itna2 四个 cookie。

  2. 纯 HTTP 复用:ChictrClient.get_html() 用 FetcherSession(impersonate="chrome") 带上这组 cookie 请求,不再触碰浏览器。

  3. 实测摊薄效果:solve_count = 1 支撑了 ≥13 次请求(含 3 并发 + 10 并发分页), 期间未触发重新过盾。一次过盾约 4.7–6.0s,此后约 1.5s/页。

设计上还有几处关键细节:

机制

实现

原因

双重检查锁

ensure() 先无锁查 is_fresh(),未命中再进 threading.Lock 复查后才 _solve()

多线程并发首次请求时只跑一次浏览器

常驻 HTTP 会话

FetcherSession 是工厂式 context manager,构造时进入一次并保持常驻,由 close() 退出

避免每个请求重建连接池

底层串行化

self._lock 包住 self._http.get(...)

curl_cffi 的底层 session 并非线程安全,并发是「排队」而非真并行

内容检测兜底

_is_challenge() 检测响应里是否只有 acw_sc__v2、缺少 retrieve.js/myPagination.js/showproj

不信任 TTL,信任响应内容

一次重试

命中挑战页或请求异常 → solver.invalidate(reason) → 重试一次(_retry=False 防递归)

恢复路径短且不会打转

代码里 COOKIE_TTL_SECONDS = 55 * 60(55 分钟,比 acw_sc__v2 的 1 小时 expires 留了 5 分钟余量)。 但实测 cookie 实际存活约 37.5 分钟,TTL 判断偏乐观。

这个偏差不会造成故障,因为兜底不在 TTL 而在内容检测: cookie 提前失效时,请求会返回挑战页而非报错,_is_challenge() 立刻识别出来 → invalidate() 清空 cookie → 重新过盾 → 重试。实测约 5.7s 恢复,调用方只感知到一次稍慢的请求。

结论:fresh 只是「提前刷新」的优化信号,真正的正确性由响应内容检测保证。 运维上不应把 solver.fresh == false 当成故障(见监控)。

Node 侧客户端(src/runtime/sidecar-client.ts)

行为

实现

默认关闭

isSidecarEnabled() 只在 CHICTR_USE_SIDECAR 为 1/true/yes/on 时返回 true

失败即回退

任何失败(不可达、非 JSON、非 2xx、payload.error、0 条有效结果)返回 null,由 src/index.ts 回退 Playwright

快速失败

请求带 AbortController + CHICTR_SIDECAR_TIMEOUT_MS(默认 30000ms)

状态可见

getSidecarStatus() → {enabled, available, baseUrl, lastError},经 get_runtime_metrics 暴露

get_trial_detail 需要先有 project_id:客户端先查缓存里的「注册号 → project_id」映射, 没有就先用 /search?regno= 查一次拿 project_id,再调 /detail?proj=。

惰性初始化浏览器(关键修复)

原 src/index.ts 的 CallToolRequestSchema handler 在 switch 之前无条件执行 new BrowserManager() + initialize() —— 后果是即使 sidecar 命中也会先拉起 Chromium, sidecar 的全部价值被这一行抵消。

现已改为 ensureBrowser() 惰性函数,只有真正会走 Playwright 的分支(含 sidecar 回退) 才初始化 Chromium。决定性验证:在一台刻意不安装 Playwright 浏览器内核的机器上 (Executable doesn't exist at .../chromium_headless_shell-1234/...),修复前 search_trials 直接抛错,修复后同样调用返回真实数据 —— 证明全程未触碰浏览器。


🚀 部署

用户视角:三步走

# 1. 体检(只读,不改任何东西;退出码 1 = 需要处理)
chictr-mcp-server doctor

# 2. 一键修复(创建 venv + 装依赖;联网,首次约 40s ~ 3min)
chictr-mcp-server setup

# 3. 启动 sidecar(前台运行,首次会过一次盾)
chictr-mcp-server sidecar

然后让 MCP 客户端走 HTTP 路径:

export CHICTR_USE_SIDECAR=1
chictr-mcp-server --transport=stdio

setup 默认不下载浏览器内核(省 560 MB)。sidecar 的纯 HTTP 常规路径不需要它; 只有「首次过盾」才需要 Chromium。需要时加 --browser 显式授权下载。

也可以把这三步交给 AI —— MCP 工具 check_environment 会返回同样的体检结论和修复建议。

退出码约定

码

含义

调用方应对

0

环境就绪 / 操作成功

继续

1

未就绪,需要用户动作

提示用户跑 setup

2

自举失败(网络、磁盘、权限)

展示错误,可重试

自动化的三个层次

  1. 启动时自检(已实现,无需配置):MCP 服务每次启动做一次体检,结果只写 stderr (stdout 是 MCP 协议通道,写进去会破坏协议)。体检永不阻塞启动、永不抛异常 —— 环境好坏都能启动服务,只是能力不同。

    [chictr-mcp-server] 环境未就绪:未找到 Python 虚拟环境
    [chictr-mcp-server] 运行 "chictr-mcp-server setup" 自动修复
  2. 首次运行自动下载(可选,需显式授权):setup --browser 或代码里 bootstrapEnvironment({ installBrowser: true, onProgress })。 刻意不做静默自动下载 —— 560 MB 在用户不知情时下载是冒犯行为。

  3. 自举健壮性:bootstrapEnvironment() 是幂等的(已存在的 venv 不重建、已装的依赖 不重装,中断后重跑从断点继续);pip 镜像按 首选 → 其他 → 官方源 自动回退 (默认阿里云;清华镜像对 pip 26 的新版 User-Agent 返回 403,会把错误伪装成 Could not find a version that satisfies the requirement scrapling[fetchers] (from versions: none), 极具误导性,故镜像回退是必需而非优化)。

环境变量总表

变量

默认

说明

CHICTR_HOME

自动推断

项目根目录(打包后位置非常规时必须设)

CHICTR_VENV

<root>/.venv

虚拟环境目录

CHICTR_USE_SIDECAR

未设置(关闭)

设为 1/true/yes/on 启用 sidecar 优先路径

CHICTR_SIDECAR_URL

http://127.0.0.1:8848

sidecar 地址

CHICTR_SIDECAR_TIMEOUT_MS

30000

sidecar 单次调用超时

CHICTR_SIDECAR_HOST / CHICTR_SIDECAR_PORT

127.0.0.1 / 8848

sidecar 监听地址(Python 侧)

CHICTR_SIDECAR_LOG

INFO

sidecar 日志级别

HTTP_PROXY / HTTPS_PROXY

未设置

代理(可选)

用户环境要求

要求

说明

Node.js

≥ 20(打包版自带,可忽略)

Python

≥ 3.10(setup 自动发现 python3.13/3.12/3.11/3.10/python3)

磁盘

纯 HTTP 路径约 400 MB;含浏览器约 1 GB

网络

首次 setup 需联网(约 40s ~ 3min)

如果用户没有 Python:doctor 会报 venv: missing 并提示安装 Python。这是 setup 唯一无法自动解决的依赖。

⚠️ pip install --user 在部分 macOS 上会因权限失败 (OSError: [Errno 1] Operation not permitted: .../site-packages/w3lib),必须用 venv。

⚠️ .venv 默认建在项目根。若安装目录只读(macOS /Applications、Windows Program Files),必须设置 CHICTR_HOME 指向可写目录;isWritable() 会在体检报告里提示。

监控与排障

curl -sS http://127.0.0.1:8848/health
# {"ok": true, "solver": {"fresh": true, "age_seconds": 57.0, "solve_count": 1, "last_error": null,
#  "cookie_names": ["acw_sc__v2","acw_tc","ssxmod_itna","ssxmod_itna2"]}}
  • solver.solve_count:累计过盾次数。稳态下应长期为 1(约每小时 +1)。 快速上涨说明 cookie 被反复判失效,应检查是否被限流。

  • solver.last_error:最近一次过盾失败原因,正常为 null。

  • MCP 侧可通过 get_runtime_metrics 看 sidecar: { enabled, available, baseUrl, lastError }。

症状

原因

处理

/health 连不上

sidecar 未启动

chictr-mcp-server sidecar

日志 ERROR: No Cloudflare challenge found.

正常噪声

ChiCTR 用阿里盾不是 CF,可忽略;成功标志是紧随的 INFO 过盾成功,耗时 X.Xs

{"error":"challenge_unsolved"} (503)

过盾后仍返回挑战页

通常是 IP 被限流,稍后重试

MCP 返回 Playwright 报错

sidecar 未启用或不可达

确认 CHICTR_USE_SIDECAR=1 且 curl /health 正常

from versions: none

pip 镜像 403

已自动回退;或 setup --mirror=aliyun

体检报 venv missing 但目录存在

venv 损坏

setup 会检测并重建;或 clean --yes 后重来

启动即崩 Could not locate the bindings file

better-sqlite3 未针对当前 Node ABI 构建

见下

# better-sqlite3 原生绑定(Node 22 / ABI v127);npm rebuild 在受限沙箱会因
# 无法写 ~/Library/Caches/node-gyp 而失败,用预编译包绕过
cd node_modules/better-sqlite3 && npx prebuild-install -r node -t 22.19.0

全新安装实测记录

在一个「什么都没有」的隔离目录(只有 package.json + dist/ + sidecar/chictr_sidecar.py, 共 592 KB)验证:

步骤

结果

doctor

✅ 正确报未就绪,退出码 1,给出 actions

setup --mirror=tsinghua

✅ 清华失败 → 自动回退阿里云成功(2m35s)

doctor

✅ 全部就绪(venv 3.13.14 / 依赖 / 浏览器内核 / 脚本)

启动 sidecar

✅ 过盾成功 6.7s

真实搜索「胰腺癌」

✅ total 469 / total_pages 47 / 返回 10 条

MCP 工具 check_environment

✅ 22ms(缓存命中)

即:一台干净机器上,592 KB 的包 + 一条 setup 命令 = 可用。


🎯 应用场景

场景

怎么用

为什么合适

临床试验情报监测

按靶点/疾病关键词定时 search_trials,对新增注册号批量 get_trial_detail

原站是实时数据(WHO ICTRP 为每周同步,有滞后);约 1.5s/页使每日轮询成本可忽略

RAG / 知识库入库

search_trials 拉列表 → get_trial_detail 拉 55+(最多 76 个)字段的结构化详情 → 写入向量库

详情页解析为中英对照的结构化字段,比裸 HTML 更适合切分与检索;v3.0.1 起含纳排标准、联系人、伦理委员会联系方式

患者匹配与入组筛选

用疾病名/基因突变(如 KRAS G12D、胰腺癌)搜索,再用纳入/排除标准过滤

get_trial_detail 直接返回「纳入标准」「排除标准」「研究实施地点」「招募状态」

AI Agent 的循证工具

作为 MCP server 挂到 Cherry Studio / Claude Desktop 等客户端

中国临床试验数据对上 AI 助手是通用缺口;10 个工具里有 5 个是给 Agent 自诊断用的

科研回顾与趋势分析

按 year 参数逐年检索,统计某靶点/机构的注册量变化

支持按年份、注册号、关键词组合查询,max_results 上限 100

部署自检(Agent 自助排障)

check_environment → 按返回的 actions 决定是否调 setup

让 AI 能在用户机器上自行判断「缺 Python 还是缺依赖还是 sidecar 没起」


📋 可用工具

共 10 个 MCP 工具(v3.0.0 起新增 check_environment;工具数未变,v3.0.1 扩充的是 get_trial_detail 的字段覆盖)。

#

工具

用途

1

search_trials

按关键词 / 注册号 / 年份搜索

2

get_trial_detail

按注册号查询完整详情

3

get_cache_stats

单层缓存统计(命中率等)

4

clear_cache

清除所有缓存

5

get_cache_stats_v2

双层缓存统计(L1 内存 + L2 SQLite)

6

get_runtime_metrics

运行时编排指标 + sidecar 状态

7

get_access_state

访问状态机(NORMAL/SUSPECTED/CHALLENGED/COOLDOWN/RECOVERY)

8

check_environment

本机环境体检(Python venv / 依赖 / 浏览器内核 / sidecar),只读,不下载不改文件

9

prepare_verification_session

创建人工验证会话

10

resume_after_verification

人工验证后恢复访问状态

search_trials

// 按关键词
{ "name": "search_trials", "arguments": { "keyword": "KRAS", "max_results": 20 } }

// 按注册号
{ "name": "search_trials", "arguments": { "registration_number": "ChiCTR2500111173" } }

// 按年份
{ "name": "search_trials", "arguments": { "year": 2024, "max_results": 20 } }

// 组合
{ "name": "search_trials", "arguments": { "keyword": "KRAS", "year": 2024, "max_results": 10 } }

参数说明:

参数

类型

必填

说明

keyword

string

否

注册题目关键词,最长 200,如 KRAS G12C、胰腺癌

registration_number

string

否

注册号,形如 ^ChiCTR\d{8,}$,最长 32

year

integer

否

注册年份,最小 2000,最大为当前年份 + 1

max_results

integer

否

最大返回数,默认 20,范围 1–100

get_trial_detail

{ "name": "get_trial_detail", "arguments": { "registration_number": "ChiCTR2500108082" } }

返回详情页的全部结构化字段。v3.0.1 起解析器改为按 HTML 表格结构抽取,字段数由 29 个提升到 76 个、平均 58.6 字段/条(对 468 个真实页面实测),并已逐字段回查 HTML 原文校验(全库 28809 个字段值逐一回查,0 处不符、0 处伪造)。

主要字段分组:

分组

字段

注册信息

注册号 注册号状态 注册时间 最近更新日期 注册题目 研究课题的正式科学名称 研究课题代号(代码) 在二级注册机构或其它机构的注册号

联系人

申请注册联系人 申请注册联系人电话 申请注册联系人传真 申请注册联系人电子邮件 申请注册联系人通讯地址 申请注册联系人邮政编码,以及 研究负责人 对应的同一套 6 个字段

伦理委员会

是否获伦理委员会批准 伦理委员会批件文号 批准本研究的伦理委员会名称 伦理委员会批准日期 伦理委员会联系人 伦理委员会联系地址 伦理委员会联系人电话 伦理委员会联系人邮箱

单位与来源

申请人所在单位 研究负责人所在单位 研究实施负责(组长)单位 经费或物资来源 国家 省(直辖市) 市(区县) 单位(医院) 具体地址 单位级别

设计与目的

研究类型 研究所处阶段 研究设计 研究目的 药物成份或治疗方案详述 研究实施时间 征募观察对象时间

干预与样本

干预措施 组别 样本量 干预措施代码

结局指标

指标中文名 指标类型 测量时间点 测量方法 主要终点 次要终点

招募与共享

征募研究对象情况 年龄范围 性别 随机方法 盲法 是否公开试验完成后的统计结果 是否共享原始数据 共享原始数据的方式(说明 数据采集和管理(说明 数据与安全监察委员会 注册人

标本

采集人体标本 标本中文名 标本去向

全量 468 条中 100% 覆盖的字段有 37 个:注册号 注册号状态 注册时间 最近更新日期 注册题目 研究课题的正式科学名称 研究疾病 研究类型 研究设计 研究目的 研究所处阶段 研究实施时间 征募观察对象时间 征募研究对象情况 随机方法(请说明由何人用什么方法产生随机序列) 年龄范围 性别 指标中文名 指标类型 说明 是否获伦理委员会批准 是否公开试验完成后的统计结果 申请人所在单位 申请注册联系人 申请注册联系人电话 申请注册联系人电子邮件 申请注册联系人通讯地址 研究负责人 研究负责人电话 研究负责人电子邮件 研究负责人通讯地址 研究实施负责(组长)单位 研究实施负责(组长)单位地址 经费或物资来源 注册人 纳入标准 排除标准。

覆盖率差异是页面本身留空导致的,不是解析失败:次要研究终点 1/468、研究疾病代码 5/468、 研究负责人网址(自愿提供) 6/468、干预措施代码 7/468、测量方法 60/468 均已抽查确认原页面为空。

check_environment

{ "name": "check_environment", "arguments": { "refresh": false } }

refresh: true 忽略 30 秒缓存强制重新探测。返回 root / node / venv / pythonDeps / browser / playwrightBrowser / sidecarScript / sidecar / summary / actions / canRunBrowserless。 canRunBrowserless 是判断「能否免浏览器检索」的关键信号。

缓存与状态工具

{ "name": "get_cache_stats",  "arguments": {} }
{ "name": "clear_cache",      "arguments": {} }
{ "name": "get_cache_stats_v2","arguments": {} }
{ "name": "get_runtime_metrics","arguments": {} }
{ "name": "get_access_state", "arguments": {} }

人工验证会话

{ "name": "prepare_verification_session",
  "arguments": { "target_url": "https://www.chictr.org.cn/searchproj.html", "timeout_ms": 300000 } }

{ "name": "resume_after_verification", "arguments": { "verification_id": "verify_xxx" } }

📡 MCP 配置说明

最简配置(npx)

{
  "mcpServers": {
    "chictr": {
      "command": "npx",
      "args": ["-y", "chictr-mcp-server@latest"]
    }
  }
}

全局安装后

npm install -g chictr-mcp-server
{ "mcpServers": { "chictr": { "command": "chictr-mcp-server" } } }

启用 sidecar(推荐)

{
  "mcpServers": {
    "chictr": {
      "command": "node",
      "args": ["/Users/you/chictr_trials/dist/index.js"],
      "env": { "CHICTR_USE_SIDECAR": "1" }
    }
  }
}

前置:先另开一个进程跑 chictr-mcp-server sidecar。

带代理

{
  "mcpServers": {
    "chictr": {
      "command": "npx",
      "args": ["-y", "chictr-mcp-server"],
      "env": { "HTTP_PROXY": "http://your-proxy-server:port" }
    }
  }
}

通信方式

方式

状态

说明

stdio

✅ 支持(默认)

标准输入输出

http

计划中

http://localhost:3000/mcp

sse

计划中

http://localhost:3000/mcp

测试 MCP 服务

npx @modelcontextprotocol/inspector npx -y chictr-mcp-server

🛠️ CLI 命令行工具

package.json 提供两个 bin:chictr-mcp-server(dist/index.js)与 chictr-setup (dist/cli/setup-cli.js)。

npm install -g chictr-mcp-server   # 全局安装
npx -y chictr-mcp-server           # 或直接 npx(推荐)

命令

说明

chictr-mcp-server

启动 STDIO MCP 服务

chictr-mcp-server doctor

环境体检(只读,不修改任何文件)

chictr-mcp-server setup

安装缺失依赖(幂等,可重复运行)

chictr-mcp-server setup --browser

同时下载浏览器内核(约 560 MB,需联网)

chictr-mcp-server setup --mirror=aliyun

指定 PyPI 镜像(aliyun 默认 / tencent / official / tsinghua)

chictr-mcp-server setup --timeout=1800

单步超时(秒,默认 1800)

chictr-mcp-server sidecar [--port=8848]

前台启动 sidecar(自动带 --warmup,透传 -- 参数)

chictr-mcp-server clean [--yes]

删除虚拟环境以便重装

npm scripts 等价入口:npm run doctor / npm run setup / npm run sidecar。


🗄️ 批量抓取入库(tools/chictr_crawl.py)

需要把某个疾病的全量试验落成本地数据时用这个脚本。它随包分发 (node_modules/chictr-mcp-server/tools/chictr_crawl.py),只走 sidecar, 不碰浏览器与 TLS 指纹,因此必须先让 sidecar 跑起来。

# 1) 起 sidecar(另开一个终端,保持前台运行)
npx chictr-mcp-server sidecar

# 2) 先预览规模(只拉列表,不抓详情)
python3 node_modules/chictr-mcp-server/tools/chictr_crawl.py --keyword 胰腺癌 --list-years

# 3) 正式抓取 → SQLite(结构化字段 + 原始 HTML 双存)
python3 node_modules/chictr-mcp-server/tools/chictr_crawl.py --keyword 胰腺癌

# 4) 导出(JSON 不含原始 HTML;HTML 单独落盘便于核对原文)
python3 node_modules/chictr-mcp-server/tools/chictr_crawl.py --keyword 胰腺癌 --export-json
python3 node_modules/chictr-mcp-server/tools/chictr_crawl.py --keyword 胰腺癌 --export-html

参数

说明

--keyword

检索关键词,默认 胰腺癌。默认库文件按关键词派生为 data/chictr_<关键词>.db,换关键词不会覆盖

--years

只抓指定年份,如 --years 2026 2025;默认 2010 – 今年

--list-years

只统计各年条数并估算耗时,不抓详情

--delay

请求间隔秒,默认 0.6

--refresh

强制重抓已存在的记录(默认跳过)

--db / --json / --html-dir

自定义输出路径

--export-json / --export-html

导出 JSON / 独立 HTML 文件 + _manifest.json

增量行为:去重键是 project_id(不是注册号)——2018 年前注册的试验 ChiCTR 没发注册号(列表里该字段为 null),只按注册号去重会把 2010–2017 的记录整批丢掉(实测少 31 条)。重跑脚本只补缺失项,已抓过的直接跳过, 因此定时重跑即是增量同步。

存储结构:主表 trials 以 project_id 为主键,除全字段 JSON (fields_json)与原始素材(raw_html / raw_text)外,另把 研究设计/研究疾病/纳入标准/排除标准/研究负责人 等映射为独立列并建索引, 方便直接 SQL 过滤。


📈 性能对比

路径

搜索耗时

说明

旧 Playwright 路径(回退形态)

45s 超时上限 + 每页 5–10s 人为延迟(另有页间 3–6s、翻页前 2–4s 随机延迟)

src/services/search.ts 原逻辑

sidecar 路径(推荐形态)

约 1.5s/页,搜索端到端 ~1.5–2.1s,详情 ~2s

纯 HTTP + cookie 复用,无浏览器

端到端响应时间下降约一个数量级,且去掉了原有的随机延迟规避逻辑。

其他基线(实测):

指标

数值

冷启动过盾

4.7 / 5.9 / 6.0 / 6.7s(多次冷启动)

热态单页检索(regno)

1.46s

热态单页检索(title)

~1.5s

详情页

~2s

一次过盾支撑请求数

≥ 13(solve_count 保持 1)

并发

3 并发 3/3 成功;10 并发 10/10 成功(共 100 条),期间未重新过盾

cookie 实际存活

约 37.5 分钟(TTL 配置 55 分钟偏乐观,靠内容检测兜底,5.7s 恢复)

缓存

搜索结果 5 分钟,详情 10 分钟;双层 L1 内存 + L2 SQLite

服务端返回示例

{
  "results": [
    {
      "registration_number": "ChiCTR2500108082",
      "title": "谷氨酰胺联合奥沙利铂、卡培他滨(XELOX)和贝伐珠单抗一线治疗KRAS G12D基因突变型晚期结直肠癌的单臂Ⅱ期探索性研究",
      "study_type": "干预性研究",
      "registration_date": "2025/08/25",
      "institution": "浙江大学医学院附属第二医院"
    }
  ]
}

🔔 版本更新

v3.0.2 (2026-10-04)

  • ✅ 重写详情页解析器:字段数 29 → 76,平均 21.9 → 58.6 字段/条。根因是旧实现把整页压平成一串文本再用正则猜边界,而详情页实际是标准表格(标签在 <td class="left_title">,值在紧邻的下一个 <td>)。改为按表格结构解析后,英文标签不再混入值、值也不再越过相邻字段

  • ✅ 补上此前完全缺失的字段:联系人/电话/传真/邮箱/通讯地址/邮编(申请注册联系人与研究负责人各一套)、伦理委员会联系人/地址/电话/邮箱、研究负责人所在单位、国家/省/市/单位(医院)/具体地址、研究所处阶段、组别、标本信息、数据共享与安全监察等

  • ✅ 修复 900 字上限导致超长字段静默丢弃:实测「纳入标准」2242 字、「排除标准」1410 字、「研究实施地点」6964 字,旧正则 (.{0,900}?) 匹配不到 → 字段整个消失。上限放宽至 60000

  • ✅ 修复老式注册号漏匹配:2018 年前的编号形如 ChiCTR-IIR-17013424,旧正则 ChiCTR\d{6,} 抓不到。改为 ChiCTR(?:-[A-Z]{2,4})?-?\d{4,}

  • ✅ 修复区块标题被当作字段:测量指标/采集人体标本/研究实施地点/申办者/干预措施 的值在嵌套子表格里,朴素配对会抽到 '组别'、'国家'、'Outcomes' 这类伪值

  • ✅ 修复空值被兜底正则伪造:页面留空的字段(研究课题代号(代码) 等)会从扁平文本抽到下个标签名,产生 'Study subject ID' 假值

  • ✅ 修复 HTML 实体未解码:部分单元格用字面 &#32;/&#39; 书写,导致「具体地址」入库为 '上海市&#32;普陀区…'

  • ✅ 修复导航栏同名标签误匹配:页面顶部「按经费或物资来源统计」早于正文真实字段(偏移 141 vs 3364),正文起点现定位到「注册号:」之后

  • ✅ 修复复合结局指标块串味:主要终点 旧值会吞掉后续子标签(Measure time point of outcome: …)

  • ✅ 校验:对 468 个真实页面逐字段回查 HTML 原文,全库 28809 个字段值 0 处不符、0 处伪造值;重抓 468/468 成功

  • ✅ 修复**「纳入标准」「排除标准」整字段丢失**(发布后复查发现,影响全部记录):两个标签从未进入字段白名单,结构解析直接跳过——尽管单元格配对本身正确。实测覆盖率 0/468,修复后 468/468

  • ✅ 修复**_TAG_RE 吞掉正文中的小于号**:旧正则 r"<[^>]+>" 把 '转导效率<10%…' 里的 <10% 当成标签起点,一路删到后面某个 >,导致 9 条记录正文被静默抹掉。收紧为 r"</?[A-Za-z!][^>]*>"

  • ✅ 修复英文行空值覆盖中文行真实值:同一标签在页面出现两次(中文行 + 英文行),英文行值为空,旧代码的无条件赋值会覆盖中文行的真实值

  • ✅ tools/chictr_crawl.py 随包分发(npm install 后位于 node_modules/chictr-mcp-server/tools/):批量抓取入库脚本,支持 --keyword 换关键词、--list-years 预览规模、--export-json/--export-html 导出、按 project_id 去重、幂等续跑

v3.0.0 (2026-04-10)

  • ✅ 新增 Python sidecar 通道:用 Scrapling(curl_cffi TLS 指纹 + patchright 反检测内核)过一次阿里盾挑战拿到 cookie,之后全程纯 HTTP。搜索从「每页 5–10s」降到 ~1.5s,详情 ~2s

  • ✅ 新增环境自举:chictr-mcp-server doctor / setup / clean 三个 CLI 子命令,一条 setup 命令即可在干净机器上创建 venv、装依赖、按需装浏览器内核

  • ✅ 新增 MCP 工具 check_environment:Agent 可自查本机环境(Python / 依赖 / 浏览器内核 / sidecar 在线状态),按返回的 actions 决定是否调用 setup

  • ✅ 修复浏览器惰性初始化:原先任何工具调用都会先拉起 Chromium,导致 sidecar 命中也要付浏览器的代价;现改为仅在真正走 Playwright 时才初始化

  • ✅ npm 包内分发 sidecar 脚本(sidecar/chictr_sidecar.py),装上即具备 sidecar 能力

  • ✅ 修复 Python 版本下限从未生效的问题(MIN_PYTHON 此前未被使用,低版本 Python 会被误判为「就绪」)

  • ✅ 缓存清除工具不再附带旧的随机延时(sidecar 通道下不再需要人为降速)

v2.0.2 (2026-04-09)

  • ✅ 修复详情空对象缓存命中问题(自动失效并重查)

  • ✅ 进一步提升详情查询的有效内容获取稳定性

v2.0.1 (2026-04-09)

  • ✅ 修复 Cherry Studio 场景下 ./cache 路径导致的启动失败(ENOENT)

  • ✅ 默认缓存路径调整为 ~/.chictr/cache/chictr_cache.db

  • ✅ 增加 /tmp/chictr/cache/chictr_cache.db 兜底路径

v2.0.0 (2026-04-09)

  • ✅ 新增请求编排层(限速/重试/熔断)

  • ✅ 新增 Session 池化与生命周期回收

  • ✅ 新增挑战状态机与恢复工具(get_access_state / prepare_verification_session / resume_after_verification)

  • ✅ 新增双层缓存(L1 内存 + L2 SQLite)与 get_cache_stats_v2

v1.2.1 (2025-01-17)

  • ✅ 更新 README,添加多维度搜索示例

  • ✅ 添加版本升级指南

  • ✅ 提供 Cherrystudio 缓存清除方案

v1.2.0 (2025-01-17)

  • ✅ 新增按注册号搜索(registration_number 参数)

  • ✅ 新增按年份搜索(year 参数,默认当前年份)

  • ✅ 所有搜索参数改为可选

  • ✅ 修复详情查询 400 错误(使用正确的 project_id)

v1.1.0 (2025-01-17)

  • ✅ 修复分页功能,支持多页结果获取

  • ✅ 支持代理配置(HTTP_PROXY/HTTPS_PROXY)

  • ✅ 增加验证码检测与友好错误提示


🛠️ 技术栈

层

技术

Node 侧

TypeScript、MCP SDK、Cheerio、node-cache、better-sqlite3、Playwright

Python sidecar

Python ≥ 3.10、Scrapling(scrapling[fetchers])、curl_cffi、patchright、标准库 ThreadingHTTPServer


⚠️ 已知限制

诚实记录,未解决或未验证的项:

  1. Node 侧 Playwright 回退路径在本机不可用,且补装内核也无意义:Node playwright 1.62.1 需要 chromium-1234,而缓存里只有 chromium-1243(Python 侧 patchright 用的)。 但真正过不了阿里盾的是原生 Playwright,不是缺内核 —— dev/browser-removal/WAF_RESEARCH.md 记录的「真实 Chromium 同样卡死」正是用原生 Playwright + page.setContent() 得到的; 能过盾的是 patchright(反检测补丁)+ 真实导航。因此不建议补装 Node 侧内核(多约 359MB 换来一个仍会失败的路径,还会引入两套 Chromium 内核互相冲突),正确做法是把 sidecar 视为唯一路径。doctor 里该项显示 ❌ 属预期,不影响退出码(src/runtime/env-probe.ts:391 注明 Playwright 是「可选回退路径,不是故障」)。

  2. cookie 过期边界:实测存活约 37.5 分钟 < TTL 配置的 55 分钟。靠内容检测 + 重试恢复 (实测 5.7s),但 TTL 只是一个乐观的提前刷新信号。

  3. 并发上限未探明:只测到 10 并发(10/10 成功)。ChictrClient._lock 串行化了底层 HTTP 会话(curl_cffi session 非线程安全),所以并发是「排队」而非真并行。请勿盲目加大。

  4. 长驻稳定性未验证:StealthySession 长时间运行的稳定性未测(当前每次过盾新建一次 浏览器上下文并退出)。

  5. 请求频率上限未测:阿里盾在更高 QPS 下是否二次拦截未知。

  6. 研究实施时间 字段含中英对照噪音:值为 '从 \n From \n 2024-01-01 00:00:00 至 \n To \n 2027-01-01 00:00:00' 形态,中英标签夹在值中间。已不再是「粘连下一个字段」,但含英文对照 文本,需要时可自行剥离 From/To。

  7. ICTRP 双通道尚未实现:dev/browser-removal/DUAL_CHANNEL_ARCHITECTURE.md 是设计稿; 本项目当前没有 WHO ICTRP 通道,也没有 source= 参数。

  8. headless 模式下无法手动处理验证码:频繁请求仍可能触发滑动验证码。


⚖️ 合规说明

  • ChiCTR 在 re3data 登记为数据访问 restricted,站点页脚为 All rights reserved.。

  • 无官方 API、无 bulk 下载通道。

  • 因此:「可获得 ≠ 可再分发」。 本项目定位为受控环境下的个人研究/情报用途, 不做数据再分发。若要长期依赖 ChiCTR 直连,建议同时推进对 chictr-s7@wchscu.cn 的正式授权问询。

  • 绕过 WAF 是与站点访问决策的对抗,属政策判断而非技术判断 —— 技术结论是 「访问可行」,合规判断请自行确认。相关背景见 dev/browser-removal/SCRAPLING_SOLUTION.md §6 与 SIDECAR_RUNBOOK.md。


📄 许可证

MIT License

🙏 致谢

本项目使用中国临床试验注册中心 (ChiCTR) 的公开数据,感谢 ChiCTR 为医学研究做出的贡献。 特别感谢小胰宝和小x宝社区的❤️贡献与付出,用爱心与人工智能为癌症/罕见病患者及其家庭提供支持!

📞 相关文档

文件

说明

dev/browser-removal/SCRAPLING_SOLUTION.md

过盾可行性三层实测证据(含复现命令)

dev/browser-removal/SIDECAR_RUNBOOK.md

sidecar 运维手册

dev/browser-removal/SIDECAR_VERIFICATION.md

sidecar 端到端验证报告与实测数据

dev/browser-removal/DEPLOYMENT.md

开箱即用部署指南(dmg / exe)

dev/browser-removal/DUAL_CHANNEL_ARCHITECTURE.md

双通道降级架构(设计稿)

sidecar/chictr_sidecar.py

sidecar 实现(过盾 + 解析 + HTTP 服务)

src/runtime/sidecar-client.ts

Node 侧客户端(默认关闭,失败自动回退)

如有问题,请提交 GitHub Issue。

Available Tools

4 tools
clear_cacheB

清除所有缓存数据

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. '清除所有缓存数据' implies a destructive mutation (clearing all cache), but it doesn't specify whether this action is reversible, requires authentication, has side effects (e.g., temporary slowdown), or returns confirmation. For a mutation tool with zero annotation coverage, this is a significant gap in safety and operational context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence ('清除所有缓存数据') with zero waste. It's front-loaded with the core action and resource, making it immediately understandable. No extraneous words or structural issues are present.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (destructive mutation with no parameters) and lack of annotations/output schema, the description is incomplete. It doesn't cover behavioral aspects like permissions needed, confirmation process, or impact on system performance. For a tool that clears all cache data, this minimal description leaves critical operational gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has 0 parameters, and schema description coverage is 100% (empty schema). The description doesn't need to explain parameters, as there are none. It appropriately focuses on the tool's action without redundant parameter details, meeting the baseline for zero-parameter tools.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description '清除所有缓存数据' (Clear all cache data) clearly states the tool's action and resource with a specific verb ('清除' - clear) and target ('所有缓存数据' - all cache data). It distinguishes from sibling tools like 'get_cache_stats' (read-only) and 'search_trials' (different resource), though it doesn't explicitly mention these distinctions. The purpose is unambiguous but lacks explicit sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., admin permissions), consequences (e.g., performance impact), or when to avoid it (e.g., during peak usage). With siblings like 'get_cache_stats' for monitoring, the description fails to help the agent choose between read and write operations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_cache_statsB

获取缓存统计信息,包括搜索缓存和详情缓存的命中率等

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. While it mentions what statistics are retrieved (hit rates for search and detail caches), it doesn't disclose important behavioral traits like whether this requires authentication, rate limits, what format the statistics are returned in, or whether this is a read-only operation. The description is minimal and lacks operational context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that states the purpose without unnecessary elaboration. It's appropriately sized for a zero-parameter tool that retrieves statistics, though it could be slightly more structured by explicitly stating it's a read operation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has no parameters, no annotations, and no output schema, the description provides basic purpose information but lacks important context. For a statistics retrieval tool, it should ideally mention the format of returned data, whether it's real-time or cached statistics, and any authentication requirements. The description is minimally adequate but has clear gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has 0 parameters with 100% schema description coverage, so the baseline is 4. The description appropriately doesn't discuss parameters since none exist, and the schema already fully documents this (empty object).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: '获取缓存统计信息' (get cache statistics) with specific mention of '搜索缓存和详情缓存的命中率等' (hit rates for search cache and detail cache). It uses a specific verb ('获取' - get) and resource ('缓存统计信息' - cache statistics), though it doesn't explicitly distinguish from sibling tools like clear_cache.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. There's no mention of when this tool should be used, when it shouldn't, or how it relates to sibling tools like clear_cache, get_trial_detail, or search_trials.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_trial_detailB

根据注册号查询临床试验的完整详细信息

ParametersJSON Schema
NameRequiredDescriptionDefault
registration_numberYes临床试验注册号,如 'ChiCTR2400084905'

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It states this is a query operation, implying it's likely read-only and non-destructive, but doesn't explicitly confirm this or describe other traits like error handling, rate limits, authentication needs, or what constitutes '完整详细信息' (complete detailed information) in the response. For a tool with zero annotation coverage, this leaves significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's purpose without any wasted words. It's appropriately sized for a simple lookup tool and front-loaded with the core functionality, making it easy to parse quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's low complexity (one required parameter, no nested objects) and high schema coverage, the description is adequate but has clear gaps. It lacks output schema information, so the agent doesn't know what '完整详细信息' entails in the response. Combined with no annotations and minimal behavioral context, this leaves the description incomplete for optimal agent use, though it meets minimum viability for a simple query tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds minimal semantic context beyond the input schema. It mentions the parameter '注册号' (registration number), which aligns with the schema's 'registration_number' property. However, with 100% schema description coverage (the schema already documents the parameter as '临床试验注册号,如 'ChiCTR2400084905''), the description doesn't provide additional details like format examples or usage nuances. This meets the baseline score of 3 when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: '根据注册号查询临床试验的完整详细信息' (Query complete detailed information of clinical trials based on registration number). It specifies the verb ('查询' - query) and resource ('临床试验的完整详细信息' - complete detailed information of clinical trials), making the purpose unambiguous. However, it doesn't explicitly differentiate from sibling tools like 'search_trials', which might have overlapping functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention sibling tools like 'search_trials' or clarify scenarios where this tool is preferred (e.g., for looking up specific trials by exact registration number versus broader searches). There's also no information about prerequisites or exclusions, leaving usage context implied at best.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_trialsC

搜索ChiCTR临床试验。支持按标题关键词、注册号、年份进行搜索,返回试验列表。

ParametersJSON Schema
NameRequiredDescriptionDefault
keywordNo注册题目关键词,如 'KRAS G12C'、'胰腺癌' 等(可选)
registration_numberNo临床试验注册号,如 'ChiCTR2500111173'(可选)
yearNo注册年份,如 2024、2025,默认为当前年份(可选)
max_resultsNo最大返回结果数,默认20

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. While it mentions the search functionality and return format (trial list), it lacks important behavioral details: whether this is a read-only operation (implied but not stated), any rate limits, authentication requirements, pagination behavior beyond max_results, or what happens when no results are found. The description provides basic functional information but misses key operational context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately concise - a single sentence that efficiently communicates the core functionality. It's front-loaded with the main action (searching ChiCTR clinical trials) followed by the search criteria and return format. There's no wasted language, though it could potentially benefit from slightly more detail given the lack of annotations and output schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of a search tool with 4 parameters, no annotations, and no output schema, the description is incomplete. While it covers the basic 'what', it lacks important context: what the returned trial list contains, how results are ordered, whether all parameters are optional (implied but not stated), error conditions, or any limitations of the search functionality. The absence of output schema means the description should ideally provide some indication of return format.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description mentions the three main search parameters (title keywords, registration number, year) which aligns with the input schema's properties. However, with 100% schema description coverage, the schema already provides excellent parameter documentation including examples and defaults. The description adds minimal value beyond what's in the schema - it confirms the parameter purposes but doesn't provide additional context about parameter interactions, search logic, or edge cases.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: searching ChiCTR clinical trials with specific search criteria (title keywords, registration number, year) and returning a list of trials. It uses specific verbs ('搜索' - search, '返回' - return) and identifies the resource (ChiCTR临床试验). However, it doesn't explicitly differentiate from sibling tools like 'get_trial_detail' which likely retrieves detailed information about a specific trial rather than searching multiple trials.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. While it mentions what the tool does, it doesn't indicate when it's appropriate to use search_trials versus get_trial_detail (which presumably gets details for a specific trial) or other siblings. There's no mention of prerequisites, limitations, or typical use cases beyond the basic functionality.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv1.2.1
    • First observedclear_cache
    • First observedget_cache_stats
    • First observedget_trial_detail
    • First observedsearch_trials

TDQS

B3.4/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose with no overlap: clear_cache removes data, get_cache_stats retrieves performance metrics, get_trial_detail fetches a single trial by ID, and search_trials finds multiple trials based on criteria. An agent can easily differentiate these functions.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern in snake_case: clear_cache, get_cache_stats, get_trial_detail, and search_trials. This uniformity makes the set predictable and easy to understand.

Tool Count4/5

With 4 tools, the count is reasonable for a ChiCTR trials server, covering core operations like search and detail retrieval. It feels slightly thin but not inadequate, as it supports basic workflows without bloat.

Completeness3/5

The toolset covers search and detail retrieval for trials, plus cache management, but lacks update or creation tools for trials, which might be expected in a full CRUD lifecycle. This gap could limit agent actions in dynamic scenarios.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers