AgentStack
MCP verified MIT-0 Self-run

Sample Mcp Server On Ecs

mcp-aws-samples-sample-mcp-server-on-ecs · by aws-samples

MCP server from aws-samples/sample-mcp-server-on-ecs.

No reviews yet
0 installs
16 views
0.0% view→install

Install

$ agentstack add mcp-aws-samples-sample-mcp-server-on-ecs

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Sample Mcp Server On Ecs? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

MCP on Amazon ECS with Service Connect

This project demonstrates how to deploy Model Context Protocol (MCP) servers on Amazon Elastic Container Service (Amazon ECS) with AWS Fargate using Amazon ECS Service Connect for service-to-service communication. You build and deploy a three-tier application where a Gradio web interface sends natural language queries to an AI agent, which uses MCP tools to search a product catalog stored in Amazon Simple Storage Service (Amazon S3).

Solution Architecture Overview

Workflow

  1. A user submits a natural language query (e.g., "Find laptops under $1000") through the Gradio UI, which is exposed to the internet via an Application Load Balancer provisioned by Amazon ECS Express Mode.
  2. The UI forwards the request over Amazon ECS Service Connect to the Agent Service running in a private subnet.
  3. The Agent invokes Amazon Bedrock (Nova Lite model) to interpret the query and determine which MCP tools to call.
  4. The Agent connects to the MCP Server over Amazon ECS Service Connect using the Streamable HTTP transport.
  5. The MCP Server executes the tool call — searching, filtering, or retrieving product data from an Amazon S3 bucket.
  6. The MCP Server returns results through the chain: MCP Server → Agent → UI → User.

All inter-service communication stays within the Amazon VPC. Only the UI service is publicly accessible.

Key Components

| Component | Technology | Role | | -------------------- | ------------------------------- | -------------------------------------------------- | | UI Service | Gradio on Amazon ECS | Web chat interface, public-facing via ALB | | Agent Service | Strands Agents + Amazon Bedrock | Orchestrates AI reasoning and MCP tool calls | | MCP Server | FastMCP on Amazon ECS | Exposes product catalog as MCP tools over Streamable HTTP | | Service Connect | AWS Cloud Map + Envoy proxy | Service-to-service discovery and routing | | ECS Express Mode | Managed ALB + auto-scaling | Automated public endpoint with HTTPS | | Product Catalog | Amazon S3 | Stores product data as JSON | | Infrastructure | AWS CloudFormation | Amazon VPC, subnets, IAM roles, Amazon ECS cluster |

Prerequisites

  • AWS CLI v2 ≥ 2.32.0 — run aws --version to check
  • Docker ≥ 20.10 with buildx support — run docker --version to check
  • Git — to clone the repository
  • jq — run jq --version to check
  • Bash shell — macOS/Linux terminal or WSL on Windows
  • Amazon Bedrock model access — enable the Amazon Nova Lite model in your AWS account via the Amazon Bedrock console

Directory Structure

├── cloudformation/
│   └── infrastructure.yaml      # Amazon VPC, Amazon ECS cluster, IAM roles, Amazon ECR repos
├── mcp-server/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── app/mcp_server.py        # FastMCP server with S3 integration
├── agent/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── app/agent.py             # Strands agent with Bedrock
├── ui/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── app.py                   # Gradio chat interface
├── sample-data/
│   └── product-catalog.json     # Sample product data
├── scripts/
│   ├── setup-env.sh             # Export CloudFormation outputs as env vars
│   ├── generate-service-connect-configs.sh  # Generate Service Connect configs
│   └── cleanup.sh               # Resource cleanup script
└── docs/
    └── TROUBLESHOOTING.md       # Common issues and solutions

Deployment

Estimated deployment time: 25-30 minutes

Step 1: Clone Repository and Set Variables

git clone https://github.com/aws-samples/sample-mcp-server-on-ecs.git
cd sample-mcp-server-on-ecs

# Set your variables (modify these for your environment)
export STACK_NAME=ecs-mcp-blog
export AWS_REGION=us-west-2
export AWS_PROFILE=default

Step 2: Deploy Infrastructure

The AWS CloudFormation template provisions all the infrastructure in a single stack: an Amazon Virtual Private Cloud (Amazon VPC) with public and private subnets across two Availability Zones, an Amazon ECS cluster with AWS Fargate capacity providers, Amazon Elastic Container Registry (Amazon ECR) repositories for the three container images, an Amazon S3 bucket for product data, AWS Identity and Access Management (IAM) roles with least-privilege permissions, security groups that restrict traffic between services, and an AWS Cloud Map namespace for Service Connect discovery.

Using a single stack means you can deploy the entire environment with one command and tear it down cleanly when you're done.

aws cloudformation deploy \
  --template-file cloudformation/infrastructure.yaml \
  --stack-name $STACK_NAME \
  --capabilities CAPABILITY_NAMED_IAM \
  --region $AWS_REGION \
  --profile $AWS_PROFILE

Validate: Confirm the stack completed successfully before proceeding.

# Should output CREATE_COMPLETE
aws cloudformation describe-stacks \
  --stack-name $STACK_NAME \
  --region $AWS_REGION \
  --profile $AWS_PROFILE \
  --query 'Stacks[0].StackStatus' \
  --output text

> If the status is not CREATE_COMPLETE, check the events for errors: > ``bash > aws cloudformation describe-stack-events \ > --stack-name $STACK_NAME \ > --region $AWS_REGION \ > --profile $AWS_PROFILE \ > --query 'StackEvents[?ResourceStatus==CREATE_FAILED].[LogicalResourceId,ResourceStatusReason]' \ > --output table > ``

Step 3: Get Stack Outputs

Run the provided setup script to export all CloudFormation stack outputs as environment variables. These variables are referenced in subsequent steps to deploy and configure the Amazon ECS services.

source scripts/setup-env.sh

You should see output confirming all variables are set. If any value shows empty or you see an error, verify the stack completed successfully in Step 2.

Step 4: Login to Amazon ECR

Before you can push container images, Docker needs to authenticate with Amazon ECR. The following command retrieves a temporary authentication token and passes it to docker login.

aws ecr get-login-password --region $AWS_REGION --profile $AWS_PROFILE | \
  docker login --username AWS --password-stdin $ECR_REGISTRY

You should see Login Succeeded in the output. If you receive an authorization error, verify that your AWS CLI credentials have ecr:GetAuthorizationToken permission and that the ECR_REGISTRY variable from Step 3 is set correctly.

Step 5: Upload Product Catalog

aws s3 cp sample-data/product-catalog.json s3://$S3_BUCKET/product-catalog.json \
  --region $AWS_REGION --profile $AWS_PROFILE

Validate:

# Should return product-catalog.json with size ~5 KiB
aws s3 ls s3://$S3_BUCKET/ --region $AWS_REGION --profile $AWS_PROFILE

Step 6: Build and Push Docker Images

Each service has its own Dockerfile in its directory. The --platform linux/amd64 flag is required because Amazon ECS with AWS Fargate runs Linux x86_64 containers. If you build on Apple Silicon (M1/M2/M3) without this flag, the task will fail at runtime with an exec format error. The --push flag combines the build and push into a single command.

# MCP Server
docker buildx build --platform linux/amd64 \
  -t $ECR_REGISTRY/${STACK_NAME}-mcp-server:latest \
  ./mcp-server --push

# Agent
docker buildx build --platform linux/amd64 \
  -t $ECR_REGISTRY/${STACK_NAME}-agent:latest \
  ./agent --push

# UI
docker buildx build --platform linux/amd64 \
  -t $ECR_REGISTRY/${STACK_NAME}-ui:latest \
  ./ui --push

Validate: Confirm all three images exist in Amazon ECR.

# Each should return an imageDigest — if empty, the push failed
for repo in mcp-server agent ui; do
  echo "--- ${STACK_NAME}-${repo} ---"
  aws ecr describe-images \
    --repository-name ${STACK_NAME}-${repo} \
    --region $AWS_REGION \
    --profile $AWS_PROFILE \
    --query 'imageDetails[0].[imageTags[0],imageSizeInBytes]' \
    --output text
done

Step 7: Create Service Connect Config Files

Amazon ECS Service Connect uses AWS Cloud Map for service discovery and an Envoy sidecar proxy for traffic routing. Each service needs a JSON configuration that defines its discovery name, port mapping, and log destination. The MCP Server and Agent register themselves as discoverable endpoints so other services can reach them by name (for example, http://mcp-server:8080). The UI is a client-only consumer — it doesn't register itself but needs the Envoy sidecar to resolve the Agent's address.

Run the following script to generate the Service Connect configuration files for all three services:

./scripts/generate-service-connect-configs.sh

You should see output listing the three generated config files in config/.

Step 8: Deploy Amazon ECS Services

You deploy the services in dependency order: MCP Server first (no upstream dependencies), then Agent (depends on MCP Server), and finally the UI (depends on Agent). The MCP Server and Agent run as standard Amazon ECS services in private subnets with no public IP addresses — Amazon ECS Service Connect handles all inter-service routing. The UI uses Amazon ECS Express Mode, which automatically provisions an Application Load Balancer, target group, and auto-scaling policy.

MCP Server Service
aws ecs create-service \
  --cluster $CLUSTER_NAME \
  --service-name mcp-server-service \
  --task-definition ${STACK_NAME}-mcp-server \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[$PRIVATE_SUBNETS],securityGroups=[$MCP_SG],assignPublicIp=DISABLED}" \
  --service-connect-configuration file://config/${STACK_NAME}-mcp-server-service-connect.json \
  --region $AWS_REGION \
  --profile $AWS_PROFILE
Agent Service
aws ecs create-service \
  --cluster $CLUSTER_NAME \
  --service-name agent-service \
  --task-definition ${STACK_NAME}-agent \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[$PRIVATE_SUBNETS],securityGroups=[$AGENT_SG],assignPublicIp=DISABLED}" \
  --service-connect-configuration file://config/${STACK_NAME}-agent-service-connect.json \
  --region $AWS_REGION \
  --profile $AWS_PROFILE

Validate: Wait for MCP Server and Agent to stabilize before deploying UI.

echo "Waiting 90 seconds for tasks to start..."
sleep 90

aws ecs describe-services \
  --cluster $CLUSTER_NAME \
  --services mcp-server-service agent-service \
  --region $AWS_REGION \
  --profile $AWS_PROFILE \
  --query 'services[].[serviceName,status,runningCount,desiredCount]' \
  --output table

Both services should show runningCount: 1. If runningCount is 0, check for task failures:

TASK_ARN=$(aws ecs list-tasks --cluster $CLUSTER_NAME --service-name mcp-server-service \
  --desired-status STOPPED --region $AWS_REGION --profile $AWS_PROFILE \
  --query 'taskArns[0]' --output text)

if [ "$TASK_ARN" != "None" ] && [ -n "$TASK_ARN" ]; then
  aws ecs describe-tasks --cluster $CLUSTER_NAME --tasks $TASK_ARN \
    --region $AWS_REGION --profile $AWS_PROFILE \
    --query 'tasks[0].[stoppedReason,containers[].reason]' --output text
else
  echo "No stopped tasks found for mcp-server-service"
fi
UI Service (Express Mode)

The UI service uses ECS Express Mode, which provisions an Application Load Balancer automatically. Deploy it in three steps:

Create the UI service:

aws ecs create-express-gateway-service \
  --cluster $CLUSTER_NAME \
  --service-name ui-service \
  --execution-role-arn $EXECUTION_ROLE \
  --infrastructure-role-arn $INFRA_ROLE \
  --primary-container "{
    \"image\": \"${UI_ECR}:latest\",
    \"containerPort\": 7860,
    \"awsLogsConfiguration\": {
      \"logGroup\": \"${UI_LOG_GROUP}\",
      \"logStreamPrefix\": \"ecs\"
    },
    \"environment\": [
      {\"name\": \"AGENT_ENDPOINT\", \"value\": \"http://agent:3000\"}
    ]
  }" \
  --task-role-arn $UI_TASK_ROLE \
  --network-configuration subnets=$PUBLIC_SUBNETS,securityGroups=$UI_SG \
  --cpu "256" \
  --memory "512" \
  --scaling-target minTaskCount=1,maxTaskCount=4,autoScalingMetric=AVERAGE_CPU,autoScalingTargetValue=70 \
  --tags key=Project,value=ECS-MCP-Blog \
  --region $AWS_REGION \
  --profile $AWS_PROFILE

Wait for the service to stabilize (~4 minutes):

aws ecs wait services-stable \
  --cluster $CLUSTER_NAME \
  --services ui-service \
  --region $AWS_REGION \
  --profile $AWS_PROFILE

Add Service Connect (required for UI to reach Agent):

aws ecs update-service \
  --cluster $CLUSTER_NAME \
  --service ui-service \
  --service-connect-configuration file://config/${STACK_NAME}-ui-service-connect.json \
  --force-new-deployment \
  --region $AWS_REGION \
  --profile $AWS_PROFILE

> Note: Express Mode does not support Service Connect on initial creation. The update step adds the Envoy sidecar needed for UI to communicate with Agent via http://agent:3000.

Wait for the Service Connect deployment to complete (~6 minutes):

Express Mode uses canary deployments with a bake period. The following command waits until the new task (with Service Connect) is stable and the old task has drained:

aws ecs wait services-stable \
  --cluster $CLUSTER_NAME \
  --services ui-service \
  --region $AWS_REGION \
  --profile $AWS_PROFILE

Step 9: Verify Deployment

# Check service status
aws ecs describe-services \
  --cluster $CLUSTER_NAME \
  --services mcp-server-service agent-service ui-service \
  --region $AWS_REGION \
  --profile $AWS_PROFILE \
  --query 'services[].[serviceName,status,runningCount,desiredCount]' \
  --output table

All three services should show ACTIVE with runningCount matching desiredCount:

--------------------------------------------
|             DescribeServices             |
+---------------------+---------+----+-----+
|  mcp-server-service |  ACTIVE |  2 |  2  |
|  agent-service      |  ACTIVE |  1 |  1  |
|  ui-service         |  ACTIVE |  1 |  1  |
+---------------------+---------+----+-----+

> ⚠️ If ui-service shows 2/1 (runningCount > desiredCount): The canary deployment is still in progress. Wait a few more minutes and re-run the command. Do not proceed until all services show matching counts.

# Get UI public URL via Express Mode API
UI_SERVICE_ARN=$(aws ecs describe-services --cluster $CLUSTER_NAME --services ui-service \
  --region $AWS_REGION --profile $AWS_PROFILE \
  --query 'services[0].serviceArn' --output text)

UI_ENDPOINT=$(aws ecs describe-express-gateway-service \
  --service-arn $UI_SERVICE_ARN \
  --region $AWS_REGION --profile $AWS_PROFILE \
  --query 'service.activeConfigurations[0].ingressPaths[0].endpoint' --output text)

echo "UI URL: https://${UI_ENDPOINT}/"

Step 10: Test the Application

Open the UI URL in your browser. You should see a Gradio chat interface with a text input field. Try the following queries to verify the end-to-end flow through all three services:

  • "Show me electronics under $100"
  • "What laptops do you have?"
  • "Find running shoes in stock"

Each query travels the full chain: the UI sends it to the Agent

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.