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! 🚀
