DEV Community

Cover image for From Local MCP Server to AWS Deployment in Two Commands - The Code-Only Version
Dennis Traub for AWS

Posted on

From Local MCP Server to AWS Deployment in Two Commands - The Code-Only Version

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:

Run these commands to verify the prerequisites:

python --version
uv --version     
aws --version
aws sts get-caller-identity --no-cli-pager
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Step 2: Test Locally

In the first terminal:

# Start the MCP server:
uv run server.py
Enter fullscreen mode Exit fullscreen mode

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":[{...}]}}
Enter fullscreen mode Exit fullscreen mode

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":[{...}]}}
Enter fullscreen mode Exit fullscreen mode

If you see connection errors, ensure the server is running in the other terminal

In the first terminal:

  • Press Ctrl/Cmd+C to 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

This single command orchestrates the entire deployment process.

  1. It creates an ECR Repository to store the container image,
  2. an IAM Role with permissions for the runtime to pull images from ECR, execute your container, and write logs to CloudWatch,
  3. and an IAM Role for CodeBuild to build the container and push it to ECR.
  4. Then it zips and uploads your server files to S3,
  5. runs the build process, pushes the image,
  6. 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\""
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Note: We're using the AWS CLI because curl won'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\""
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)