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