Google Cloud
Run the server on Cloud Run or GKE
Google Cloud offers two ways to run the container. Cloud Run is serverless: it scales with traffic and you pay per request. GKE is managed Kubernetes, for when you already run a cluster.
Every platform needs the same settings from your server: listen on all interfaces (HOST=0.0.0.0), use the port the platform expects (PORT), run over HTTP (MCP_TRANSPORT_TYPE=http), and point the health check at /mcp/health. Docker explains each one.
Cloud Run
Cloud Build builds the image from your Dockerfile, and gcloud run deploy starts it with your settings and secrets.
# Enable APIs
gcloud services enable run.googleapis.com cloudbuild.googleapis.com
# Build with Cloud Build
gcloud builds submit --tag gcr.io/PROJECT_ID/my-mcp-server
# Deploy
gcloud run deploy my-mcp-server \
--image gcr.io/PROJECT_ID/my-mcp-server \
--platform managed \
--region us-central1 \
--allow-unauthenticated \
--set-env-vars HOST=0.0.0.0,MCP_TRANSPORT_TYPE=http \
--set-secrets JWT_SECRET=jwt-secret:latestCloud Run injects PORT, and nitrostack-py start reads it from the environment. Remove --allow-unauthenticated and use Cloud Run IAM if the server is private. Clients connect to https://<service-url>/mcp.
GKE
This manifest runs two replicas behind a load balancer, reads JWT_SECRET from a Kubernetes secret, and uses /mcp/health as the readiness probe.
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
spec:
replicas: 2
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
containers:
- name: mcp-server
image: gcr.io/PROJECT_ID/my-mcp-server:latest
ports:
- containerPort: 3000
env:
- name: HOST
value: "0.0.0.0"
- name: MCP_TRANSPORT_TYPE
value: "http"
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: mcp-secrets
key: jwt-secret
readinessProbe:
httpGet:
path: /mcp/health
port: 3000
---
apiVersion: v1
kind: Service
metadata:
name: mcp-server
spec:
type: LoadBalancer
selector:
app: mcp-server
ports:
- port: 80
targetPort: 3000Apply it to your cluster:
kubectl apply -f deployment.yamlSecrets
Prefer injecting secrets as environment variables (--set-secrets on Cloud Run, secretKeyRef on GKE). To read one from code instead, use Secret Manager:
from google.cloud import secretmanager
def get_secret(project_id: str, name: str) -> str:
client = secretmanager.SecretManagerServiceClient()
path = f"projects/{project_id}/secrets/{name}/versions/latest"
return client.access_secret_version(name=path).payload.data.decode("utf-8")Logs
Cloud Run sends container stdout and stderr to Cloud Logging automatically. Print JSON with a severity field to get structured entries:
import json
import sys
def log_entry(message: str, severity: str = "INFO") -> None:
print(json.dumps({"severity": severity, "message": message}), file=sys.stderr)