DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Model Context Protocol Vs Agent2Agent: Practical Integration with Enterprise Data
  • Amazon Quick: AWS's Agentic Workspace, Explained for Engineers
  • Compliance Automated Standard Solution (COMPASS), Part 11: Compliance as Code, the OSCAL MCP Server Way
  • Migrate a Hardcoded LangGraph Agent to LaunchDarkly AI Configs in 20 Minutes

Trending

  • Your AI Agent Tests Are Passing, But Your Agent Is Still Broken
  • AWS Kiro: The Agentic IDE That Makes Specs the Unit of Work
  • Beyond Conversation: Mastering Context with Claude Code Skills and Agents
  • Spring Boot Done Right: Lessons From a 400-Module Codebase
  1. DZone
  2. Data Engineering
  3. AI/ML
  4. MCP + AWS AgentCore: Give Your AI Agent Real Tools in 60 Minutes

MCP + AWS AgentCore: Give Your AI Agent Real Tools in 60 Minutes

A hands-on walkthrough on building an AI agent with real tools using AWS Bedrock AgentCore Runtime. FastMCP and the Strands agent.

By 
Jubin Abhishek Soni user avatar
Jubin Abhishek Soni
DZone Core CORE ·
Apr. 08, 26 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
3.6K Views

Join the DZone community and get the full member experience.

Join For Free

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:

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:

Shell
 
# 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:

Shell
 
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:

Python
 
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-http transport, 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:

Python
 
# Start the server
uv run server.py


In another terminal, test it with the MCP inspector or a quick curl:

Shell
 
# 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:

Python
 
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:

Shell
 
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 entry point 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:

Shell
 
# 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:

Shell
 
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:

Shell
 
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:

Python
 
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 are 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:

Shell
 
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

  • https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/what-is-bedrock-agentcore.html
  • https://github.com/strands-agents/sdk-python
  • https://aws.amazon.com/solutions/guidance/deploying-model-context-protocol-servers-on-aws
AI AWS Tool

Opinions expressed by DZone contributors are their own.

Related

  • Model Context Protocol Vs Agent2Agent: Practical Integration with Enterprise Data
  • Amazon Quick: AWS's Agentic Workspace, Explained for Engineers
  • Compliance Automated Standard Solution (COMPASS), Part 11: Compliance as Code, the OSCAL MCP Server Way
  • Migrate a Hardcoded LangGraph Agent to LaunchDarkly AI Configs in 20 Minutes

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook