Skip to main content
Glama
unrealandychan

Math MCP Server

README.md
# Math MCP Server on Google Kubernetes Engine (GKE)

An enterprise-grade, secure, and production-ready implementation of a remote **Model Context Protocol (MCP)** Server deployed to **Google Kubernetes Engine (GKE)** using the modern **GKE Gateway API**.

This repository implements the architecture outlined in the Google Cloud guide: [Build and Deploy a Remote MCP Server to GKE in 30 Minutes](https://cloud.google.com/blog/topics/developers-practitioners/build-and-deploy-a-remote-mcp-server-to-gke-in-30-minutes).

---

## πŸ›οΈ Architecture & Clean Coding Standards

Unlike basic tutorial code, this repository follows strict **Domain-Driven Design (DDD)** and **Clean Code** principles to ensure that the server is robust, maintainable, and easily extendable.

### πŸ”‘ Core Principles Applied
1. **Separation of Concerns (DDD):** 
   - **Domain Layer (`src/domain`):** Contains the core business logic (`MathService`) and domain invariants. It has **zero dependencies** on the transport protocol or framework (FastMCP/Starlette).
   - **Transport Layer (`src/transport`):** Handles protocol-specific transport (FastMCP tools and HTTP routes) and acts as an adapter, delegating requests to the Domain Layer.
2. **Robust Guard Clauses & Validation:** Complete validation of input boundaries. Errors are intercepted at the domain boundary, raising domain-specific exceptions (`InvalidInputError`), which are safely handled and translated at the transport layer.
3. **Enterprise-Grade Logging:** Zero `print()` statements are used. Standard python `logging` is configured with meaningful contexts, log levels, timestamps, and line numbers.
4. **Strict Type Annotations:** 100% type-annotated code, facilitating static analysis and reducing runtime type mismatch bugs.
5. **Google-Style Docstrings:** All modules, classes, and methods are fully documented following the Google style guide.
6. **Robust Integration Testing:** Real integration test client verifying not only successful scenarios but also asserting that domain boundary rules (guard clauses) successfully intercept invalid inputs.

---

## πŸ“ Repository Structure

```text
gke-mcp-server/
β”œβ”€β”€ Dockerfile              # Multi-stage optimized Docker build using Astral uv
β”œβ”€β”€ README.md               # Comprehensive documentation and deployment guide
β”œβ”€β”€ deployment.yaml         # Kubernetes Deployment and Service resource definitions
β”œβ”€β”€ gateway.yaml            # GKE Gateway API, GCPBackendPolicy, and HealthCheckPolicy
β”œβ”€β”€ pyproject.toml          # Astral uv dependency and environment configuration
β”œβ”€β”€ server.py               # Main application entrypoint configuring global logging
β”œβ”€β”€ test_mcp_server.py      # Integration test suite validating success and boundary cases
└── src/
    β”œβ”€β”€ __init__.py
    β”œβ”€β”€ domain/
    β”‚   β”œβ”€β”€ __init__.py
    β”‚   β”œβ”€β”€ exceptions.py   # Custom domain-level exceptions (e.g. InvalidInputError)
    β”‚   └── services.py     # Core Domain service containing business logic & validations
    └── transport/
        β”œβ”€β”€ __init__.py
        └── mcp_server.py   # Transport layer registering FastMCP tools & health endpoints
```

---

## πŸš€ Local Development & Quickstart

We use **`uv`** as our Python project manager for extremely fast, reproducible installations.

### 1. Prerequisites
Ensure you have the following installed on your local machine:
- **Python 3.10+** (Python 3.11/3.12 recommended)
- **`uv`** (Astral's package manager)

### 2. Install Dependencies
Run the following command to set up the virtual environment and sync dependencies:
```bash
uv sync
```

### 3. Run the Server Locally
To start the Math MCP server on port `3000` locally:
```bash
uv run server.py
```
You should see logging indicating the server has booted:
```text
[2026-06-19 14:48:26] [INFO] [math_mcp_server] - Starting streamable-http server...
```

### 4. Run Integration Tests
While the local server is running, execute the integration tests in a separate terminal:
```bash
uv run test_mcp_server.py
```
The test client will connect to the server, list the available tools, run calculations, and **assert correctness** of outputs and domain constraints:
```text
[INFO] [test_mcp_client] - Verification PASSED: 15 + 25 = 40
[INFO] [test_mcp_client] - Verification PASSED: 50 - 15 = 35
[INFO] [test_mcp_client] - Verification PASSED: Server successfully intercepted input.
```

---

## ☸️ Production Deployment to GKE

This section describes how to containerize the server and deploy it to a secure, scalable **GKE Autopilot** cluster using **Google-Managed SSL Certificates** and the **Kubernetes Gateway API**.

### 1. Environment Setup
Configure your active Google Cloud project and region variables:
```bash
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1

# Authenticate with Google Cloud
gcloud auth login
gcloud config set project $PROJECT_ID
```

### 2. Create the GKE Cluster
Initiate a GKE Autopilot cluster optimized for cost and fast scaling:
```bash
gcloud container clusters create-auto mcp-cluster \
  --region $REGION \
  --release-channel rapid \
  --async
```

### 3. Build & Register Container Image
Set up a Google Artifact Registry repository to host the Docker image:
```bash
# Create the Docker repository
gcloud artifacts repositories create mcp-repo \
  --repository-format=docker \
  --location=$REGION

# Build and push the image using Google Cloud Build
gcloud builds submit --tag $REGION-docker.pkg.dev/$PROJECT_ID/mcp-repo/math-mcp-server:latest .
```

### 4. Deploy Workloads to GKE
Once the cluster creation is complete, fetch cluster credentials:
```bash
# Verify cluster status
gcloud container clusters list

# Get cluster kubectl credentials
gcloud container clusters get-credentials mcp-cluster --region $REGION
```

Now, update the container image path in `deployment.yaml` with your actual project details (replace `REGION` and `PROJECT_ID`), and apply the manifest:
```bash
# Apply deployment and service
kubectl apply -f deployment.yaml

# Verify pods are successfully running
kubectl get pods -l app=mcp-server
```

---

## πŸ”’ Securing & Routing Traffic (GKE Gateway API)

Instead of using legacy Ingress, this deployment uses the modern **GKE L7 Gateway API** combined with **Google-Managed SSL Certificates** for production-grade security.

### 1. Reserve Static External IP
Reserve a global static IP for the Gateway:
```bash
gcloud compute addresses create mcp-server-ip --global

# Retrieve the IP address
export MCP_SERVER_IP=$(gcloud compute addresses describe mcp-server-ip --global --format="value(address)")
echo "Your Static Load Balancer IP: $MCP_SERVER_IP"
```
**Important:** Go to your DNS provider and point your domain's **DNS A record** (e.g., `mcp.yourdomain.com`) to `$MCP_SERVER_IP`.

### 2. Create Google-Managed SSL Certificate
Replace `mcp.yourdomain.com` with your fully-qualified domain name:
```bash
gcloud compute ssl-certificates create mcp-cert --domains mcp.yourdomain.com --global
```

### 3. Apply Gateway Routing Configuration
Modify `gateway.yaml` to replace `mcp.yourdomain.com` with your domain, then deploy the routing rules:
```bash
kubectl apply -f gateway.yaml
```

The GKE Gateway controller will automatically provision a Cloud Load Balancer, bind the global static IP, attach the Google-managed SSL certificate, and route `/mcp` prefixes to the backend service.

### 4. Verify Routing Features
* **Session Affinity (`GCPBackendPolicy`):** Configures client-IP affinity to ensure SSE stream sessions from the same client stay anchored to the same backend pod.
* **Health Checks (`HealthCheckPolicy`):** Routes external GKE health check probes directly to the custom `/healthz` Starlette route on port `3000`.

Check the status of the gateway provisioning:
```bash
kubectl get gateway mcp-gateway
```

### 5. Run Production Integration Test
Point the test client to your secure production domain to run tests over SSL:
```bash
export MCP_SERVER_URL="https://mcp.yourdomain.com/mcp"
uv run test_mcp_server.py
```

---

## 🧹 Cleanup Resources

To prevent recurring charges on your Google Cloud project, tear down all resources when finished:

```bash
# Delete Kubernetes resources
kubectl delete -f gateway.yaml
kubectl delete -f deployment.yaml

# Delete global static resources
gcloud compute addresses delete mcp-server-ip --global --quiet
gcloud compute ssl-certificates delete mcp-cert --quiet

# Delete Artifact Registry repository
gcloud artifacts repositories delete mcp-repo --location=$REGION --quiet

# Delete GKE Cluster
gcloud container clusters delete mcp-cluster --region $REGION --quiet
```

---

## πŸ“ License
This project is licensed under the [MIT License](LICENSE).