A multi-language MCP server implementation for FHIR patient data access with tools for retrieving patient information, age, allergies, and IDs
This server has severe definition quality issues. Tool definitions are present with schemas and descriptions, but there are critical problems with naming consistency, duplication, and parameter design. The codebase shows 7 tools declared, but tools 1, 6 and tools 2, 7 are duplicates (GetPatientAge appears twice, FindPatientId appears twice). Tool 3-5 are Python variants (snake_case) of the same functionality. This indicates poor composition and namespace management. Descriptions are extremely minimal (10-20 chars), falling well below the 10-1024 char baseline and the 50-200 char LLM-optimized range. All parameter descriptions are present but terse. Schemas are present and properly typed (JSON Schema format visible in McpToolExtensions.cs), but parameter 'patientId' and 'firstName' lack clear contextual constraints. No output schemas are documented in the visible code. Error handling exists (McpClientCallToolService shows try-catch with error messaging) but responses are generic text without recovery guidance or error classification.
Finds a patient id given a first name and last name
Finds a patient id given a first name and last name
Gets the age of a patient.
Gets the age of a patient.
Finds a patient id given a first name and last name
Gets the age of a patient.
Gets the known allergies for a patient.
Tool name duplication: GetPatientAge and FindPatientId appear twice in the registry (tools 1+6, 2+7). This creates ambiguity and violates single-responsibility composition. Agents will not know which variant to call.
Mixed naming conventions: TypeScript tools use PascalCase (GetPatientAge, FindPatientId) while Python tools use snake_case (get_patient_age, find_patient_id). This inconsistency forces LLMs to guess the correct name and breaks tool discovery. Choose one convention across all implementations.
Descriptions are critically short: 'Gets the age of a patient' (27 chars) and 'Finds a patient id given a first name and last name' (51 chars). These fall well below the recommended 50-200 char LLM-optimized range and lack WHEN to use, context, or outcomes. Minimum viable description: 'Retrieves the patient's current age by patient ID. Use this after identifying the patient with find_patient_id(). Returns age in years.'
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | F | 47 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 49 | - | v1 |
Optional parameters without defaults or constraints: patientId is marked optional ('This is optional if patient context already exists'), but there is no documentation of HOW this context is maintained or provided. Agents cannot determine whether to pass patientId, leading to missing-parameter errors. Either make it required or implement and document context-passing mechanism.
No output schema documentation visible: The source code does not show what fields are returned by any tool (e.g., does get_patient_age return { age: number } or { age: number, dateOfBirth: string, ageInMonths: number }?). Without documented return schemas, LLMs cannot plan downstream tool calls or extract necessary data.
Error messages are generic without recovery guidance: McpClientCallToolService returns 'An internal exception occurred with message: {exception.Message}' and 'A tool handler was not found for the tool: {context.Params.Name}.' These do not tell LLMs what to do next (retry, try a different tool, check parameters). Add actionable recovery guidance like 'Tool not found. Available tools: [list]' or 'Invalid patientId format. Expected 5-digit numeric ID.'
No tool annotations present: The schema does not include readOnlyHint, destructiveHint, or idempotentHint. All these tools appear to be read-only (Risk: READ_ONLY per metadata), but this is not formally declared in the tool schema. Add tool annotations so agents know safe vs. unsafe operations.