Install
$ agentstack add mcp-blencorp-clinicaltrials-mcp-server Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Pipes remote content directly into a shell (remote code execution).
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ● Shell / process execution Used
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
ClinicalTrials.gov Explorer (by BLEN)
MCP server for ClinicalTrials.gov. Exposes the v2 REST API as a typed TypeScript SDK that the model calls from inside a secure sandbox, following Cloudflare's "code mode" pattern.
> Display name in the Claude directory: ClinicalTrials.gov Explorer (by BLEN). > Unaffiliated with NIH / NLM / ClinicalTrials.gov. Data retrieved live from > .
Table of contents
- [What "code mode" means here](#what-code-mode-means-here)
- [Quickstart — use it with Claude Desktop (npx)](#quickstart--use-it-with-claude-desktop-npx)
- [Build an MCPB desktop extension](#build-an-mcpb-desktop-extension)
- [Local development](#local-development)
- [Prerequisites](#prerequisites)
- [Troubleshooting:
No sandbox executor available](#troubleshooting-no-sandbox-executor-available) - [Setup](#setup)
- [Running the server locally](#running-the-server-locally)
- [Point Claude Desktop at your dev checkout (stdio)](#point-claude-desktop-at-your-dev-checkout-stdio)
- [Useful scripts](#useful-scripts)
- [Architecture at a glance](#architecture-at-a-glance)
- [MCP tools](#mcp-tools)
- [Configuration reference](#configuration-reference)
- [Sandbox](#sandbox)
- [Remote (hosted) mode](#remote-hosted-mode)
- [AWS deploy](#aws-deploy)
- [Directory submission](#directory-submission)
- [Testing](#testing)
- [Behavior when no sandbox is installed](#behavior-when-no-sandbox-is-installed)
- [Data handling](#data-handling)
- [Contributing, support, license](#contributing-support-license)
What "code mode" means here
Rather than exposing each API operation as a separate MCP tool (the usual pattern, which quickly blows up the LLM's context), this server ships three tools:
| Tool | Purpose | |---|---| | search_api | BM25 search over endpoints + study-field docs. Returns a TypeScript snippet the model should paste. | | describe_schema | Look up study-field docs by exact path or prefix. | | execute | Run a short async TypeScript body against a typed ctgov SDK in a sandboxed V8 isolate (or Deno fallback). |
The full ClinicalTrials.gov surface fits in a few hundred tokens this way. The model writes one function, chains calls, filters locally, and returns results.
Quickstart — use it with Claude Desktop (npx)
Prerequisites
- Node.js 20.10+ (
22.xrecommended when you want the smoothest
isolated-vm experience)
- One sandbox runtime — the
executetool needs somewhere to run
untrusted code. You need either:
isolated-vm(installed automatically bypnpm install/npm install
when prebuilt binaries exist for your platform, or when a C toolchain + Python 3 are available for a native build), or
- the Deno CLI on
PATHas a fallback
(brew install deno on macOS, curl -fsSL https://deno.land/install.sh | sh on Linux/WSL).
If neither is present, the server will start but refuse execute calls with No sandbox executor available. See [Troubleshooting](#troubleshooting-no-sandbox-executor-available) for the fix.
# One-time sanity check
npx -y @blen/clinicaltrial-mcp-server --version
Add to your Claude Desktop config (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json, Windows: %APPDATA%\Claude\claude_desktop_config.json):
{
"mcpServers": {
"ctgov": {
"command": "npx",
"args": ["-y", "@blen/clinicaltrial-mcp-server"]
}
}
}
Restart Claude Desktop; the tools appear under the ClinicalTrials.gov Explorer entry.
Build an MCPB desktop extension
This repo can now generate a Claude Desktop extension bundle (.mcpb) using Anthropic's MCPB workflow. The default bundle is cross-platform for Claude Desktop on macOS and Windows: it embeds the Deno helpers needed by the execute sandbox so end users do not have to install Deno themselves.
Build the extension:
pnpm install
pnpm mcpb:pack
That usually produces:
.mcpb/dist/clinicaltrial-mcp-server-.mcpb
Useful commands:
pnpm mcpb:prepare # stage a clean production bundle under .mcpb/stage
pnpm mcpb:validate # validate the generated manifest with the official CLI
pnpm mcpb:pack # validate + pack the final .mcpb archive
Notes:
- The pack step requires a local Deno binary on the build machine. The script
copies the host binary into the extension bundle, downloads matching helpers for the other bundled targets, and points CTGOV_DENO_ROOT at the packaged helper directory.
- The default target set is
darwin-arm64,darwin-x64, andwin32-x64, so
the same .mcpb works in Claude Desktop on macOS and Windows.
- To narrow or customize the bundle, set
CTGOV_MCPB_TARGETS, for example:
CTGOV_MCPB_TARGETS=darwin-arm64,win32-x64 pnpm mcpb:pack.
- Installation in Claude Desktop follows Anthropic's MCPB flow:
double-click the file, drag it into Claude Desktop, or use Developer -> Extensions -> Install Extension.
Local development
Clone the repo, install, run tests, and point Claude at your local checkout.
Prerequisites
- Node.js 20.10+ (
22.xrecommended for nativeisolated-vm) - pnpm 10.33+ (
corepack enable && corepack prepare pnpm@10.33.0 --activate) - A sandbox runtime — at least one of:
isolated-vm(default, fastest). Native addon; installs viapnpm install.
Prebuilt binaries cover common platforms; when they don't, a C toolchain + Python 3 is required (build-essential on Debian/Ubuntu; xcode-select --install on macOS).
- Deno — the subprocess fallback. Install with
brew install deno / curl -fsSL https://deno.land/install.sh | sh.
- (Optional) Docker + BuildKit for the container build
- (Optional) Terraform ≥ 1.7 and AWS CLI v2 for the AWS deploy module
Troubleshooting: No sandbox executor available
If pnpm test prints CtGovError: No sandbox executor available. Install isolated-vm (...) or the Deno CLI (...), your machine has neither sandbox configured. Pick one:
Easiest — install Deno (works on every platform, no toolchain needed):
# macOS
brew install deno
# Linux / WSL
curl -fsSL https://deno.land/install.sh | sh
Then re-run pnpm test. The sandbox-requiring tests will pick up Deno automatically; you'll see them pass instead of skip.
Alternative — make isolated-vm build locally:
# macOS (Apple Silicon or Intel)
xcode-select --install # installs clang + headers
# pnpm's prebuild fallback will invoke node-gyp against system Python 3
# Debian / Ubuntu
sudo apt-get install -y build-essential python3
# Then rebuild just this native dep:
pnpm rebuild isolated-vm
node -e 'require("isolated-vm"); console.log("ok")'
If pnpm install finished with Ignored build scripts: isolated-vm — pnpm's approve-builds policy blocked the native compile. This repo ships pnpm-workspace.yaml with onlyBuiltDependencies: [isolated-vm] which should allow it; if it still complains, run pnpm approve-builds once and re-run pnpm install.
Sandbox-requiring test suites (sandbox.spec.ts, sandboxChaos.spec.ts, httpServer.spec.ts, authHttp.spec.ts, embeddedEndToEnd.spec.ts) skip gracefully when neither sandbox is available so the rest of the suite stays green.
Setup
git clone https://github.com/blencorp/claude-playground.git
cd claude-playground
cp .env.example .env # edit values; see Configuration reference
pnpm install # builds isolated-vm via node-gyp
pnpm typecheck # strict TS pass
pnpm lint # eslint
pnpm test # ~60 tests, stdio | Streamable HTTP + OAuth"]
Server["buildMcpServertools: search_api, describe_schema, execute"]
Supervisor["SubjectQuota (per-sub + global 10 rps)HttpClient: undici, LRU, retry, zod, audit, stale-cache"]
Sandbox["Sandbox (isolated-vm | deno)AST allow-list (acorn)ctgov.* SDK via RPC only"]
CTGov[("ClinicalTrials.gov v2")]
Transport --> Server
Server --> Supervisor
Supervisor --> Sandbox
Supervisor --> CTGov
Details in [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md).
MCP tools
search_api(query, k?)
Semantic BM25 search over the 9 API endpoints and the curated study-field dictionary. Returns a TypeScript snippet the model should paste plus a short summary of hit counts.
describe_schema(path? | prefix?)
Lookup for study-data fields by exact dotted path (e.g. protocolSection.eligibilityModule.eligibilityCriteria) or by prefix (e.g. protocolSection.designModule).
execute(code, timeoutMs?, memoryMb?)
Wraps the submitted body as async (ctgov) => { } and runs it in the sandbox. The return value is JSON-serialized and returned. The SDK surface inside the sandbox:
ctgov.studies.search(params) // GET /studies
ctgov.studies.searchAll(params, o?) // async iterator over pages
ctgov.studies.get(nctId, params?) // GET /studies/{nctId}
ctgov.studies.metadata(params?) // GET /studies/metadata
ctgov.studies.searchAreas() // GET /studies/search-areas
ctgov.studies.enums() // GET /studies/enums
ctgov.stats.size() // GET /stats/size
ctgov.stats.fieldValues({fields}) // GET /stats/field/values
ctgov.stats.fieldSizes({fields}) // GET /stats/field/sizes
ctgov.version() // GET /version
Five worked examples live in [examples/](./examples).
Configuration reference
All configuration is via environment variables. Copy [.env.example](./.env.example) to .env and edit.
Transport / runtime
| Variable | Default | Description | |---|---|---| | PORT | 8080 | HTTP port (used with --http). | | HOST | 0.0.0.0 | HTTP bind address. | | CTGOV_LOG_LEVEL | info | debug / info / warn / error. | | CTGOV_SANDBOX | auto | isolate / deno / auto. Equivalent to --sandbox. | | CTGOV_DENO_PATH | (PATH lookup) | Explicit path to the deno binary for the fallback executor. | | CTGOV_DENO_ROOT | (none) | Directory containing bundled Deno helpers laid out as -/deno(.exe); mainly used by the packaged MCPB bundle. |
Auth
| Variable | Default | Description | |---|---|---| | CTGOV_AUTH_PROVIDER | none | clerk / workos / auth0 / generic-oidc / embedded / none. | | CTGOV_AUTH_ISSUER | (none) | Authorization server URL. Required when provider ≠ none. | | CTGOV_AUTH_JWKS_URL | derived | Defaults to ${issuer}/.well-known/jwks.json (or ${issuer}/as/jwks.json for embedded). | | CTGOV_AUTH_RESOURCE | https://clinicaltrials.mcp.blencorp.com/mcp | RFC 8707 audience / our resource identifier. | | CTGOV_AUTH_SCOPES | ctgov.read | Whitespace- or comma-separated list. |
Network / rate limiting
| Variable | Default | Description | |---|---|---| | CTGOV_TRUST_PROXY | (off) | Set to 1 when the server sits behind a known reverse proxy (Railway, Cloudflare, ALB). The per-IP limiter will then read the first entry of x-forwarded-for instead of the socket peer. | | CTGOV_IP_RPS | 5 | Sustained per-IP request rate on /mcp and /as/*. Health checks are exempt. | | CTGOV_IP_BURST | 20 | Burst capacity per IP. Requests over the bucket get 429 with Retry-After. | | CTGOV_MAX_SESSIONS | 500 | Upper bound on concurrent MCP sessions (idle-swept every minute). |
Embedded AS (self-host only)
| Variable | Description | |---|---| | CTGOV_EMBEDDED_USERS | alice:pass1,bob:pass2 — HTTP Basic credentials resolved during /as/authorize. |
Tests / scripts
| Variable | Description | |---|---| | CTGOV_LIVE | Set to 1 to enable test/liveIntegration.spec.ts (hits real CT.gov). | | CTGOV_BASE | Override the upstream base URL for the regen:sdk and verify:schema scripts. |
Sandbox
Auto-selects isolated-vm (V8 isolate, native addon) and falls back to a deno subprocess with --no-prompt --allow-none --no-npm --no-remote when the native addon isn't available. Both enforce identical policy:
- No network, no filesystem, no env, no FFI, no subprocess.
- AST preflight (acorn) rejects direct escape hatches like
import,
dynamic import(), eval, new Function, process, __host, and unsafe prototype access before execution. The runtime sandbox remains the primary security boundary.
- 15 s wall-clock, 64 MB heap (isolate mode); per-call overrides available
via timeoutMs / memoryMb.
- Only capability: the
ctgov.*binding proxied over host RPC.
Force a specific executor:
clinicaltrial-mcp-server --sandbox isolate # require isolated-vm
clinicaltrial-mcp-server --sandbox deno # require deno on PATH
clinicaltrial-mcp-server --sandbox auto # default
Remote (hosted) mode
Run the same server over the MCP Streamable HTTP transport with a real OAuth Authorization Server in front.
With Clerk (production default)
export CTGOV_AUTH_PROVIDER=clerk
export CTGOV_AUTH_ISSUER=https://clerk.blencorp.com
export CTGOV_AUTH_RESOURCE=https://clinicaltrials.mcp.blencorp.com/mcp
export CTGOV_AUTH_SCOPES=ctgov.read
node dist/bin.js --http --port 8080
Clerk (or WorkOS / Auth0 / any OIDC AS) must be configured with RFC 7591 Dynamic Client Registration so Claude can self-register, and the audience / resource set to https://clinicaltrials.mcp.blencorp.com/mcp (RFC 8707).
With the built-in embedded AS (self-host)
export CTGOV_AUTH_PROVIDER=embedded
export CTGOV_AUTH_ISSUER=https://your.host # must match where this server is reachable
export CTGOV_AUTH_RESOURCE=https://your.host/mcp
export CTGOV_EMBEDDED_USERS=alice:wonderland
node dist/bin.js --http --port 8080
Endpoints exposed:
| Path | Purpose | |---|---| | POST /mcp | MCP Streamable HTTP (stateful per-session transport). | | GET /healthz, /readyz | ALB health checks. | | GET /.well-known/oauth-protected-resource | RFC 9728 PRM. | | GET /.well-known/oauth-authorization-server | RFC 8414 AS metadata (proxied from issuer, or served directly by the embedded AS). | | POST /as/register | (embedded) RFC 7591 DCR. | | GET /as/authorize | (embedded) OAuth 2.1 + PKCE S256. | | POST /as/token | (embedded) authorizationcode / refreshtoken grants. | | GET /as/jwks.json | (embedded) public JWKS. |
A missing/invalid bearer returns 401 with WWW-Authenticate: Bearer realm="…", resource_metadata="…" pointing at the PRM.
AWS deploy
Terraform module at [deploy/aws/](./deploy/aws/README.md) stands up:
ECS Fargate + ALB + ACM + Route 53 + Secrets Manager + CloudWatch Logs + WAFv2
on clinicaltrials.mcp.blencorp.com, with a GitHub Actions OIDC deployer role. The container is built from [deploy/Dockerfile](./deploy/Dockerfile) (multi-stage, non-root, tini, isolated-vm compiled in).
cd deploy/aws
cp terraform.tfvars.example terraform.tfvars # edit clerk_issuer, CIDRs, etc.
terraform init
terraform apply
# Build & push first image
ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
ECR=$(terraform output -raw ecr_repository_url)
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin "$ACCOUNT.dkr.ecr.us-east-1.amazonaws.com"
docker buildx build --platform linux/amd64 -t "$ECR:v0.1.0-alpha.0" -f deploy/Dockerfile . --push
aws ecs update-service --cluster clinicaltrial-mcp-server --service clinicaltrial-mcp-server --force-new-deployment
Subsequent deploys happen automatically from [.github/workflows/deploy.yml](./.github/workflows/deploy.yml) on pushes to main.
Railway deploy (alternative)
Railway can host the same container directly from [deploy/Dockerfile](./deploy/Dockerfile). Railway terminates TLS, manages th
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: blencorp
- Source: blencorp/clinicaltrials-mcp-server
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.