Model Context Protocol (MCP) lets AI clients connect to tools and external data sources through a consistent interface. Running an MCP server locally is useful during development, but what if you want to host one remotely?
In this tutorial, we'll deploy a Python weather MCP server built with FastMCP to Amazon EKS, expose it through a Kubernetes LoadBalancer Service, and connect it to Claude Code and Pydantic AI Agent.
What we're building
Our application follows this logical request flow:
Claude Code (MCP client)
|
| HTTP /mcp
v
AWS Load Balancer
|
v
Kubernetes Service
|
+------------------+
| |
v v
MCP Server Pod 1 MCP Server Pod 2
Python :8000 Python :8000
\ /
Amazon EKS worker nodes
Amazon ECR --> container image pulled by worker nodes
The Deployment maintains two replicas of our application. Each pod runs one Python MCP server container, although Kubernetes pods can contain multiple containers. The Service gives those pods a stable network entry point and requests an AWS load balancer.
This is a logical diagram: the precise network path depends on how the AWS load balancer targets nodes or pods.
You can find the code on github.
Local Test
First we build the docker image for the demo project using:
docker build -t mcp-server:v1.0.0 .
And run the application in a local container using:
docker run -p 3000:8000 --name weather-mcp-service mcp-server:v1.0.0
To inspect the application on local use the MCP Inspector:
npx @modelcontextprotocol/inspector
Prerequisites
You'll need an AWS account with permissions (I'm using the AdministratorAccess) for ECR, EKS, and associated infrastructure, plus the AWS CLI, Docker with Buildx, eksctl, kubectl and Claude Code installed.
Cost reminder: EKS, EC2 instances, and load balancers can incur charges. Delete the demo resources when you're finished.
1. Sign in to AWS
I'm using an AWS SSO profile called demo:
export AWS_PROFILE=demo
aws sso login --profile demo
aws sts get-caller-identity
The first command makes demo the active profile for this terminal session, so we don't need to add --profile to every AWS command.
2. Create an Amazon ECR repository
Amazon Elastic Container Registry (ECR) will store our Docker image.
aws ecr create-repository \
--repository-name mcp-server \
--region us-east-1
Next, authenticate Docker to your ECR registry. Replace <AWS_ACCOUNT_ID> with the account ID returned by aws sts get-caller-identity:
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin \
<AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com
The Docker login target is the registry hostname, not the repository URL.
3. Build and push the Docker image
From the directory containing the application's Dockerfile, run:
docker buildx build \
--platform linux/amd64 \
-t <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/mcp-server:v1.0.0 \
--push .
I'm explicitly building for linux/amd64 because the demo uses x86-based EC2 instances, while development may happen on an Apple Silicon Mac. A .dockerignore file should exclude local files such as .venv/.
4. Provision an EKS cluster
Create eksctl-cluster.yaml:
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: mcp-cluster
region: us-east-1
managedNodeGroups:
- name: mcp-nodes
instanceType: t3.medium
desiredCapacity: 2
minSize: 1
maxSize: 3
Then create the cluster and check the worker nodes:
eksctl create cluster -f eksctl-cluster.yaml
kubectl get nodes
This provisions an EKS cluster with a managed node group initially targeting two EC2 instances. The minimum and maximum values define scaling limits; they don't install an autoscaler by themselves.
5. Deploy the MCP server
Kubernetes uses a Deployment to maintain the desired number of application pods. Create deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server-deployment
spec:
replicas: 2
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
containers:
- name: mcp-server
image: <AWS_ACCOUNT_ID>.dkr.ecr.us-east-1.amazonaws.com/mcp-server:v1.0.0
ports:
- containerPort: 8000
Replace <AWS_ACCOUNT_ID> before applying it. The MCP application must listen on 0.0.0.0:8000 inside its container.
kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get pods -o wide
To follow the logs from both replicas:
kubectl logs -f -l app=mcp-server \
--all-containers=true \
--prefix=true
Notice that we can inspect the pods and their logs before creating a Service. The Service is for stable networking and exposure; it doesn't create the pods.
6. Expose the application
Create service.yaml:
apiVersion: v1
kind: Service
metadata:
name: mcp-server-service
spec:
type: LoadBalancer
selector:
app: mcp-server
ports:
- port: 80
targetPort: 8000
Apply it:
kubectl apply -f service.yaml
kubectl get svc
Wait for an AWS hostname to appear under EXTERNAL-IP. The Service selects pods with the app: mcp-server label and makes the application available through the load balancer.
7. Connect Claude Code to the remote MCP server
Copy the load balancer hostname from the previous command. Then, from your project directory, run:
claude mcp add weather \
--scope project \
--transport http \
http://<LOAD_BALANCER_HOSTNAME>/mcp
This registers the already deployed MCP server with Claude Code under the name weather. It doesn't install another copy of the server on your machine.
Check the configuration:
claude mcp list
claude mcp get weather
Inside Claude Code, use /mcp to inspect the connection. Then ask a weather-related question that your server's tools support and see Claude Code invoke the remote MCP tool.
Important: Public HTTP is appropriate only for a controlled demo. For production, use HTTPS, authentication/authorization, and appropriate network restrictions before exposing MCP tools.
8. Clean up
When you're finished, delete the EKS cluster:
eksctl delete cluster -f eksctl-cluster.yaml
Check that the associated AWS load balancer and other cluster resources have been removed. ECR is separate, so the repository and its images remain until you delete them:
aws ecr delete-repository \
--repository-name mcp-server \
--region us-east-1 \
--force
Only run that final command if you want to permanently delete the repository and its images.
Final thoughts
We've taken a Python MCP server from a local application to a remotely accessible service running on Kubernetes. Along the way, we used Docker to package it, ECR to store the image, EKS to run two replicas, and a LoadBalancer Service to expose it to Claude Code.
This is a demo architecture, not a production-hardened setup, but it gives you a practical foundation for hosting your own MCP tools in AWS.
Thanks for watching! Subscribe to the channel for more content like this.
Happy coding! 🚀
Top comments (0)