1
0
mirror of synced 2026-05-22 22:53:35 +00:00

Feature/cfn samples mcp server (#464)

* Add CloudFormation samples for MCP Server on AgentCore Runtime

- Created 04-cfn-samples/ directory with production-ready CloudFormation templates
- Added mcp-server-agentcore-runtime sample with:
  - Complete CloudFormation template (mcp-server-template.yaml)
  - Automated deployment scripts (deploy.sh, test.sh, cleanup.sh)
  - Authentication helper (get_token.py)
  - MCP client test script (test_mcp_server.py)
  - Comprehensive documentation (README.md, DETAILED_GUIDE.md)
- Features:
  - One-command deployment with automated Docker image building
  - JWT authentication via Cognito
  - ARM64 Docker images built via CodeBuild
  - Three sample MCP tools (add_numbers, multiply_numbers, greet_user)
  - Least-privilege IAM roles
  - Complete troubleshooting guide

* Add omrsamer to CONTRIBUTORS.md

* Add three additional CloudFormation samples

- Added basic-runtime: Simple agent without tools or memory
- Added multi-agent-runtime: Two-agent system with orchestrator and specialist
- Added weather-agent-runtime: Complete agent with browser, code interpreter, and memory
- Updated main README with all four samples and comprehensive documentation

* Add deployment and cleanup scripts for all CFN samples

- Added deploy.sh and cleanup.sh for basic-runtime
- Added deploy.sh and cleanup.sh for multi-agent-runtime
- Added deploy.sh and cleanup.sh for weather-agent-runtime
- All scripts are executable and follow the same pattern as mcp-server-agentcore-runtime
- Scripts include proper error handling and user-friendly output

* Add comprehensive README documentation for CFN samples with architecture diagrams

- Added detailed README.md files for basic-runtime, multi-agent-runtime, and weather-agent-runtime
- Included architecture PNG diagrams for basic-runtime and multi-agent-runtime
- Standardized testing sections across all READMEs (AWS CLI and Console only)
- Removed Python testing sections for consistency
- Added deployment, testing, troubleshooting, and cost estimate sections
- Updated CONTRIBUTORS.md with contributor name
- Updated main 04-cfn-samples README.md

* Add architecture diagram to mcp-server-agentcore-runtime README

- Added architecture.png with visual diagram
- Updated README to use local PNG instead of tutorial reference
- Added detailed architecture component descriptions

* Update get_token.py cosmetic changes

* Fix Python formatting to pass ruff linter

* Restructure infrastructure samples: rename to 04-infrastructure-as-code and organize CloudFormation templates

- Rename 04-cfn-samples to 04-infrastructure-as-code
- Create cloudformation subfolder for better organization
- Rename weather-agent-runtime to end-to-end-weather-agent
- Rename weather agent template.yaml to end-to-end-weather-agent.yaml
- Update all documentation and scripts to reflect new structure
- Update main README with new paths and folder structure
- All Python files pass ruff formatting checks

* Update CloudFormation examples to use us-west-2 region and remove production-ready language

- Changed all deploy.sh, cleanup.sh, and test.sh scripts from us-east-1 to us-west-2
- Updated all README files with CLI examples to use us-west-2
- Updated Python helper scripts (get_token.py, test_mcp_server.py) to use us-west-2 in examples
- Updated multi-agent-runtime template.yaml default region to us-west-2
- Removed 'production-ready' language from README files, replaced with 'complete'
- All 4 CloudFormation examples now consistently use us-west-2 region

* Resolve CONTRIBUTORS.md merge conflict - include all contributors from both branches

---------

Signed-off-by: Maira Ladeira Tanke <102240958+mttanke@users.noreply.github.com>
Co-authored-by: Maira Ladeira Tanke <102240958+mttanke@users.noreply.github.com>
This commit is contained in:
omrsamer
2025-10-14 14:18:20 +01:00
committed by GitHub
parent 683d63fa02
commit 534d438500
25 changed files with 5543 additions and 1 deletions
+245
View File
@@ -0,0 +1,245 @@
# CloudFormation Samples for Amazon Bedrock AgentCore
CloudFormation templates for deploying Amazon Bedrock AgentCore resources.
## Overview
These CloudFormation templates enable you to:
- Deploy AgentCore resources consistently across environments
- Automate infrastructure provisioning with Infrastructure as Code
- Maintain version control of your infrastructure
- Implement AWS best practices for security and monitoring
## 📚 Available Samples
### 01. [Hosting MCP Server on AgentCore Runtime](./cloudformation/mcp-server-agentcore-runtime/)
Deploy a complete MCP (Model Context Protocol) server with automated Docker image building and JWT authentication.
**What it deploys:**
- Amazon ECR Repository for Docker images
- AWS CodeBuild for automated ARM64 builds
- Amazon Cognito for JWT authentication
- IAM roles with least-privilege policies
- Lambda functions for custom resource automation
- Amazon Bedrock AgentCore Runtime hosting the MCP server
**Sample MCP Tools:**
- `add_numbers` - Adds two numbers
- `multiply_numbers` - Multiplies two numbers
- `greet_user` - Greets a user by name
**Deployment time:** ~10-15 minutes
**Estimated cost:** ~$50-100/month
**Quick start:**
```bash
cd cloudformation/mcp-server-agentcore-runtime
./deploy.sh
./test.sh
```
---
### 02. [Basic Agent Runtime](./cloudformation/basic-runtime/)
Deploy a basic AgentCore Runtime with a simple Strands agent - no additional tools or memory.
**What it deploys:**
- Amazon ECR Repository
- AWS CodeBuild for ARM64 Docker image building
- IAM roles with least-privilege policies
- Lambda functions for automation
- Basic AgentCore Runtime with simple agent
**Use case:** Simple agent deployment without memory, code interpreter, or browser tools
**Deployment time:** ~10-15 minutes
**Estimated cost:** ~$50-100/month
**Quick start:**
```bash
aws cloudformation create-stack \
--stack-name basic-agent-demo \
--template-body file://cloudformation/basic-runtime/template.yaml \
--capabilities CAPABILITY_IAM \
--region us-west-2
```
---
### 03. [Multi-Agent Runtime](./cloudformation/multi-agent-runtime/)
Deploy a multi-agent system where Agent1 (orchestrator) can invoke Agent2 (specialist) for complex tasks.
**What it deploys:**
- Two ECR Repositories (one per agent)
- AWS CodeBuild projects for both agents
- IAM roles with agent-to-agent invocation permissions
- Lambda functions for automation
- Two AgentCore Runtimes with agent-to-agent communication
**Architecture:**
- **Agent1 (Orchestrator)**: Routes requests and delegates to Agent2
- **Agent2 (Specialist)**: Handles detailed analysis and complex tasks
**Deployment time:** ~15-20 minutes
**Estimated cost:** ~$100-200/month
**Quick start:**
```bash
aws cloudformation create-stack \
--stack-name multi-agent-demo \
--template-body file://cloudformation/multi-agent-runtime/template.yaml \
--capabilities CAPABILITY_IAM \
--region us-west-2
```
---
### 04. [End-to-End Weather Agent with Tools and Memory](./cloudformation/end-to-end-weather-agent/)
Deploy a complete weather-based activity planning agent with browser automation, code interpreter, and memory.
**What it deploys:**
- Amazon ECR Repository
- AWS CodeBuild for ARM64 Docker image building
- S3 bucket for results storage
- IAM roles with comprehensive permissions
- Lambda functions for automation
- AgentCore Runtime with Strands agent
- **Browser Tool** for web scraping weather data
- **Code Interpreter Tool** for weather analysis
- **Memory** for storing user preferences
**Features:**
- Scrapes weather data from weather.gov using browser automation
- Analyzes weather conditions using Python code execution
- Stores and retrieves user activity preferences
- Generates personalized activity recommendations
- Saves results to S3 bucket
**Deployment time:** ~15-20 minutes
**Estimated cost:** ~$100-150/month
**Quick start:**
```bash
aws cloudformation create-stack \
--stack-name weather-agent-demo \
--template-body file://cloudformation/end-to-end-weather-agent/end-to-end-weather-agent.yaml \
--capabilities CAPABILITY_IAM \
--region us-west-2
```
---
## Prerequisites
Before deploying any CloudFormation template, ensure you have:
1. **AWS Account** with appropriate permissions
2. **AWS CLI** installed and configured
```bash
aws configure
```
3. **Access to Amazon Bedrock AgentCore** (preview)
4. **IAM Permissions** to create:
- CloudFormation stacks
- IAM roles and policies
- ECR repositories
- Lambda functions
- CodeBuild projects
- AgentCore resources
- S3 buckets (for weather agent)
## General Usage Pattern
Each sample follows a consistent structure:
```bash
# Deploy
aws cloudformation create-stack \
--stack-name <stack-name> \
--template-body file://<sample-directory>/template.yaml \
--capabilities CAPABILITY_IAM \
--region <region>
# Monitor deployment
aws cloudformation describe-stacks \
--stack-name <stack-name> \
--region <region>
# Cleanup
aws cloudformation delete-stack \
--stack-name <stack-name> \
--region <region>
```
Default values:
- Stack name: Varies by sample (see quick start commands)
- Region: `us-west-2`
## Repository Structure
```
04-infrastructure-as-code/
├── README.md # This file
└── cloudformation/ # CloudFormation samples
├── mcp-server-agentcore-runtime/ # MCP Server sample
│ ├── deploy.sh # Deployment script
│ ├── test.sh # Testing script
│ ├── cleanup.sh # Cleanup script
│ ├── mcp-server-template.yaml # CloudFormation template
│ ├── get_token.py # Authentication helper
│ ├── test_mcp_server.py # MCP client test
│ ├── README.md # Sample documentation
│ └── DETAILED_GUIDE.md # Technical deep-dive
├── basic-runtime/ # Basic agent sample
│ └── template.yaml # CloudFormation template
├── multi-agent-runtime/ # Multi-agent sample
│ └── template.yaml # CloudFormation template
└── end-to-end-weather-agent/ # Weather agent sample
└── end-to-end-weather-agent.yaml # CloudFormation template
```
### Stack Creation Fails
Check CloudFormation events:
```bash
aws cloudformation describe-stack-events \
--stack-name <stack-name> \
--region <region>
```
### Permission Issues
Ensure your IAM user/role has:
- `CloudFormationFullAccess` or equivalent
- Permissions to create all resources in the template
- `iam:PassRole` for service roles
### CodeBuild Failures
Check CodeBuild logs:
```bash
aws codebuild batch-get-builds \
--ids <build-id> \
--region <region>
```
### Resource Limits
Check AWS service quotas:
```bash
aws service-quotas list-service-quotas \
--service-code <service-code>
```
## Additional Resources
- [Amazon Bedrock AgentCore Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/agentcore.html)
- [AWS CloudFormation Documentation](https://docs.aws.amazon.com/cloudformation/)
- [CloudFormation Best Practices](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html)
- [CloudFormation Template Reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/AWS_BedrockAgentCore.html)
- [Original Tutorials](../01-tutorials/)
@@ -0,0 +1,241 @@
# Basic AgentCore Runtime
This CloudFormation template deploys a basic Amazon Bedrock AgentCore Runtime with a simple Strands agent. This is the simplest possible AgentCore deployment, perfect for getting started and understanding the core concepts without additional complexity.
## Table of Contents
- [Overview](#overview)
- [Architecture](#architecture)
- [Prerequisites](#prerequisites)
- [Deployment](#deployment)
- [Testing](#testing)
- [Sample Queries](#sample-queries)
- [Cleanup](#cleanup)
- [Cost Estimate](#cost-estimate)
- [Troubleshooting](#troubleshooting)
- [🤝 Contributing](#-contributing)
- [📄 License](#-license)
## Overview
This template creates a minimal AgentCore deployment that includes:
- **AgentCore Runtime**: Hosts a simple Strands agent
- **ECR Repository**: Stores the Docker container image
- **IAM Roles**: Provides necessary permissions
- **CodeBuild Project**: Automatically builds the ARM64 Docker image
- **Lambda Functions**: Custom resources for automation
This makes it ideal for:
- Learning AgentCore basics
- Quick prototyping
- Understanding the core deployment pattern
- Building a foundation before adding complexity
## Architecture
![Basic AgentCore Runtime Architecture](architecture.png)
The architecture consists of:
- **User**: Sends questions to the agent and receives responses
- **AWS CodeBuild**: Builds the ARM64 Docker container image with the agent code
- **Amazon ECR Repository**: Stores the container image
- **AgentCore Runtime**: Hosts the Basic Agent container
- **Basic Agent**: Simple Strands agent that processes user queries
- Invokes Amazon Bedrock LLMs to generate responses
- **IAM Roles**:
- IAM role for CodeBuild (builds and pushes images)
- IAM role for Agent Execution (runtime permissions)
- **Amazon Bedrock LLMs**: Provides the AI model capabilities for the agent
## Prerequisites
### AWS Account Setup
1. **AWS Account**: You need an active AWS account with appropriate permissions
- [Create AWS Account](https://aws.amazon.com/account/)
- [AWS Console Access](https://aws.amazon.com/console/)
2. **AWS CLI**: Install and configure AWS CLI with your credentials
- [Install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
- [Configure AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html)
```bash
aws configure
```
3. **Bedrock Model Access**: Enable access to Amazon Bedrock models in your AWS region
- Navigate to [Amazon Bedrock Console](https://console.aws.amazon.com/bedrock/)
- Go to "Model access" and request access to:
- Anthropic Claude models (recommended: Claude 3.5 Sonnet or Claude 3 Haiku)
- [Bedrock Model Access Guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html)
4. **Required Permissions**: Your AWS user/role needs permissions for:
- CloudFormation stack operations
- ECR repository management
- IAM role creation
- Lambda function creation
- CodeBuild project creation
- BedrockAgentCore resource creation
## Deployment
### Option 1: Using the Deploy Script (Recommended)
```bash
# Make the script executable
chmod +x deploy.sh
# Deploy the stack
./deploy.sh
```
The script will:
1. Deploy the CloudFormation stack
2. Wait for stack creation to complete
3. Display the AgentCore Runtime ID
### Option 2: Using AWS CLI
```bash
# Deploy the stack
aws cloudformation create-stack \
--stack-name basic-agent-demo \
--template-body file://template.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--region us-west-2
# Wait for stack creation
aws cloudformation wait stack-create-complete \
--stack-name basic-agent-demo \
--region us-west-2
# Get the Runtime ID
aws cloudformation describe-stacks \
--stack-name basic-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs[?OutputKey==`AgentRuntimeId`].OutputValue' \
--output text
```
### Option 3: Using AWS Console
1. Navigate to [CloudFormation Console](https://console.aws.amazon.com/cloudformation/)
2. Click "Create stack" → "With new resources"
3. Upload the `template.yaml` file
4. Enter stack name: `basic-agent-demo`
5. Review parameters (or use defaults)
6. Check "I acknowledge that AWS CloudFormation might create IAM resources"
7. Click "Create stack"
### Deployment Time
- **Expected Duration**: 10-15 minutes
- **Main Steps**:
- Stack creation: ~2 minutes
- Docker image build (CodeBuild): ~8-10 minutes
- Runtime provisioning: ~2-3 minutes
## Testing
### Using AWS CLI
```bash
# Get the Runtime ID from stack outputs
RUNTIME_ID=$(aws cloudformation describe-stacks \
--stack-name basic-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs[?OutputKey==`AgentRuntimeId`].OutputValue' \
--output text)
# Invoke the agent
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-id $RUNTIME_ID \
--qualifier DEFAULT \
--payload '{"prompt": "What is 2+2?"}' \
--region us-west-2 \
response.json
# View the response
cat response.json
```
### Using AWS Console
1. Navigate to [Bedrock AgentCore Console](https://console.aws.amazon.com/bedrock-agentcore/)
2. Go to "Runtimes" in the left navigation
3. Find your runtime (name starts with `basic_agent_demo_`)
4. Click on the runtime name
5. Click "Test" button
6. Enter test payload:
```json
{
"prompt": "What is 2+2?"
}
```
7. Click "Invoke"
## Sample Queries
Try these queries to test your basic agent:
1. **Simple Math**:
```json
{"prompt": "What is 2+2?"}
```
2. **General Knowledge**:
```json
{"prompt": "What is the capital of France?"}
```
3. **Explanation Request**:
```json
{"prompt": "Explain what Amazon Bedrock is in simple terms"}
```
4. **Creative Task**:
```json
{"prompt": "Write a haiku about cloud computing"}
```
5. **Reasoning**:
```json
{"prompt": "If I have 5 apples and give away 2, how many do I have left?"}
```
## Cleanup
### Using the Cleanup Script (Recommended)
```bash
# Make the script executable
chmod +x cleanup.sh
# Delete the stack
./cleanup.sh
```
### Using AWS CLI
```bash
aws cloudformation delete-stack \
--stack-name basic-agent-demo \
--region us-west-2
# Wait for deletion to complete
aws cloudformation wait stack-delete-complete \
--stack-name basic-agent-demo \
--region us-west-2
```
### Using AWS Console
1. Navigate to [CloudFormation Console](https://console.aws.amazon.com/cloudformation/)
2. Select the `basic-agent-demo` stack
3. Click "Delete"
4. Confirm deletion
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

@@ -0,0 +1,63 @@
#!/bin/bash
# Cleanup script for Basic Agent Runtime CloudFormation stack
# This script deletes the CloudFormation stack and all associated resources
set -e
# Configuration
STACK_NAME="${1:-basic-agent-demo}"
REGION="${2:-us-west-2}"
echo "=========================================="
echo "Cleaning up Basic Agent Runtime"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo "=========================================="
# Confirm deletion
read -p "Are you sure you want to delete the stack '$STACK_NAME'? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "Cleanup cancelled."
exit 0
fi
echo ""
echo "Deleting CloudFormation stack..."
aws cloudformation delete-stack \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "✓ Stack deletion initiated successfully!"
echo ""
echo "Waiting for stack deletion to complete..."
echo "This may take a few minutes..."
echo ""
aws cloudformation wait stack-delete-complete \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "=========================================="
echo "✓ Stack deleted successfully!"
echo "=========================================="
echo ""
echo "All resources have been cleaned up."
echo ""
else
echo ""
echo "✗ Stack deletion failed or timed out"
echo "Check the CloudFormation console for details"
exit 1
fi
else
echo ""
echo "✗ Failed to initiate stack deletion"
exit 1
fi
@@ -0,0 +1,80 @@
#!/bin/bash
# Deploy script for Basic Agent Runtime CloudFormation stack
# This script deploys a basic AgentCore Runtime with a simple Strands agent
set -e
# Configuration
STACK_NAME="${1:-basic-agent-demo}"
REGION="${2:-us-west-2}"
TEMPLATE_FILE="template.yaml"
echo "=========================================="
echo "Deploying Basic Agent Runtime"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo "=========================================="
# Check if template file exists
if [ ! -f "$TEMPLATE_FILE" ]; then
echo "Error: Template file '$TEMPLATE_FILE' not found!"
exit 1
fi
# Deploy the CloudFormation stack
echo ""
echo "Creating CloudFormation stack..."
aws cloudformation create-stack \
--stack-name "$STACK_NAME" \
--template-body file://"$TEMPLATE_FILE" \
--capabilities CAPABILITY_NAMED_IAM \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "✓ Stack creation initiated successfully!"
echo ""
echo "Waiting for stack creation to complete..."
echo "This will take approximately 10-15 minutes..."
echo ""
aws cloudformation wait stack-create-complete \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "=========================================="
echo "✓ Stack deployed successfully!"
echo "=========================================="
echo ""
echo "Stack Outputs:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs' \
--output table \
--region "$REGION"
echo ""
echo "Agent Runtime ID:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`AgentRuntimeId`].OutputValue' \
--output text \
--region "$REGION"
echo ""
echo "To delete this stack, run:"
echo " ./cleanup.sh $STACK_NAME $REGION"
echo ""
else
echo ""
echo "✗ Stack creation failed or timed out"
echo "Check the CloudFormation console for details"
exit 1
fi
else
echo ""
echo "✗ Failed to initiate stack creation"
exit 1
fi
@@ -0,0 +1,591 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: "Basic AgentCore deployment - Simple agent runtime without memory, code interpreter, or browser"
# ============================================================================
# PARAMETERS SECTION
# ============================================================================
Parameters:
# Agent Configuration
AgentName:
Type: String
Default: "BasicAgent"
Description: "Name for the agent runtime"
AllowedPattern: "^[a-zA-Z][a-zA-Z0-9_]{0,47}$"
ConstraintDescription: "Must start with a letter, max 48 characters, alphanumeric and underscores only"
# Container Configuration
ImageTag:
Type: String
Default: "latest"
Description: "Tag for the Docker image"
# Network Configuration
NetworkMode:
Type: String
Default: "PUBLIC"
Description: "Network mode for AgentCore resources"
AllowedValues:
- PUBLIC
- PRIVATE
# ECR Configuration
ECRRepositoryName:
Type: String
Default: "basic-agent"
Description: "Name of the ECR repository"
# ============================================================================
# METADATA SECTION
# ============================================================================
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Agent Configuration"
Parameters:
- AgentName
- NetworkMode
- Label:
default: "Container Configuration"
Parameters:
- ECRRepositoryName
- ImageTag
ParameterLabels:
AgentName:
default: "Agent Name"
NetworkMode:
default: "Network Mode"
ECRRepositoryName:
default: "ECR Repository Name"
ImageTag:
default: "Image Tag"
# ============================================================================
# RESOURCES SECTION
# ============================================================================
Resources:
# ========================================================================
# ECR MODULE - Container Registry
# ========================================================================
ECRRepository:
Type: AWS::ECR::Repository
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
RepositoryName: !Sub "${AWS::StackName}-${ECRRepositoryName}"
ImageTagMutability: MUTABLE
EmptyOnDelete: true
ImageScanningConfiguration:
ScanOnPush: true
RepositoryPolicyText:
Version: "2012-10-17"
Statement:
- Sid: AllowPullFromAccount
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-ecr-repository"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: ECR
# ========================================================================
# IAM MODULE - Security and Permissions
# ========================================================================
# Agent Execution Role
AgentExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-agent-execution-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AssumeRolePolicy
Effect: Allow
Principal:
Service: bedrock-agentcore.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ArnLike:
aws:SourceArn: !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/BedrockAgentCoreFullAccess
Policies:
- PolicyName: AgentCoreExecutionPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: ECRImageAccess
Effect: Allow
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
- ecr:BatchCheckLayerAvailability
Resource: !GetAtt ECRRepository.Arn
- Sid: ECRTokenAccess
Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: "*"
- Sid: CloudWatchLogs
Effect: Allow
Action:
- logs:DescribeLogStreams
- logs:CreateLogGroup
- logs:DescribeLogGroups
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
- Sid: XRayTracing
Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
Resource: "*"
- Sid: CloudWatchMetrics
Effect: Allow
Resource: "*"
Action: cloudwatch:PutMetricData
Condition:
StringEquals:
cloudwatch:namespace: bedrock-agentcore
- Sid: GetAgentAccessToken
Effect: Allow
Action:
- bedrock-agentcore:GetWorkloadAccessToken
- bedrock-agentcore:GetWorkloadAccessTokenForJWT
- bedrock-agentcore:GetWorkloadAccessTokenForUserId
Resource:
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default"
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:workload-identity-directory/default/workload-identity/*"
- Sid: BedrockModelInvocation
Effect: Allow
Action:
- bedrock:InvokeModel
- bedrock:InvokeModelWithResponseStream
Resource: "*"
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-agent-execution-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# CodeBuild Service Role
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-codebuild-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: CodeBuildPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: CloudWatchLogs
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*"
- Sid: ECRAccess
Effect: Allow
Action:
- ecr:BatchCheckLayerAvailability
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:GetAuthorizationToken
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
Resource:
- !GetAtt ECRRepository.Arn
- "*"
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-codebuild-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# Lambda Custom Resource Role
CustomResourceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-custom-resource-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CustomResourcePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: ECRAccess
Effect: Allow
Action:
- ecr:ListImages
- ecr:BatchDeleteImage
- ecr:GetAuthorizationToken
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
Resource: !GetAtt ECRRepository.Arn
- Sid: CodeBuildAccess
Effect: Allow
Action:
- codebuild:StartBuild
- codebuild:BatchGetBuilds
- codebuild:BatchGetProjects
Resource: !GetAtt AgentImageBuildProject.Arn
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-custom-resource-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# ========================================================================
# LAMBDA MODULE - Custom Resources
# ========================================================================
CodeBuildTriggerFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-codebuild-trigger"
Description: "Triggers CodeBuild projects as CloudFormation custom resource"
Handler: index.handler
Role: !GetAtt CustomResourceRole.Arn
Runtime: python3.9
Timeout: 900
Code:
ZipFile: |
import boto3
import cfnresponse
import json
import logging
import time
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
logger.info('Received event: %s', json.dumps(event))
try:
if event['RequestType'] == 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
return
project_name = event['ResourceProperties']['ProjectName']
wait_for_completion = event['ResourceProperties'].get('WaitForCompletion', 'true').lower() == 'true'
logger.info(f"Attempting to start CodeBuild project: {project_name}")
logger.info(f"Wait for completion: {wait_for_completion}")
# Start the CodeBuild project
codebuild = boto3.client('codebuild')
# First, verify the project exists
try:
project_info = codebuild.batch_get_projects(names=[project_name])
if not project_info['projects']:
raise Exception(f"CodeBuild project '{project_name}' not found")
logger.info(f"CodeBuild project '{project_name}' found")
except Exception as e:
logger.error(f"Error checking project existence: {str(e)}")
raise
response = codebuild.start_build(projectName=project_name)
build_id = response['build']['id']
logger.info(f"Successfully started build: {build_id}")
if not wait_for_completion:
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'BuildId': build_id,
'Status': 'STARTED'
})
return
# Wait for the build to complete
max_wait_time = context.get_remaining_time_in_millis() / 1000 - 30 # Leave 30s buffer
start_time = time.time()
while True:
if time.time() - start_time > max_wait_time:
error_message = f"Build {build_id} timed out"
logger.error(error_message)
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': error_message})
return
build_response = codebuild.batch_get_builds(ids=[build_id])
build_status = build_response['builds'][0]['buildStatus']
if build_status == 'SUCCEEDED':
logger.info(f"Build {build_id} succeeded")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'BuildId': build_id,
'Status': build_status
})
return
elif build_status in ['FAILED', 'FAULT', 'STOPPED', 'TIMED_OUT']:
error_message = f"Build {build_id} failed with status: {build_status}"
logger.error(error_message)
# Get build logs for debugging
try:
logs_info = build_response['builds'][0].get('logs', {})
if logs_info.get('groupName') and logs_info.get('streamName'):
logger.info(f"Build logs available in CloudWatch")
except Exception as log_error:
logger.warning(f"Could not get log information: {log_error}")
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Error': error_message,
'BuildId': build_id
})
return
logger.info(f"Build {build_id} status: {build_status}")
time.sleep(30) # Check every 30 seconds
except Exception as e:
logger.error('Error: %s', str(e))
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Error': str(e)
})
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-codebuild-trigger"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: Lambda
# ========================================================================
# CODEBUILD MODULE - Container Image Building
# ========================================================================
AgentImageBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub "${AWS::StackName}-basic-agent-build"
Description: !Sub "Build basic agent Docker image for ${AWS::StackName}"
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: NO_ARTIFACTS
Environment:
Type: ARM_CONTAINER
ComputeType: BUILD_GENERAL1_LARGE
Image: aws/codebuild/amazonlinux2-aarch64-standard:3.0
PrivilegedMode: true
EnvironmentVariables:
- Name: AWS_DEFAULT_REGION
Value: !Ref AWS::Region
- Name: AWS_ACCOUNT_ID
Value: !Ref AWS::AccountId
- Name: IMAGE_REPO_NAME
Value: !Ref ECRRepository
- Name: IMAGE_TAG
Value: !Ref ImageTag
- Name: STACK_NAME
Value: !Ref AWS::StackName
Source:
Type: NO_SOURCE
BuildSpec: |
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image for basic agent ARM64...
# Step 1.1: Create requirements.txt
- |
cat > requirements.txt << 'EOF'
strands-agents
boto3
bedrock-agentcore
EOF
# Step 1.2: Create my_agent.py (simplified basic version)
- |
cat > my_agent.py << 'EOF'
from strands import Agent
import os
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
def create_basic_agent() -> Agent:
"""Create a basic agent with simple functionality"""
system_prompt = """You are a helpful assistant. Answer questions clearly and concisely."""
return Agent(
system_prompt=system_prompt,
name="BasicAgent"
)
@app.entrypoint
async def invoke(payload=None):
"""Main entrypoint for the agent"""
try:
# Get the query from payload
query = payload.get("prompt", "Hello, how are you?") if payload else "Hello, how are you?"
# Create and use the agent
agent = create_basic_agent()
response = agent(query)
return {
"status": "success",
"response": response.message['content'][0]['text']
}
except Exception as e:
return {
"status": "error",
"error": str(e)
}
if __name__ == "__main__":
app.run()
EOF
# Step 1.3: Create Dockerfile
- |
cat > Dockerfile << 'EOF'
FROM public.ecr.aws/docker/library/python:3.11-slim
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
RUN pip install aws-opentelemetry-distro>=0.10.1
ENV AWS_REGION=us-west-2
ENV AWS_DEFAULT_REGION=us-west-2
# Create non-root user
RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore
EXPOSE 8080
EXPOSE 8000
COPY . .
CMD ["opentelemetry-instrument", "python", "-m", "my_agent"]
EOF
# Step 1.4: Build the image
- echo Building ARM64 image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- echo ARM64 Docker image pushed successfully
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-basic-build"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: CodeBuild
# CUSTOM RESOURCE - Trigger Image Build
TriggerImageBuild:
Type: Custom::CodeBuildTrigger
DependsOn:
- ECRRepository
- AgentImageBuildProject
- CodeBuildTriggerFunction
Properties:
ServiceToken: !GetAtt CodeBuildTriggerFunction.Arn
ProjectName: !Ref AgentImageBuildProject
WaitForCompletion: "true"
# ========================================================================
# AGENTCORE MODULE - Runtime Only (No Tools)
# ========================================================================
# AgentCore Runtime
AgentRuntime:
Type: AWS::BedrockAgentCore::Runtime
DependsOn:
- TriggerImageBuild
Properties:
AgentRuntimeName: !Sub
- "${StackNameUnderscore}_${AgentName}"
- StackNameUnderscore: !Join ["_", !Split ["-", !Ref "AWS::StackName"]]
AgentRuntimeArtifact:
ContainerConfiguration:
ContainerUri: !Sub "${ECRRepository.RepositoryUri}:${ImageTag}"
RoleArn: !GetAtt AgentExecutionRole.Arn
NetworkConfiguration:
NetworkMode: !Ref NetworkMode
Description: !Sub "Basic agent runtime for ${AWS::StackName}"
# ============================================================================
# OUTPUTS SECTION
# ============================================================================
Outputs:
# AGENTCORE MODULE OUTPUTS
AgentRuntimeId:
Description: "ID of the created agent runtime"
Value: !GetAtt AgentRuntime.AgentRuntimeId
Export:
Name: !Sub "${AWS::StackName}-AgentRuntimeId"
ECRRepositoryUri:
Description: "URI of the ECR repository"
Value: !GetAtt ECRRepository.RepositoryUri
Export:
Name: !Sub "${AWS::StackName}-ECRRepositoryUri"
AgentExecutionRoleArn:
Description: "ARN of the agent execution role"
Value: !GetAtt AgentExecutionRole.Arn
Export:
Name: !Sub "${AWS::StackName}-AgentExecutionRoleArn"
@@ -0,0 +1,371 @@
# End-to-End Weather Agent with Tools and Memory
This CloudFormation template deploys a complete Amazon Bedrock AgentCore Runtime with a sophisticated weather-based activity planning agent. This demonstrates the full power of AgentCore by integrating Browser tool, Code Interpreter, Memory, and S3 storage in a single deployment.
## Table of Contents
- [Overview](#overview)
- [Architecture](#architecture)
- [Prerequisites](#prerequisites)
- [Deployment](#deployment)
- [Testing](#testing)
- [Sample Queries](#sample-queries)
- [How It Works](#how-it-works)
- [Cleanup](#cleanup)
- [Cost Estimate](#cost-estimate)
- [Troubleshooting](#troubleshooting)
- [🤝 Contributing](#-contributing)
- [📄 License](#-license)
## Overview
This template creates a comprehensive AgentCore deployment that showcases:
### Core Components
- **AgentCore Runtime**: Hosts a Strands agent with multiple tools
- **Browser Tool**: Web automation for scraping weather data from weather.gov
- **Code Interpreter**: Python code execution for weather analysis
- **Memory**: Stores user activity preferences
- **S3 Bucket**: Stores generated activity recommendations
- **ECR Repository**: Container image storage
- **IAM Roles**: Comprehensive permissions for all components
### Agent Capabilities
The Weather Activity Planner agent can:
1. **Scrape Weather Data**: Uses browser automation to fetch 8-day forecasts from weather.gov
2. **Analyze Weather**: Generates and executes Python code to classify days as GOOD/OK/POOR
3. **Retrieve Preferences**: Accesses user activity preferences from memory
4. **Generate Recommendations**: Creates personalized activity suggestions based on weather and preferences
5. **Store Results**: Saves recommendations as Markdown files in S3
### Use Cases
- Weather-based activity planning
- Automated web scraping and data analysis
- Multi-tool agent orchestration
- Memory-driven personalization
- Asynchronous task processing
## Architecture
![End-to-End Weather Agent Architecture](architecture.png)
The architecture demonstrates a complete AgentCore deployment with multiple integrated tools:
**Core Components:**
- **User**: Sends weather-based activity planning queries
- **AWS CodeBuild**: Builds the ARM64 Docker container image with the agent code
- **Amazon ECR Repository**: Stores the container image
- **AgentCore Runtime**: Hosts the Weather Activity Planner Agent
- **Weather Agent**: Strands agent that orchestrates multiple tools
- Invokes Amazon Bedrock LLMs for reasoning and code generation
- **Browser Tool**: Web automation for scraping weather data from weather.gov
- **Code Interpreter Tool**: Executes Python code for weather analysis
- **Memory**: Stores user activity preferences (30-day retention)
- **S3 Bucket**: Stores generated activity recommendations
- **IAM Roles**: Comprehensive permissions for all components
**Workflow:**
1. User sends query: "What should I do this weekend in Richmond VA?"
2. Agent extracts city and uses Browser Tool to scrape 8-day forecast
3. Agent generates Python code and uses Code Interpreter to classify weather
4. Agent retrieves user preferences from Memory
5. Agent generates personalized recommendations
6. Agent stores results in S3 bucket using use_aws tool
## Prerequisites
### AWS Account Setup
1. **AWS Account**: You need an active AWS account with appropriate permissions
- [Create AWS Account](https://aws.amazon.com/account/)
- [AWS Console Access](https://aws.amazon.com/console/)
2. **AWS CLI**: Install and configure AWS CLI with your credentials
- [Install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
- [Configure AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html)
```bash
aws configure
```
3. **Bedrock Model Access**: Enable access to Amazon Bedrock models in your AWS region
- Navigate to [Amazon Bedrock Console](https://console.aws.amazon.com/bedrock/)
- [Bedrock Model Access Guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html)
4. **Required Permissions**: Your AWS user/role needs permissions for:
- CloudFormation stack operations
- ECR repository management
- IAM role creation
- Lambda function creation
- CodeBuild project creation
- BedrockAgentCore resource creation (Runtime, Browser, CodeInterpreter, Memory)
- S3 bucket creation
## Deployment
### Option 1: Using the Deploy Script (Recommended)
```bash
# Make the script executable
chmod +x deploy.sh
# Deploy the stack
./deploy.sh
```
The script will:
1. Deploy the CloudFormation stack
2. Wait for stack creation to complete
3. Display all resource IDs (Runtime, Browser, CodeInterpreter, Memory, S3 Bucket)
### Option 2: Using AWS CLI
```bash
# Deploy the stack
aws cloudformation create-stack \
--stack-name weather-agent-demo \
--template-body file://end-to-end-weather-agent.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--region us-west-2
# Wait for stack creation
aws cloudformation wait stack-create-complete \
--stack-name weather-agent-demo \
--region us-west-2
# Get all outputs
aws cloudformation describe-stacks \
--stack-name weather-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs'
```
### Option 3: Using AWS Console
1. Navigate to [CloudFormation Console](https://console.aws.amazon.com/cloudformation/)
2. Click "Create stack" → "With new resources"
3. Upload the `end-to-end-weather-agent.yaml` file
4. Enter stack name: `weather-agent-demo`
5. Review parameters (or use defaults)
6. Check "I acknowledge that AWS CloudFormation might create IAM resources"
7. Click "Create stack"
### Deployment Time
- **Expected Duration**: 15-20 minutes
- **Main Steps**:
- Stack creation: ~2 minutes
- Docker image build (CodeBuild): ~10-12 minutes
- Runtime and tools provisioning: ~3-5 minutes
- Memory initialization: ~1 minute
## Testing
### Using AWS CLI
```bash
# Get the Runtime ID from stack outputs
RUNTIME_ID=$(aws cloudformation describe-stacks \
--stack-name weather-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs[?OutputKey==`AgentRuntimeId`].OutputValue' \
--output text)
# Get the S3 bucket name
BUCKET_NAME=$(aws cloudformation describe-stacks \
--stack-name weather-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs[?OutputKey==`ResultsBucket`].OutputValue' \
--output text)
# Invoke the agent
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-id $RUNTIME_ID \
--qualifier DEFAULT \
--payload '{"prompt": "What should I do this weekend in Richmond VA?"}' \
--region us-west-2 \
response.json
# View the immediate response
cat response.json
# Wait a few minutes for processing, then check S3 for results
aws s3 ls s3://$BUCKET_NAME/
# Download the results
aws s3 cp s3://$BUCKET_NAME/results.md ./results.md
cat results.md
```
### Using AWS Console
1. Navigate to [Bedrock AgentCore Console](https://console.aws.amazon.com/bedrock-agentcore/)
2. Go to "Runtimes" in the left navigation
3. Find your runtime (name starts with `weather_agent_demo_`)
4. Click on the runtime name
5. Click "Test" button
6. Enter test payload:
```json
{
"prompt": "What should I do this weekend in Richmond VA?"
}
```
7. Click "Invoke"
8. View the immediate response
9. Wait 2-3 minutes for background processing
10. Navigate to [S3 Console](https://console.aws.amazon.com/s3/) to download results.md from the results bucket
## Sample Queries
Try these queries to test the weather agent:
1. **Weekend Planning**:
```json
{"prompt": "What should I do this weekend in Richmond VA?"}
```
2. **Specific City**:
```json
{"prompt": "Plan activities for next week in San Francisco"}
```
3. **Different Location**:
```json
{"prompt": "What outdoor activities can I do in Seattle this week?"}
```
4. **Vacation Planning**:
```json
{"prompt": "I'm visiting Austin next week. What should I plan based on the weather?"}
```
## How It Works
### Step-by-Step Workflow
1. **User Query**: "What should I do this weekend in Richmond VA?"
2. **City Extraction**: Agent extracts "Richmond VA" from the query
3. **Weather Scraping** (Browser Tool):
- Navigates to weather.gov
- Searches for Richmond VA
- Clicks "Printable Forecast"
- Extracts 8-day forecast data (date, high, low, conditions, wind, precipitation)
- Returns JSON array of weather data
4. **Code Generation** (LLM):
- Agent generates Python code to classify weather days
- Classification rules:
- GOOD: 65-80°F, clear, no rain
- OK: 55-85°F, partly cloudy, slight rain
- POOR: <55°F or >85°F, cloudy/rainy
5. **Code Execution** (Code Interpreter):
- Executes the generated Python code
- Returns list of tuples: `[('2025-09-16', 'GOOD'), ('2025-09-17', 'OK'), ...]`
6. **Preference Retrieval** (Memory):
- Fetches user activity preferences from memory
- Preferences stored by weather type:
```json
{
"good_weather": ["hiking", "beach volleyball", "outdoor picnic"],
"ok_weather": ["walking tours", "outdoor dining", "park visits"],
"poor_weather": ["indoor museums", "shopping", "restaurants"]
}
```
7. **Recommendation Generation** (LLM):
- Combines weather analysis with user preferences
- Creates day-by-day activity recommendations
- Formats as Markdown document
8. **Storage** (S3 via use_aws tool):
- Saves recommendations to S3 bucket as `results.md`
- User can download and review recommendations
### Asynchronous Processing
The agent runs asynchronously to handle long-running tasks:
- Immediate response: "Processing started..."
- Background processing: Completes all steps
- Results available in S3 after ~2-3 minutes
## Cleanup
### Using the Cleanup Script (Recommended)
```bash
# Make the script executable
chmod +x cleanup.sh
# Delete the stack
./cleanup.sh
```
**Note**: If cleanup fails due to active browser sessions, see the AWS CLI cleanup method below for manual session termination.
### Using AWS CLI
```bash
# Step 1: Empty the S3 bucket (required before deletion)
BUCKET_NAME=$(aws cloudformation describe-stacks \
--stack-name weather-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs[?OutputKey==`ResultsBucket`].OutputValue' \
--output text)
aws s3 rm s3://$BUCKET_NAME --recursive
# Step 2: Terminate any active browser sessions
# Get the Browser ID
BROWSER_ID=$(aws cloudformation describe-stacks \
--stack-name weather-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs[?OutputKey==`BrowserId`].OutputValue' \
--output text)
# List active sessions
aws bedrock-agentcore list-browser-sessions \
--browser-id $BROWSER_ID \
--region us-west-2
# Terminate each active session (replace SESSION_ID with actual session ID from list command)
# Repeat this command for each active session
aws bedrock-agentcore terminate-browser-session \
--browser-id $BROWSER_ID \
--session-id SESSION_ID \
--region us-west-2
# Step 3: Delete the stack
aws cloudformation delete-stack \
--stack-name weather-agent-demo \
--region us-west-2
# Wait for deletion to complete
aws cloudformation wait stack-delete-complete \
--stack-name weather-agent-demo \
--region us-west-2
```
**Important**: Browser sessions are automatically created when the agent uses the browser tool. Always terminate active sessions before deleting the stack to avoid deletion failures.
### Using AWS Console
1. Navigate to [S3 Console](https://console.aws.amazon.com/s3/)
2. Find the bucket (name format: `<stack-name>-results-<account-id>`, e.g., `weather-agent-demo-results-123456789012`)
3. Empty the bucket
4. Navigate to [Bedrock AgentCore Console](https://console.aws.amazon.com/bedrock-agentcore/)
5. Go to "Browsers" in the left navigation
6. Find your browser (name starts with `weather_agent_demo_browser`)
7. Click on the browser name
8. In the "Sessions" tab, terminate any active sessions
9. Navigate to [CloudFormation Console](https://console.aws.amazon.com/cloudformation/)
10. Select the `weather-agent-demo` stack
11. Click "Delete"
12. Confirm deletion
Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

@@ -0,0 +1,63 @@
#!/bin/bash
# Cleanup script for Weather Agent Runtime CloudFormation stack
# This script deletes the CloudFormation stack and all associated resources
set -e
# Configuration
STACK_NAME="${1:-weather-agent-demo}"
REGION="${2:-us-west-2}"
echo "=========================================="
echo "Cleaning up Weather Agent Runtime"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo "=========================================="
# Confirm deletion
read -p "Are you sure you want to delete the stack '$STACK_NAME'? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "Cleanup cancelled."
exit 0
fi
echo ""
echo "Deleting CloudFormation stack..."
aws cloudformation delete-stack \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "✓ Stack deletion initiated successfully!"
echo ""
echo "Waiting for stack deletion to complete..."
echo "This may take a few minutes..."
echo ""
aws cloudformation wait stack-delete-complete \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "=========================================="
echo "✓ Stack deleted successfully!"
echo "=========================================="
echo ""
echo "All resources have been cleaned up."
echo ""
else
echo ""
echo "✗ Stack deletion failed or timed out"
echo "Check the CloudFormation console for details"
exit 1
fi
else
echo ""
echo "✗ Failed to initiate stack deletion"
exit 1
fi
@@ -0,0 +1,109 @@
#!/bin/bash
# Deploy script for Weather Agent Runtime CloudFormation stack
# This script deploys a complete weather agent with browser, code interpreter, and memory
set -e
# Configuration
STACK_NAME="${1:-weather-agent-demo}"
REGION="${2:-us-west-2}"
TEMPLATE_FILE="end-to-end-weather-agent.yaml"
echo "=========================================="
echo "Deploying Weather Agent Runtime"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo "=========================================="
# Check if template file exists
if [ ! -f "$TEMPLATE_FILE" ]; then
echo "Error: Template file '$TEMPLATE_FILE' not found!"
exit 1
fi
# Deploy the CloudFormation stack
echo ""
echo "Creating CloudFormation stack..."
aws cloudformation create-stack \
--stack-name "$STACK_NAME" \
--template-body file://"$TEMPLATE_FILE" \
--capabilities CAPABILITY_NAMED_IAM \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "✓ Stack creation initiated successfully!"
echo ""
echo "Waiting for stack creation to complete..."
echo "This will take approximately 15-20 minutes..."
echo "(Building Docker image, deploying agent with browser, code interpreter, and memory)"
echo ""
aws cloudformation wait stack-create-complete \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "=========================================="
echo "✓ Stack deployed successfully!"
echo "=========================================="
echo ""
echo "Stack Outputs:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs' \
--output table \
--region "$REGION"
echo ""
echo "Agent Runtime ID:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`AgentRuntimeId`].OutputValue' \
--output text \
--region "$REGION"
echo ""
echo "Browser ID:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`BrowserId`].OutputValue' \
--output text \
--region "$REGION"
echo ""
echo "Code Interpreter ID:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`CodeInterpreterId`].OutputValue' \
--output text \
--region "$REGION"
echo ""
echo "Memory ID:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`MemoryId`].OutputValue' \
--output text \
--region "$REGION"
echo ""
echo "Results Bucket:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`ResultsBucket`].OutputValue' \
--output text \
--region "$REGION"
echo ""
echo "To delete this stack, run:"
echo " ./cleanup.sh $STACK_NAME $REGION"
echo ""
else
echo ""
echo "✗ Stack creation failed or timed out"
echo "Check the CloudFormation console for details"
exit 1
fi
else
echo ""
echo "✗ Failed to initiate stack creation"
exit 1
fi
@@ -0,0 +1,181 @@
# Hosting MCP Server on AgentCore Runtime - CloudFormation
## Overview
This CloudFormation template deploys an MCP (Model Context Protocol) server on Amazon Bedrock AgentCore Runtime. It demonstrates how to host MCP tools on AgentCore Runtime using infrastructure as code, with automated deployment scripts for a streamlined experience.
The template uses the Amazon Bedrock AgentCore Python SDK to wrap agent functions as an MCP server compatible with Amazon Bedrock AgentCore. It handles the MCP server details so you can focus on your agent's core functionality.
When hosting tools, the Amazon Bedrock AgentCore Python SDK implements the [Stateless Streamable HTTP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports) transport protocol with the `MCP-Session-Id` header for session isolation. Your MCP server will be hosted on port `8000` and provide one invocation path: the `mcp-POST` endpoint.
### Tutorial Details
| Information | Details |
|:--------------------|:----------------------------------------------------------|
| Tutorial type | Hosting Tools |
| Tool type | MCP server |
| Tutorial components | CloudFormation, AgentCore Runtime, MCP server |
| Tutorial vertical | Cross-vertical |
| Example complexity | Easy |
| SDK used | Amazon BedrockAgentCore Python SDK and MCP Client |
### Architecture
![MCP Server AgentCore Runtime Architecture](architecture.png)
This CloudFormation template deploys a simple MCP server with 3 tools: `add_numbers`, `multiply_numbers`, and `greet_user`.
The architecture consists of:
- **User/MCP Client**: Sends requests to the MCP server with JWT authentication
- **Amazon Cognito**: Provides JWT-based authentication
- User Pool with pre-created test user (testuser/MyPassword123!)
- User Pool Client for application access
- **AWS CodeBuild**: Builds the ARM64 Docker container image with the MCP server
- **Amazon ECR Repository**: Stores the container image
- **AgentCore Runtime**: Hosts the MCP Server
- **MCP Server**: Exposes three tools via HTTP transport
- `add_numbers`: Adds two numbers
- `multiply_numbers`: Multiplies two numbers
- `greet_user`: Greets a user by name
- Validates JWT tokens from Cognito
- Processes MCP tool invocations
- **IAM Roles**:
- IAM role for CodeBuild (builds and pushes images)
- IAM role for AgentCore Runtime (runtime permissions)
### Key Features
* **One-Command Deployment** - Automated scripts handle everything
* **Complete Infrastructure** - Full infrastructure as code
* **Secure by Default** - JWT authentication with Cognito
* **Automated Build** - CodeBuild creates ARM64 Docker images
* **Easy Testing** - Automated test script included
* **Simple Cleanup** - One command removes all resources
## What Gets Deployed
The CloudFormation stack creates:
- **Amazon ECR Repository** - Stores the MCP server Docker image
- **AWS CodeBuild Project** - Builds ARM64 Docker image automatically
- **Amazon Cognito User Pool** - JWT authentication
- **Cognito User Pool Client** - Application client configuration
- **Cognito User** - Pre-created test user (testuser/MyPassword123!)
- **IAM Roles** - Least-privilege permissions for all services
- **Lambda Functions** - Custom resource automation
- **Amazon Bedrock AgentCore Runtime** - Hosts the MCP server
**MCP Server Tools**:
- `add_numbers` - Adds two numbers together
- `multiply_numbers` - Multiplies two numbers
- `greet_user` - Greets a user by name
## Prerequisites
- AWS CLI configured with appropriate credentials
- AWS account with permissions to create:
- CloudFormation stacks
- ECR repositories
- CodeBuild projects
- Cognito User Pools
- IAM roles and policies
- Lambda functions
- Bedrock AgentCore Runtime
- Python 3.8+ (for testing)
- `boto3` and `mcp` Python packages (installed automatically by test script)
## Quick Start
### 1. Deploy the Stack
```bash
cd 04-infrastructure-as-code/cloudformation/mcp-server-agentcore-runtime
./deploy.sh
```
The deployment takes approximately **10-15 minutes** and includes:
- Creating all AWS resources
- Building the Docker image
- Pushing to ECR
- Starting the AgentCore Runtime
### 2. Test the MCP Server
After deployment completes:
```bash
./test.sh
```
This will:
- Authenticate with Cognito
- Test all three MCP tools
- Display the results
### 3. Cleanup
When you're done:
```bash
./cleanup.sh
```
This removes all created resources.
## Understanding the Components
#### Authentication Flow
1. User authenticates with Cognito using username/password
2. Cognito returns an access token (JWT)
3. Access token is passed as Bearer token to AgentCore Runtime
4. AgentCore Runtime validates the token with Cognito
5. If valid, the MCP server processes the request
#### MCP Server Implementation
The MCP server is embedded in the CodeBuild buildspec and includes:
```python
from bedrock_agentcore.mcp import MCPServer
server = MCPServer()
@server.tool()
def add_numbers(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
@server.tool()
def multiply_numbers(a: int, b: int) -> int:
"""Multiply two numbers together."""
return a * b
@server.tool()
def greet_user(name: str) -> str:
"""Greet a user by name."""
return f"Hello, {name}!"
```
#### Docker Image Build
CodeBuild automatically:
1. Creates a Python 3.12 ARM64 environment
2. Installs dependencies
3. Creates the MCP server code
4. Builds the Docker image
5. Pushes to ECR
6. Triggers AgentCore Runtime update
## Additional Resources
- [Amazon Bedrock AgentCore Documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/agentcore.html)
- [Model Context Protocol Specification](https://modelcontextprotocol.io/)
- [CloudFormation Template Reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/AWS_BedrockAgentCore.html)
- [Original Tutorial](../../01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server/)
- [Detailed Technical Guide](DETAILED_GUIDE.md)
Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

@@ -0,0 +1,42 @@
#!/bin/bash
# Cleanup script for MCP Server deployment
set -e
STACK_NAME="${1:-mcp-server-demo}"
REGION="${2:-us-west-2}"
echo "=========================================="
echo "MCP Server Cleanup Script"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo ""
read -p "⚠️ This will delete all resources. Continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cleanup cancelled"
exit 0
fi
echo ""
echo "🗑️ Deleting CloudFormation stack..."
aws cloudformation delete-stack \
--stack-name "$STACK_NAME" \
--region "$REGION"
echo "✓ Stack deletion initiated"
echo ""
echo "⏳ Waiting for stack deletion to complete..."
aws cloudformation wait stack-delete-complete \
--stack-name "$STACK_NAME" \
--region "$REGION"
echo ""
echo "=========================================="
echo "✅ Cleanup Complete!"
echo "=========================================="
echo ""
echo "All resources have been deleted."
echo ""
@@ -0,0 +1,70 @@
#!/bin/bash
# Streamlined deployment script for MCP Server on AgentCore Runtime
set -e
STACK_NAME="${1:-mcp-server-demo}"
REGION="${2:-us-west-2}"
echo "=========================================="
echo "MCP Server Deployment Script"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo ""
# Deploy CloudFormation stack
echo "📦 Deploying CloudFormation stack..."
aws cloudformation create-stack \
--stack-name "$STACK_NAME" \
--template-body file://mcp-server-template.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--region "$REGION"
echo "✓ Stack creation initiated"
echo ""
# Wait for stack to complete
echo "⏳ Waiting for stack to complete (this takes ~10-15 minutes)..."
aws cloudformation wait stack-create-complete \
--stack-name "$STACK_NAME" \
--region "$REGION"
echo "✓ Stack deployment complete!"
echo ""
# Get stack outputs
echo "📋 Retrieving stack outputs..."
CLIENT_ID=$(aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`CognitoUserPoolClientId`].OutputValue' \
--output text \
--region "$REGION")
AGENT_ARN=$(aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`MCPServerRuntimeArn`].OutputValue' \
--output text \
--region "$REGION")
echo ""
echo "=========================================="
echo "✅ Deployment Complete!"
echo "=========================================="
echo ""
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo "Client ID: $CLIENT_ID"
echo "Agent ARN: $AGENT_ARN"
echo ""
echo "Test Credentials:"
echo " Username: testuser"
echo " Password: MyPassword123!"
echo ""
echo "=========================================="
echo "Next Steps:"
echo "=========================================="
echo ""
echo "Test your MCP server:"
echo " ./test.sh $STACK_NAME $REGION"
echo ""
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""
Simple Cognito Authentication Script
Matches the approach from the original tutorial
"""
import boto3
import sys
def get_token(client_id, username, password, region=None):
"""Get authentication token from Cognito."""
# Use provided region or default from environment/config
if region:
cognito_client = boto3.client("cognito-idp", region_name=region)
else:
cognito_client = boto3.client("cognito-idp")
try:
auth_response = cognito_client.initiate_auth(
ClientId=client_id,
AuthFlow="USER_PASSWORD_AUTH",
AuthParameters={"USERNAME": username, "PASSWORD": password},
)
return auth_response["AuthenticationResult"]["AccessToken"]
except Exception as e:
print(f"Error: {e}")
print("Troubleshooting:")
print(" - Verify the Client ID is correct")
print(" - Ensure you're using the correct region")
print(" - Check that the user exists and password is correct")
print(" - Verify USER_PASSWORD_AUTH is enabled for this client")
sys.exit(1)
def main():
if len(sys.argv) < 4 or len(sys.argv) > 5:
print("Usage: python get_token.py <client_id> <username> <password> [region]")
print("\nExamples:")
print(" python get_token.py abc123xyz testuser MyPassword123!")
print(" python get_token.py abc123xyz testuser MyPassword123! us-west-2")
sys.exit(1)
client_id = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
region = sys.argv[4] if len(sys.argv) == 5 else None
if region:
print(f"Authenticating with Cognito in region {region}...")
else:
print("Authenticating with Cognito...")
token = get_token(client_id, username, password, region)
print("\n" + "=" * 70)
print("Authentication Successful!")
print("=" * 70)
print("\nAccess Token:")
print(token)
print("\n" + "=" * 70)
print("Export Command:")
print("=" * 70)
print(f'\nexport JWT_TOKEN="{token}"')
print("\nThen use in curl:")
print('curl -H "Authorization: Bearer $JWT_TOKEN" <your-api-url>')
print()
if __name__ == "__main__":
main()
@@ -0,0 +1,725 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: "MCP Server on AgentCore Runtime - Deploy an MCP server with custom tools (add_numbers, multiply_numbers, greet_user)"
# ============================================================================
# PARAMETERS SECTION
# ============================================================================
Parameters:
# Agent Configuration
AgentName:
Type: String
Default: "MCPServerAgent"
Description: "Name for the MCP server runtime"
AllowedPattern: "^[a-zA-Z][a-zA-Z0-9_]{0,47}$"
ConstraintDescription: "Must start with a letter, max 48 characters, alphanumeric and underscores only"
# Container Configuration
ImageTag:
Type: String
Default: "latest"
Description: "Tag for the Docker image"
# Network Configuration
NetworkMode:
Type: String
Default: "PUBLIC"
Description: "Network mode for AgentCore resources"
AllowedValues:
- PUBLIC
- PRIVATE
# ECR Configuration
ECRRepositoryName:
Type: String
Default: "mcp-server"
Description: "Name of the ECR repository"
# ============================================================================
# METADATA SECTION
# ============================================================================
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Agent Configuration"
Parameters:
- AgentName
- NetworkMode
- Label:
default: "Container Configuration"
Parameters:
- ECRRepositoryName
- ImageTag
ParameterLabels:
AgentName:
default: "Agent Name"
NetworkMode:
default: "Network Mode"
ECRRepositoryName:
default: "ECR Repository Name"
ImageTag:
default: "Image Tag"
# ============================================================================
# RESOURCES SECTION
# ============================================================================
Resources:
# ========================================================================
# ECR MODULE - Container Registry
# ========================================================================
ECRRepository:
Type: AWS::ECR::Repository
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
RepositoryName: !Sub "${AWS::StackName}-${ECRRepositoryName}"
ImageTagMutability: MUTABLE
EmptyOnDelete: true
ImageScanningConfiguration:
ScanOnPush: true
RepositoryPolicyText:
Version: "2012-10-17"
Statement:
- Sid: AllowPullFromAccount
Effect: Allow
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-ecr-repository"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: ECR
# ========================================================================
# COGNITO MODULE - Authentication
# ========================================================================
CognitoUserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: !Sub "${AWS::StackName}-user-pool"
Policies:
PasswordPolicy:
MinimumLength: 8
RequireUppercase: false
RequireLowercase: false
RequireNumbers: false
RequireSymbols: false
Schema:
- Name: email
AttributeDataType: String
Required: false
Mutable: true
UserPoolTags:
Name: !Sub "${AWS::StackName}-user-pool"
StackName: !Ref AWS::StackName
Module: Cognito
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
ClientName: !Sub "${AWS::StackName}-client"
UserPoolId: !Ref CognitoUserPool
GenerateSecret: false
ExplicitAuthFlows:
- ALLOW_USER_PASSWORD_AUTH
- ALLOW_REFRESH_TOKEN_AUTH
PreventUserExistenceErrors: ENABLED
CognitoUser:
Type: AWS::Cognito::UserPoolUser
Properties:
UserPoolId: !Ref CognitoUserPool
Username: testuser
MessageAction: SUPPRESS
SetCognitoUserPassword:
Type: Custom::CognitoSetPassword
DependsOn: CognitoUser
Properties:
ServiceToken: !GetAtt CognitoPasswordSetterFunction.Arn
UserPoolId: !Ref CognitoUserPool
Username: testuser
Password: MyPassword123!
# ========================================================================
# IAM MODULE - Security and Permissions
# ========================================================================
# Agent Execution Role
AgentExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-agent-execution-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AssumeRolePolicy
Effect: Allow
Principal:
Service: bedrock-agentcore.amazonaws.com
Action: sts:AssumeRole
Condition:
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ArnLike:
aws:SourceArn: !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/BedrockAgentCoreFullAccess
Policies:
- PolicyName: AgentCoreExecutionPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: ECRImageAccess
Effect: Allow
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
- ecr:BatchCheckLayerAvailability
Resource: !GetAtt ECRRepository.Arn
- Sid: ECRTokenAccess
Effect: Allow
Action:
- ecr:GetAuthorizationToken
Resource: "*"
- Sid: CloudWatchLogs
Effect: Allow
Action:
- logs:DescribeLogStreams
- logs:CreateLogGroup
- logs:DescribeLogGroups
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
- Sid: XRayTracing
Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
Resource: "*"
- Sid: CloudWatchMetrics
Effect: Allow
Resource: "*"
Action: cloudwatch:PutMetricData
Condition:
StringEquals:
cloudwatch:namespace: bedrock-agentcore
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-agent-execution-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# CodeBuild Service Role
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-codebuild-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: CodeBuildPolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: CloudWatchLogs
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*"
- Sid: ECRAccess
Effect: Allow
Action:
- ecr:BatchCheckLayerAvailability
- ecr:GetDownloadUrlForLayer
- ecr:BatchGetImage
- ecr:GetAuthorizationToken
- ecr:PutImage
- ecr:InitiateLayerUpload
- ecr:UploadLayerPart
- ecr:CompleteLayerUpload
Resource:
- !GetAtt ECRRepository.Arn
- "*"
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-codebuild-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# Lambda Custom Resource Role
CustomResourceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${AWS::StackName}-custom-resource-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CustomResourcePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: CodeBuildAccess
Effect: Allow
Action:
- codebuild:StartBuild
- codebuild:BatchGetBuilds
- codebuild:BatchGetProjects
Resource: !GetAtt MCPServerImageBuildProject.Arn
- Sid: CognitoAccess
Effect: Allow
Action:
- cognito-idp:AdminSetUserPassword
Resource: !GetAtt CognitoUserPool.Arn
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-custom-resource-role"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: IAM
# ========================================================================
# LAMBDA MODULE - Custom Resources
# ========================================================================
CodeBuildTriggerFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-codebuild-trigger"
Description: "Triggers CodeBuild projects as CloudFormation custom resource"
Handler: index.handler
Role: !GetAtt CustomResourceRole.Arn
Runtime: python3.9
Timeout: 900
Code:
ZipFile: |
import boto3
import cfnresponse
import json
import logging
import time
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
logger.info('Received event: %s', json.dumps(event))
try:
if event['RequestType'] == 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
return
project_name = event['ResourceProperties']['ProjectName']
wait_for_completion = event['ResourceProperties'].get('WaitForCompletion', 'true').lower() == 'true'
logger.info(f"Attempting to start CodeBuild project: {project_name}")
logger.info(f"Wait for completion: {wait_for_completion}")
codebuild = boto3.client('codebuild')
try:
project_info = codebuild.batch_get_projects(names=[project_name])
if not project_info['projects']:
raise Exception(f"CodeBuild project '{project_name}' not found")
logger.info(f"CodeBuild project '{project_name}' found")
except Exception as e:
logger.error(f"Error checking project existence: {str(e)}")
raise
response = codebuild.start_build(projectName=project_name)
build_id = response['build']['id']
logger.info(f"Successfully started build: {build_id}")
if not wait_for_completion:
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'BuildId': build_id,
'Status': 'STARTED'
})
return
max_wait_time = context.get_remaining_time_in_millis() / 1000 - 30
start_time = time.time()
while True:
if time.time() - start_time > max_wait_time:
error_message = f"Build {build_id} timed out"
logger.error(error_message)
cfnresponse.send(event, context, cfnresponse.FAILED, {'Error': error_message})
return
build_response = codebuild.batch_get_builds(ids=[build_id])
build_status = build_response['builds'][0]['buildStatus']
if build_status == 'SUCCEEDED':
logger.info(f"Build {build_id} succeeded")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'BuildId': build_id,
'Status': build_status
})
return
elif build_status in ['FAILED', 'FAULT', 'STOPPED', 'TIMED_OUT']:
error_message = f"Build {build_id} failed with status: {build_status}"
logger.error(error_message)
try:
logs_info = build_response['builds'][0].get('logs', {})
if logs_info.get('groupName') and logs_info.get('streamName'):
logger.info(f"Build logs available in CloudWatch")
except Exception as log_error:
logger.warning(f"Could not get log information: {log_error}")
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Error': error_message,
'BuildId': build_id
})
return
logger.info(f"Build {build_id} status: {build_status}")
time.sleep(30)
except Exception as e:
logger.error('Error: %s', str(e))
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Error': str(e)
})
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-codebuild-trigger"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: Lambda
CognitoPasswordSetterFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub "${AWS::StackName}-cognito-password-setter"
Description: "Sets Cognito user password"
Handler: index.handler
Role: !GetAtt CustomResourceRole.Arn
Runtime: python3.9
Timeout: 300
Code:
ZipFile: |
import boto3
import cfnresponse
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
logger.info('Received event: %s', json.dumps(event))
try:
if event['RequestType'] == 'Delete':
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
return
user_pool_id = event['ResourceProperties']['UserPoolId']
username = event['ResourceProperties']['Username']
password = event['ResourceProperties']['Password']
cognito = boto3.client('cognito-idp')
# Set permanent password
cognito.admin_set_user_password(
UserPoolId=user_pool_id,
Username=username,
Password=password,
Permanent=True
)
logger.info(f"Password set successfully for user: {username}")
cfnresponse.send(event, context, cfnresponse.SUCCESS, {
'Status': 'SUCCESS'
})
except Exception as e:
logger.error('Error: %s', str(e))
cfnresponse.send(event, context, cfnresponse.FAILED, {
'Error': str(e)
})
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-cognito-password-setter"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: Lambda
# ========================================================================
# CODEBUILD MODULE - Container Image Building
# ========================================================================
MCPServerImageBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub "${AWS::StackName}-mcp-server-build"
Description: !Sub "Build MCP server Docker image for ${AWS::StackName}"
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: NO_ARTIFACTS
Environment:
Type: ARM_CONTAINER
ComputeType: BUILD_GENERAL1_LARGE
Image: aws/codebuild/amazonlinux2-aarch64-standard:3.0
PrivilegedMode: true
EnvironmentVariables:
- Name: AWS_DEFAULT_REGION
Value: !Ref AWS::Region
- Name: AWS_ACCOUNT_ID
Value: !Ref AWS::AccountId
- Name: IMAGE_REPO_NAME
Value: !Ref ECRRepository
- Name: IMAGE_TAG
Value: !Ref ImageTag
- Name: STACK_NAME
Value: !Ref AWS::StackName
Source:
Type: NO_SOURCE
BuildSpec: |
version: 0.2
phases:
pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo Build started on `date`
- echo Building the Docker image for MCP server ARM64...
# Create requirements.txt
- |
cat > requirements.txt << 'EOF'
mcp>=1.10.0
boto3
bedrock-agentcore
EOF
# Create mcp_server.py
- |
cat > mcp_server.py << 'EOF'
from mcp.server.fastmcp import FastMCP
from starlette.responses import JSONResponse
mcp = FastMCP(host="0.0.0.0", stateless_http=True)
@mcp.tool()
def add_numbers(a: int, b: int) -> int:
"""Add two numbers together"""
return a + b
@mcp.tool()
def multiply_numbers(a: int, b: int) -> int:
"""Multiply two numbers together"""
return a * b
@mcp.tool()
def greet_user(name: str) -> str:
"""Greet a user by name"""
return f"Hello, {name}! Nice to meet you."
if __name__ == "__main__":
mcp.run(transport="streamable-http")
EOF
# Create Dockerfile
- |
cat > Dockerfile << 'EOF'
FROM public.ecr.aws/docker/library/python:3.11-slim
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
ENV AWS_REGION=us-west-2
ENV AWS_DEFAULT_REGION=us-west-2
# Create non-root user
RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore
EXPOSE 8000
COPY . .
CMD ["python", "-m", "mcp_server"]
EOF
# Build the image
- echo Building ARM64 image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- echo ARM64 Docker image pushed successfully
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-mcp-server-build"
- Key: StackName
Value: !Ref AWS::StackName
- Key: Module
Value: CodeBuild
# CUSTOM RESOURCE - Trigger Image Build
TriggerImageBuild:
Type: Custom::CodeBuildTrigger
DependsOn:
- ECRRepository
- MCPServerImageBuildProject
- CodeBuildTriggerFunction
Properties:
ServiceToken: !GetAtt CodeBuildTriggerFunction.Arn
ProjectName: !Ref MCPServerImageBuildProject
WaitForCompletion: "true"
# ========================================================================
# AGENTCORE MODULE - MCP Server Runtime
# ========================================================================
MCPServerRuntime:
Type: AWS::BedrockAgentCore::Runtime
DependsOn:
- TriggerImageBuild
Properties:
AgentRuntimeName: !Sub
- "${StackNameUnderscore}_${AgentName}"
- StackNameUnderscore: !Join ["_", !Split ["-", !Ref "AWS::StackName"]]
AgentRuntimeArtifact:
ContainerConfiguration:
ContainerUri: !Sub "${ECRRepository.RepositoryUri}:${ImageTag}"
RoleArn: !GetAtt AgentExecutionRole.Arn
NetworkConfiguration:
NetworkMode: !Ref NetworkMode
ProtocolConfiguration: MCP
AuthorizerConfiguration:
CustomJWTAuthorizer:
AllowedClients:
- !Ref CognitoUserPoolClient
DiscoveryUrl: !Sub "https://cognito-idp.${AWS::Region}.amazonaws.com/${CognitoUserPool}/.well-known/openid-configuration"
Description: !Sub "MCP server runtime for ${AWS::StackName}"
# ============================================================================
# OUTPUTS SECTION
# ============================================================================
Outputs:
# AGENTCORE MODULE OUTPUTS
MCPServerRuntimeId:
Description: "ID of the created MCP server runtime"
Value: !GetAtt MCPServerRuntime.AgentRuntimeId
Export:
Name: !Sub "${AWS::StackName}-MCPServerRuntimeId"
MCPServerRuntimeArn:
Description: "ARN of the created MCP server runtime"
Value: !GetAtt MCPServerRuntime.AgentRuntimeArn
Export:
Name: !Sub "${AWS::StackName}-MCPServerRuntimeArn"
MCPServerInvocationURL:
Description: "URL to invoke the MCP server"
Value: !Sub
- "https://bedrock-agentcore.${AWS::Region}.amazonaws.com/runtimes/${EncodedArn}/invocations?qualifier=DEFAULT"
- EncodedArn: !Join
- ""
- - !Select [0, !Split [":", !GetAtt MCPServerRuntime.AgentRuntimeArn]]
- "%3A"
- !Select [1, !Split [":", !GetAtt MCPServerRuntime.AgentRuntimeArn]]
- "%3A"
- !Select [2, !Split [":", !GetAtt MCPServerRuntime.AgentRuntimeArn]]
- "%3A"
- !Select [3, !Split [":", !GetAtt MCPServerRuntime.AgentRuntimeArn]]
- "%3A"
- !Select [4, !Split [":", !GetAtt MCPServerRuntime.AgentRuntimeArn]]
- "%3A"
- !Select [5, !Split [":", !GetAtt MCPServerRuntime.AgentRuntimeArn]]
- "%2F"
- !Select [1, !Split ["/", !GetAtt MCPServerRuntime.AgentRuntimeArn]]
Export:
Name: !Sub "${AWS::StackName}-MCPServerInvocationURL"
# ECR OUTPUTS
ECRRepositoryUri:
Description: "URI of the ECR repository"
Value: !GetAtt ECRRepository.RepositoryUri
Export:
Name: !Sub "${AWS::StackName}-ECRRepositoryUri"
# IAM OUTPUTS
AgentExecutionRoleArn:
Description: "ARN of the agent execution role"
Value: !GetAtt AgentExecutionRole.Arn
Export:
Name: !Sub "${AWS::StackName}-AgentExecutionRoleArn"
# COGNITO OUTPUTS
CognitoUserPoolId:
Description: "ID of the Cognito User Pool"
Value: !Ref CognitoUserPool
Export:
Name: !Sub "${AWS::StackName}-CognitoUserPoolId"
CognitoUserPoolClientId:
Description: "ID of the Cognito User Pool Client"
Value: !Ref CognitoUserPoolClient
Export:
Name: !Sub "${AWS::StackName}-CognitoUserPoolClientId"
CognitoDiscoveryUrl:
Description: "Cognito OIDC Discovery URL"
Value: !Sub "https://cognito-idp.${AWS::Region}.amazonaws.com/${CognitoUserPool}/.well-known/openid-configuration"
Export:
Name: !Sub "${AWS::StackName}-CognitoDiscoveryUrl"
# AUTHENTICATION INFO
TestUsername:
Description: "Test username for authentication"
Value: "testuser"
TestPassword:
Description: "Test password for authentication"
Value: "MyPassword123!"
GetTokenCommand:
Description: "Command to get authentication token"
Value: !Sub |
python get_token.py ${CognitoUserPoolClient} testuser MyPassword123!
@@ -0,0 +1,63 @@
#!/bin/bash
# Streamlined testing script for MCP Server
set -e
STACK_NAME="${1:-mcp-server-demo}"
REGION="${2:-us-west-2}"
echo "=========================================="
echo "MCP Server Testing Script"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo ""
# Get stack outputs
echo "📋 Retrieving stack configuration..."
CLIENT_ID=$(aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`CognitoUserPoolClientId`].OutputValue' \
--output text \
--region "$REGION")
AGENT_ARN=$(aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`MCPServerRuntimeArn`].OutputValue' \
--output text \
--region "$REGION")
if [ -z "$CLIENT_ID" ] || [ -z "$AGENT_ARN" ]; then
echo "❌ Error: Could not retrieve stack outputs"
echo " Make sure the stack '$STACK_NAME' exists in region '$REGION'"
exit 1
fi
echo "✓ Configuration retrieved"
echo ""
# Get authentication token
echo "🔐 Getting authentication token..."
TOKEN_OUTPUT=$(python get_token.py "$CLIENT_ID" testuser MyPassword123! "$REGION" 2>&1)
# Extract token from output (it's the line after "Access Token:")
JWT_TOKEN=$(echo "$TOKEN_OUTPUT" | grep -A 1 "Access Token:" | tail -n 1 | tr -d '[:space:]')
if [ -z "$JWT_TOKEN" ]; then
echo "❌ Error: Could not get authentication token"
echo "$TOKEN_OUTPUT"
exit 1
fi
echo "✓ Authentication successful"
echo ""
# Test MCP server
echo "🧪 Testing MCP server..."
echo ""
python test_mcp_server.py "$AGENT_ARN" "$JWT_TOKEN" "$REGION"
echo ""
echo "=========================================="
echo "✅ Testing Complete!"
echo "=========================================="
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""
Test script for deployed MCP server
Uses the MCP Python client library to properly communicate with the server
"""
import asyncio
import sys
from datetime import timedelta
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def test_mcp_server(agent_arn, bearer_token, region):
"""Test the deployed MCP server."""
# Encode the ARN for URL
encoded_arn = agent_arn.replace(":", "%3A").replace("/", "%2F")
mcp_url = f"https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT"
headers = {
"authorization": f"Bearer {bearer_token}",
"Content-Type": "application/json",
}
print(f"Connecting to: {mcp_url}")
print()
try:
async with streamablehttp_client(
mcp_url, headers, timeout=timedelta(seconds=120), terminate_on_close=False
) as (read_stream, write_stream, _):
async with ClientSession(read_stream, write_stream) as session:
print("🔄 Initializing MCP session...")
await session.initialize()
print("✓ MCP session initialized\n")
print("🔄 Listing available tools...")
tool_result = await session.list_tools()
print("\n📋 Available MCP Tools:")
print("=" * 50)
for tool in tool_result.tools:
print(f"🔧 {tool.name}: {tool.description}")
print("\n🧪 Testing MCP Tools:")
print("=" * 50)
# Test add_numbers
print("\n Testing add_numbers(5, 3)...")
add_result = await session.call_tool(
name="add_numbers", arguments={"a": 5, "b": 3}
)
print(f" Result: {add_result.content[0].text}")
# Test multiply_numbers
print("\n✖️ Testing multiply_numbers(4, 7)...")
multiply_result = await session.call_tool(
name="multiply_numbers", arguments={"a": 4, "b": 7}
)
print(f" Result: {multiply_result.content[0].text}")
# Test greet_user
print("\n👋 Testing greet_user('Alice')...")
greet_result = await session.call_tool(
name="greet_user", arguments={"name": "Alice"}
)
print(f" Result: {greet_result.content[0].text}")
print("\n✅ MCP tool testing completed!")
except Exception as e:
print(f"❌ Error: {e}")
sys.exit(1)
def main():
if len(sys.argv) != 4:
print("Usage: python test_mcp_server.py <agent_arn> <bearer_token> <region>")
print("\nExample:")
print(
" python test_mcp_server.py arn:aws:bedrock-agentcore:... eyJraWQiOiJ... us-west-2"
)
sys.exit(1)
agent_arn = sys.argv[1]
bearer_token = sys.argv[2]
region = sys.argv[3]
asyncio.run(test_mcp_server(agent_arn, bearer_token, region))
if __name__ == "__main__":
main()
@@ -0,0 +1,295 @@
# Multi-Agent AgentCore Runtime
This CloudFormation template demonstrates a multi-agent architecture where one agent (orchestrator) can invoke another agent (specialist) to handle complex tasks. This pattern is useful for building sophisticated AI systems with specialized capabilities.
## Table of Contents
- [Overview](#overview)
- [Architecture](#architecture)
- [Prerequisites](#prerequisites)
- [Deployment](#deployment)
- [Testing](#testing)
- [Sample Queries](#sample-queries)
- [Cleanup](#cleanup)
- [Cost Estimate](#cost-estimate)
- [Troubleshooting](#troubleshooting)
- [🤝 Contributing](#-contributing)
- [📄 License](#-license)
## Overview
This template creates a two-agent system that demonstrates agent-to-agent communication:
### Agent 1: Orchestrator Agent
- **Role**: Main entry point for user queries
- **Capabilities**:
- Handles simple queries directly
- Delegates complex tasks to Agent 2
- Has a tool to invoke Agent 2's runtime
- **Use Cases**: Routing, task delegation, simple Q&A
### Agent 2: Specialist Agent
- **Role**: Expert agent for detailed analysis
- **Capabilities**:
- Provides in-depth analytical responses
- Handles complex reasoning tasks
- Focuses on accuracy and completeness
- **Use Cases**: Data analysis, expert knowledge, detailed explanations
### Key Features
- **Multi-Agent Communication**: Agent 1 can invoke Agent 2 using `bedrock-agentcore:InvokeAgentRuntime`
- **Automatic Orchestration**: Agent 1 decides when to delegate based on query complexity
- **Independent Deployment**: Each agent has its own ECR repository and runtime
- **Modular Architecture**: Easy to extend with additional specialized agents
## Architecture
![Multi-Agent AgentCore Runtime Architecture](architecture.png)
The architecture consists of:
- **User**: Sends questions to Agent 1 (Orchestrator) and receives responses
- **Agent 1 - Orchestrator Agent**:
- **AWS CodeBuild**: Builds the ARM64 Docker container image for Agent 1
- **Amazon ECR Repository**: Stores Agent 1's container image
- **AgentCore Runtime**: Hosts the Orchestrator Agent
- Routes simple queries directly
- Delegates complex queries to Agent 2 using the `call_specialist_agent` tool
- Invokes Amazon Bedrock LLMs for reasoning
- **IAM Role**: Permissions to invoke Agent 2's runtime and access Bedrock
- **Agent 2 - Specialist Agent**:
- **AWS CodeBuild**: Builds the ARM64 Docker container image for Agent 2
- **Amazon ECR Repository**: Stores Agent 2's container image
- **AgentCore Runtime**: Hosts the Specialist Agent
- Provides detailed analysis and expert responses
- Invokes Amazon Bedrock LLMs for in-depth reasoning
- **IAM Role**: Standard runtime permissions and Bedrock access
- **Amazon Bedrock LLMs**: Provides AI model capabilities for both agents
- **Agent-to-Agent Communication**: Agent 1 can invoke Agent 2's runtime via `bedrock-agentcore:InvokeAgentRuntime` API
## Prerequisites
### AWS Account Setup
1. **AWS Account**: You need an active AWS account with appropriate permissions
- [Create AWS Account](https://aws.amazon.com/account/)
- [AWS Console Access](https://aws.amazon.com/console/)
2. **AWS CLI**: Install and configure AWS CLI with your credentials
- [Install AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
- [Configure AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html)
```bash
aws configure
```
3. **Bedrock Model Access**: Enable access to Amazon Bedrock models in your AWS region
- Navigate to [Amazon Bedrock Console](https://console.aws.amazon.com/bedrock/)
- Go to "Model access" and request access to:
- Anthropic Claude models
- [Bedrock Model Access Guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html)
4. **Required Permissions**: Your AWS user/role needs permissions for:
- CloudFormation stack operations
- ECR repository management
- IAM role creation
- Lambda function creation
- CodeBuild project creation
- BedrockAgentCore resource creation
## Deployment
### Option 1: Using the Deploy Script (Recommended)
```bash
# Make the script executable
chmod +x deploy.sh
# Deploy the stack
./deploy.sh
```
The script will:
1. Deploy the CloudFormation stack
2. Wait for stack creation to complete
3. Display both Agent Runtime IDs
### Option 2: Using AWS CLI
```bash
# Deploy the stack
aws cloudformation create-stack \
--stack-name multi-agent-demo \
--template-body file://template.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--region us-west-2
# Wait for stack creation
aws cloudformation wait stack-create-complete \
--stack-name multi-agent-demo \
--region us-west-2
# Get the Runtime IDs
aws cloudformation describe-stacks \
--stack-name multi-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs'
```
### Option 3: Using AWS Console
1. Navigate to [CloudFormation Console](https://console.aws.amazon.com/cloudformation/)
2. Click "Create stack" → "With new resources"
3. Upload the `template.yaml` file
4. Enter stack name: `multi-agent-demo`
5. Review parameters (or use defaults)
6. Check "I acknowledge that AWS CloudFormation might create IAM resources"
7. Click "Create stack"
## Testing
### Test Agent 1 (Orchestrator)
Agent 1 is your main entry point. It will handle simple queries directly or delegate to Agent 2 for complex tasks.
#### Using AWS CLI
```bash
# Get Agent1 Runtime ID
AGENT1_ID=$(aws cloudformation describe-stacks \
--stack-name multi-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs[?OutputKey==`Agent1RuntimeId`].OutputValue' \
--output text)
# Test with a simple query (Agent1 handles directly)
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-id $AGENT1_ID \
--qualifier DEFAULT \
--payload '{"prompt": "Hello, how are you?"}' \
--region us-west-2 \
response.json
# Test with a complex query (Agent1 delegates to Agent2)
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-id $AGENT1_ID \
--qualifier DEFAULT \
--payload '{"prompt": "Provide a detailed analysis of cloud computing benefits"}' \
--region us-west-2 \
response.json
cat response.json
```
### Using AWS Console
1. Navigate to [Bedrock AgentCore Console](https://console.aws.amazon.com/bedrock-agentcore/)
2. Go to "Runtimes" in the left navigation
3. Find Agent1 runtime (name starts with `multi_agent_demo_OrchestratorAgent`)
4. Click on the runtime name
5. Click "Test" button
6. Enter test payload:
```json
{
"prompt": "Hello, how are you?"
}
```
7. Click "Invoke"
### Test Agent 2 (Specialist) Directly
You can also test Agent 2 directly to see its specialized capabilities.
```bash
# Get Agent2 Runtime ID
AGENT2_ID=$(aws cloudformation describe-stacks \
--stack-name multi-agent-demo \
--region us-west-2 \
--query 'Stacks[0].Outputs[?OutputKey==`Agent2RuntimeId`].OutputValue' \
--output text)
# Invoke Agent2 directly
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-id $AGENT2_ID \
--qualifier DEFAULT \
--payload '{"prompt": "Explain quantum computing in detail"}' \
--region us-west-2 \
response.json
```
## Sample Queries
### Queries that Agent 1 Handles Directly
These simple queries don't require specialist knowledge:
1. **Greetings**:
```json
{"prompt": "Hello, how are you?"}
```
2. **Simple Math**:
```json
{"prompt": "What is 5 + 3?"}
```
### Queries that Trigger Agent 2 Delegation
These complex queries require expert analysis:
1. **Detailed Analysis**:
```json
{"prompt": "Provide a detailed analysis of the benefits and drawbacks of serverless architecture"}
```
2. **Expert Knowledge**:
```json
{"prompt": "Explain the CAP theorem and its implications for distributed systems"}
```
3. **Complex Reasoning**:
```json
{"prompt": "Compare and contrast different machine learning algorithms for time series forecasting"}
```
4. **In-depth Explanation**:
```json
{"prompt": "Provide expert analysis on best practices for securing cloud infrastructure"}
```
## Cleanup
### Using the Cleanup Script (Recommended)
```bash
# Make the script executable
chmod +x cleanup.sh
# Delete the stack
./cleanup.sh
```
### Using AWS CLI
```bash
aws cloudformation delete-stack \
--stack-name multi-agent-demo \
--region us-west-2
# Wait for deletion to complete
aws cloudformation wait stack-delete-complete \
--stack-name multi-agent-demo \
--region us-west-2
```
### Using AWS Console
1. Navigate to [CloudFormation Console](https://console.aws.amazon.com/cloudformation/)
2. Select the `multi-agent-demo` stack
3. Click "Delete"
4. Confirm deletion
Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

@@ -0,0 +1,63 @@
#!/bin/bash
# Cleanup script for Multi-Agent Runtime CloudFormation stack
# This script deletes the CloudFormation stack and all associated resources
set -e
# Configuration
STACK_NAME="${1:-multi-agent-demo}"
REGION="${2:-us-west-2}"
echo "=========================================="
echo "Cleaning up Multi-Agent Runtime"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo "=========================================="
# Confirm deletion
read -p "Are you sure you want to delete the stack '$STACK_NAME'? (yes/no): " CONFIRM
if [ "$CONFIRM" != "yes" ]; then
echo "Cleanup cancelled."
exit 0
fi
echo ""
echo "Deleting CloudFormation stack..."
aws cloudformation delete-stack \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "✓ Stack deletion initiated successfully!"
echo ""
echo "Waiting for stack deletion to complete..."
echo "This may take a few minutes..."
echo ""
aws cloudformation wait stack-delete-complete \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "=========================================="
echo "✓ Stack deleted successfully!"
echo "=========================================="
echo ""
echo "All resources have been cleaned up."
echo ""
else
echo ""
echo "✗ Stack deletion failed or timed out"
echo "Check the CloudFormation console for details"
exit 1
fi
else
echo ""
echo "✗ Failed to initiate stack deletion"
exit 1
fi
@@ -0,0 +1,88 @@
#!/bin/bash
# Deploy script for Multi-Agent Runtime CloudFormation stack
# This script deploys two AgentCore Runtimes where Agent1 can invoke Agent2
set -e
# Configuration
STACK_NAME="${1:-multi-agent-demo}"
REGION="${2:-us-west-2}"
TEMPLATE_FILE="template.yaml"
echo "=========================================="
echo "Deploying Multi-Agent Runtime"
echo "=========================================="
echo "Stack Name: $STACK_NAME"
echo "Region: $REGION"
echo "=========================================="
# Check if template file exists
if [ ! -f "$TEMPLATE_FILE" ]; then
echo "Error: Template file '$TEMPLATE_FILE' not found!"
exit 1
fi
# Deploy the CloudFormation stack
echo ""
echo "Creating CloudFormation stack..."
aws cloudformation create-stack \
--stack-name "$STACK_NAME" \
--template-body file://"$TEMPLATE_FILE" \
--capabilities CAPABILITY_NAMED_IAM \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "✓ Stack creation initiated successfully!"
echo ""
echo "Waiting for stack creation to complete..."
echo "This will take approximately 15-20 minutes..."
echo "(Building two Docker images and deploying two agents)"
echo ""
aws cloudformation wait stack-create-complete \
--stack-name "$STACK_NAME" \
--region "$REGION"
if [ $? -eq 0 ]; then
echo ""
echo "=========================================="
echo "✓ Stack deployed successfully!"
echo "=========================================="
echo ""
echo "Stack Outputs:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs' \
--output table \
--region "$REGION"
echo ""
echo "Agent1 (Orchestrator) Runtime ARN:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`Agent1RuntimeArn`].OutputValue' \
--output text \
--region "$REGION"
echo ""
echo "Agent2 (Specialist) Runtime ARN:"
aws cloudformation describe-stacks \
--stack-name "$STACK_NAME" \
--query 'Stacks[0].Outputs[?OutputKey==`Agent2RuntimeArn`].OutputValue' \
--output text \
--region "$REGION"
echo ""
echo "To delete this stack, run:"
echo " ./cleanup.sh $STACK_NAME $REGION"
echo ""
else
echo ""
echo "✗ Stack creation failed or timed out"
echo "Check the CloudFormation console for details"
exit 1
fi
else
echo ""
echo "✗ Failed to initiate stack creation"
exit 1
fi
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -39,4 +39,6 @@
- Evandro Franco
- greg-aws
- Frank Dallezotte
- Omar Elkharbotly
- Chintan Patel
- Shreyas Subramanian