π AWS just solved the biggest AI agent bottleneck
When Anthropic announced MCP in late November 2024, i immediately saw the potential. But there was a problem - no good way to run MCP servers on AWS Lambda.
Spent weeks trying different approaches. Built a custom AppSync solution that simulated streamable HTTP and Server-Sent Events. It technically worked but required too much plumbing.
Meanwhile, Cloudflare Workers had native MCP support. Google Cloud Run made it simple too. But if you've built on AWS, you know Lambda and API Gateway are the gold standard for serverless.
Then in early 2025, AWS quietly released AgentCore Gateway.
Game over.
No more custom glue code.
No more M×N tool chaos.
No more protocol headaches.
Here's how to build it in 8 steps:
π€― The Problem
AI agents need tools. APIs, databases, Lambda functions, other agents.
But connecting each agent to each tool gets messy fast.
100 agents × 1,000 tools = 100,000 connections.
Companies are hitting this wall right now.
β¨ The Solution
AgentCore Gateway fixes this.
It's a managed service that gives every agent the same tool interface.
Lambda Function → AgentCore Gateway → MCP Protocol → AI Agents
π οΈ What We're Building
β
Serverless math API via Lambda
β
Secure JWT authentication via Cognito
β
MCP-compatible gateway via AgentCore
β
AI agent integration via Strands/Claude
Time to complete: ~30 minutes
Lines of code: <100 per step
π Prerequisites
- β AWS CLI configured with admin permissions
- β Python 3.10+ (required for Strands agents)
- β AWS Account with Bedrock access in us-east-1
Step 1: Create Lambda Execution Role
What we're doing: Setting up permissions for our serverless function.
# Create trust policy
cat > lambda-trust-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
EOF
# Create the role
aws iam create-role \
--role-name lambda-execution-role \
--assume-role-policy-document file://lambda-trust-policy.json
# Attach execution policy
aws iam attach-role-policy \
--role-name lambda-execution-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
β Result: Lambda can now execute and write logs
Step 2: Deploy Math Lambda Function
What we're doing: Creating the serverless function that becomes our AI tool.
File: create-lambda.py
import boto3
import json
import zipfile
import io
# The actual Lambda function code
lambda_code = '''
import json
def handler(event, context):
operation = event.get('operation', 'add')
a = event.get('a', 0)
b = event.get('b', 0)
if operation == 'add':
result = a + b
elif operation == 'multiply':
result = a * b
else:
result = 0
return {
'result': result,
'operation': operation,
'inputs': {'a': a, 'b': b}
}
'''
# Deploy it
lambda_client = boto3.client('lambda', region_name='us-east-1')
sts = boto3.client('sts')
account_id = sts.get_caller_identity()['Account']
# Package and create
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
zip_file.writestr('index.py', lambda_code)
zip_buffer.seek(0)
response = lambda_client.create_function(
FunctionName='math-tools-function',
Runtime='python3.12',
Role=f'arn:aws:iam::{account_id}:role/lambda-execution-role',
Handler='index.handler',
Code={'ZipFile': zip_buffer.read()}
)
print(f"β
Lambda deployed: {response['FunctionArn']}")
Test it:
python3 create-lambda.py
aws lambda invoke --function-name math-tools-function \
--payload '{"operation":"add","a":15,"b":25}' response.json
cat response.json # Should show: {"result": 40, ...}
β Result: Working serverless math API
Step 3: Setup Cognito Authentication
What we're doing: Creating JWT token authentication (no AWS credentials needed for agents).
File: setup-cognito.py
import boto3
import time
cognito = boto3.client('cognito-idp', region_name='us-east-1')
# 1. User Pool
pool_response = cognito.create_user_pool(
PoolName='agentcore-gateway-pool',
Policies={'PasswordPolicy': {'MinimumLength': 8}}
)
user_pool_id = pool_response['UserPool']['Id']
# 2. Resource Server with scopes
cognito.create_resource_server(
UserPoolId=user_pool_id,
Identifier='agentcore-gateway',
Name='AgentCore Gateway',
Scopes=[
{'ScopeName': 'read', 'ScopeDescription': 'Read access'},
{'ScopeName': 'write', 'ScopeDescription': 'Write access'}
]
)
# 3. M2M Client (machine-to-machine)
client_response = cognito.create_user_pool_client(
UserPoolId=user_pool_id,
ClientName='agentcore-gateway-client',
GenerateSecret=True,
AllowedOAuthFlows=['client_credentials'],
AllowedOAuthScopes=['agentcore-gateway/read', 'agentcore-gateway/write'],
AllowedOAuthFlowsUserPoolClient=True
)
# 4. Domain for token endpoint
domain_name = f"agentcore-{int(time.time())}"
cognito.create_user_pool_domain(Domain=domain_name, UserPoolId=user_pool_id)
# Save config
with open('cognito-config.txt', 'w') as f:
f.write(f"USER_POOL_ID={user_pool_id}\n")
f.write(f"CLIENT_ID={client_response['UserPoolClient']['ClientId']}\n")
f.write(f"CLIENT_SECRET={client_response['UserPoolClient']['ClientSecret']}\n")
f.write(f"TOKEN_URL=https://{domain_name}.auth.us-east-1.amazoncognito.com/oauth2/token\n")
print("β
Cognito setup complete - JWT tokens ready!")
β Result: Secure JWT authentication system
Step 4: Create AgentCore Gateway
What we're doing: Building the MCP endpoint that transforms Lambda into agent tools.
File: create-gateway.py
import boto3
import json
# Load Cognito config
config = {}
with open('cognito-config.txt', 'r') as f:
for line in f:
key, value = line.strip().split('=', 1)
config[key] = value
# Create AgentCore role first
iam = boto3.client('iam')
sts = boto3.client('sts')
account_id = sts.get_caller_identity()['Account']
trust_policy = {
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "bedrock-agentcore.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
iam.create_role(
RoleName='AgentCoreGatewayRole',
AssumeRolePolicyDocument=json.dumps(trust_policy)
)
# Attach Lambda invoke permission
iam.attach_role_policy(
RoleName='AgentCoreGatewayRole',
PolicyArn='arn:aws:iam::aws:policy/service-role/AWSLambdaRole'
)
# Create the gateway
agentcore = boto3.client('bedrock-agentcore-control', region_name='us-east-1')
gateway_response = agentcore.create_gateway(
name='MathToolsGateway',
description='Math operations via MCP',
roleArn=f'arn:aws:iam::{account_id}:role/AgentCoreGatewayRole',
protocolType='MCP',
authorizerType='CUSTOM_JWT',
authorizerConfiguration={
'customJWTAuthorizer': {
'discoveryUrl': f'https://cognito-idp.us-east-1.amazonaws.com/{config["USER_POOL_ID"]}/.well-known/openid-configuration',
'allowedClients': [config['CLIENT_ID']]
}
}
)
print(f"β
Gateway created: {gateway_response['gatewayUrl']}")
# Save gateway info
with open('gateway-config.txt', 'w') as f:
f.write(f"GATEWAY_ID={gateway_response['gatewayId']}\n")
f.write(f"GATEWAY_URL={gateway_response['gatewayUrl']}\n")
β Result: MCP-compatible gateway endpoint
Step 5: Connect Lambda as MCP Tool
What we're doing: Defining the tool schema that tells AI agents how to use our math functions.
File: add-lambda-target.py
import boto3
# Load configs
gateway_config = {}
with open('gateway-config.txt', 'r') as f:
for line in f:
key, value = line.strip().split('=', 1)
gateway_config[key] = value
sts = boto3.client('sts')
account_id = sts.get_caller_identity()['Account']
lambda_arn = f'arn:aws:lambda:us-east-1:{account_id}:function:math-tools-function'
agentcore = boto3.client('bedrock-agentcore-control', region_name='us-east-1')
# Add Lambda as MCP tool
agentcore.create_gateway_target(
gatewayIdentifier=gateway_config['GATEWAY_ID'],
name='MathTarget',
description='Math operations target',
targetConfiguration={
'mcp': {
'lambda': {
'lambdaArn': lambda_arn,
'toolSchema': {
'inlinePayload': [
{
'name': 'add_numbers',
'description': 'Add two numbers together',
'inputSchema': {
'type': 'object',
'properties': {
'a': {'type': 'number', 'description': 'First number'},
'b': {'type': 'number', 'description': 'Second number'}
},
'required': ['a', 'b']
}
}
]
}
}
}
},
credentialProviderConfigurations=[
{'credentialProviderType': 'GATEWAY_IAM_ROLE'}
]
)
print("β
Lambda connected as MCP tool!")
β Result: Lambda function is now discoverable by AI agents
Step 6: Test with JWT Authentication
What we're doing: Verifying everything works with token-based auth (not AWS credentials).
File: test-gateway.py
import requests
import json
# Load configs
cognito_config = {}
with open('cognito-config.txt', 'r') as f:
for line in f:
key, value = line.strip().split('=', 1)
cognito_config[key] = value
gateway_config = {}
with open('gateway-config.txt', 'r') as f:
for line in f:
key, value = line.strip().split('=', 1)
gateway_config[key] = value
# Get JWT token (NO AWS credentials needed!)
def get_jwt_token():
response = requests.post(
cognito_config['TOKEN_URL'],
data=f"grant_type=client_credentials&client_id={cognito_config['CLIENT_ID']}&client_secret={cognito_config['CLIENT_SECRET']}",
headers={'Content-Type': 'application/x-www-form-urlencoded'}
)
return response.json()['access_token']
# Test MCP protocol
token = get_jwt_token()
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {token}"
}
# List available tools
payload = {
"jsonrpc": "2.0",
"id": "test",
"method": "tools/list"
}
response = requests.post(gateway_config['GATEWAY_URL'], headers=headers, json=payload)
result = response.json()
print("β
Available tools:")
print(json.dumps(result, indent=2))
# Call the math tool
if 'result' in result and 'tools' in result['result']:
tool_name = result['result']['tools'][0]['name']
call_payload = {
"jsonrpc": "2.0",
"id": "math-test",
"method": "tools/call",
"params": {
"name": tool_name,
"arguments": {"a": 15, "b": 25, "operation": "add"}
}
}
call_response = requests.post(gateway_config['GATEWAY_URL'], headers=headers, json=call_payload)
print(f"\nβ
Math result: {call_response.json()}")
β Result: Gateway working with JWT authentication
Step 7: Connect AI Agents
What we're doing: Connecting real AI agents using JWT tokens (not AWS profiles).
Option A: Strands Agents
File: strands-agent.py
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp.mcp_client import MCPClient
from mcp.client.streamable_http import streamablehttp_client
import requests
# Load configs (same as test file)
cognito_config = {}
with open('cognito-config.txt', 'r') as f:
for line in f:
key, value = line.strip().split('=', 1)
cognito_config[key] = value
gateway_config = {}
with open('gateway-config.txt', 'r') as f:
for line in f:
key, value = line.strip().split('=', 1)
gateway_config[key] = value
def get_jwt_token():
response = requests.post(
cognito_config['TOKEN_URL'],
data=f"grant_type=client_credentials&client_id={cognito_config['CLIENT_ID']}&client_secret={cognito_config['CLIENT_SECRET']}",
headers={'Content-Type': 'application/x-www-form-urlencoded'}
)
return response.json()['access_token']
# Create MCP client with JWT auth
token = get_jwt_token()
mcp_client = MCPClient(
lambda: streamablehttp_client(
gateway_config['GATEWAY_URL'],
headers={"Authorization": f"Bearer {token}"}
)
)
with mcp_client:
# Get tools
tools = []
result = mcp_client.list_tools_sync()
tools.extend(result)
print(f"β
Found tools: {[tool.tool_name for tool in tools]}")
# Create agent with Bedrock model
model = BedrockModel(
model_id="us.amazon.nova-pro-v1:0",
temperature=0.7
)
agent = Agent(model=model, tools=tools)
# Test the agent
result = agent("Can you add 15 and 25 using the available math tools?")
print(f"\nπ€ Agent result: {result}")
Install and run:
pip install strands-agents boto3 requests
python3 strands-agent.py
Option B: Claude Desktop
File: claude-config.json
{
"mcpServers": {
"agentcore-math": {
"command": "python3",
"args": ["./strands-agent.py"]
}
}
}
cp claude-config.json ~/Library/Application\ Support/Claude/claude_desktop_config.json
β Result: AI agents can now use your Lambda functions as tools!
Step 8: Advanced Features
Semantic Search
Enable intelligent tool discovery:
search_config = {
"mcp": {"searchType": "SEMANTIC", "supportedVersions": ["2025-03-26"]}
}
agentcore.create_gateway(
# ... other params
protocolConfiguration=search_config
)
Monitoring
Built-in CloudWatch metrics:
- Invocations - Total requests
- Latency - Response times
- Errors - 4xx/5xx rates
- TargetExecutionTime - Lambda performance
π What You Built
Lambda Function → AgentCore Gateway → MCP Protocol → AI Agents
β
Serverless math API - No infrastructure to manage
β
JWT Authentication - Enterprise-grade security
β
MCP Compatibility - Works with any agent framework
β
Auto Tool Discovery - Agents find tools automatically
β
Built-in Monitoring - CloudWatch integration
π What Changed
Before:
- Custom code for each agent-tool pair
- Protocol translation problems
- Complex security setup
- M×N scaling issues
After:
- β One gateway, unlimited tools
- β Automatic MCP translation
- β Built-in JWT security
- β Linear scaling
π‘ What's Next
- Add More Tools - Connect more Lambda functions
- OpenAPI Integration - Connect existing REST APIs
- Multi-Agent Systems - Build agent teams
- Production Setup - Add monitoring and alerts
- Semantic Search - Enable smart tool discovery
π― Key Points
AgentCore Gateway is API Gateway for agents.
β
No custom code - Managed service
β
Built-in security - OAuth 2.0 with Cognito
β
Works everywhere - MCP protocol standard
β
Scales automatically - Serverless architecture
Your Lambda functions are now AI agent tools.
That's it.
Built with Amazon Bedrock AgentCore Gateway.
Want to build your own? Follow this tutorial.
#AWS #AI #Serverless #AgentCore #MCP #BedrockAgentCore