Back to Devoured - June 24, 2026
DEVOURED
Build and Deploy a Remote MCP Server to GKE in 30 Minutes

Build and Deploy a Remote MCP Server to GKE in 30 Minutes

DevOps Google Cloud
You can now deploy remote Model Context Protocol (MCP) servers on GKE Autopilot to centralize AI tool access and improve service scalability.
What: The guide demonstrates packaging a FastMCP-based Python server into a container and deploying it on GKE Autopilot. It uses Gateway API for SSL and GCPBackendPolicy for session affinity.
Why it matters: Centralizing MCP servers on Kubernetes prevents redundant local tool execution, ensures consistent tool updates, and enables secure, shared access for agents across an organization.
Takeaway: Follow the provided steps to host your internal MCP tools on GKE to create a shared, scalable backend for AI agents.
Decoder
  • MCP (Model Context Protocol): An open-standard protocol introduced by Anthropic that allows LLMs to interact with external tools and data sources consistently.
  • Streamable HTTP: An MCP transport layer allowing server/client communication over standard HTTP connections.
Original article

Build and Deploy a Remote MCP Server to GKE in 30 Minutes

Integrating context from tools and data sources into LLMs can be challenging, which impacts the ease of development for AI agents. To address this challenge, Anthropic introduced the Model Context Protocol (MCP), which standardizes how applications provide context to these models. Developers often want to build an MCP server for their APIs to make them available to fellow developers, allowing them to use it as context in their own applications. Google Kubernetes Engine (GKE) provides a scalable, reliable, and secure environment to deploy these remote MCP servers.

This guide shows the straightforward process of setting up a secure remote MCP server on GKE.

MCP transports

The Model Context Protocol follows a client-server architecture. It initially only supported running the server locally using the stdio transport. The protocol has since evolved and now supports remote access transports, specifically Streamable HTTP.

With Streamable HTTP, the server operates as an independent process that can handle multiple client connections. This transport uses HTTP POST and GET requests. The server must provide a single HTTP endpoint path that supports both POST and GET methods, such as https://example.com/mcp. You can learn more about the different transports in the official documentation.

Benefits of running an MCP server on GKE

Running an MCP server remotely on GKE provides several architecture benefits:

  • Scalability: GKE Autopilot is built to handle highly variable traffic. Since MCP Servers are stateless, GKE can scale horizontally to handle spikes in demand efficiently.
  • Centralized access: Teams can share access to a centralized MCP server, allowing developers to connect from local machines, Agents or pipelines instead of running redundant local servers. Updates to the central server immediately benefit everyone.
  • Enhanced security: The Kubernetes Gateway API combined with SSL certificates provides an easy way to force secure, encrypted traffic. This allows only secure connections to the MCP server, preventing unauthorized access.

Prerequisites

Before starting, ensure the following tools are installed:

  • python 3.10 or higher
  • uv (for package and project management, see the installation documentation)
  • Google Cloud SDK (gcloud)
  • kubectl command-line tool

Installation

Prepare environment variables

export PROJECT_ID=$(gcloud config get-value project) 
export REGION=us-central1

Create a folder, mcp-on-gke, to store the code for the server and deployment.

mkdir mcp-on-gke && cd mcp-on-gke

Now configure the Google Cloud credentials and set the active project.

gcloud auth login 
gcloud config set project $PROJECT_ID

Initiate the GKE Autopilot cluster creation in the background.

gcloud container clusters create-auto mcp-cluster \
 --region $REGION \
 --release-channel rapid \
 --async

Use uv to create a project, which will generate a pyproject.toml file.

uv init

Math MCP server

Large language models are excellent at non-deterministic tasks, such as generating text, summarizing ideas, and reasoning about concepts. However, they can be unreliable for deterministic tasks like math operations. To solve this, developers can create tools that provide valuable context. Using FastMCP, a framework for building MCP servers in Python, it is possible to create a simple math server with two tools: add and subtract.

First, add FastMCP as a dependency.

uv add fastmcp 
uv add asyncio

Copy the following code into server.py to create the server.

from fastmcp import FastMCP
from starlette.requests import Request
from starlette.responses import PlainTextResponse
import asyncio
import logging

logger = logging.getLogger(__name__)
logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)

mcp_port=3000

# Initialize the FastMCP server
server = FastMCP(
    "Math Server",
)

@server.tool()
def add(a: int, b: int) -> int:
    """Add two numbers together."""
    return a + b

@server.tool()
def subtract(a: int, b: int) -> int:
    """Subtract the second number from the first."""
    return a - b

@server.custom_route("/healthz", methods=["GET"])
async def health_check(request: Request) -> PlainTextResponse:
    """Simple health check endpoint that returns a 200 OK response"""
    return PlainTextResponse("OK")

if __name__ == "__main__":
    logger.info(f" MCP server started on port {mcp_port}")
    asyncio.run(
        server.run_async(
            transport="streamable-http",
            host="0.0.0.0",
            port=mcp_port
        )
    )

Testing the MCP server locally

Create the test_mcp_server.py script to connect to test the MCP Server.

from fastmcp import Client, FastMCP
import asyncio
import logging

# Connect to the remote MCP server
client = Client("https://localhost:3000/mcp")

async def test_remote_server():
    async with client:
        # Basic server interaction
        await client.ping()
        # List available operations
        tools = await client.list_tools()
        print(f"Available tools: {tools} \n")
        # Execute add operation
        result = await client.call_tool("add", {"a": 5, "b": 3})
        print(f"Result of addition: {result} \n")
        # Execute subtract operation
        result = await client.call_tool("subtract", {"a": 5, "b": 3})
        print(f"Result of subtraction: {result} \n")

if __name__ == "__main__":
    asyncio.run(test_remote_server())

Run the MCP server locally to test the connection:

uv run server.py

Building the container image

First, prepare the Dockerfile:

FROM python:3.10-slim
COPY --from=ghcr.io/astral-sh/uv:0.4.15 /uv /bin/uv
WORKDIR /app
COPY pyproject.toml .
COPY server.py .
RUN uv sync
CMD ["uv", "run", "server.py"]

Set up Artifact Registry

gcloud artifacts repositories create mcp-repo --repository-format=docker --location=$REGION

Build and push the image in parallel

gcloud builds submit --tag $REGION-docker.pkg.dev/$PROJECT_ID/mcp-repo/math-mcp-server:latest

Deploying to GKE with Gateway API and SSL

Create a deployment.yaml file to define the Kubernetes Deployment and Service.

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: $REGION-docker.pkg.dev/$PROJECT_ID/mcp-repo/math-mcp-server:latest
        ports:
        - containerPort: 3000
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /healthz
            port: 3000
          initialDelaySeconds: 15
          periodSeconds: 20
        readinessProbe:
          httpGet:
            path: /healthz
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: mcp-service
spec:
  selector:
    app: mcp-server
  ports:
  - port: 80
    targetPort: 3000

Apply this configuration to the cluster:

kubectl apply -f deployment.yaml

To secure the connection, use a Google-managed SSL certificate and attach it to a Gateway API resource. First, reserve a static IP address for your load balancer:

gcloud compute addresses create mcp-server-ip --global 
export MCP_SERVER_IP=$(gcloud compute addresses describe mcp-server-ip --global --format="value(address)")

Create a Google-Managed Certificate.

gcloud compute ssl-certificates create mcp-cert --domains mcp.yourdomain.com --global

Create a gateway.yaml file to provision the load balancer and configure Transport Layer Security (TLS) termination.

apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
  name: mcp-gateway
spec:
  gatewayClassName: gke-l7-global-external-managed
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      options:
        networking.gke.io/pre-shared-certs: mcp-cert
    addresses:
    - type: NamedAddress
      value: mcp-server-ip
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: mcp-route
spec:
  parentRefs:
  - name: mcp-gateway
  hostnames:
  - "mcp.yourdomain.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /mcp
    backendRefs:
    - name: mcp-service
      port: 80
---
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
  name: mcp-backend-policy
spec:
  default:
    sessionAffinity:
      type: CLIENT_IP
  targetRef:
    group: ""
    kind: Service
    name: mcp-service
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
  name: mcp-health
  namespace: default
spec:
  default:
    checkIntervalSec: 15
    timeoutSec: 5
    healthyThreshold: 1
    unhealthyThreshold: 2
    logConfig:
      enabled: false
    config:
      type: HTTP
      httpHealthCheck:
        port: 3000
        requestPath: /healthz
  targetRef:
    group: ""
    kind: Service
    name: mcp-service

Deploying this configuration creates the infrastructure required to route external traffic securely to the MCP server.

kubectl apply -f gateway.yaml
Explore all 56 articles in today's digest

Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi