Clinical Terminology MCP

Deploy to AWS ECS

Deploy Clinical Terminology MCP servers to AWS ECS with Fargate.

Deploy MCP servers to AWS Elastic Container Service using Fargate for serverless container execution.

Prerequisites

  • AWS CLI configured with appropriate credentials
  • Docker installed locally
  • AWS account with permissions for ECR, ECS, IAM, and Secrets Manager

Create ECR Repository

# Create repository
aws ecr create-repository \
  --repository-name clinical-terminology-mcp/snomed-mcp \
  --region us-east-1

# Get login token
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin \
  <account-id>.dkr.ecr.us-east-1.amazonaws.com

# Build and push
docker build -t snomed-mcp -f snomed-mcp/Dockerfile .
docker tag snomed-mcp:latest \
  <account-id>.dkr.ecr.us-east-1.amazonaws.com/clinical-terminology-mcp/snomed-mcp:latest
docker push \
  <account-id>.dkr.ecr.us-east-1.amazonaws.com/clinical-terminology-mcp/snomed-mcp:latest

Store Secrets

For servers requiring credentials (ICD-11, LOINC, UMLS), store them in Secrets Manager:

# ICD-11 credentials
aws secretsmanager create-secret \
  --name clinical-terminology-mcp/icd11 \
  --secret-string '{"ICD11_CLIENT_ID":"your-id","ICD11_CLIENT_SECRET":"your-secret"}'

# UMLS API key
aws secretsmanager create-secret \
  --name clinical-terminology-mcp/umls \
  --secret-string '{"UMLS_API_KEY":"your-key"}'

# LOINC credentials
aws secretsmanager create-secret \
  --name clinical-terminology-mcp/loinc \
  --secret-string '{"LOINC_USERNAME":"your-user","LOINC_PASSWORD":"your-pass"}'

Create ECS Cluster

aws ecs create-cluster \
  --cluster-name clinical-terminology-mcp \
  --capacity-providers FARGATE FARGATE_SPOT \
  --default-capacity-provider-strategy \
    capacityProvider=FARGATE,weight=1 \
    capacityProvider=FARGATE_SPOT,weight=3

Create Task Execution Role

# Create trust policy
cat > trust-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Service": "ecs-tasks.amazonaws.com"},
    "Action": "sts:AssumeRole"
  }]
}
EOF

# Create role
aws iam create-role \
  --role-name ecsTaskExecutionRole \
  --assume-role-policy-document file://trust-policy.json

# Attach policies
aws iam attach-role-policy \
  --role-name ecsTaskExecutionRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

# Add Secrets Manager access
cat > secrets-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["secretsmanager:GetSecretValue"],
    "Resource": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:clinical-terminology-mcp/*"
  }]
}
EOF

aws iam put-role-policy \
  --role-name ecsTaskExecutionRole \
  --policy-name SecretsManagerAccess \
  --policy-document file://secrets-policy.json

Create Task Definition

cat > task-definition.json << 'EOF'
{
  "family": "snomed-mcp",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::<account-id>:role/ecsTaskExecutionRole",
  "containerDefinitions": [{
    "name": "snomed-mcp",
    "image": "<account-id>.dkr.ecr.us-east-1.amazonaws.com/clinical-terminology-mcp/snomed-mcp:latest",
    "portMappings": [{
      "containerPort": 8080,
      "protocol": "tcp"
    }],
    "environment": [
      {"name": "MCP_TRANSPORT", "value": "http"},
      {"name": "MCP_HTTP_ADDR", "value": ":8080"},
      {"name": "MCP_LOG_FORMAT", "value": "json"},
      {"name": "MCP_METRICS_ENABLED", "value": "true"}
    ],
    "healthCheck": {
      "command": ["CMD-SHELL", "wget -q --spider http://localhost:8080/health/live || exit 1"],
      "interval": 30,
      "timeout": 5,
      "retries": 3,
      "startPeriod": 10
    },
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/clinical-terminology-mcp",
        "awslogs-region": "us-east-1",
        "awslogs-stream-prefix": "snomed-mcp"
      }
    }
  }]
}
EOF

aws ecs register-task-definition --cli-input-json file://task-definition.json

For servers with secrets (e.g., ICD-11), add secrets to the container definition:

"secrets": [
  {
    "name": "ICD11_CLIENT_ID",
    "valueFrom": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:clinical-terminology-mcp/icd11:ICD11_CLIENT_ID::"
  },
  {
    "name": "ICD11_CLIENT_SECRET",
    "valueFrom": "arn:aws:secretsmanager:us-east-1:<account-id>:secret:clinical-terminology-mcp/icd11:ICD11_CLIENT_SECRET::"
  }
]

Create Application Load Balancer

# Create ALB
aws elbv2 create-load-balancer \
  --name clinical-terminology-mcp-alb \
  --subnets subnet-xxx subnet-yyy \
  --security-groups sg-xxx \
  --scheme internet-facing \
  --type application

# Create target group
aws elbv2 create-target-group \
  --name snomed-mcp-tg \
  --protocol HTTP \
  --port 8080 \
  --vpc-id vpc-xxx \
  --target-type ip \
  --health-check-path /health/ready \
  --health-check-interval-seconds 30

# Create listener
aws elbv2 create-listener \
  --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:<account-id>:loadbalancer/app/clinical-terminology-mcp-alb/xxx \
  --protocol HTTPS \
  --port 443 \
  --certificates CertificateArn=arn:aws:acm:us-east-1:<account-id>:certificate/xxx \
  --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:us-east-1:<account-id>:targetgroup/snomed-mcp-tg/xxx

Create ECS Service

aws ecs create-service \
  --cluster clinical-terminology-mcp \
  --service-name snomed-mcp \
  --task-definition snomed-mcp \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx,subnet-yyy],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \
  --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:<account-id>:targetgroup/snomed-mcp-tg/xxx,containerName=snomed-mcp,containerPort=8080" \
  --health-check-grace-period-seconds 60

Verify Deployment

# Check service status
aws ecs describe-services \
  --cluster clinical-terminology-mcp \
  --services snomed-mcp

# Test health endpoint
curl https://your-alb-domain.com/health

# Test MCP endpoint
curl -X POST https://your-alb-domain.com/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"initialize","params":{},"id":1}'

Auto Scaling (Optional)

# Register scalable target
aws application-autoscaling register-scalable-target \
  --service-namespace ecs \
  --resource-id service/clinical-terminology-mcp/snomed-mcp \
  --scalable-dimension ecs:service:DesiredCount \
  --min-capacity 1 \
  --max-capacity 10

# Create scaling policy
aws application-autoscaling put-scaling-policy \
  --service-namespace ecs \
  --resource-id service/clinical-terminology-mcp/snomed-mcp \
  --scalable-dimension ecs:service:DesiredCount \
  --policy-name cpu-scaling \
  --policy-type TargetTrackingScaling \
  --target-tracking-scaling-policy-configuration '{
    "TargetValue": 70.0,
    "PredefinedMetricSpecification": {
      "PredefinedMetricType": "ECSServiceAverageCPUUtilization"
    },
    "ScaleOutCooldown": 60,
    "ScaleInCooldown": 60
  }'