Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions agentcore-gateway-lambda-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Amazon Bedrock AgentCore Gateway with Lambda tools

This pattern deploys an Amazon Bedrock AgentCore Gateway that exposes Lambda functions as MCP (Model Context Protocol) tools. Any MCP-compatible client can discover and invoke the tools via the Gateway's MCP endpoint.

Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/agentcore-gateway-lambda-cdk

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Node.js 22+](https://nodejs.org/en/download/) installed
* [AWS CDK v2](https://docs.aws.amazon.com/cdk/v2/guide/getting-started.html) installed
* [Python 3.9+](https://www.python.org/downloads/) with `boto3` installed (for testing)

## Architecture

```
┌──────────────┐ ┌──────────────────────────┐ ┌──────────────────┐
│ MCP Client │────▶│ AgentCore Gateway │────▶│ Lambda Function │
│ (Agent/CLI) │ │ (MCP protocol, IAM auth) │ │ (Tool handler) │
└──────────────┘ └──────────────────────────┘ └──────────────────┘
│ tools/list │ │ get_weather │
│ tools/call │ │ get_time │
└──────────────────────────┘ └──────────────────┘
```

## How it works

1. The AgentCore Gateway is created with MCP protocol and IAM authentication.
2. A Lambda function is registered as a Gateway target with inline tool definitions (get_weather, get_time).
3. MCP clients send `tools/list` to discover available tools and `tools/call` to invoke them.
4. The Gateway routes tool calls to the Lambda function, passing the tool arguments. The Lambda returns MCP-formatted content responses.

## Deployment Instructions

1. Clone the repository:
```bash
git clone https://github.com/aws-samples/serverless-patterns
cd serverless-patterns/agentcore-gateway-lambda-cdk
```

2. Install dependencies:
```bash
npm install
```

3. Deploy the stack:
```bash
cdk deploy
```

4. Note the outputs printed after deployment. You will need `GatewayUrl` for testing.
Example output:
```
Outputs:
AgentcoreGatewayLambdaStack.GatewayId = tool-gateway-bdastack-mkrkf8fntt
AgentcoreGatewayLambdaStack.GatewayUrl = https://tool-gateway-bdastack-mkrkf8fntt.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
AgentcoreGatewayLambdaStack.TargetId = KNBRZ24GFJ
AgentcoreGatewayLambdaStack.FunctionName = AgentcoreGatewayLambdaStack-ToolFn-AbCdEfGh
```

## Testing

The Gateway uses IAM authentication, so requests must be signed with SigV4. Replace `<GatewayUrl>` with the `GatewayUrl` value from the deploy output, and `<your-region>` with your deployment region (e.g. `us-east-1`).

1. List available tools:
```python
import boto3, json, urllib.request
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest

session = boto3.Session()
creds = session.get_credentials().get_frozen_credentials()
url = "<GatewayUrl>"
region = "<your-region>"

payload = json.dumps({"jsonrpc": "2.0", "method": "tools/list", "id": 1})
req = AWSRequest(method="POST", url=url, data=payload,
headers={"Content-Type": "application/json"})
SigV4Auth(creds, "bedrock-agentcore", region).add_auth(req)

http_req = urllib.request.Request(url, data=payload.encode(),
headers=dict(req.headers), method="POST")
resp = urllib.request.urlopen(http_req, timeout=30)
print(json.dumps(json.loads(resp.read()), indent=2))
```

2. Call a tool:
```python
payload = json.dumps({
"jsonrpc": "2.0", "method": "tools/call", "id": 2,
"params": {"name": "city-tools___get_weather",
"arguments": {"city": "Tokyo"}}
})
req = AWSRequest(method="POST", url=url, data=payload,
headers={"Content-Type": "application/json"})
SigV4Auth(creds, "bedrock-agentcore", region).add_auth(req)

http_req = urllib.request.Request(url, data=payload.encode(),
headers=dict(req.headers), method="POST")
resp = urllib.request.urlopen(http_req, timeout=30)
print(json.dumps(json.loads(resp.read()), indent=2))
```

## Cleanup

```bash
cdk destroy
```

----
Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
12 changes: 12 additions & 0 deletions agentcore-gateway-lambda-cdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { AgentcoreGatewayLambdaStack } from "../lib/agentcore-gateway-lambda-stack";

const app = new cdk.App();
new AgentcoreGatewayLambdaStack(app, "AgentcoreGatewayLambdaStack", {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
});
3 changes: 3 additions & 0 deletions agentcore-gateway-lambda-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "npx ts-node --prefer-ts-exts bin/app.ts"
}
50 changes: 50 additions & 0 deletions agentcore-gateway-lambda-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"title": "Amazon Bedrock AgentCore Gateway with Lambda tools",
"description": "Deploy an AgentCore Gateway with Lambda tool targets, exposing tools via the MCP protocol with IAM authentication.",
"language": "TypeScript",
"level": "300",
"framework": "AWS CDK",
"services": {
"from": "bedrockagentcore",
"to": "lambda"
},
"introBox": {
"headline": "How it works",
"text": [
"This pattern deploys an Amazon Bedrock AgentCore Gateway that exposes Lambda functions as MCP (Model Context Protocol) tools. The Gateway handles tool discovery (tools/list) and invocation (tools/call) with IAM-based authentication.",
"AgentCore Gateway provides a unified connectivity layer between AI agents and tools. Lambda functions are registered as targets with inline tool schemas, enabling any MCP-compatible client to discover and invoke them."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/agentcore-gateway-lambda-cdk",
"templateURL": "serverless-patterns/agentcore-gateway-lambda-cdk",
"projectFolder": "agentcore-gateway-lambda-cdk",
"templateFile": "lib/agentcore-gateway-lambda-stack.ts"
}
},
"resources": {
"bullets": [
{ "text": "Amazon Bedrock AgentCore Gateway", "link": "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-building.html" },
{ "text": "Model Context Protocol (MCP)", "link": "https://modelcontextprotocol.io/" },
{ "text": "Run custom MCP proxies on AgentCore Runtime", "link": "https://aws.amazon.com/blogs/machine-learning/run-custom-mcp-proxies-serverless-on-amazon-bedrock-agentcore-runtime/" }
]
},
"deploy": {
"text": ["cdk deploy"],
"file": "lib/agentcore-gateway-lambda-stack.ts"
},
"testing": {
"text": ["See the README for testing instructions."]
},
"cleanup": {
"text": ["cdk destroy"]
},
"authors": [
{
"name": "Nithin Chandran R",
"bio": "Technical Account Manager at AWS",
"linkedin": "nithin-chandran-r"
}
]
}
101 changes: 101 additions & 0 deletions agentcore-gateway-lambda-cdk/lib/agentcore-gateway-lambda-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as cdk from "aws-cdk-lib";
import * as agentcore from "aws-cdk-lib/aws-bedrockagentcore";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import { Construct } from "constructs";

export class AgentcoreGatewayLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Lambda tool handler
const toolFn = new lambda.Function(this, "ToolFn", {
runtime: lambda.Runtime.NODEJS_22_X,
handler: "index.handler",
code: lambda.Code.fromAsset("src/tool-lambda"),
timeout: cdk.Duration.seconds(30),
memorySize: 256,
description: "AgentCore Gateway Lambda tool target",
});

// Gateway IAM role
const gatewayRole = new iam.Role(this, "GatewayRole", {
assumedBy: new iam.ServicePrincipal("bedrock-agentcore.amazonaws.com"),
description: "Role for AgentCore Gateway to invoke Lambda tools",
});
toolFn.grantInvoke(gatewayRole);

// AgentCore Gateway (MCP protocol, IAM auth)
const gateway = new agentcore.CfnGateway(this, "Gateway", {
name: `tool-gateway-${cdk.Names.uniqueId(this).slice(-8).toLowerCase()}`,
protocolType: "MCP",
authorizerType: "AWS_IAM",
roleArn: gatewayRole.roleArn,
description: "MCP Gateway exposing Lambda tools",
});

// Gateway target — Lambda with inline tool definitions
const target = new agentcore.CfnGatewayTarget(this, "ToolTarget", {
gatewayIdentifier: gateway.attrGatewayIdentifier,
name: "city-tools",
description: "Weather and time tools backed by Lambda",
credentialProviderConfigurations: [
{
credentialProviderType: "GATEWAY_IAM_ROLE",
},
],
targetConfiguration: {
mcp: {
lambda: {
lambdaArn: toolFn.functionArn,
toolSchema: {
inlinePayload: [
{
name: "get_weather",
description: "Get current weather for a city",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "City name (e.g. Tokyo, London)",
},
},
required: ["city"],
},
},
{
name: "get_time",
description: "Get current UTC time for a city",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "City name",
},
},
required: ["city"],
},
},
],
},
},
},
},
});

new cdk.CfnOutput(this, "GatewayId", {
value: gateway.attrGatewayIdentifier,
});
new cdk.CfnOutput(this, "GatewayUrl", {
value: gateway.attrGatewayUrl,
});
new cdk.CfnOutput(this, "TargetId", {
value: target.attrTargetId,
});
new cdk.CfnOutput(this, "FunctionName", {
value: toolFn.functionName,
});
}
}
15 changes: 15 additions & 0 deletions agentcore-gateway-lambda-cdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "agentcore-gateway-lambda-cdk",
"version": "1.0.0",
"bin": { "app": "bin/app.ts" },
"scripts": { "build": "tsc", "cdk": "cdk" },
"dependencies": {
"aws-cdk-lib": "^2.180.0",
"constructs": "^10.4.2"
},
"devDependencies": {
"@types/node": "^22.0.0",
"ts-node": "^10.9.0",
"typescript": "~5.7.0"
}
}
29 changes: 29 additions & 0 deletions agentcore-gateway-lambda-cdk/src/tool-lambda/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// AgentCore Gateway Lambda tool target handler
// The Gateway routes MCP tool calls to this Lambda, passing only the arguments.
exports.handler = async (event) => {
const city = typeof event.city === "string"
? event.city.slice(0, 100).replace(/[^\w\s-]/g, "")
: "";

if (!city) {
return {
content: [{ type: "text", text: JSON.stringify({ error: "city is required" }) }],
isError: true,
};
}

return {
content: [
{
type: "text",
text: JSON.stringify({
city,
temperature: `${Math.floor(Math.random() * 30 + 5)}°C`,
condition: ["Sunny", "Cloudy", "Rainy", "Windy"][Math.floor(Math.random() * 4)],
humidity: `${Math.floor(Math.random() * 60 + 30)}%`,
utcTime: new Date().toISOString(),
}),
},
],
};
};
17 changes: 17 additions & 0 deletions agentcore-gateway-lambda-cdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["es2020"],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"esModuleInterop": true,
"outDir": "build",
"rootDir": "."
},
"exclude": ["node_modules", "cdk.out"]
}
Loading