In this walkthrough, I'll show you the simplest way to deploy an MCP server on AWS, using the Amazon Bedrock AgentCore Runtime - and how to call it with your IAM credentials - no complicated OAuth setup required.
Note: This is a code-only walkthrough. For detailed explanations, see the full tutorial.
Prerequisites
Python and AWS setup
To follow this walkthrough, you'll need:
- Python 3.11 or higher,
- the uv package manager (or pip),
- an AWS account,
- and the AWS CLI installed and configured.
Run these commands to verify the prerequisites:
python --version
uv --version
aws --version
aws sts get-caller-identity --no-cli-pager
To troubleshoot, please follow the links above.
The AgentCore Starter Toolkit
The Bedrock AgentCore Starter Toolkit will simplify the entire process to two commands, agentcore configure and agentcore deploy.
To install it, run:
uv tool install -U bedrock-agentcore-starter-toolkit
Step 1: Create a small MCP server
Prepare a new Python project for the MCP server:
# Start with an empty folder for your project
mkdir mcp-on-agentcore-runtime
cd mcp-on-agentcore-runtime
# Create and cd into a subdirectory for the MCP server
mkdir mcp-server
cd mcp-server
# Initialize a new Python project and add the `mcp` package:
uv init --bare
uv add mcp
Create the MCP server
# Create a simple MCP server in `server.py`:
cat << 'EOF' > server.py
import random
from mcp.server.fastmcp import FastMCP
mcp = FastMCP(host="0.0.0.0", stateless_http=True)
@mcp.tool()
def roll_d20(number_of_dice: int = 1) -> dict:
"""Rolls one or more 20-sided dice (d20)"""
if number_of_dice < 1:
return {
"error": f"number_of_dice must be at least 1"
}
rolls = [random.randint(1, 20) for _ in range(number_of_dice)]
return {
"number_of_dice": number_of_dice,
"rolls": rolls,
"total": sum(rolls)
}
def main():
mcp.run(transport="streamable-http")
if __name__ == "__main__":
main()
EOF
Step 2: Test Locally
In the first terminal:
# Start the MCP server:
uv run server.py
In a second terminal:
List the tools:
# Send a list tools request
curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'
# Response:
# event: message
# data: {"jsonrpc":"2.0","id":1,"result":{"tools":[{...}]}}
Call the tool:
# Send a tool call request
curl -X POST http://127.0.0.1:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "roll_d20",
"arguments": { "number_of_dice": 2 }
}
}'
# Response:
# event: message
# data: {"jsonrpc":"2.0","id":1,"result":{"content":[{...}]}}
If you see connection errors, ensure the server is running in the other terminal
In the first terminal:
- Press
Ctrl/Cmd+Cto stop the server
Step 3: Configure the deployment AWS
# Navigate back up to the project root
cd ..
# Configure the MCP server deployment.
# This will use the account and region configured in the AWS CLI
agentcore configure \
--entrypoint mcp-server/server.py \
--requirements-file mcp-server/pyproject.toml \
--disable-memory --disable-otel \
--non-interactive \
--deployment-type container \
--protocol MCP \
--name my_mcp_server
# Output:
# [...]
# Config saved to: .../.bedrock_agentcore.yaml
# [...]
# If you want to view the configuration, you can run:
agentcore status
Step 4: Deploy to AWS
# Now you can launch the deployment process
agentcore deploy
# Output:
# Launching Bedrock AgentCore (codebuild mode - RECOMMENDED)
# [...]
# Setting up AWS resources (ECR repository, execution roles)
# [...]
# Deployment completed successfully
This single command orchestrates the entire deployment process.
- It creates an ECR Repository to store the container image,
- an IAM Role with permissions for the runtime to pull images from ECR, execute your container, and write logs to CloudWatch,
- and an IAM Role for CodeBuild to build the container and push it to ECR.
- Then it zips and uploads your server files to S3,
- runs the build process, pushes the image,
- and deploys the MCP server to AgentCore Runtime
The server is now ready to accept MCP requests.
Step 5: Test with the AWS CLI
Get the MCP server ARN:
# Extract the server's ARN from `.bedrock_agentcore.yaml`:
export MCP_SERVER_ARN=$( \
grep 'agent_arn:' .bedrock_agentcore.yaml | \
awk '{print $2}' | tr -d \"\')
# To see if it worked, type:
echo "MCP_SERVER_ARN=\"$MCP_SERVER_ARN\""
List the tools:
# Create a file with the list tools request
cat > list-tools-request.json <<'EOF'
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/list"
}
EOF
# To list the tools, send the request to the AgentCore Runtime
# Note that, even though this is called `invoke-agent-runtime`,
# it will call the deployed MCP server.
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-arn $MCP_SERVER_ARN \
--content-type "application/json" \
--accept "application/json, text/event-stream" \
--no-cli-pager \
--payload fileb://list-tools-request.json \
./list-tools-output.txt
# Output:
# [...]
# mcpSessionId: [MCP Session ID]
# runtimeSessionId: [AgentCore Runtime ID - same as MCP Session ID]
# [...]
# View the results:
cat list-tools-output.txt
Call the tool:
# Create a file with the tool call request
cat > tool-call-request.json <<'EOF'
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "roll_d20",
"arguments": {
"number_of_dice": 2
}
}
}
EOF
aws bedrock-agentcore invoke-agent-runtime \
--agent-runtime-arn $MCP_SERVER_ARN \
--content-type "application/json" \
--accept "application/json, text/event-stream" \
--no-cli-pager \
--payload fileb://tool-call-request.json \
./tool-call-output.txt
# Output:
# [...]
# mcpSessionId: [MCP Session ID]
# runtimeSessionId: [AgentCore Runtime ID - same as MCP Session ID]
# [...]
# View the results:
cat tool-call-output.txt
Note: We're using the AWS CLI because
curlwon't work anymore. The endpoint is protected by AWS IAM and can only be called with valid credentials.
Step 6: Connect an AI Agent
Extract the information required to build the MCP server URL:
# Extract the required information from `.bedrock_agentcore.yaml`:
export RUNTIME_ID=$( \
grep 'agent_id:' .bedrock_agentcore.yaml | \
awk '{print $2}' | tr -d \"\')
export ACCOUNT_ID=$( \
grep 'account:' .bedrock_agentcore.yaml | \
awk '{print $2}' | tr -d \"\')
export REGION=$( \
grep 'region:' .bedrock_agentcore.yaml | \
awk '{print $2}' | tr -d \"\')
# To see if it worked, type:
echo "RUNTIME_ID=\"$RUNTIME_ID\""
echo "AWS_ACCOUNT_ID=\"$ACCOUNT_ID\""
echo "AWS_REGION=\"$REGION\""
Prepare a new Python project for the AI agent:
# Create and cd into a subdirectory for the AI agent
mkdir agent
cd agent
# Initialize a new Python project:
uv init --bare
Add the MCP Proxy for AWS (this enables IAM-based auth for MCP - quick intro to using it with other frameworks, like LangChain, Llamaindex, etc.) and the Strands Agents SDK:
uv add mcp-proxy-for-aws strands-agents
Create the agent:
# Create a simple MCP server in `agent.py`:
cat << EOF > agent.py
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp_proxy_for_aws.client import aws_iam_streamablehttp_client
runtime_id = "$RUNTIME_ID"
account_id = "$ACCOUNT_ID"
region = "$REGION"
def main():
host = f"https://bedrock-agentcore.{region}.amazonaws.com"
path = f"runtimes/{runtime_id}/invocations"
query_params = f"qualifier=DEFAULT&accountId={account_id}"
url = f"{host}/{path}?{query_params}"
mcp_client_factory = lambda: aws_iam_streamablehttp_client(
terminate_on_close=False,
aws_service="bedrock-agentcore",
aws_region=region,
endpoint=url
)
prompt = "Roll 3 dice"
with MCPClient(mcp_client_factory) as mcp_client:
mcp_tools = mcp_client.list_tools_sync()
agent = Agent(tools=mcp_tools)
print(f"\nUser: {prompt}")
print("\nAgent:")
agent(prompt)
print()
if __name__ == "__main__":
main()
EOF
Now run the agent:
uv run agent.py
# Output:
# User: Roll 3 dice
#
# Agent:
# The agent will call the roll_d20 tool and display the results
Note: You may see a deprecation warning:
DeprecationWarning: Use 'streamable_http_client' instead. This warning can be safely ignored. The official MCP library recently renamed their client. By the time you read this, it may already be fixed.
Cleanup
When you're done experimenting, destroy the deployment to avoid ongoing costs:
# Navigate back to the project root
cd ..
# This command removes all resources created during deployment
agentcore destroy
If you learned something new, it would be great if you could like this post. For detailed explanations of each step, see the full tutorial.
Top comments (0)