Skip to main content
Glama
u9401066

Medical Calculator MCP Server

by u9401066

Medical Calculator MCP Server 🏥

A DDD-architected medical calculator service providing clinical scoring tools for AI Agent integration via MCP (Model Context Protocol).

繁體中文版 (Traditional Chinese)

Python 3.11+ MCP SDK License CI Tests References uv Code Style Architecture PRs Welcome


📖 Table of Contents


Related MCP server: MedCalc MCP Server

🎯 Features

  • 🔌 MCP Native Integration: Built with FastMCP SDK for seamless AI agent integration

  • 🔍 Intelligent Tool Discovery: Two-level key system + Tool Relation Graph (Hypergraph) for smart tool selection

  • 🛡️ Smart Parameter Matching: Alias support, fuzzy matching, and typo tolerance

  • ⚠️ Boundary Validation: Literature-backed clinical range checking with automatic warnings

  • 🏗️ Clean DDD Architecture: Onion architecture with clear separation of concerns

  • 📚 Evidence-Based: All 152 calculators cite peer-reviewed research (100% coverage, Vancouver style)

  • 🔒 Type Safe: Full Python type hints with dataclass entities

  • 🌐 Bilingual: Chinese/English documentation and tool descriptions


🤔 Why This Project?

The Problem

When AI agents (like Claude, GPT) need to perform medical calculations, they face challenges:

  1. Hallucination Risk: LLMs may generate incorrect formulas or values

  2. Version Confusion: Multiple versions of same calculator (e.g., MELD vs MELD-Na vs MELD 3.0)

  3. No Discovery Mechanism: How does an agent know which tool to use for "cardiac risk assessment"?

The Solution

This project provides:

Feature

Description

Validated Calculators

Peer-reviewed, tested formulas

Tool Discovery

AI can search by specialty, condition, or clinical question

MCP Protocol

Standard protocol for AI-tool communication

Paper References

Every calculator cites original research

🧪 Development Methodology

We employ a human-in-the-loop, AI-augmented workflow to ensure clinical accuracy:

  1. Domain Specification: Human experts define the target medical specialty or clinical domain.

  2. AI-Driven Search: AI agents perform comprehensive searches for the latest clinical guidelines and consensus.

  3. Guideline Extraction: Systematically identify recommended scoring systems and calculations mentioned in those guidelines.

  4. Source Validation: Trace back to original peer-reviewed primary papers to verify exact formulas and coefficients.

  5. Implementation: Develop validated calculation tools with precise parameters and evidence-based interpretations.


🔬 Research Framework

This project implements a Neuro-Symbolic Framework for reliable medical calculation, combining LLM understanding with validated symbolic computation.

Academic Positioning

Challenge

Traditional LLM

Our Solution

Calculation Accuracy

~50% (MedCalc-Bench)

>95% via validated formulas

Parameter Extraction

Vocabulary mismatch

ParamMatcher (60+ aliases)

Safety Guardrails

No clinical constraints

BoundaryValidator (PMID-backed)

Tool Discovery

Keyword/RAG only

Two-Level Key + Hypergraph

Strategic Next Step

Three-Module Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                     NEURO-SYMBOLIC MEDICAL REASONING                        │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌───────────────────┐   ┌───────────────────┐   ┌───────────────────┐     │
│  │  Discovery Engine │ → │ Reasoning Interface│ → │    Safety Layer   │     │
│  │  (Tool Selection) │   │  (Param Matching)  │   │  (Validation)     │     │
│  │                   │   │                    │   │                   │     │
│  │  • High/Low Keys  │   │  • Alias Matching  │   │  • Range Check    │     │
│  │  • Hypergraph     │   │  • Fuzzy Match     │   │  • PMID Citation  │     │
│  │  • Context-Aware  │   │  • Multi-lingual   │   │  • Error Messages │     │
│  └───────────────────┘   └───────────────────┘   └───────────────────┘     │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Core Contributions

  1. Semantic Parameter Mapping (ParamMatcher): Resolves vocabulary mismatch between clinical text and calculator parameters through alias tables, fuzzy matching, and suffix normalization.

  2. Literature-Based Guardrails (BoundaryValidator): Validates input values against clinically impossible ranges derived from peer-reviewed literature (17+ parameters with PMID citations).

  3. Context-Aware Tool Discovery: Two-level key system + Clinical Knowledge Graph for intelligent tool recommendation based on clinical context.

🏆 Levels of Academic Value

Level

Contribution

Scholarly Focus

L1

Validated Symbolic Engine

Extends LLM with deterministic precision

L2

Hierarchical Tool Discovery

Solves RAG precision in high-stakes domains

L3

Robust Semantic Extraction

Resolves the "Vocabulary Mismatch" problem

L4

Knowledge-Gated Safety Layer

Unique: Literature-derived constraint verification

L5

Clinical Hypergraph Agent

Cross-specialty workflow reasoning

📄 For detailed research roadmap and benchmark strategy, see ROADMAP.md


🏗️ Architecture

┌─────────────────────────────────────────────────────────────┐
│                    infrastructure/mcp/                       │
│                (MCP Server, Handlers, Resources)             │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  MedicalCalculatorServer                             │    │
│  │  ├── handlers/DiscoveryHandler (discover, list...)   │    │
│  │  ├── handlers/CalculatorHandler (calculate_*)        │    │
│  │  └── resources/CalculatorResourceHandler             │    │
│  └─────────────────────────────────────────────────────┘    │
└──────────────────────────┬──────────────────────────────────┘
                           │ uses
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                     application/                             │
│               (Use Cases, DTOs, Validation)                  │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  DiscoveryUseCase, CalculateUseCase                  │    │
│  │  DiscoveryRequest/Response, CalculateRequest/Response│    │
│  └─────────────────────────────────────────────────────┘    │
└──────────────────────────┬──────────────────────────────────┘
                           │ depends on
                           ▼
┌─────────────────────────────────────────────────────────────┐
│                       domain/                                │
│            (Entities, Services, Value Objects)               │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  BaseCalculator, ToolMetadata, ScoreResult          │    │
│  │  LowLevelKey, HighLevelKey, ToolRegistry            │    │
│  └─────────────────────────────────────────────────────┘    │
│                    【Core, Zero Dependencies】                │
└─────────────────────────────────────────────────────────────┘

Key Design Decisions

Decision

Rationale

DDD Onion

Domain logic isolated from infrastructure

FastMCP

Native Python MCP SDK, simple decorator-based API

Dataclasses

Immutable, type-safe entities

Two-Level Keys

Enable both precise lookup and exploratory discovery

Layered Validation

3-layer validation (MCP/Application/Domain)

Validation Architecture

┌─────────────────────────────────────────────────────────────┐
│  Layer 1: MCP (Infrastructure)                               │
│  └── Pydantic + JSON Schema: Type validation                │
│      (Automatic from Annotated[type, Field(description)])   │
├─────────────────────────────────────────────────────────────┤
│  Layer 2: Application (Use Case)                             │
│  ├── ParamMatcher: Intelligent parameter matching           │
│  │   (Alias, fuzzy, suffix matching with typo tolerance)    │
│  └── BoundaryValidator: Clinical range validation           │
│      (Literature-backed warnings for extreme values)        │
├─────────────────────────────────────────────────────────────┤
│  Layer 3: Domain (Calculator)                                │
│  └── ParameterValidator: Medical logic validation           │
│      (22 parameter specs with valid ranges)                 │
└─────────────────────────────────────────────────────────────┘

Domain validation module (src/domain/validation/):

  • rules.py: Base classes (RangeRule, EnumRule, TypeRule, CustomRule)

  • parameter_specs.py: 22 medical parameter specifications

  • validators.py: ParameterValidator with validate_params() function

  • boundaries.py: BoundarySpec with literature-backed clinical ranges

Parameter Matching (src/domain/services/param_matcher.py):

  • Alias matching: crserum_creatinine, hrheart_rate

  • Fuzzy matching: creatninecreatinine (typo tolerance)

  • Suffix stripping: creatinine_mg_dlcreatinine


🚀 Quick Start

Prerequisites

  • Python 3.11+ (required by MCP SDK)

  • uv package manager (recommended) - Install uv

Installation

# Clone repository
git clone https://github.com/u9401066/medical-calc-mcp.git
cd medical-calc-mcp

# Install uv (if not already installed)
# macOS/Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# Sync dependencies (creates .venv automatically)
uv sync

Run MCP Server

# Start MCP server (stdio transport)
uv run python -m src.main

# Or with MCP development inspector
uv run mcp dev src/main.py

OpenClaw Compatibility

This repository is intentionally structured so OpenClaw-style crawlers, MCP registries, and autonomous coding agents can discover it, install it, and operate it safely with minimal guessing.

Discovery Keywords

  • MCP server

  • medical calculator MCP

  • FastMCP

  • stdio MCP server

  • SSE MCP server

  • evidence-based medical scoring

  • AI agent clinical tools

  • schema-first calculation

  • safe retry guidance

Why This Repo Is OpenClaw-Friendly

  • Clear canonical workflow: discover(...) -> get_tool_schema(tool_id) -> calculate(tool_id, params)

  • Start-here guidance is exposed in multiple MCP surfaces:

    • Prompt: tool_usage_playbook()

    • Resource: guide://tool-usage-playbook

    • Index: calculator://list

  • Smart resolver handles fuzzy tool ids and specialty names across tools and resources

  • Failed calls return retry-friendly fields such as guidance, suggestions, resolved_value, and param_template

  • Supports local stdio and hosted sse / http transports

Minimal Install

git clone https://github.com/u9401066/medical-calc-mcp.git
cd medical-calc-mcp
uv sync
uv run python -m src.main
1. Read resource: guide://tool-usage-playbook
2. Read resource: calculator://list
3. Call tool: discover(by="keyword", value="clinical problem")
4. Call tool: get_tool_schema("tool_id")
5. Call tool: calculate("tool_id", {...})

Example MCP Client Config

{
  "mcpServers": {
    "medical-calc": {
      "command": "uv",
      "args": ["run", "python", "-m", "src.main"],
      "cwd": "/path/to/medical-calc-mcp"
    }
  }
}

Hosted Mode for Remote Crawlers / Agents

uv run python -m src.main --mode sse
# or
uv run python -m src.main --mode http

If your OpenClaw deployment ranks repositories by install clarity and MCP readiness, this repo now exposes a direct install path, explicit transport modes, and a schema-first SOP designed to reduce agent misuse.

Configure with VS Code Copilot ⭐ NEW

The project includes a .vscode/mcp.json configuration file for seamless VS Code Copilot integration.

Automatic Setup:

Simply open this project in VS Code - the MCP server will be auto-discovered!

// .vscode/mcp.json (included in repo)
{
  "servers": {
    "medical-calc-mcp": {
      "type": "stdio",
      "command": "uv",
      "args": ["run", "python", "-m", "src.main"]
    }
  }
}

Enable MCP in VS Code:

  1. Open VS Code Settings (Ctrl+,)

  2. Search for chat.mcp

  3. Enable Chat: Mcp Discovery Enabled

  4. Restart VS Code

Usage:

In GitHub Copilot Chat, use @medical-calc-mcp to access calculators:

@medical-calc-mcp Calculate SOFA score with PaO2/FiO2=200, platelets=80...

Configure with Claude Desktop

Add to your claude_desktop_config.json:

{
  "mcpServers": {
    "medical-calc": {
      "command": "uv",
      "args": ["run", "python", "-m", "src.main"],
      "cwd": "/path/to/medical-calc-mcp"
    }
  }
}

🚀 Deployment Modes ⭐ NEW

This project supports multiple deployment modes for different use cases:

┌─────────────────────────────────────────────────────────────────────┐
│                        Deployment Options                            │
├─────────────────┬─────────────────┬─────────────────────────────────┤
│   REST API      │   MCP SSE       │   MCP stdio                     │
│   (Port 8080)   │   (Port 8000)   │   (Local)                       │
├─────────────────┼─────────────────┼─────────────────────────────────┤
│ ✅ Any HTTP     │ ✅ MCP Clients  │ ✅ Claude Desktop               │
│    client       │    (remote)     │ ✅ VS Code Copilot              │
│ ✅ Custom Agent │ ✅ Docker/Cloud │ ✅ MCP Inspector                │
│ ✅ Web Apps     │                 │                                 │
│ ✅ Python/JS    │                 │                                 │
└─────────────────┴─────────────────┴─────────────────────────────────┘

Mode

Command

Port

Best For

api

uv run python src/main.py --mode api

8080

Custom agents, web apps, scripts

sse

uv run python src/main.py --mode sse

8000

Remote MCP clients, Docker

stdio

uv run python src/main.py --mode stdio

-

Local Claude Desktop, VS Code

📘 For detailed deployment instructions, see docs/DEPLOYMENT.md


🤖 Agent Integration ⭐ NEW

Python Agent Example

import requests

class MedicalCalculatorClient:
    def __init__(self, base_url: str = "http://localhost:8080"):
        self.api_url = f"{base_url}/api/v1"

    def search(self, query: str) -> list:
        r = requests.get(f"{self.api_url}/search", params={"q": query})
        return r.json()

    def calculate(self, tool_id: str, params: dict) -> dict:
        r = requests.post(f"{self.api_url}/calculate/{tool_id}", json={"params": params})
        return r.json()

# Usage
client = MedicalCalculatorClient()

# Search for sepsis calculators
results = client.search("sepsis")

# Calculate SOFA score
result = client.calculate("sofa", {
    "pao2_fio2_ratio": 200,
    "platelets": 100,
    "bilirubin": 2.0,
    "gcs_score": 13,
    "creatinine": 2.5
})
print(f"SOFA Score: {result['result']['value']}")

LangChain / OpenAI Function Calling

See docs/DEPLOYMENT.md for LangChain and OpenAI integration examples.

Quick API Test

# Start API server
uv run python src/main.py --mode api --port 8080

# Test endpoints
curl http://localhost:8080/health
curl "http://localhost:8080/api/v1/search?q=sepsis"
curl -X POST "http://localhost:8080/api/v1/calculate/gcs" \
  -H "Content-Type: application/json" \
  -d '{"params": {"eye_response": 4, "verbal_response": 5, "motor_response": 6}}'

🐳 Docker Deployment ⭐ NEW

The MCP server can run as a remote SSE (Server-Sent Events) server via Docker, enabling:

  • 🌐 Remote access from any MCP-compatible client

  • ☁️ Cloud deployment (AWS, GCP, Azure, etc.)

  • 🔄 Easy scaling with Docker Compose or Kubernetes

Quick Start with Docker

# Build and run
docker-compose up -d

# Or build manually
docker build -t medical-calc-mcp .
docker run -p 8000:8000 medical-calc-mcp

# Check service is running
curl -sf http://localhost:8000/sse -o /dev/null && echo "OK"

Transport Modes

Mode

Use Case

Port

Command

stdio

Local Claude Desktop

-

uv run python -m src.main

sse

Remote MCP (Docker/Cloud)

8000

uv run python -m src.main --mode sse

http

Streamable HTTP transport

8000

uv run python -m src.main --mode http

⚠️ Important: SSE/HTTP modes bind to 0.0.0.0 by default for remote access.

Quick Start Commands

# 1. STDIO Mode - For Claude Desktop (local)
uv run python -m src.main

# 2. SSE Mode - For remote agents (Docker/Cloud)
uv run python -m src.main --mode sse
uv run python -m src.main --mode sse --host 0.0.0.0 --port 9000  # Custom port

# 3. HTTP Mode - Streamable HTTP transport
uv run python -m src.main --mode http

Remote MCP Client Configuration

Claude Desktop (Remote SSE):

{
  "mcpServers": {
    "medical-calc": {
      "url": "http://localhost:8000/sse"
    }
  }
}

For cloud deployment, replace localhost with your server address:

{
  "mcpServers": {
    "medical-calc": {
      "url": "https://your-server.example.com/sse"
    }
  }
}

API Endpoints

⚠️ FastMCP SSE mode only provides these endpoints:

Endpoint

Method

Description

/sse

GET

SSE connection endpoint

/messages/

POST

MCP message endpoint

Environment Variables

Variable

Default

Description

MCP_MODE

stdio

Transport mode (stdio, sse, http)

MCP_HOST

0.0.0.0

Host to bind

MCP_PORT

8000

Port to bind

LOG_LEVEL

INFO

Logging level

DEBUG

false

Enable debug mode

Docker Compose Example

version: '3.8'
services:
  # MCP Server (SSE mode)
  medical-calc-mcp:
    build: .
    ports:
      - "8000:8000"
    environment:
      - MCP_MODE=sse

  # REST API Server (FastAPI)
  medical-calc-api:
    build: .
    ports:
      - "8080:8080"
    command: ["python", "src/main.py", "--mode", "api", "--port", "8080"]

🔒 HTTPS Deployment ⭐ NEW

Enable HTTPS for secure communication in production environments with flexible certificate configuration.

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                        HTTPS Deployment                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   ┌─────────────┐                                                   │
│   │   Client    │                                                   │
│   │ (Browser/   │                                                   │
│   │  AI Agent)  │                                                   │
│   └──────┬──────┘                                                   │
│          │ HTTPS (TLS 1.2/1.3)                                      │
│          ▼                                                          │
│   ┌──────────────────────────────────────────────────────────┐      │
│   │                    Nginx Reverse Proxy                    │      │
│   │  ┌─────────────────────────────────────────────────────┐ │      │
│   │  │ • TLS Termination (SSL Certificates)                │ │      │
│   │  │ • Rate Limiting (30/60 req/s)                       │ │      │
│   │  │ • Security Headers (XSS, CSRF protection)           │ │      │
│   │  │ • SSE Optimization (long-lived connections)         │ │      │
│   │  └─────────────────────────────────────────────────────┘ │      │
│   └──────────────┬───────────────────────┬───────────────────┘      │
│                  │ HTTP (internal)        │ HTTP (internal)         │
│                  ▼                        ▼                         │
│   ┌──────────────────────┐    ┌──────────────────────┐              │
│   │   MCP SSE Server     │    │   REST API Server    │              │
│   │   (Port 8000)        │    │   (Port 8080)        │              │
│   │                      │    │                      │              │
│   │ • /sse               │    │ • /api/v1/*          │              │
│   │ • /messages          │    │ • /docs (Swagger)    │              │
│   │ • /health            │    │ • /health            │              │
│   └──────────────────────┘    └──────────────────────┘              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

External Endpoints (HTTPS):
├── https://localhost/        → MCP SSE (via Nginx :443)
├── https://localhost/sse     → SSE Connection
├── https://localhost:8443/   → REST API (via Nginx :8443)
└── https://localhost:8443/docs → Swagger UI

Internal (HTTP, Docker network only):
├── http://medical-calc-mcp:8000  → MCP Server
└── http://medical-calc-api:8080  → API Server

SSL Environment Variables

Variable

Default

Description

SSL_ENABLED

false

Enable SSL/TLS (true/false)

SSL_KEYFILE

-

Path to SSL private key file

SSL_CERTFILE

-

Path to SSL certificate file

SSL_CA_CERTS

-

Path to CA certificates (optional)

SSL_DIR

./nginx/ssl

SSL cert directory (Docker only)

Best for production and team environments.

# Step 1: Generate SSL certificates
chmod +x scripts/generate-ssl-certs.sh
./scripts/generate-ssl-certs.sh

# Step 2: Start HTTPS services
./scripts/start-https-docker.sh up

# Other commands
./scripts/start-https-docker.sh down     # Stop services
./scripts/start-https-docker.sh logs     # View logs
./scripts/start-https-docker.sh restart  # Restart
./scripts/start-https-docker.sh status   # Check status

Custom Certificates (Docker):

# Use custom certificate directory
SSL_DIR=/path/to/your/certs docker-compose -f docker-compose.https.yml up -d

# Use Let's Encrypt certificates
SSL_DIR=/etc/letsencrypt/live/example.com docker-compose -f docker-compose.https.yml up -d

Endpoints:

Service

URL

Description

MCP SSE

https://localhost/

MCP Server-Sent Events

MCP SSE

https://localhost/sse

SSE connection

REST API

https://localhost:8443/

REST API root

Swagger UI

https://localhost:8443/docs

API documentation

Health

https://localhost/health

MCP health check

Health

https://localhost:8443/health

API health check

Option 2: Local Development (No Docker)

Uses Python/Uvicorn native SSL support for quick local testing.

# Step 1: Generate SSL certificates (or use your own)
./scripts/generate-ssl-certs.sh

# Step 2: Start HTTPS services
./scripts/start-https-local.sh          # Start both MCP and API
./scripts/start-https-local.sh sse      # Start MCP SSE only
./scripts/start-https-local.sh api      # Start REST API only

Custom Certificates (Local):

# Use custom certificate paths via environment variables
SSL_KEYFILE=/path/to/server.key \
SSL_CERTFILE=/path/to/server.crt \
./scripts/start-https-local.sh

# Custom ports
SSL_KEYFILE=/certs/key.pem SSL_CERTFILE=/certs/cert.pem \
MCP_PORT=9000 API_PORT=9001 \
./scripts/start-https-local.sh

# Direct command with CLI arguments
python -m src.main --mode sse --port 8443 \
    --ssl-keyfile /path/to/server.key \
    --ssl-certfile /path/to/server.crt

Endpoints:

Service

URL

Description

MCP SSE

https://localhost:8443/

MCP Server-Sent Events

REST API

https://localhost:9443/

REST API

Swagger UI

https://localhost:9443/docs

API documentation

Option 3: Production with Let's Encrypt

For real domain names with free trusted certificates.

# 1. Edit nginx/nginx.conf, uncomment these lines:
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

# 2. Use certbot to obtain certificates:
sudo certbot certonly --webroot -w /var/www/certbot \
  -d your-domain.com -d api.your-domain.com

# 3. Start services with Let's Encrypt certs
SSL_DIR=/etc/letsencrypt/live/your-domain.com \
docker-compose -f docker-compose.https.yml up -d

Trust Self-Signed Certificates

To avoid browser warnings during development:

Linux (Ubuntu/Debian):

sudo cp nginx/ssl/ca.crt /usr/local/share/ca-certificates/medical-calc-dev.crt
sudo update-ca-certificates

macOS:

sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain nginx/ssl/ca.crt

Windows:

1. Double-click nginx/ssl/ca.crt
2. Install Certificate → Local Machine
3. Place in "Trusted Root Certification Authorities"

Claude Desktop Configuration (HTTPS)

{
  "mcpServers": {
    "medical-calc": {
      "url": "https://localhost/sse"
    }
  }
}

For production with a real domain:

{
  "mcpServers": {
    "medical-calc": {
      "url": "https://mcp.your-domain.com/sse"
    }
  }
}

Files Overview

File

Description

nginx/nginx.conf

Nginx configuration with TLS, rate limiting, SSE optimization

docker-compose.https.yml

Docker Compose for HTTPS deployment

scripts/generate-ssl-certs.sh

Generate self-signed SSL certificates

scripts/start-https-docker.sh

Start/stop Docker HTTPS services

scripts/start-https-local.sh

Start local HTTPS (supports custom certs)

src/infrastructure/mcp/config.py

SslConfig class for SSL configuration

SSL Configuration Reference

Scenario

Cert Location

Configuration Method

Docker (default)

nginx/ssl/

No config needed

Docker (custom)

Custom path

SSL_DIR env var or volumes

Docker (Let's Encrypt)

/etc/letsencrypt/...

Modify nginx/nginx.conf

Local (default)

nginx/ssl/

No config needed

Local (custom)

Custom path

SSL_KEYFILE + SSL_CERTFILE env vars

CLI direct

Custom path

--ssl-keyfile + --ssl-certfile args

Troubleshooting

Certificate not trusted:

# Regenerate certificates
rm -rf nginx/ssl/*
./scripts/generate-ssl-certs.sh

# Then re-add to system trust store (see above)

Port already in use:

# Check what's using the port
sudo lsof -i :443
sudo lsof -i :8443

# Kill the process or use different ports

Docker container not starting:

# Check logs
docker-compose -f docker-compose.https.yml logs nginx
docker-compose -f docker-compose.https.yml logs medical-calc-mcp

# Rebuild
docker-compose -f docker-compose.https.yml up -d --build

SSE connection timeout:

# Nginx is configured for 24h timeout, but if issues persist:
# Check nginx/nginx.conf has these settings:
proxy_read_timeout 24h;
proxy_send_timeout 24h;
proxy_buffering off;

🌐 REST API ⭐ NEW

Besides MCP protocol, the server also provides a standalone REST API for direct HTTP access.

Quick Start

# Start API server
uv run python src/main.py --mode api --port 8080

# With uvicorn (production)
uv run uvicorn src.infrastructure.api.server:app --host 0.0.0.0 --port 8080

API Documentation

Once running, visit:

REST API Endpoints

Endpoint

Method

Description

/health

GET

Health check

/api/v1/calculators

GET

List all calculators

/api/v1/calculators/{tool_id}

GET

Get calculator info

/api/v1/search?q={keyword}

GET

Search calculators

/api/v1/specialties

GET

List specialties

/api/v1/specialties/{specialty}

GET

List by specialty

/api/v1/calculate/{tool_id}

POST

Execute calculation

Example: Calculate CKD-EPI

# Using curl
curl -X POST "http://localhost:8080/api/v1/calculate/ckd_epi_2021" \
  -H "Content-Type: application/json" \
  -d '{"params": {"serum_creatinine": 1.2, "age": 65, "sex": "female"}}'

Response:

{
  "success": true,
  "calculator": "ckd_epi_2021",
  "result": {
    "score_name": "CKD-EPI 2021",
    "value": 49.2,
    "unit": "mL/min/1.73m²",
    "interpretation": {
      "summary": "G3a: Mildly to moderately decreased",
      "severity": "moderate"
    }
  }
}

Quick Calculate Endpoints

Some calculators have dedicated endpoints with query parameters:

# CKD-EPI (Query parameters)
curl "http://localhost:8080/api/v1/ckd-epi?serum_creatinine=1.2&age=65&sex=female"

# SOFA Score
curl -X POST "http://localhost:8080/api/v1/sofa?pao2_fio2_ratio=200&platelets=100&bilirubin=2.0&cardiovascular=dopamine_lte_5&gcs_score=13&creatinine=2.5"

🔐 Security ⭐ NEW

Security Features

This project implements multiple security layers:

Layer

Feature

Description

HTTPS

TLS 1.2/1.3 encryption

All traffic encrypted via Nginx

Input Validation

3-layer validation

Pydantic → ParameterValidator → Domain rules

CORS

Configurable origins

Environment variable controlled

Rate Limiting

Nginx + Application level

Dual-layer protection (optional)

API Authentication

Optional API Key

Disabled by default, enable via env

Security Headers

XSS/CSRF protection

X-Frame-Options, X-Content-Type-Options

Dependencies

Vulnerability scanning

pip-audit integrated

No Database

In-memory only

No SQL injection risk

No Secrets

Stateless

No credentials stored

📖 For detailed HTTPS deployment instructions, see HTTPS Deployment.

🔑 Optional Security Features

All optional security features are DISABLED by default. Enable via environment variables:

Rate Limiting (Application Level)

# Enable rate limiting
SECURITY_RATE_LIMIT_ENABLED=true   # Default: false
SECURITY_RATE_LIMIT_RPM=60         # Requests per minute (default: 60)
SECURITY_RATE_LIMIT_BURST=10       # Burst size (default: 10)
SECURITY_RATE_LIMIT_BY_IP=true     # Per-IP rate limiting (default: true)

API Key Authentication

# Enable API authentication
SECURITY_AUTH_ENABLED=true         # Default: false
SECURITY_API_KEYS=key1,key2,key3   # Comma-separated API keys (min 8 chars each)
SECURITY_AUTH_HEADER=X-API-Key     # Header name (default: X-API-Key)
SECURITY_AUTH_PARAM=api_key        # Query param name (default: api_key)

Usage Example:

# With header
curl -H "X-API-Key: your-api-key" http://localhost:8000/sse

# With query parameter
curl "http://localhost:8000/sse?api_key=your-api-key"

# With Bearer token
curl -H "Authorization: Bearer your-api-key" http://localhost:8000/sse

Security Scenarios

Scenario

Rate Limit

Auth

Configuration

Local Development

❌ Off

❌ Off

Default (no env vars)

Internal Network

✅ On

❌ Off

SECURITY_RATE_LIMIT_ENABLED=true

Public API

✅ On

✅ On

Both enabled + API keys

Configuration

CORS Configuration:

# Development (default) - Allow all origins
CORS_ORIGINS="*"

# Production - Restrict to specific domains
CORS_ORIGINS="https://your-app.com,https://api.your-app.com"

Other Security Settings:

# API Server
API_HOST=0.0.0.0   # Use 127.0.0.1 for local only
API_PORT=8080

# MCP Server
MCP_HOST=0.0.0.0   # Use 127.0.0.1 for local only
MCP_PORT=8000

Production Recommendations

Item

Recommendation

HTTPS

✅ Use provided Nginx + SSL config

CORS

Set specific CORS_ORIGINS

Rate Limiting

✅ Enable application-level rate limiting

Authentication

✅ Enable API key authentication

Network

Run in private network/VPC

Certificates

Use Let's Encrypt for production

Monitoring

Enable access logging

Dependency Security

# Check for known vulnerabilities
uv run pip-audit --strict

# Upgrade all packages
uv sync --upgrade

# Lock dependencies
uv lock

Security Audit Results (2025-06)

Passed Checks:

  • No SQL/Command injection vulnerabilities

  • No hardcoded secrets or credentials

  • No sensitive data exposure in error messages

  • Input validation at all layers

  • Dependencies updated (no known CVEs)

⚠️ Notes:

  • Default CORS is permissive (*) - configure for production

  • No built-in authentication - add at infrastructure layer if needed

  • Medical calculations are for reference only - not for clinical decisions


🔍 Tool Discovery

The Two-Level Key System combined with Tool Relation Graph is the core innovation of this project:

Discovery Philosophy

When an AI agent needs a medical calculator, it uses Unified Discovery:

┌─────────────────────────────────────────────────────────────┐
│  discover() - Unified Entry Point (v3.0)                     │
├─────────────────────────────────────────────────────────────┤
│  Path A: Explore All Categories                              │
│  ① discover() → {specialties: [...], contexts: [...]}       │
│  ② discover(by="specialty", value="critical_care")          │
│  ③ get_tool_schema("sofa_score") → params, references       │
│  ④ calculate("sofa_score", {...params})                      │
├─────────────────────────────────────────────────────────────┤
│  Path B: Context-based                                       │
│  ① discover(by="context", value="preoperative_assessment")  │
│  ② get_tool_schema("rcri") → params, param_sources          │
│  ③ calculate("rcri", {...params})                            │
├─────────────────────────────────────────────────────────────┤
│  Path C: Keyword Search                                      │
│  ① discover(by="keyword", value="sepsis")                    │
│  ② get_tool_schema("qsofa_score")                            │
│  ③ calculate("qsofa_score", {...params})                     │
├─────────────────────────────────────────────────────────────┤
│  Path D: Graph-based Discovery                               │
│  ① get_related_tools("sofa_score") → [qsofa, apache_ii...]  │
│  ② find_tools_by_params(["creatinine", "age"]) → [tools...] │
└─────────────────────────────────────────────────────────────┘

Every step returns next_step hints, so the Agent never gets lost!

Tool Relation Graph (Hypergraph)

The ToolRelationGraph connects tools based on:

Relation Type

Weight

Example

SHARED_PARAM

0.2

SOFA ↔ APACHE II (both use creatinine)

SAME_SPECIALTY

0.3

SOFA ↔ qSOFA (both Critical Care)

SAME_CONTEXT

0.2

RCRI ↔ ASA (both Preoperative Assessment)

# Find related tools via graph traversal
get_related_tools("sofa_score")
# → [{"tool_id": "qsofa_score", "similarity": 0.85},
#    {"tool_id": "apache_ii", "similarity": 0.72}, ...]

# Reverse lookup: "I have these values, what can I calculate?"
find_tools_by_params(["creatinine", "bilirubin", "inr"])
# → [meld_score, child_pugh, ...]

Unified Calculate Interface (v2.0)

Instead of 75+ individual calculator tools, we provide a single unified calculate() tool:

# Old approach (deprecated):
# calculate_sofa(pao2_fio2=300, platelets=150, ...)

# New approach (v2.0):
calculate(
    tool_id="sofa_score",
    params={
        "pao2_fio2_ratio": 300,
        "platelets": 150,
        "bilirubin": 1.2,
        # ... other params
    }
)

Benefits:

  • 🎯 Token Efficient: Only 6 tools instead of 75+ in context

  • 🔍 Discovery First: Use discover() to find the right calculator

  • 📖 Self-Documenting: get_tool_schema() shows exact params needed

Low Level Key (Precise Selection)

For precise tool selection when you know exactly what you need:

LowLevelKey(
    tool_id="ckd_epi_2021",           # Unique identifier
    name="CKD-EPI 2021",              # Human-readable name
    purpose="Calculate eGFR",          # What it does
    input_params=["age", "sex", "creatinine"],  # Required inputs
    output_type="eGFR with CKD staging"         # Output format
)

High Level Key (Intelligent Discovery)

For intelligent discovery when exploring options:

HighLevelKey(
    specialties=(Specialty.NEPHROLOGY, Specialty.INTERNAL_MEDICINE),
    conditions=("chronic kidney disease", "CKD", "renal impairment"),
    clinical_contexts=(ClinicalContext.STAGING, ClinicalContext.DRUG_DOSING),
    clinical_questions=(
        "What is the patient's kidney function?",
        "Should I adjust drug dosage for renal function?",
    ),
    icd10_codes=("N18", "N19"),
    keywords=("eGFR", "GFR", "creatinine", "kidney function")
)

🔑 Key Feature: Multi-Specialty Tools

One tool can belong to multiple High Level categories!

Example: SOFA Score belongs to:

Category

Values

Specialties

Critical Care, Emergency Medicine, Internal Medicine, Pulmonology

Conditions

Sepsis, Septic Shock, Organ Dysfunction, MODS

Contexts

Severity Assessment, Prognosis, ICU Management, Diagnosis

This means:

  • Search "sepsis" → Returns SOFA, qSOFA, NEWS, ...

  • Search "critical care" → Returns SOFA, APACHE II, RASS, GCS, CAM-ICU, ...

  • Search "organ dysfunction" → Returns SOFA, ...

Consolidated MCP Tools (v3.0)

Layer

Tool

Purpose

High-Level

discover(by, value, limit)

Unified discovery (specialty/context/keyword/all)

High-Level

get_related_tools(tool_id)

Graph-based related tool discovery

High-Level

find_tools_by_params(params)

Reverse lookup by available parameters

Low-Level

get_tool_schema(tool_id)

Full metadata + param schemas + references

Low-Level

calculate(tool_id, params)

Execute single calculation

Low-Level

calculate_batch(calculations)

Batch calculations with cross-analysis

Total: 6 tools (consolidated from 12 in v2.0)

Example: AI Agent Workflow

User: "I need to assess this patient's cardiac risk before surgery"

# Step 1: Agent uses hierarchical navigation
Agent: list_contexts()
       → Returns: [..., "preoperative_assessment", ...]
       → next_step: "list_by_context('preoperative_assessment')"

# Step 2: Filter by context
Agent: list_by_context("preoperative_assessment")
       → Returns: [rcri, asa_physical_status, mallampati_score, ...]
       → next_step: "get_calculator_info('rcri')"

# Step 3: Get tool details
Agent: get_calculator_info("rcri")
       → Returns: Full metadata with input params, references
       → next_step: "calculate_rcri(...)"

# Step 4: Calculate
Agent: calculate_rcri(high_risk_surgery=True, ischemic_heart_disease=True, ...)
       → Returns: Score, risk percentage, recommendations

Example: ICU Sepsis Workup

User: "Evaluate this ICU patient for sepsis"

Agent: search_calculators("sepsis")
       → Returns: SOFA, qSOFA, NEWS2, APACHE II

# Per Sepsis-3 guidelines:

Agent: calculate_qsofa(respiratory_rate=24, systolic_bp=95, altered_mentation=True)
       → qSOFA = 3 (High risk, prompt evaluation needed)

Agent: calculate_sofa(pao2_fio2_ratio=200, platelets=80, bilirubin=2.5, ...)
       → SOFA = 8 (Sepsis confirmed if infection suspected, ≥2 point increase)

🔧 Available Tools

Quality Snapshot: 2095 collected tests | 287 PMIDs | 245 DOIs | 100% citation coverage

📋 See Full Roadmap → | Contributing Guide →

📑 Quick Navigation

This README no longer carries a hand-maintained calculator inventory. The same generated source now feeds repository docs and MkDocs pages.

Registry Snapshot: 152 calculators across 31 specialties

Specialty

Tools

Critical Care

18

Geriatrics

13

Cardiology

11

Anesthesiology

9

Emergency Medicine

9

Psychiatry

9

You can still inspect the live registry via python scripts/count_tools.py, calculator://list, or list_calculators() from your MCP client.


Generated calculator catalog

The full tool inventory and specialty summary are generated directly from the registry to remove README drift risk.


🔍 Discovery Tools

Step 1: Entry Points

Tool

Description

list_specialties()

📋 List available specialties (returns next_step)

list_contexts()

📋 List available clinical contexts (returns next_step)

list_calculators()

📋 List all registered calculators

Step 2: Filter by Category

Tool

Description

list_by_specialty(specialty)

Filter tools by medical specialty

list_by_context(context)

Filter tools by clinical context

search_calculators(keyword)

🔍 Quick keyword search

Step 3: Get Details

Tool

Description

get_calculator_info(tool_id)

📖 Get params, references, examples

Step 4: Execute Calculation

Tool

Description

calculate(tool_id, params)

🧮 Unified calculator (supports all 75+ calculators)

↑ Back to Navigation


📦 Resources

Resource URI

Description

calculator://list

Markdown list of all calculators

calculator://{tool_id}/references

Paper references for a calculator

calculator://{tool_id}/parameters

Input parameter definitions

calculator://{tool_id}/info

Full calculator metadata


📝 Prompts

Prompts provide guided multi-tool workflows for common clinical scenarios:

Prompt

Description

sepsis_evaluation

qSOFA → SOFA → RASS → CAM-ICU workflow

preoperative_risk_assessment

ASA → RCRI → Mallampati workflow

icu_daily_assessment

RASS → CAM-ICU → GCS → SOFA daily rounds

pediatric_drug_dosing

Weight-based dosing + MABL + transfusion

acute_kidney_injury_assessment

CKD-EPI + AKI staging workflow

Usage:

# In MCP client, request a prompt:
prompt: sepsis_evaluation
→ Returns structured workflow with step-by-step guidance

↑ Back to Navigation


📖 Usage Examples

Python Examples ⭐ NEW

The project includes ready-to-run example scripts in the examples/ folder:

# Basic usage examples
uv run python examples/basic_usage.py

# Clinical workflow examples
uv run python examples/clinical_workflows.py

Available Examples:

File

Description

basic_usage.py

Individual calculator usage (CKD-EPI, SOFA, RCRI, CHA₂DS₂-VASc, Wells PE)

clinical_workflows.py

Multi-calculator clinical scenarios (Sepsis, Preop, Chest Pain, AF)

Example 1: CKD-EPI 2021 (eGFR)

Input:

{
  "serum_creatinine": 1.2,
  "age": 65,
  "sex": "female"
}

Output:

{
  "score_name": "CKD-EPI 2021",
  "result": 67.1,
  "unit": "mL/min/1.73m²",
  "interpretation": {
    "summary": "Mildly decreased kidney function (G2)",
    "stage": "G2",
    "recommendation": "Monitor kidney function annually; adjust renally-excreted drugs"
  },
  "references": [{
    "citation": "Inker LA, et al. N Engl J Med. 2021;385(19):1737-1749.",
    "doi": "10.1056/NEJMoa2102953"
  }]
}

Example 2: Tool Discovery

Query: search_calculators("airway")

Output:

{
  "keyword": "airway",
  "count": 1,
  "tools": [{
    "tool_id": "mallampati_score",
    "name": "Modified Mallampati Classification",
    "purpose": "Predict difficult intubation based on oropharyngeal visualization",
    "specialties": ["anesthesiology", "emergency_medicine"],
    "input_params": ["mallampati_class"]
  }]
}

Example 3: RCRI Cardiac Risk

Input:

{
  "high_risk_surgery": true,
  "ischemic_heart_disease": true,
  "heart_failure": false,
  "cerebrovascular_disease": false,
  "insulin_diabetes": true,
  "creatinine_above_2": false
}

Output:

{
  "score_name": "Revised Cardiac Risk Index",
  "result": 3,
  "interpretation": {
    "summary": "RCRI Class III - Elevated cardiac risk",
    "risk_percentage": "6.6%",
    "recommendation": "Consider cardiology consultation; optimize medical therapy"
  }
}

📜 References

All calculators cite original peer-reviewed research. See references/README.md for complete citations.

📋 Guideline Mapping

We systematically map our calculators to clinical guidelines:

We systematically map our calculators to major clinical guideline reviews, and this overview is generated from the same source used by the docs and website.

Tracked coverage: 65/65 recommended tools across 16 domains.

Domain

Implemented

Total

Coverage

Sepsis / Critical Care

9

9

100%

Cardiovascular

9

9

100%

GI Bleeding

3

3

100%

Liver Disease

6

6

100%

Kidney Disease

2

2

100%

Respiratory / Pneumonia

5

5

100%

Thromboembolism

4

4

100%

Neurology

7

7

100%

Anesthesiology

6

6

100%

Trauma

4

4

100%

Burns

2

2

100%

Pediatrics

2

2

100%

Oncology

2

2

100%

Nutrition

2

2

100%

Rheumatology

1

1

100%

Osteoporosis

1

1

100%

Citation Format

We use Vancouver style citations:

Inker LA, Eneanya ND, Coresh J, et al. New Creatinine- and Cystatin C-Based
Equations to Estimate GFR without Race. N Engl J Med. 2021;385(19):1737-1749.
doi:10.1056/NEJMoa2102953

👨‍💻 Development

Project Status

Phase

Status

Description

Modernization

✅ Complete

Migrated to uv, 100% mypy --strict coverage, ruff integration

Phase 1-8

✅ Complete

Foundation, 78 Calculators, MCP Integration, Validation Layer

Phase 13

✅ Complete

Additional Clinical Tools (ABCD2, mRS, TIMI STEMI, Rockall, FIB-4)

Phase 17-18

✅ Complete

Obstetrics (Bishop, Ballard), Trauma (ISS, TBSA, Parkland)

Quick Start (Developer)

# 1. Install uv (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. Setup environment and install dependencies
uv sync

# CI-parity install using the lock file
uv sync --frozen --extra dev --group dev

# 3. Run tests
uv run pytest

# 4. Run MCP server in dev mode
uv run mcp dev src/main.py

🧪 Testing

Testing Strategy

We maintain a high-quality codebase with 2,019 collected tests and automated coverage reporting in CI.

┌─────────────────────────────────────────────────────────────────┐
│                        Testing Pyramid                          │
├─────────────────────────────────────────────────────────────────┤
│                     E2E Tests (MCP Protocol)                     │
│                    (700+ tests covering all tools)               │
│                               ╱  ╲                               │
│           Integration Tests              MCP Inspector           │
│          (Use Cases + Registry)          (Manual Testing)        │
│                  ╱              ╲                                │
│      Unit Tests (Domain)    Validation Tests                     │
│      (940+ tests for logic) (Parameter constraints)              │
└─────────────────────────────────────────────────────────────────┘

Running Tests

# Run all tests
uv run pytest

# Run with coverage
uv run pytest --cov=src --cov-report=html

# Run specific layer tests
uv run pytest tests/test_acid_base.py -v

# Run with verbose output
uv run pytest -v --tb=short

Type Safety

The project enforces strict type checking across the entire codebase.

# Run strict type check
uv run mypy --no-incremental --strict src tests

# Run linter
uv run ruff check src tests

# Auto-fix linting issues
uv run ruff check --fix src tests

API Contract

The REST API OpenAPI contract is tracked as a generated artifact so schema drift is caught in CI before downstream clients break.

# Refresh the generated OpenAPI snapshot
uv run python scripts/generate_openapi_spec.py

# Refresh the generated REST API reference
uv run python scripts/generate_rest_api_docs.py

# Verify generated docs and API contract are current
uv run python scripts/check_project_consistency.py --check-tests

Dependency upgrade policy is documented in docs/DEPENDENCY_UPGRADE_PLAYBOOK.md.

CI/CD Pipeline

The project uses GitHub Actions for continuous integration with the following features:

┌─────────────────────────────────────────────────────────────┐
│                    Push to develop                          │
├─────────────────────────────────────────────────────────────┤
│  auto-fix:                                                  │
│    • ruff check --fix (auto-fix linting)                    │
│    • ruff format (auto-format code)                         │
│    • uv lock (update dependency lock)                       │
│    • Auto-commit back to develop [skip ci]                  │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                    test (3.11, 3.12, 3.13)                  │
├─────────────────────────────────────────────────────────────┤
│    • ruff check (lint)                                      │
│    • ruff format --check (format check)                     │
│    • mypy (type check)                                      │
│    • pytest (tests + coverage ≥90%)                         │
└─────────────────────────────────────────────────────────────┘
                            ↓ (main only)
┌─────────────────────────────────────────────────────────────┐
│                    docker + release                         │
├─────────────────────────────────────────────────────────────┤
│    • Build & test Docker image (/health endpoint)           │
│    • Auto-create GitHub Release when version changes        │
└─────────────────────────────────────────────────────────────┘

Feature

Description

Auto-fix on develop

Automatically fix linting/formatting issues

Multi-Python testing

Tests on Python 3.11, 3.12, 3.13

Docker health check

Uses /health endpoint for liveness probes

Auto-release

Creates GitHub Release when pyproject.toml version changes

Concurrency control

Cancels in-progress runs for same branch


🛠️ Requirements

  • Python 3.11+

  • uv - Fast Python package manager (required)

  • MCP SDK (FastMCP) - Installed automatically via uv sync


Roadmap

📋 See Full Roadmap → for detailed implementation plans

2025 Q4 (Current)                2026 Q1                          2026 Q2
───────────────────────────────────────────────────────────────────────────────
Phase 8: ✅ Complete             Phase 9-10: Acid-Base/Cardio    Phase 11-14: Complete
├── ✅ HAS-BLED (2024 ESC)       ├── Anion Gap, Delta Ratio      ├── Resp/Oxygenation
├── ✅ Child-Pugh               ├── Corrected QT, Shock Index    ├── Neuro/Sedation
└── ✅ KDIGO AKI                └── A-a Gradient, IBW           ├── Infectious Disease
                                                                 └── Common Utilities
Phase 9: ✅ Complete
├── ✅ Anion Gap
├── ✅ Delta Ratio
├── ✅ Corrected Sodium
├── ✅ Winter's Formula
├── ✅ Osmolar Gap
└── ✅ Free Water Deficit

Recently Added Calculators (Phase 13 Complete ✅)

Priority

Tool ID

Name

Status

Reference

✅ Done

abcd2

ABCD2 Score

Complete

Johnston 2007

✅ Done

modified_rankin_scale

Modified Rankin Scale (mRS)

Complete

van Swieten 1988

✅ Done

timi_stemi

TIMI STEMI Risk Score

Complete

Morrow 2000

✅ Done

rockall_score

Rockall Score

Complete

Rockall 1996

✅ Done

fib4_index

FIB-4 Index

Complete

Sterling 2006


📄 License

Apache 2.0 - See LICENSE


🙏 Acknowledgments

  • Model Context Protocol - Anthropic's open protocol for AI-tool communication

  • FastMCP - Python SDK for MCP

  • Original authors of all cited medical calculators and scoring systems

Available Tools

6 tools
calculateA

🧮 通用醫學計算工具

使用指定的計算器執行計算。支援所有 75+ 種醫學計算器。

使用流程:

  1. 先用 discover(by="keyword" | "specialty" | "context") 找工具

  2. 用 get_tool_schema(tool_id) 查看需要的參數

  3. 呼叫 calculate(tool_id, params) 執行計算

嚴格規則:

  • 不要猜參數名稱,必須以 get_tool_schema() 回傳為準

  • 不要把 calculate 當搜尋工具;tool_id 不確定時先用 discover()

  • 如果回傳 guidance 或 param_template,先依該內容修正後再重試

Args: tool_id: 計算器 ID (例如: "sofa", "apache_ii", "ckd_epi_2021") params: 計算參數字典 (從 get_tool_schema 取得參數名稱)

Returns: 計算結果,包含: - success: 是否成功 - score_name: 評分名稱 - result: 計算結果 (分數或數值) - unit: 單位 - interpretation: 臨床解讀 - references: 參考文獻

Examples:

Example 1 - SOFA Score:

calculate("sofa_score", {
    "pao2_fio2_ratio": 300,
    "is_mechanically_ventilated": False,
    "platelets": 150,
    "bilirubin": 1.2,
    "map_value": 70,
    "gcs_score": 15,
    "creatinine": 1.0,
    "urine_output_24h": 1500
})

Example 2 - CKD-EPI 2021:

calculate("ckd_epi_2021", {
    "serum_creatinine": 1.2,
    "age": 65,
    "sex": "male"
})

Example 3 - RCRI (Revised Cardiac Risk Index):

calculate("rcri", {
    "high_risk_surgery": True,
    "ischemic_heart_disease": False,
    "heart_failure": False,
    "cerebrovascular_disease": False,
    "insulin_therapy": True,
    "creatinine_above_2": False
})

常用計算器:

  • Critical Care: sofa_score, apache_ii, news2_score, qsofa_score, glasgow_coma_scale

  • Cardiology: chads2_vasc, heart_score, rcri

  • Nephrology: ckd_epi_2021, kdigo_aki

  • Anesthesiology: asa_physical_status, mallampati_score, stop_bang

  • Hepatology: meld_score, child_pugh

⏮️ 上一步: get_tool_schema(tool_id) 查看完整參數說明

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes
tool_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior4/5

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

With no annotations provided, the description carries the full behavioral disclosure burden. It does so well by documenting the return contract (success, score_name, result, unit, interpretation, references), the validation stance ('不要猜參數名稱,必須以 get_tool_schema() 回傳為準'), and the error/retry pattern involving guidance and param_template. It stops short of a 5 because it doesn't address side effects, idempotency, or failure modes for invalid params, though these are less critical for a pure computation tool.

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 long but excellently structured: purpose first, then workflow, strict rules, args, returns, examples, and a reference list. Every section is high-value given the tool's complexity (opaque params, 75+ calculators, 0% schema coverage). It loses one point for minor redundancy (the footer '⏮️ 上一步' restates the workflow already covered in the usage steps), but the front-loading of purpose and rules is exemplary.

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

Completeness5/5

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

Given the tool's high complexity — a dispatcher over 75+ calculators with fully opaque parameters and no annotations — the description is complete. It covers invocation workflow, input semantics with executable examples, output structure, retry guidance, and mapping of common tool_ids to specialties. An agent has everything needed to call this tool correctly without opening the schema, which is remarkable at 0% schema coverage.

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

Parameters5/5

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

Schema description coverage is 0% and the schema is nearly empty (tool_id as a bare string, params as an unconstrained object). The description fully compensates: it explains tool_id as a calculator ID with concrete examples ('sofa', 'apache_ii', 'ckd_epi_2021'), describes params as a dictionary sourced from get_tool_schema, provides three complete worked examples with exact parameter shapes, and lists common tool_ids by specialty.

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

Purpose5/5

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

The description states a specific verb and resource: '使用指定的計算器執行計算。支援所有 75+ 種醫學計算器' (execute calculations with the specified calculator, supporting 75+ medical calculators). It clearly differentiates from siblings by explicitly saying '不要把 calculate 當搜尋工具' (don't use calculate as a search tool), distinguishing it from discover and get_tool_schema.

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

Usage Guidelines5/5

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

The description provides an explicit 3-step workflow (discover → get_tool_schema → calculate), names alternatives directly, and gives a hard exclusion rule: 'tool_id 不確定時先用 discover()' (use discover() first when tool_id is uncertain). It also specifies when to retry: '如果回傳 guidance 或 param_template,先依該內容修正後再重試'.

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

calculate_batchA

🧮 批次計算多個工具 - 減少 round-trip,提高效率

一次執行多個計算,並提供結果間的事實關係分析。 適合需要同時評估多個指標的臨床情境。

Args: calculations: 計算請求列表,每個元素包含: - tool_id: 計算器 ID - params: 參數字典

Returns: - results: 各計算的結果 - summary: 結果摘要 - cross_analysis: 結果間的事實關係 (非臨床推理) - all_success: 是否全部成功

Example - Sepsis 評估:

calculate_batch([
    {"tool_id": "qsofa_score", "params": {
        "respiratory_rate": 24,
        "systolic_bp": 95,
        "altered_mentation": True
    }},
    {"tool_id": "sofa_score", "params": {
        "pao2_fio2_ratio": 200,
        "platelets": 80,
        "bilirubin": 2.5,
        "gcs_score": 13,
        "creatinine": 2.0
    }}
])

Cross-analysis 提供的事實判斷 (非推理):

  • "qSOFA ≥ 2 且 SOFA ≥ 2: 符合 Sepsis-3 定義標準"

  • "RCRI ≥ 2: 符合高心臟風險標準"

💡 這不是臨床建議,只是根據指引標準的事實陳述

ParametersJSON Schema
NameRequiredDescriptionDefault
calculationsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.1/5.0
Behavior3/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. It does add useful behavioral context: cross_analysis is 'factual relationships, not clinical reasoning', it is 'not clinical advice', and all_success indicates whether all calculations succeeded. However, it does not clarify partial-failure behavior, independence of calculations, or whether the operation has any side effects.

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 well-structured with Args, Returns, Example, and Cross-analysis sections, and it front-loads the purpose. It is slightly repetitive around the 'non-clinical' disclaimer, but the example and return-field breakdown justify the length.

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

Completeness4/5

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

The description covers the input contract, return fields, an end-to-end example, and the nature of cross-analysis, which is enough for most calls. It leaves valid tool_id discovery to siblings such as find_tools_by_params and does not specify failure behavior, but the batch wrapper is intentionally generic and this is adequate.

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 input schema is nearly empty (array of free-form objects, 0% coverage), so the description must compensate. It does so by explaining that each calculation has tool_id and params, and by providing a concrete sepsis example. It does not enumerate all valid tool_id values, but for a generic batch wrapper those are inherently open-ended.

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

Purpose5/5

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

The description opens with '批次計算多個工具' and '一次執行多個計算', clearly stating that this tool executes multiple calculations in one call and provides cross-analysis. This distinguishes it from the sibling 'calculate' tool, which presumably handles a single calculation, and from discovery/schema tools.

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

Usage Guidelines4/5

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

The description says it is '適合需要同時評估多個指標的臨床情境', giving concrete context for when to use it. It does not explicitly state when not to use it or name the single-calculation alternative, but the batch-vs-single distinction is strongly implied.

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

discoverA

🔍 統一的工具發現入口 (High-Level)

幫助 Agent 找到合適的醫學計算器。支援多種發現模式。

Args: by: 發現模式 - "all": 列出所有分類 (專科和臨床情境) - 預設 - "specialty": 依專科篩選工具 - "context": 依臨床情境篩選工具 - "keyword": 關鍵字搜尋 - "tools": 列出所有工具 value: 篩選值 (當 by 不是 "all" 或 "tools" 時必填) limit: 最多回傳幾個結果

Returns: 根據模式返回不同內容: - all: 可用的專科和情境清單 - specialty/context: 該分類下的工具清單 - keyword: 匹配的工具清單 - tools: 所有工具清單

Examples:

# 弱模型安全流程: 先 discover,再 get_tool_schema
discover(by="keyword", value="sepsis")
# → 取得候選 tool_id 之後再呼叫 get_tool_schema("qsofa_score")

# 查看所有分類 (起點)
discover()
# → {"specialties": [...], "contexts": [...]}

# 依專科篩選
discover(by="specialty", value="critical_care")
# → {"tools": [{"tool_id": "sofa_score", ...}, ...]}

# 依臨床情境篩選
discover(by="context", value="preoperative_assessment")
# → {"tools": [{"tool_id": "rcri", ...}, ...]}

# 關鍵字搜尋
discover(by="keyword", value="sepsis")
# → {"tools": [{"tool_id": "qsofa_score", ...}, ...]}

# 列出所有工具
discover(by="tools", limit=50)
# → {"tools": [...], "count": 75}

規則:

  • 不要根據記憶猜 tool_id,先用 discover() 拿 canonical id

  • 不要直接 calculate,先用 get_tool_schema(tool_id)

⏭️ 下一步: 找到工具後,使用 get_tool_schema(tool_id) 查看參數

ParametersJSON Schema
NameRequiredDescriptionDefault
byNoall
limitNo
valueNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

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

No annotations are present, so the description carries the full burden, and it responds with mode-specific return behavior plus guardrails against guessing IDs and calling calculate prematurely. This is thorough for a non-destructive discovery tool. It does not explicitly state read-only behavior or error handling, but these are less critical for a lookup endpoint.

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 well-structured with Overview, Args, Returns, Examples, Rules, and next-step sections, making it easy to scan. There is some redundancy between the Returns prose and the extensive Examples block, and the emoji/labels add minor noise. Overall it earns high marks but could be tightened.

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

Completeness5/5

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

All five modes, parameter constraints, return shapes, and workflow rules are covered, and the examples show valid call signatures for every mode. The 'next step' to get_tool_schema closes the discovery-to-call loop. For a high-level discovery tool with no annotations and a bare schema, this is effectively complete.

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

Parameters5/5

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

The schema has 0% description coverage, but the Args section fully defines 'by' with its five modes, explains that 'value' is required whenever 'by' is not 'all' or 'tools', and clarifies 'limit' as the maximum number of results. Every parameter receives meaningful semantic context that the schema itself lacks.

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 its purpose: it is the '統一工具發現入口' that helps an agent find appropriate medical calculators, and it documents five discovery modes. This is a specific verb+resource statement that is easy to act on. However, it does not explicitly distinguish itself from sibling discovery tools like find_tools_by_params or get_related_tools.

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

Usage Guidelines4/5

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

The description gives explicit workflow rules: never guess tool_id from memory, use discover() first, and do not go directly to calculate — instead call get_tool_schema(tool_id). This is strong when-to-use guidance. But it does not mention when to prefer discover over find_tools_by_params or get_related_tools, so alternative-selection guidance is incomplete.

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

find_tools_by_paramsA

🔍 根據已有參數找工具 (High-Level 反向查找)

「我有這些數值,可以計算什麼?」

Args: params: 參數名稱列表 Examples: ["creatinine", "age", "weight"] ["gcs", "pupil", "motor"] ["bilirubin", "inr", "ascites"]

Returns: 可使用這些參數的工具清單

Example:

find_tools_by_params(["age", "creatinine", "bilirubin"])
# → {"tools": [
#      {"tool_id": "meld_score", "input_params": [...]},
#      {"tool_id": "ckd_epi_2021", "input_params": [...]},
#      ...
#    ]}

💡 適合場景: 已有病患數據,想知道能計算哪些評分

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior3/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. It discloses the return format through an example output, which is helpful, but it does not mention any limitations, behavior on empty results, or performance characteristics. For a simple read-only lookup, this is adequate but not exhaustive.

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 well-structured with sections for Args, Returns, an example, and a usage tip. Though it uses emojis and extra formatting, every part adds value. It is not overly verbose for the amount of information conveyed.

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

Completeness4/5

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

The tool is simple and the description covers the key aspects: purpose, input examples, return structure, and a typical use case. Although it doesn't discuss error cases or alternative tools, for the given complexity it is sufficiently complete for an agent to call it correctly.

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 schema has one parameter 'params' with minimal description (0% schema description coverage). The tool description compensates by providing multiple examples of valid values and clarifying it expects a list of parameter names. This gives the agent a clear understanding of what to pass.

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: reverse lookup of tools given parameters. It provides a title and examples that make the function unmistakable. However, it does not explicitly differentiate from siblings like get_related_tools, which might also appear in a related-context search, so it earns a 4 rather than a 5.

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

Usage Guidelines4/5

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

The description includes a '適合場景' (suitable scenario) section that tells the agent when to use it: when the user has patient data and wants to know which scores can be calculated. It also provides multiple concrete examples of parameter lists. It does not mention when not to use it or alternatives, but the guidance is clear enough for a lookup tool.

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

get_tool_schemaA

📋 取得工具完整資訊 + 參數 Schema + 來源提示 (Low-Level)

整併了原本的 get_calculator_info 和 get_calculation_schema, 提供 Agent 執行計算所需的所有資訊:

  1. 工具基本資訊: 名稱、用途、專科、情境

  2. 參數 Schema: 每個參數的類型、單位、正常範圍

  3. 來源提示: 參數通常從哪裡取得 (Parameter Provenance)

  4. 參考文獻: PMID/DOI (100% 覆蓋率,Vancouver style)

Args: tool_id: 計算器 ID (從 discover() 取得) include_references: 是否包含參考文獻 (預設 True) - True: 包含完整參考文獻 (citation, PMID, DOI) - False: 省略以節省 tokens include_param_sources: 是否包含參數來源提示 (預設 True) - True: 包含 clinical_hint, common_sources, normal_range - False: 只返回基本 type/unit

Returns: 完整的工具資訊,包含: - tool_id, name, purpose - specialties, contexts (High-Level 分類) - required_params (必要參數列表) - param_schemas (每個參數的詳細 Schema) - references (參考文獻,若 include_references=True)

Example:

# 完整資訊 (預設)
get_tool_schema("ckd_epi_2021")

# 只要基本資訊 (節省 tokens)
get_tool_schema("ckd_epi_2021", include_references=False, include_param_sources=False)

⏭️ 下一步: calculate(tool_id, params) 執行計算

ParametersJSON Schema
NameRequiredDescriptionDefault
tool_idYes
include_referencesNo
include_param_sourcesNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/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 explains what the tool returns in detail, including the effect of include_references and include_param_sources (including token-saving behavior), and states that references have 100% coverage in Vancouver style. It does not explicitly declare that the operation is read-only, but the 'get' verb and return structure make that clear.

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 well-structured with emoji headers, numbered lists, Args/Returns sections, an example, and a next-step pointer. It is longer than minimal, but each section serves a purpose; the summary line partially duplicates the following list, but overall the organization makes scanning easy and front-loads the core purpose.

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

Completeness5/5

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

For a 3-parameter info-retrieval tool, the description is complete: it names the sole required parameter, explains both optional flags with defaults, lists return contents, provides a concrete example, and points to the next action. An output schema exists, so not explaining return values further is acceptable. An agent can call this tool correctly with no ambiguity.

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

Parameters5/5

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

Schema description coverage is 0%, but the Args section fully compensates: tool_id is defined as the calculator ID obtained from discover(), and both boolean flags explain their True/False behavior and defaults. This adds meaning far beyond the plain input schema, giving an agent everything it needs to set each parameter correctly.

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 function: retrieving complete tool information, parameter schema, and source hints for a calculator tool. It lists the exact content returned (basic info, param schemas, references) and positions itself as a low-level helper for calculation workflows. However, it doesn't explicitly differentiate itself from siblings like get_related_tools or find_tools_by_params, though the 'next step: calculate' hint helps.

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

Usage Guidelines4/5

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

The description gives clear usage context: the tool_id should come from discover(), and after getting the schema, the next step is calculate(tool_id, params). Example calls show how to optionally trim results to save tokens. It doesn't explicitly state when not to use it or name alternative tools, but the discover → get_tool_schema → calculate flow implies the intended position in the workflow.

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. 6 tool updatesv1.6.2
    • First observedcalculate
    • First observedcalculate_batch
    • First observeddiscover
    • First observedfind_tools_by_params
    • First observedget_related_tools
    • First observedget_tool_schema

TDQS

A4.1/5.0

Scored across 6 tools

Disambiguation4/5

The three discovery tools (discover, find_tools_by_params, get_related_tools) have distinct primary intents—categorical discovery, reverse parameter lookup, and similarity-based relatedness—but they could still be confused in edge cases, especially discover(by='keyword') versus find_tools_by_params. The calculation tools are clearly distinct.

Naming Consistency4/5

All names use snake_case and imperative verbs, which is consistent, but the pattern is not uniformly verb_noun: 'discover' and 'calculate' are bare verbs while others have objects. This is a minor deviation rather than a chaotic mix.

Tool Count5/5

Six tools is well-scoped for a meta-server wrapping 75+ calculator implementations. The surface is kept small by exposing discovery, schema retrieval, single calculation, and batch calculation rather than one tool per calculator.

Completeness5/5

The server covers the full workflow: discover available tools, inspect required schemas, run single or batch calculations, and find alternative/related calculators. Since calculators are static and read-only, no update/delete operations are expected. The tool set leaves no obvious dead ends.

Maintenance

ActivityInactive
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers