Amazon Bedrock AgentCore is in preview release and is subject to change.
Note: This guide is based on the official AWS documentation but uses a non-interactive deployment method and provides visibility into the IAM roles and ECR repositories created, making the process more transparent. The original guide uses interactive prompts which this guide avoids.
This guide has been tested on Linux x86_64/amd64 systems. While Bedrock AgentCore runs on ARM64, the deployment process uses CodeBuild for cross-platform compilation.
Recommended environments:
- Linux x86_64/amd64 machines
- GitHub Codespaces
- WSL2 on Windows
- EC2 instances (x86_64)
Note: Running this guide on ARM64 machines (e.g., Apple Silicon Macs, ARM EC2 instances) may produce different results and some scripts might require modifications. The agentcore CLI automatically handles the ARM64 build process via CodeBuild regardless of your local architecture.
Amazon Bedrock AgentCore is supported in the following AWS Regions:
- US East (N. Virginia) -
us-east-1
- US West (Oregon) -
us-west-2
- Europe (Frankfurt) -
eu-central-1
- Asia Pacific (Sydney) -
ap-southeast-2
Reference: AWS Documentation (as of 02/09/2025)
This guide demonstrates deploying a FastMCP server to AWS Bedrock AgentCore with OAuth authentication via AWS Cognito.
FastMCP Server (Python with MCP protocol)
↓
AgentCore CLI (configures deployment)
↓
AWS CodeBuild (cross-platform ARM64 build)
↓
Docker Container (ARM64 Linux)
↓
ECR Repository (stores container image)
↓
Bedrock AgentCore Runtime (hosts MCP server)
↓
JWT Authentication (Cognito User Pool)
↓
MCP Client (VSCode or custom Python client)
This guide uses a sample my_mcp_server.py
built with FastMCP for demonstration. However:
- Any MCP server that supports the
streamable-http
transport mode should work with Bedrock AgentCore - Off-the-shelf MCP servers using FastMCP library can be deployed following the same process
- MCP servers in other languages (Go, TypeScript, etc.) may also be compatible if they support HTTP streaming transport
- Note: Hosting non-Python MCP servers like terraform-mcp-server (Go) hasn't been tested yet but should theoretically work with appropriate Dockerfile modifications
Before starting, ensure you have the following tools installed:
- AWS CLI configured with appropriate credentials
- Python 3.8 or later
- pip package manager
- Docker (optional, only for local builds)
Install the Amazon Bedrock AgentCore CLI:
pip install bedrock-agentcore-starter-toolkit
- All steps must be run in sequence with the same AGENT_NAME
- If you restart, you must begin from Step 1 with a new unique name
- Each step builds on the previous one and saves variables to
deployment.env
- If you close your terminal, run
source deployment.env
to reload variables
Create a unique agent name with timestamp and set AWS region. Save these to a file so they persist across terminal sessions.
# Generate unique agent name with timestamp
export AGENT_NAME="mcp_$(date +%Y%m%d_%H%M%S)"
export AWS_REGION="eu-central-1"
# Save to env file for persistence
cat > deployment.env << EOF
export AGENT_NAME="$AGENT_NAME"
export AWS_REGION="$AWS_REGION"
EOF
echo "Agent name: $AGENT_NAME"
echo "AWS Region: $AWS_REGION"
echo ""
echo "Environment saved to deployment.env"
echo "To reload in new session: source deployment.env"
Expected output:
- Should display a unique agent name like: mcp_20250902_190000
- Should display AWS Region: eu-central-1
✅ Step 1 Verified: Working
Create the FastMCP server and requirements file.
# Create FastMCP server
cat > my_mcp_server.py << 'EOF'
# my_mcp_server.py
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 requirements file
cat > requirements.txt << 'EOF'
fastmcp
mcp
pydantic
httpx
botocore
EOF
# Verify files were created
ls -la my_mcp_server.py requirements.txt
Expected output:
- Should show two files created with non-zero size
✅ Step 2 Verified: Working
Create a Cognito user pool for OAuth authentication.
# Load our environment
source deployment.env
# Create Cognito pool
POOL_NAME="${AGENT_NAME}_pool"
POOL_ID=$(aws cognito-idp create-user-pool --pool-name "$POOL_NAME" --region $AWS_REGION --query 'UserPool.Id' --output text)
# Create client WITHOUT secret (simpler authentication)
CLIENT_ID=$(aws cognito-idp create-user-pool-client \
--user-pool-id $POOL_ID \
--client-name "${AGENT_NAME}_client" \
--no-generate-secret \
--explicit-auth-flows ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
--region $AWS_REGION \
--query 'UserPoolClient.ClientId' \
--output text)
# Save to env file
cat >> deployment.env << EOF
export POOL_ID="$POOL_ID"
export CLIENT_ID="$CLIENT_ID"
EOF
# Display results
echo "Pool ID: $POOL_ID"
echo "Client ID: $CLIENT_ID"
echo ""
echo "Cognito credentials saved to deployment.env"
Expected output:
- Should display a Pool ID starting with region like: eu-central-1_XXXXXXXX
- Should display a Client ID (alphanumeric string)
✅ Step 3 Verified: Working
Create a test user in the Cognito pool.
# Load our environment
source deployment.env
# Create test user
aws cognito-idp admin-create-user \
--user-pool-id $POOL_ID \
--username "[email protected]" \
--user-attributes Name=email,[email protected] Name=email_verified,Value=true \
--temporary-password "TempPass123!" \
--message-action SUPPRESS \
--region $AWS_REGION > /dev/null
# Set permanent password
aws cognito-idp admin-set-user-password \
--user-pool-id $POOL_ID \
--username "[email protected]" \
--password "TestPass123!" \
--permanent \
--region $AWS_REGION
echo "✅ Test user created: [email protected]"
Expected output:
- Should display: ✅ Test user created: [email protected]
- No errors should appear
✅ Step 4 Verified: Working
Create an IAM role for Bedrock AgentCore with necessary permissions.
# Load our environment
source deployment.env
ROLE_NAME="BedrockAgentCoreRole-${AGENT_NAME}"
# Create trust policy with security conditions
TRUST_POLICY=$(cat <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeRolePolicy",
"Effect": "Allow",
"Principal": {
"Service": "bedrock-agentcore.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "657579860855"
},
"ArnLike": {
"aws:SourceArn": "arn:aws:bedrock-agentcore:eu-central-1:657579860855:*"
}
}
}
]
}
EOF
)
# Create the role
aws iam create-role \
--role-name "$ROLE_NAME" \
--assume-role-policy-document "$TRUST_POLICY" \
--region $AWS_REGION > /dev/null 2>&1
# Create comprehensive inline policy (matching auto-generated roles)
POLICY_DOCUMENT=$(cat <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRImageAccess",
"Effect": "Allow",
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability"
],
"Resource": [
"arn:aws:ecr:${AWS_REGION}:657579860855:repository/*"
]
},
{
"Sid": "ECRTokenAccess",
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"logs:DescribeLogStreams",
"logs:CreateLogGroup",
"logs:DescribeLogGroups"
],
"Resource": [
"arn:aws:logs:${AWS_REGION}:657579860855:log-group:/aws/bedrock-agentcore/runtimes/*",
"arn:aws:logs:${AWS_REGION}:657579860855:log-group:*"
]
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:${AWS_REGION}:657579860855:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*"
]
},
{
"Effect": "Allow",
"Action": [
"xray:PutTraceSegments",
"xray:PutTelemetryRecords",
"xray:GetSamplingRules",
"xray:GetSamplingTargets"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Resource": "*",
"Action": "cloudwatch:PutMetricData",
"Condition": {
"StringEquals": {
"cloudwatch:namespace": "bedrock-agentcore"
}
}
},
{
"Sid": "BedrockAgentCoreRuntime",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:InvokeAgentRuntime"
],
"Resource": [
"arn:aws:bedrock-agentcore:${AWS_REGION}:657579860855:runtime/*"
]
},
{
"Sid": "BedrockAgentCoreMemory",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:CreateMemory",
"bedrock-agentcore:CreateEvent",
"bedrock-agentcore:GetEvent",
"bedrock-agentcore:GetMemory",
"bedrock-agentcore:GetMemoryRecord",
"bedrock-agentcore:ListActors",
"bedrock-agentcore:ListEvents",
"bedrock-agentcore:ListMemoryRecords",
"bedrock-agentcore:ListSessions",
"bedrock-agentcore:DeleteEvent",
"bedrock-agentcore:DeleteMemoryRecord",
"bedrock-agentcore:RetrieveMemoryRecords"
],
"Resource": [
"arn:aws:bedrock-agentcore:${AWS_REGION}:657579860855:memory/*",
"*"
]
},
{
"Sid": "BedrockAgentCoreIdentity",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:GetResourceApiKey",
"bedrock-agentcore:GetResourceOauth2Token",
"bedrock-agentcore:GetWorkloadAccessToken",
"bedrock-agentcore:GetWorkloadAccessTokenForJWT",
"bedrock-agentcore:GetWorkloadAccessTokenForUserId"
],
"Resource": [
"arn:aws:bedrock-agentcore:${AWS_REGION}:657579860855:token-vault/*",
"arn:aws:bedrock-agentcore:${AWS_REGION}:657579860855:workload-identity-directory/*"
]
},
{
"Sid": "BedrockModelInvocation",
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream",
"bedrock:ApplyGuardrail"
],
"Resource": [
"arn:aws:bedrock:*::foundation-model/*",
"arn:aws:bedrock:${AWS_REGION}:657579860855:*"
]
}
]
}
EOF
)
# Attach the inline policy
aws iam put-role-policy \
--role-name "$ROLE_NAME" \
--policy-name "BedrockAgentCoreExecutionPolicy" \
--policy-document "$POLICY_DOCUMENT" \
--region $AWS_REGION 2>/dev/null
EXECUTION_ROLE_ARN="arn:aws:iam::657579860855:role/$ROLE_NAME"
# Save to env file
cat >> deployment.env << EOF
export ROLE_NAME="$ROLE_NAME"
export EXECUTION_ROLE_ARN="$EXECUTION_ROLE_ARN"
EOF
echo "✅ Created execution role: $ROLE_NAME"
echo "Role details saved to deployment.env"
Expected output:
- Should display: ✅ Created execution role: BedrockAgentCoreRole-mcp_XXXXXXXXX
- Should display: Role details saved to deployment.env
✅ Step 5 Verified: Working
Create an ECR repository for the Docker image.
# Load our environment
source deployment.env
ECR_REPO_NAME="bedrock-agentcore-${AGENT_NAME}"
# Create ECR repository
aws ecr create-repository \
--repository-name "$ECR_REPO_NAME" \
--region $AWS_REGION > /dev/null 2>&1
ECR_URI="657579860855.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPO_NAME}"
# Save to env file
cat >> deployment.env << EOF
export ECR_REPO_NAME="$ECR_REPO_NAME"
export ECR_URI="$ECR_URI"
EOF
echo "✅ Created ECR repository: $ECR_REPO_NAME"
echo " URI: $ECR_URI"
echo "ECR details saved to deployment.env"
Expected output:
- Should display: ✅ Created ECR repository: bedrock-agentcore-mcp_XXXXXXXXX
- Should display the full ECR URI
- Should display: ECR details saved to deployment.env
✅ Step 6 Verified: Working
Configure agentcore with all the resources we created. This step prepares the configuration but doesn't deploy yet.
# Load our environment with all variables from previous steps
source deployment.env
# Build the discovery URL
DISCOVERY_URL="https://cognito-idp.${AWS_REGION}.amazonaws.com/${POOL_ID}/.well-known/openid-configuration"
# Configure agentcore (no interactive prompts)
agentcore configure \
-e my_mcp_server.py \
--name $AGENT_NAME \
--protocol MCP \
--region $AWS_REGION \
--execution-role "$EXECUTION_ROLE_ARN" \
--ecr "$ECR_URI" \
--requirements-file requirements.txt \
--authorizer-config "{\"customJWTAuthorizer\":{\"discoveryUrl\":\"$DISCOVERY_URL\",\"allowedClients\":[\"$CLIENT_ID\"]}}"
echo "✅ AgentCore configured"
# Check if .bedrock_agentcore.yaml was created
if [ -f ".bedrock_agentcore.yaml" ]; then
echo "✅ Configuration file created"
else
echo "❌ Configuration file NOT created - something went wrong"
fi
Expected output:
- Should show configuration summary with agent details
- May show platform mismatch warning (linux/amd64 vs linux/arm64) - this is normal
- Should display: ✅ AgentCore configured
- Should display: ✅ Configuration file created
- Will generate Dockerfile and .dockerignore files
- Will set the agent as default
✅ Step 7 Verified: Working
Deploy the configured agent to Bedrock AgentCore.
Important Architecture Note: Bedrock AgentCore runs on ARM64 architecture. If your deployment machine is x86_64/amd64:
- The agentcore CLI will automatically use CodeBuild for cross-platform building
- Your local files will be packaged and uploaded to an S3 bucket
- CodeBuild will build the ARM64 Docker image in the cloud
- The ARM64 image will be pushed to ECR and deployed to Bedrock AgentCore
- This process typically takes 1-2 minutes
- If the IAM role was just created, it may take 10-15 seconds to propagate
- If you see "invalid trust policy" error, wait a moment and retry
- Build failures may occur if ECR repository wasn't fully created
- Platform mismatch warning (linux/amd64 vs linux/arm64) is expected and doesn't prevent deployment
# Load our environment
source deployment.env
# Launch the agent (this takes 1-2 minutes)
agentcore launch --agent $AGENT_NAME
# Extract the agent ARN from the yaml file
AGENT_ARN=$(grep "agent_arn:" .bedrock_agentcore.yaml | tail -1 | awk '{print $2}')
# Save final ARN to env file
echo "export AGENT_ARN=\"$AGENT_ARN\"" >> deployment.env
echo "✅ Deployed to Bedrock AgentCore"
echo " Agent ARN: $AGENT_ARN"
echo ""
echo "Complete environment saved to deployment.env"
echo "You can now test the deployment or use it in other applications"
Expected output:
- Shows "🚀 Launching Bedrock AgentCore (codebuild mode - RECOMMENDED)..."
- Creates CodeBuild project and uploads source to S3
- Shows CodeBuild progress stages (QUEUED, PROVISIONING, BUILD, etc.)
- Build typically completes in 1-2 minutes
- Shows "✅ Agent created/updated" with the Agent ARN
- Displays deployment summary with:
- Agent Name and ARN
- CodeBuild ID
- ECR URI
- CloudWatch log locations
- Commands for checking status and invoking the agent
✅ Step 8 Verified: Working
Test that your MCP server is working by authenticating with Cognito and calling the deployed agent.
First, authenticate with Cognito to get a bearer token.
client_id
claim that Bedrock AgentCore expects.
# Load our environment
source deployment.env
# Get the ACCESS token from Cognito (no secret hash needed)
TOKEN_RESPONSE=$(aws cognito-idp initiate-auth \
--auth-flow USER_PASSWORD_AUTH \
--client-id $CLIENT_ID \
--auth-parameters [email protected],PASSWORD=TestPass123! \
--region $AWS_REGION \
--query 'AuthenticationResult.AccessToken' \
--output text)
export BEARER_TOKEN=$TOKEN_RESPONSE
echo "✅ Bearer token obtained"
echo "Token (first 50 chars): ${BEARER_TOKEN:0:50}..."
Create a Python test script that connects to your MCP server:
# Create the test script
cat > my_mcp_client_remote.py << 'EOF'
import asyncio
import os
import sys
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
agent_arn = os.getenv('AGENT_ARN')
bearer_token = os.getenv('BEARER_TOKEN')
region = os.getenv('AWS_REGION', 'eu-central-1')
if not agent_arn or not bearer_token:
print("Error: AGENT_ARN or BEARER_TOKEN environment variable is not set")
sys.exit(1)
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",
"Accept": "application/json, text/event-stream"
}
print(f"Invoking: {mcp_url}\n")
async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as (
read_stream,
write_stream,
_,
):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
# List available tools
print("Available tools:")
tool_result = await session.list_tools()
for tool in tool_result.tools:
print(f" - {tool.name}: {tool.description}")
# Test calling a tool
print("\nTesting add_numbers(5, 3):")
result = await session.call_tool("add_numbers", {"a": 5, "b": 3})
print(f"Result: {result.content[0].text}")
print("\nTesting greet_user('Alice'):")
result = await session.call_tool("greet_user", {"name": "Alice"})
print(f"Result: {result.content[0].text}")
asyncio.run(main())
EOF
# Install required dependencies
pip install mcp httpx --quiet
# Export the environment variables and run the test
export AGENT_ARN
export BEARER_TOKEN
export AWS_REGION
python3 my_mcp_client_remote.py
Expected output:
- Should display the MCP URL being invoked
- Should list 3 available tools: add_numbers, multiply_numbers, greet_user
- Should show "Result: 8" for add_numbers(5, 3)
- Should show "Result: Hello, Alice! Nice to meet you." for greet_user('Alice')
✅ Step 9 Verified: Working - MCP server successfully tested with all tools functioning correctly.
Create a helper script to generate VSCode MCP configuration with fresh bearer tokens:
# Create VSCode config generator for test user
cat > generate_vscode_config.sh << 'EOF'
#!/bin/bash
# Load environment
if [ ! -f "deployment.env" ]; then
echo "❌ Error: deployment.env not found. Please run deployment steps first."
exit 1
fi
source deployment.env
# Check required variables
if [ -z "$CLIENT_ID" ] || [ -z "$AWS_REGION" ] || [ -z "$AGENT_ARN" ] || [ -z "$AGENT_NAME" ]; then
echo "❌ Error: Missing required environment variables"
exit 1
fi
echo "🔐 Authenticating test user..."
# Get fresh token for test user (no secret hash needed)
BEARER_TOKEN=$(aws cognito-idp initiate-auth \
--auth-flow USER_PASSWORD_AUTH \
--client-id $CLIENT_ID \
--auth-parameters [email protected],PASSWORD=TestPass123! \
--region $AWS_REGION \
--query 'AuthenticationResult.AccessToken' \
--output text 2>/dev/null)
if [ -z "$BEARER_TOKEN" ]; then
echo "❌ Error: Failed to get bearer token"
exit 1
fi
# URL encode the ARN with double encoding for forward slash
ENCODED_ARN=$(echo $AGENT_ARN | sed 's/:/\%3A/g' | sed 's/\//\%252F/g')
# Generate config
cat > vscode_mcp_config.json << JSON
{
"servers": {
"${AGENT_NAME}": {
"url": "https://bedrock-agentcore.${AWS_REGION}.amazonaws.com/runtimes/${ENCODED_ARN}/invocations?qualifier=DEFAULT",
"type": "http",
"headers": {
"Authorization": "Bearer $BEARER_TOKEN",
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream"
}
}
}
}
JSON
echo "✅ Generated vscode_mcp_config.json"
echo " User: [email protected]"
echo " Agent: ${AGENT_NAME}"
echo " Token expires at: $(date -d '+1 hour' 2>/dev/null || date -v +1H)"
echo ""
echo "📋 To use in VSCode:"
echo " 1. Copy the contents of vscode_mcp_config.json"
echo " 2. Add to your VSCode MCP settings"
echo " 3. Restart the MCP connection"
EOF
chmod +x generate_vscode_config.sh
To generate fresh VSCode configuration:
./generate_vscode_config.sh
-
FastMCP Transport Mode: FastMCP must run in
streamable-http
mode for Bedrock AgentCore. The defaultstdio
mode will not work with HTTP requests. -
OAuth Token Type: Must use AccessToken (not IdToken) from Cognito as it contains the
client_id
claim required by Bedrock AgentCore. -
Simplified Authentication: The setup now uses a Cognito client without a secret, eliminating the need for SECRET_HASH calculation.
-
IAM Permissions: The execution role requires comprehensive permissions including X-Ray, CloudWatch, and all bedrock-agentcore operations. The role must also include proper trust policy conditions for security.
-
Accept Header Requirement: MCP connections require the Accept header to include both
application/json
andtext/event-stream
. -
URL Encoding: VSCode requires double-encoding of forward slashes in the ARN (
%252F
instead of%2F
).
After deployment, you can monitor your MCP server using these commands:
# Load environment variables
source deployment.env
# Shows deployment status, endpoints, and configuration
agentcore status --agent $AGENT_NAME
# Load environment variables
source deployment.env
# List build IDs for your project
aws codebuild list-builds-for-project \
--project-name bedrock-agentcore-${AGENT_NAME}-builder \
--region $AWS_REGION
# Get build details (replace BUILD_ID with actual ID)
aws codebuild batch-get-builds \
--ids BUILD_ID \
--region $AWS_REGION \
--query 'builds[0].{Status:buildStatus,StartTime:startTime,EndTime:endTime,LogsLink:logs.deepLink}'
# Load environment variables
source deployment.env
# View pushed container image details
aws ecr describe-images \
--repository-name bedrock-agentcore-${AGENT_NAME} \
--region $AWS_REGION \
--query 'imageDetails[0].{Tags:imageTags,Size:imageSizeInBytes,PushedAt:imagePushedAt}'
Runtime logs are created when the agent processes requests. The log group paths are shown in the deployment output and agentcore status
command:
/aws/bedrock-agentcore/runtimes/{AGENT_NAME}-{AGENT_ID}-DEFAULT
/aws/bedrock-agentcore/runtimes/{AGENT_NAME}-{AGENT_ID}-DEFAULT/runtime-logs
Example commands to tail logs:
# Load environment and get agent ID from the ARN
source deployment.env
AGENT_ID=$(echo $AGENT_ARN | cut -d'/' -f2 | cut -d'-' -f2-)
# Tail the logs
aws logs tail /aws/bedrock-agentcore/runtimes/${AGENT_NAME}-${AGENT_ID}-DEFAULT --follow
-
"Execution role has invalid trust policy"
- The IAM role may not have propagated yet
- Wait 10-15 seconds and retry the launch command
- Verify the role exists:
aws iam get-role --role-name $ROLE_NAME
-
Build fails during CodeBuild
- Check CloudWatch logs for the specific error
- Ensure all files (my_mcp_server.py, requirements.txt) exist
- Verify ECR repository was created successfully
-
"Agent already exists" error
- An agent with this name was already deployed
- Start over from Step 1 with a new unique AGENT_NAME
-
Variables not found
- Run
source deployment.env
to reload all variables - Check that deployment.env exists and contains all exports
- Run
-
403 Forbidden when testing
- Token may have expired (tokens last 1 hour)
- User password may not be set correctly
- Verify Cognito pool and client IDs match
- Ensure you're using AccessToken, not IdToken
- Check IAM role has all required permissions
-
Server not responding to HTTP requests
- Verify FastMCP is running in
streamable-http
mode (check logs) - Confirm server is listening on port 8080
- Check CloudWatch logs for startup errors
- Verify FastMCP is running in
The deployment process creates the following artifacts:
File | Purpose |
---|---|
deployment.env |
Environment variables persistence across sessions |
my_mcp_server.py |
FastMCP server implementation with tool definitions |
requirements.txt |
Python dependencies for the MCP server |
Dockerfile |
Auto-generated container definition for ARM64 deployment |
.dockerignore |
Build exclusion patterns (auto-generated) |
.bedrock_agentcore.yaml |
AgentCore configuration and deployment metadata |
my_mcp_client_remote.py |
Test client for validating MCP server connectivity |
generate_vscode_config.sh |
Script to generate fresh VSCode MCP configuration with auth tokens |
vscode_mcp_config.json |
VSCode MCP client configuration (regenerated on each run) |
AWS Resources Created:
- Cognito User Pool with test user
- IAM execution role for Bedrock AgentCore runtime
- IAM execution role for CodeBuild (auto-created by agentcore CLI)
- ECR repository with ARM64 container image
- S3 bucket for CodeBuild source uploads (auto-created)
- CodeBuild project for cross-platform builds
- Bedrock AgentCore runtime deployment
- CloudWatch log groups (created on first request)
For convenience, here's the entire deployment process in a single script:
#!/bin/bash
set -e
echo "Starting FastMCP deployment to AWS Bedrock AgentCore..."
# Step 1: Set up basic environment
export AGENT_NAME="mcp_$(date +%Y%m%d_%H%M%S)"
export AWS_REGION="eu-central-1"
cat > deployment.env << EOF
export AGENT_NAME="$AGENT_NAME"
export AWS_REGION="$AWS_REGION"
EOF
echo "Agent name: $AGENT_NAME"
echo "AWS Region: $AWS_REGION"
# Step 2: Create MCP Server Files
cat > my_mcp_server.py << 'EOF'
# my_mcp_server.py
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
cat > requirements.txt << 'EOF'
fastmcp
mcp
pydantic
httpx
botocore
EOF
# Step 3: Create Cognito User Pool
POOL_NAME="${AGENT_NAME}_pool"
POOL_ID=$(aws cognito-idp create-user-pool --pool-name "$POOL_NAME" --region $AWS_REGION --query 'UserPool.Id' --output text)
CLIENT_ID=$(aws cognito-idp create-user-pool-client \
--user-pool-id $POOL_ID \
--client-name "${AGENT_NAME}_client" \
--no-generate-secret \
--explicit-auth-flows ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
--region $AWS_REGION \
--query 'UserPoolClient.ClientId' \
--output text)
cat >> deployment.env << EOF
export POOL_ID="$POOL_ID"
export CLIENT_ID="$CLIENT_ID"
EOF
echo "Pool ID: $POOL_ID"
echo "Client ID: $CLIENT_ID"
# Step 4: Create Test User
aws cognito-idp admin-create-user \
--user-pool-id $POOL_ID \
--username "[email protected]" \
--user-attributes Name=email,[email protected] Name=email_verified,Value=true \
--temporary-password "TempPass123!" \
--message-action SUPPRESS \
--region $AWS_REGION > /dev/null
aws cognito-idp admin-set-user-password \
--user-pool-id $POOL_ID \
--username "[email protected]" \
--password "TestPass123!" \
--permanent \
--region $AWS_REGION
echo "✅ Test user created: [email protected]"
# Step 5: Create IAM Execution Role
ROLE_NAME="BedrockAgentCoreRole-${AGENT_NAME}"
TRUST_POLICY=$(cat <<EOFPOLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AssumeRolePolicy",
"Effect": "Allow",
"Principal": {
"Service": "bedrock-agentcore.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "$(aws sts get-caller-identity --query Account --output text)"
},
"ArnLike": {
"aws:SourceArn": "arn:aws:bedrock-agentcore:${AWS_REGION}:$(aws sts get-caller-identity --query Account --output text):*"
}
}
}
]
}
EOFPOLICY
)
aws iam create-role \
--role-name "$ROLE_NAME" \
--assume-role-policy-document "$TRUST_POLICY" \
--region $AWS_REGION > /dev/null 2>&1
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
POLICY_DOCUMENT=$(cat <<EOFPOLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ECRImageAccess",
"Effect": "Allow",
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchCheckLayerAvailability"
],
"Resource": [
"arn:aws:ecr:${AWS_REGION}:${ACCOUNT_ID}:repository/*"
]
},
{
"Sid": "ECRTokenAccess",
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"logs:DescribeLogStreams",
"logs:CreateLogGroup",
"logs:DescribeLogGroups"
],
"Resource": [
"arn:aws:logs:${AWS_REGION}:${ACCOUNT_ID}:log-group:/aws/bedrock-agentcore/runtimes/*",
"arn:aws:logs:${AWS_REGION}:${ACCOUNT_ID}:log-group:*"
]
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:${AWS_REGION}:${ACCOUNT_ID}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*"
]
},
{
"Effect": "Allow",
"Action": [
"xray:PutTraceSegments",
"xray:PutTelemetryRecords",
"xray:GetSamplingRules",
"xray:GetSamplingTargets"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Resource": "*",
"Action": "cloudwatch:PutMetricData",
"Condition": {
"StringEquals": {
"cloudwatch:namespace": "bedrock-agentcore"
}
}
},
{
"Sid": "BedrockAgentCoreRuntime",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:InvokeAgentRuntime"
],
"Resource": [
"arn:aws:bedrock-agentcore:${AWS_REGION}:${ACCOUNT_ID}:runtime/*"
]
},
{
"Sid": "BedrockAgentCoreMemory",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:CreateMemory",
"bedrock-agentcore:CreateEvent",
"bedrock-agentcore:GetEvent",
"bedrock-agentcore:GetMemory",
"bedrock-agentcore:GetMemoryRecord",
"bedrock-agentcore:ListActors",
"bedrock-agentcore:ListEvents",
"bedrock-agentcore:ListMemoryRecords",
"bedrock-agentcore:ListSessions",
"bedrock-agentcore:DeleteEvent",
"bedrock-agentcore:DeleteMemoryRecord",
"bedrock-agentcore:RetrieveMemoryRecords"
],
"Resource": [
"arn:aws:bedrock-agentcore:${AWS_REGION}:${ACCOUNT_ID}:memory/*",
"*"
]
},
{
"Sid": "BedrockAgentCoreIdentity",
"Effect": "Allow",
"Action": [
"bedrock-agentcore:GetResourceApiKey",
"bedrock-agentcore:GetResourceOauth2Token",
"bedrock-agentcore:GetWorkloadAccessToken",
"bedrock-agentcore:GetWorkloadAccessTokenForJWT",
"bedrock-agentcore:GetWorkloadAccessTokenForUserId"
],
"Resource": [
"arn:aws:bedrock-agentcore:${AWS_REGION}:${ACCOUNT_ID}:token-vault/*",
"arn:aws:bedrock-agentcore:${AWS_REGION}:${ACCOUNT_ID}:workload-identity-directory/*"
]
},
{
"Sid": "BedrockModelInvocation",
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream",
"bedrock:ApplyGuardrail"
],
"Resource": [
"arn:aws:bedrock:*::foundation-model/*",
"arn:aws:bedrock:${AWS_REGION}:${ACCOUNT_ID}:*"
]
}
]
}
EOFPOLICY
)
aws iam put-role-policy \
--role-name "$ROLE_NAME" \
--policy-name "BedrockAgentCoreExecutionPolicy" \
--policy-document "$POLICY_DOCUMENT" \
--region $AWS_REGION 2>/dev/null
EXECUTION_ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/$ROLE_NAME"
cat >> deployment.env << EOF
export ROLE_NAME="$ROLE_NAME"
export EXECUTION_ROLE_ARN="$EXECUTION_ROLE_ARN"
EOF
echo "✅ Created execution role: $ROLE_NAME"
# Step 6: Create ECR Repository
ECR_REPO_NAME="bedrock-agentcore-${AGENT_NAME}"
aws ecr create-repository \
--repository-name "$ECR_REPO_NAME" \
--region $AWS_REGION > /dev/null 2>&1
ECR_URI="${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${ECR_REPO_NAME}"
cat >> deployment.env << EOF
export ECR_REPO_NAME="$ECR_REPO_NAME"
export ECR_URI="$ECR_URI"
EOF
echo "✅ Created ECR repository: $ECR_REPO_NAME"
# Step 7: Configure Bedrock AgentCore
DISCOVERY_URL="https://cognito-idp.${AWS_REGION}.amazonaws.com/${POOL_ID}/.well-known/openid-configuration"
agentcore configure \
-e my_mcp_server.py \
--name $AGENT_NAME \
--protocol MCP \
--region $AWS_REGION \
--execution-role "$EXECUTION_ROLE_ARN" \
--ecr "$ECR_URI" \
--requirements-file requirements.txt \
--authorizer-config "{\"customJWTAuthorizer\":{\"discoveryUrl\":\"$DISCOVERY_URL\",\"allowedClients\":[\"$CLIENT_ID\"]}}"
echo "✅ AgentCore configured"
# Step 8: Launch to Bedrock AgentCore
echo "Deploying to Bedrock AgentCore (this takes 1-2 minutes)..."
agentcore launch --agent $AGENT_NAME
# Extract the agent ARN
AGENT_ARN=$(grep "agent_arn:" .bedrock_agentcore.yaml | tail -1 | awk '{print $2}')
echo "export AGENT_ARN=\"$AGENT_ARN\"" >> deployment.env
echo "✅ Deployment complete!"
echo " Agent ARN: $AGENT_ARN"
# Step 9: Create test client script
cat > my_mcp_client_remote.py << 'EOF'
import asyncio
import os
import sys
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
agent_arn = os.getenv('AGENT_ARN')
bearer_token = os.getenv('BEARER_TOKEN')
region = os.getenv('AWS_REGION', 'eu-central-1')
if not agent_arn or not bearer_token:
print("Error: AGENT_ARN or BEARER_TOKEN environment variable is not set")
sys.exit(1)
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",
"Accept": "application/json, text/event-stream"
}
print(f"Invoking: {mcp_url}\n")
async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as (
read_stream,
write_stream,
_,
):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
# List available tools
print("Available tools:")
tool_result = await session.list_tools()
for tool in tool_result.tools:
print(f" - {tool.name}: {tool.description}")
# Test calling a tool
print("\nTesting add_numbers(5, 3):")
result = await session.call_tool("add_numbers", {"a": 5, "b": 3})
print(f"Result: {result.content[0].text}")
print("\nTesting greet_user('Alice'):")
result = await session.call_tool("greet_user", {"name": "Alice"})
print(f"Result: {result.content[0].text}")
asyncio.run(main())
EOF
# Step 10: Create VSCode config generator
cat > generate_vscode_config.sh << 'EOFSCRIPT'
#!/bin/bash
# Load environment
if [ ! -f "deployment.env" ]; then
echo "❌ Error: deployment.env not found. Please run deployment steps first."
exit 1
fi
source deployment.env
# Check required variables
if [ -z "$CLIENT_ID" ] || [ -z "$AWS_REGION" ] || [ -z "$AGENT_ARN" ] || [ -z "$AGENT_NAME" ]; then
echo "❌ Error: Missing required environment variables"
exit 1
fi
echo "🔐 Authenticating test user..."
# Get fresh token for test user (no secret hash needed)
BEARER_TOKEN=$(aws cognito-idp initiate-auth \
--auth-flow USER_PASSWORD_AUTH \
--client-id $CLIENT_ID \
--auth-parameters [email protected],PASSWORD=TestPass123! \
--region $AWS_REGION \
--query 'AuthenticationResult.AccessToken' \
--output text 2>/dev/null)
if [ -z "$BEARER_TOKEN" ]; then
echo "❌ Error: Failed to get bearer token"
exit 1
fi
# URL encode the ARN with double encoding for forward slash
ENCODED_ARN=$(echo $AGENT_ARN | sed 's/:/\%3A/g' | sed 's/\//\%252F/g')
# Generate config
cat > vscode_mcp_config.json << JSON
{
"servers": {
"${AGENT_NAME}": {
"url": "https://bedrock-agentcore.${AWS_REGION}.amazonaws.com/runtimes/${ENCODED_ARN}/invocations?qualifier=DEFAULT",
"type": "http",
"headers": {
"Authorization": "Bearer $BEARER_TOKEN",
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream"
}
}
}
}
JSON
echo "✅ Generated vscode_mcp_config.json"
echo " User: [email protected]"
echo " Agent: ${AGENT_NAME}"
echo " Token expires at: $(date -d '+1 hour' 2>/dev/null || date -v +1H)"
echo ""
echo "📋 To use in VSCode:"
echo " 1. Copy the contents of vscode_mcp_config.json"
echo " 2. Add to your VSCode MCP settings"
echo " 3. Restart the MCP connection"
EOFSCRIPT
chmod +x generate_vscode_config.sh
echo ""
echo "Environment saved to deployment.env"
echo "Run 'source deployment.env' to load variables in new sessions"
echo ""
echo "✅ Created helper scripts:"
echo " - my_mcp_client_remote.py (test client)"
echo " - generate_vscode_config.sh (VSCode config generator)"
echo ""
echo "To test your deployment:"
echo " 1. Run: source deployment.env"
echo " 2. Run: pip install mcp httpx"
echo " 3. Get token: export BEARER_TOKEN=\$(aws cognito-idp initiate-auth --auth-flow USER_PASSWORD_AUTH --client-id \$CLIENT_ID --auth-parameters [email protected],PASSWORD=TestPass123! --region \$AWS_REGION --query 'AuthenticationResult.AccessToken' --output text)"
echo " 4. Run: python3 my_mcp_client_remote.py"
echo ""
echo "To generate VSCode configuration:"
echo " Run: ./generate_vscode_config.sh"
Save this script as deploy.sh
and run with:
chmod +x deploy.sh
./deploy.sh
Note: This script uses dynamic account ID retrieval instead of hardcoded values for better portability.