If you've been building with AI agents, you've probably hit the same wall I did: your agent needs to do things — query databases, call APIs, check systems — but wiring up each tool is a bespoke integration every time. The Model Context Protocol (MCP) solves this by giving agents a standard way to discover and invoke tools. Think of it as USB-C for AI tooling.
The problem? Most MCP tutorials stop at "run it locally with stdio." That's fine for solo dev work, but it falls apart the moment you need:
- Multiple clients connecting to the same server
- Auth, session isolation, and scaling
- A deployment that doesn't die when your laptop sleeps
AWS Bedrock AgentCore Runtime changes the equation. You write an MCP server, hand it over, and AgentCore handles containerization, scaling, IAM auth, and session isolation — each user session runs in a dedicated microVM. No ECS clusters to configure. No load balancers to tune.
In this post, we'll build a practical MCP server from scratch, deploy it to AgentCore Runtime, and connect an AI agent to it. The whole thing takes about 30-60 minutes.
What We're Building
We'll create an MCP server that exposes infrastructure health tools — the kind of thing a DevOps agent would use to check system status, list recent deployments, and surface alerts. It's more interesting than a dice roller but simple enough to follow.
Here's the architecture:
Your agent connects via IAM auth → AgentCore discovers the tools → your server executes them → results stream back. You never manage servers, containers, or networking.
Prerequisites
Before we start, make sure you have:
- Python 3.10+ and uv (or pip — but uv is faster)
- AWS CLI configured with credentials that have Bedrock AgentCore permissions
- Node.js 18+ (for the AgentCore CLI)
- An AWS account with AgentCore access (there's a free tier)
Install the AgentCore tooling:
# AgentCore CLI
npm install -g @aws/agentcore
# AgentCore Python SDK
pip install bedrock-agentcore
# AgentCore Starter Toolkit (handles scaffolding + deployment)
pip install bedrock-agentcore-starter-toolkit
Step 1: Build the MCP Server
Create your project structure:
mkdir infra-health-mcp && cd infra-health-mcp
uv init --bare
uv add mcp bedrock-agentcore
Now create server.py. We'll use FastMCP, which gives us a decorator-based API for defining tools:
from mcp.server.fastmcp import FastMCP
from datetime import datetime, timedelta
import random
mcp = FastMCP("infra-health")
@mcp.tool()
def get_service_status(service_name: str) -> dict:
"""Check the health status of a deployed service.
Args:
service_name: Name of the service to check
(e.g., 'api-gateway', 'auth-service', 'payments')
"""
# In production, this would hit your monitoring API
statuses = ["healthy", "healthy", "healthy", "degraded", "unhealthy"]
uptime = round(random.uniform(95.0, 99.99), 2)
return {
"service": service_name,
"status": random.choice(statuses),
"uptime_percent": uptime,
"last_checked": datetime.utcnow().isoformat(),
"active_instances": random.randint(2, 10),
"avg_latency_ms": round(random.uniform(12, 250), 1)
}
@mcp.tool()
def list_recent_deployments(hours: int = 24) -> list[dict]:
"""List deployments that occurred in the last N hours.
Args:
hours: Number of hours to look back (default: 24)
"""
services = ["api-gateway", "auth-service", "payments",
"notification-svc", "user-profile"]
deployers = ["ci-pipeline", "ci-pipeline", "hotfix-manual"]
deployments = []
for i in range(random.randint(1, 5)):
deploy_time = datetime.utcnow() - timedelta(
hours=random.randint(1, hours)
)
deployments.append({
"service": random.choice(services),
"version": f"v1.{random.randint(20,45)}.{random.randint(0,9)}",
"deployed_at": deploy_time.isoformat(),
"deployed_by": random.choice(deployers),
"status": random.choice(["success", "success", "rolled_back"])
})
return sorted(deployments, key=lambda d: d["deployed_at"], reverse=True)
@mcp.tool()
def get_active_alerts(severity: str = "all") -> list[dict]:
"""Retrieve currently active infrastructure alerts.
Args:
severity: Filter by severity level -
'critical', 'warning', 'info', or 'all'
"""
alerts = [
{
"id": "ALT-1024",
"severity": "warning",
"message": "auth-service p99 latency above threshold (>500ms)",
"triggered_at": (
datetime.utcnow() - timedelta(minutes=23)
).isoformat(),
"service": "auth-service"
},
{
"id": "ALT-1025",
"severity": "critical",
"message": "payments service error rate at 2.3% (threshold: 1%)",
"triggered_at": (
datetime.utcnow() - timedelta(minutes=8)
).isoformat(),
"service": "payments"
},
{
"id": "ALT-1026",
"severity": "info",
"message": "Scheduled maintenance window in 4 hours",
"triggered_at": (
datetime.utcnow() - timedelta(hours=2)
).isoformat(),
"service": "all"
},
]
if severity != "all":
alerts = [a for a in alerts if a["severity"] == severity]
return alerts
if __name__ == "__main__":
mcp.run(transport="streamable-http")
Key decisions here:
- Each tool has a clear docstring with typed args — this is what the LLM sees when deciding which tool to call, so be descriptive
- We're using
streamable-httptransport, which is what AgentCore Runtime expects - In production, you'd replace the mock data with calls to Datadog, CloudWatch, your deployment system, etc.
Step 2: Test Locally
Before deploying anything, make sure the server works:
# Start the server
uv run server.py
In another terminal, test it with the MCP inspector or a quick curl:
# Using the MCP CLI inspector
npx @modelcontextprotocol/inspector http://localhost:8000/mcp
You should see your three tools listed. Click through them, pass some args, verify the responses look right. Fix any issues now — it's much faster than debugging after deployment.
Step 3: Prepare for AgentCore Runtime
AgentCore Runtime needs your server wrapped with the BedrockAgentCoreApp. Update server.py by adding this at the top and modifying the entrypoint:
from bedrock_agentcore.runtime import BedrockAgentCoreApp
# ... (keep all your existing tool definitions) ...
# Replace the if __name__ block:
app = BedrockAgentCoreApp()
@app.entrypoint()
def handler(payload):
return mcp.run(transport="streamable-http")
if __name__ == "__main__":
app.run()
Alternatively, use the AgentCore Starter Toolkit to scaffold the project structure automatically:
agentcore init --protocol mcp
This generates the Dockerfile, IAM role config, and agentcore.json for you. Copy your server.py into the generated project and point the entrypoint to it.
Step 4: Deploy to AWS
This is the part that used to take hours of ECS/ECR/IAM wrangling. With the Starter Toolkit, it's two commands:
# Configure (generates IAM roles, ECR repo, build config)
agentcore configure
# Deploy (builds container via CodeBuild, pushes to ECR,
# deploys to AgentCore Runtime)
agentcore deploy
That's it. No Docker installed locally. No Terraform. CodeBuild handles the container image, and AgentCore Runtime manages the rest.
The output gives you a Runtime ARN — save this, you'll need it to connect your agent.
Step 5: Invoke Your Deployed Server
Test the deployed server using the AWS CLI:
aws bedrock-agent-runtime invoke-agent-runtime \
--agent-runtime-arn "arn:aws:bedrock:us-east-1:123456789:agent-runtime/your-runtime-id" \
--payload '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
--output text
You should see your three tools returned. Now try calling one:
aws bedrock-agent-runtime invoke-agent-runtime \
--agent-runtime-arn "arn:aws:bedrock:us-east-1:123456789:agent-runtime/your-runtime-id" \
--payload '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"get_active_alerts","arguments":{"severity":"critical"}},"id":2}' \
--output text
Step 6: Connect an AI Agent
Now the fun part. Let's wire this up to a Strands agent that can use our infrastructure tools conversationally:
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp.client.streamable_http import streamablehttp_client
# Connect to your deployed MCP server via IAM auth
mcp_client = MCPClient(
lambda: streamablehttp_client(
url="https://your-agentcore-endpoint/mcp",
# IAM auth is handled automatically via your AWS credentials
)
)
with mcp_client:
agent = Agent(
model="us.anthropic.claude-sonnet-4-20250514",
tools=mcp_client.list_tools_sync(),
system_prompt="""You are a DevOps assistant with access to
infrastructure health tools. When asked about system status,
check services, review recent deployments, and surface any
active alerts. Be concise and flag anything that needs
immediate attention."""
)
response = agent(
"Give me a quick health check — any services having issues? "
"And were there any recent deployments that might be related?"
)
print(response)
The agent will automatically discover the tools, decide which ones to call, and synthesize the results into a coherent answer. You'll see it call get_active_alerts, then get_service_status for the flagged services, then list_recent_deployments to correlate — all without you writing any orchestration logic.
What AgentCore Gives You for Free
It's worth pausing to appreciate what you didn't have to build:
| Concern | Without AgentCore | With AgentCore |
|---|---|---|
| Container infra | ECR + ECS/EKS + ALB | Handled |
| Session isolation | Custom session management | microVM per session |
| Auth | OAuth setup, token management | IAM SigV4 built in |
| Scaling | Auto-scaling policies, metrics | Automatic |
| Networking | VPC, security groups, NAT | Managed |
| Health checks | Custom implementation | Built in |
You wrote a Python file with tool definitions. Everything else is infrastructure you didn't touch.
Production Considerations
Before going live with real data, a few things to think about:
Replace mock data with real integrations. The tool signatures stay the same — swap random.choice(statuses) with a call to your CloudWatch API, PagerDuty, or whatever you use.
Add error handling. MCP tools should return meaningful errors, not stack traces. Wrap your integrations in try/except and return structured error responses.
Think about tool granularity. Three focused tools is better than one "do everything" tool. The LLM needs clear, specific tool descriptions to make good decisions about what to call.
Stateful vs stateless. Our server is stateless (the default and recommended mode). If you need multi-turn interactions where the server asks the user for clarification mid-execution, look into AgentCore's stateful MCP support with elicitation and sampling.
Connect to AgentCore Gateway. If your agent needs tools from multiple MCP servers, the Gateway acts as a single entry point that discovers and routes to all of them. You can also use the Responses API with a Gateway ARN to get server-side tool execution — Bedrock handles the entire orchestration loop in a single API call.
Cleanup
When you're done experimenting:
agentcore destroy
This tears down the Runtime, CodeBuild project, IAM roles, and ECR artifacts. You'll be prompted to confirm.
What's Next?
A few directions to take this further:
- Add a Gateway to combine your MCP server with AWS's open-source MCP servers (S3, DynamoDB, CloudWatch, etc.) into a single agent toolkit
- Try the AG-UI protocol alongside MCP — it standardizes how agents communicate with frontends, enabling streaming progress updates and interactive UIs
References:
strands-agents
/
sdk-python
A model-driven approach to building AI agents in just a few lines of code.
Strands Agents
A model-driven approach to building AI agents in just a few lines of code
Documentation
◆ Samples
◆ Python SDK
◆ Tools
◆ Agent Builder
◆ MCP Server
Strands Agents is a simple yet powerful SDK that takes a model-driven approach to building and running AI agents. From simple conversational assistants to complex autonomous workflows, from local development to production deployment, Strands Agents scales with your needs.
Feature Overview
- Lightweight & Flexible: Simple agent loop that just works and is fully customizable
- Model Agnostic: Support for Amazon Bedrock, Anthropic, Gemini, LiteLLM, Llama, Ollama, OpenAI, Writer, and custom providers
- Advanced Capabilities: Multi-agent systems, autonomous agents, and streaming support
- Built-in MCP: Native support for Model Context Protocol (MCP) servers, enabling access to thousands of pre-built tools
Quick Start
# Install Strands Agents
pip install strands-agents strands-agents-tools
from strands import Agent
from strands_tools import calculator
agent =…

Top comments (0)