Skip to main content
Glama

ClinicalTrials.gov MCP Server

.clinerules9 kB
# ClinicalTrials.gov MCP Server: Developer Guide & Architectural Standards **Effective Date:** 2025-07-26 **Version:** 2.0 ## Preamble This document mandates the development practices, architectural patterns, and operational procedures for the `clinicaltrialsgov-mcp-server`. It is the authoritative guide for ensuring code quality, consistency, and maintainability. All development must adhere to these standards. ## I. Core Architectural Principles Our architecture is built on a clear separation of concerns, ensuring that code is modular, testable, and easy to understand. ### 1. Logic Throws, Handlers Catch This is the immutable cornerstone of our error-handling and control-flow strategy. - **Core Logic (`logic.ts`)**: This layer's sole responsibility is the execution of pure business logic. It must be self-contained. If an operational or validation error occurs, it **must** terminate its execution by **throwing a structured `McpError`**. Logic files shall **not** contain `try...catch` blocks for formatting a final response. - **Handlers (`registration.ts`, Transports)**: This layer's responsibility is to interface with the MCP server, invoke core logic, and manage the final response lifecycle. It **must** wrap every call to the logic layer in a `try...catch` block. This is the **exclusive** location where errors are caught, processed by the `ErrorHandler`, and formatted into a definitive `CallToolResult`. ### 2. Structured, Traceable Operations Every operation must be fully traceable via structured logging and context propagation. - **`RequestContext`**: Any significant operation shall be initiated by creating a `RequestContext`. This context, containing a unique `requestId`, **must** be passed as an argument through the entire call stack. - **`Logger`**: All logging shall be performed through the centralized `logger` singleton. Every log entry **must** include the `RequestContext`. ## II. Tool Development Workflow This section mandates the workflow for creating and modifying all tools. ### A. File and Directory Structure Each tool shall reside in a dedicated directory within `src/mcp-server/tools/` and follow this structure: - **`toolName/`** - **`index.ts`**: A barrel file that exports only the `register...` function from `registration.ts`. - **`logic.ts`**: Contains the tool's core business logic. It **must** define and export the tool's Zod input and output schemas, all inferred TypeScript types, and the main logic function. - **`registration.ts`**: Registers the tool with the MCP server. It imports from `logic.ts` and implements the "Handler" role. ### B. The Authoritative Pattern: `getStudy` Tool The `clinicaltrials_get_study` tool serves as a canonical example for tool development in this project. **Step 1: Define Schemas and Logic (`logic.ts`)** The `logic.ts` file defines the "what" and "how" of the tool. It is self-contained and throws errors when it cannot fulfill its contract. ```typescript /** * @fileoverview Defines the core logic, schemas, and types for the `clinicaltrials_get_study` tool. * @module src/mcp-server/tools/getStudy/logic */ import { z } from "zod"; import { logger, type RequestContext } from "../../../utils/index.js"; import { ClinicalTrialsGovService } from "../../../services/clinical-trials-gov/ClinicalTrialsGovService.js"; import { Study } from "../../../services/clinical-trials-gov/types.js"; import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // 1. DEFINE the Zod input schema. // CRITICAL: Descriptions are sent to the LLM. They must be clear, concise, // and contain all necessary context for the model to use the tool effectively. export const GetStudyInputSchema = z.object({ nctId: z .string() .regex(/^[Nn][Cc][Tt]\d+$/) .describe("The NCT Number of the study to fetch."), // ... other fields }); // 2. DEFINE the Zod response schema for structured output. export const GetStudyOutputSchema = Study; // Assuming 'Study' is a Zod schema // 3. INFER and export TypeScript types. export type GetStudyInput = z.infer<typeof GetStudyInputSchema>; export type GetStudyOutput = z.infer<typeof GetStudyOutputSchema>; /** * 4. IMPLEMENT the core logic function. * It must remain pure; its only concerns are its inputs and its return value or thrown error. * @param params The validated input parameters. * @param context The request context for logging and tracing. * @returns A promise resolving with the structured study data. * @throws {McpError} If the logic encounters an unrecoverable issue. */ export async function getStudyLogic( params: GetStudyInput, context: RequestContext ): Promise<GetStudyOutput> { logger.debug(`Fetching study ${params.nctId}...`, { ...context }); const service = new ClinicalTrialsGovService(); const study = await service.fetchStudy(params.nctId, context); if (!study) { throw new McpError( BaseErrorCode.NOT_FOUND, `Study with NCT ID '${params.nctId}' not found.` ); } return study; } ``` **Step 2: Register the Tool and Handle Outcomes (`registration.ts`)** The `registration.ts` file wires the logic into the MCP server and handles all outcomes. ```typescript /** * @fileoverview Handles registration and error handling for the `clinicaltrials_get_study` tool. * @module src/mcp-server/tools/getStudy/registration */ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js"; import { GetStudyInput, GetStudyInputSchema, getStudyLogic, GetStudyOutputSchema } from "./logic.js"; import { McpError } from "../../../types-global/errors.js"; /** * Registers the 'clinicaltrials_get_study' tool with the MCP server. * @param server - The MCP server instance. */ export const registerGetStudyTool = async (server: McpServer): Promise<void> => { const toolName = "clinicaltrials_get_study"; server.registerTool( toolName, { title: "Get Clinical Study", description: "Retrieves detailed information for a single clinical study by its NCT number.", inputSchema: GetStudyInputSchema.shape, outputSchema: GetStudyOutputSchema.shape, // MANDATORY annotations: { readOnlyHint: true }, }, async (params: GetStudyInput, callContext) => { const handlerContext = requestContextService.createRequestContext({ toolName, parentContext: callContext }); try { const result = await getStudyLogic(params, handlerContext); return { structuredContent: result, content: [{ type: "text", text: `Successfully retrieved study ${params.nctId}.` }], }; } catch (error) { logger.error(`Error in ${toolName} handler`, { error, ...handlerContext }); const mcpError = ErrorHandler.handleError(error, { operation: toolName, context: handlerContext, input: params, }) as McpError; return { isError: true, content: [{ type: "text", text: mcpError.message }], structuredContent: { code: mcpError.code, message: mcpError.message, details: mcpError.details, }, }; } } ); logger.info(`Tool '${toolName}' registered successfully.`); }; ``` ## III. Integrating External Services For interacting with the ClinicalTrials.gov API, use the dedicated service singleton. - **`ClinicalTrialsGovService`**: Located at `src/services/clinical-trials-gov/ClinicalTrialsGovService.ts`, this class manages all interactions with the external API. It encapsulates the base URL, handles request construction, and performs initial response validation. - **Usage**: Import the `ClinicalTrialsGovService` into your tool's `logic.ts` file to use it. ## IV. Code Quality and Documentation - **JSDoc**: Every file must start with a `@fileoverview` and `@module` block. All exported functions and types must have clear, concise JSDoc comments. - **LLM-Facing Descriptions**: The tool's `title`, `description`, and all parameter descriptions in Zod schemas (`.describe()`) are transmitted directly to the LLM. They must be written with the LLM as the primary audience, being descriptive, concise, and explicit about requirements. - **Clarity Over Brevity**: Write self-documenting code with meaningful variable and function names. - **Formatting**: All code must be formatted using Prettier (`npm run format`) before committing. ## V. Security Mandates - **Input Sanitization**: All inputs from external sources must be treated as untrusted and validated with Zod. - **Secrets Management**: All secrets must be loaded from environment variables via the `config` module. - **Authentication & Authorization**: The server supports `jwt` and `oauth` modes. Protect tools by checking scopes where necessary. This guide is the single source of truth for development standards. All code reviews will be conducted against these principles.

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cyanheads/clinicaltrialsgov-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server