-
Notifications
You must be signed in to change notification settings - Fork 1k
New serverless pattern - Strands Agent on Lambda with AgentCore Smithy Bedrock Target #3221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| *-deps/ | ||
| *-package/ | ||
| *.zip | ||
| .aws-sam/ | ||
| samconfig.toml | ||
| .kiro/ | ||
| .venv/ | ||
| venv/ | ||
| __pycache__/ | ||
| *.pyc | ||
| *.so | ||
| *.egg-info/ | ||
| .pytest_cache/ | ||
| .hypothesis/ | ||
| .DS_Store |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # ============================================================ | ||
| # AgentCore Smithy Bedrock - SAM Makefile build target | ||
| # ============================================================ | ||
| # Used by `sam build` via the AgentLambdaFunction resource's | ||
| # `Metadata: BuildMethod: makefile`. This replaces the previous | ||
| # manual packaging in deploy.sh and requires NO Docker. | ||
| # | ||
| # SAM invokes `make build-AgentLambdaFunction` with ARTIFACTS_DIR | ||
| # set to the directory SAM expects the built artifacts in. | ||
| # | ||
| # Two-step install reproduces the original packaging behaviour: | ||
| # 1. Install all dependencies as manylinux2014 x86_64 wheels | ||
| # 2. Re-install pure-Python packages with --no-deps so nothing | ||
| # is skipped by the binary-only constraint | ||
| # ============================================================ | ||
|
|
||
| build-AgentLambdaFunction: | ||
| pip3 install \ | ||
| --target "$(ARTIFACTS_DIR)" \ | ||
| --platform manylinux2014_x86_64 \ | ||
| --python-version 3.12 \ | ||
| --only-binary=:all: \ | ||
| -r requirements.txt | ||
| pip3 install \ | ||
| --target "$(ARTIFACTS_DIR)" \ | ||
| --platform manylinux2014_x86_64 \ | ||
| --python-version 3.12 \ | ||
| --only-binary=:all: \ | ||
| --no-deps \ | ||
| requests urllib3 charset-normalizer idna certifi PyJWT cryptography cffi | ||
| cp -r src "$(ARTIFACTS_DIR)/" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| # Strands Agent on Lambda with AgentCore Smithy Bedrock Target | ||
|
|
||
| A serverless AI agent that uses AWS Bedrock AgentCore Gateway with a Smithy model target to call Bedrock Runtime. The agent (Claude Sonnet 4.6) calls another model (Claude Haiku 4.5) as a tool via the gateway's Converse API. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AWS Bedrock AgentCore --> Amazon Bedrock AgentCore
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bedrock Runtime --> Amazon Bedrock Runtime |
||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add the following disclaimer: 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 for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. |
||
| ## Architecture | ||
|
|
||
|  | ||
|
|
||
| ``` | ||
| User → test.sh → Agent Lambda ──────────────→ AgentCore Gateway (MCP) ──────────→ Bedrock Runtime | ||
| │ │ │ | ||
| Strands Agent CUSTOM_JWT Auth GATEWAY_IAM_ROLE | ||
| + BedrockModel (Cognito JWKS) (IAM role signs | ||
| (Sonnet 4.6) + MCP Protocol requests — no | ||
| + MCPClient + Tool Discovery API key needed) | ||
| │ + SmithyModel target │ | ||
| Cognito JWT (loaded from S3) Converse API call | ||
| validated → Claude Haiku 4.5 | ||
| ``` | ||
|
|
||
| ## How It Works | ||
|
|
||
| 1. User authenticates with Cognito and invokes the Lambda with a natural language prompt | ||
| 2. The Lambda runs a Strands SDK agent with Claude Sonnet 4.6 as the LLM | ||
| 3. The agent connects to AgentCore Gateway via MCP and discovers available tools | ||
| 4. The gateway reads the official Bedrock Runtime Smithy model (from S3) and exposes operations as MCP tools | ||
| 5. Sonnet decides to call the Converse tool, passing the user's prompt to Claude Haiku 4.5 | ||
| 6. The gateway assumes its IAM execution role, signs the request, and calls Bedrock Runtime | ||
| 7. Haiku's response flows back through the gateway → MCP → agent → user | ||
|
|
||
| ## Deploy | ||
|
|
||
| ### Step 1: Prerequisites | ||
|
|
||
| - AWS CLI v2 | ||
| - AWS SAM CLI | ||
| - Python 3.12+ | ||
| - pip3 | ||
| - `make` (used by the SAM Makefile build — no Docker required) | ||
| - AWS account with Bedrock and AgentCore enabled in `us-east-1` | ||
| - Bedrock model access enabled for Claude Sonnet 4.6 and Claude Haiku 4.5 | ||
|
|
||
| ### Step 2: Open a Terminal | ||
|
|
||
| Open a terminal on your machine and navigate to where you want to clone the project. | ||
|
|
||
| ### Step 3: Clone the Repository | ||
|
|
||
| ```bash | ||
| git clone https://github.com/aws-samples/serverless-patterns | ||
| cd serverless-patterns/strands-agentcore-smithy | ||
| ``` | ||
|
|
||
| ### Step 4: Deploy | ||
|
|
||
| One command deploys everything: | ||
|
|
||
| ```bash | ||
| bash scripts/deploy.sh | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deploy failed with the following issue: `strands-agentcore-smithy % bash scripts/deploy.shDeploying stack: agentcore-smithy-bedrock in region: us-east-1
SAM CLI update available (1.163.0); (1.162.1 installed)
|
||
| ``` | ||
|
|
||
| This will: | ||
| 1. Validate the SAM template (`sam validate`) | ||
| 2. Download the official Bedrock Runtime Smithy model and upload to S3 | ||
| 3. Build the Lambda with `sam build` (Makefile build — two-step pip3 install, no Docker) | ||
| 4. Deploy the stack with `sam deploy` (Gateway, Cognito, Lambda, IAM roles) | ||
| 5. Create a Cognito test user | ||
| 6. Generate `scripts/test.sh` with baked-in config values | ||
|
|
||
| ## Test | ||
|
|
||
| After deployment, test with: | ||
|
|
||
| ```bash | ||
| ./scripts/test.sh "Ask Haiku to write a short poem about the Beatles" | ||
| ``` | ||
|
|
||
| Or use the default prompt: | ||
|
|
||
| ```bash | ||
| ./scripts/test.sh | ||
| ``` | ||
|
|
||
| ## Project Structure | ||
|
|
||
| ``` | ||
| ├── infrastructure/ | ||
| │ ├── template.yaml # SAM template (all AWS resources) | ||
| │ └── bedrock-runtime-2023-09-30.json # Official Smithy model (for tests) | ||
| ├── Makefile # SAM Makefile build (no Docker) | ||
| ├── scripts/ | ||
| │ ├── deploy.sh # Full deployment script (SAM) | ||
| │ └── test.sh # Generated by deploy.sh | ||
| ├── src/ | ||
| │ ├── agent/ | ||
| │ │ ├── handler.py # Lambda entry point | ||
| │ │ ├── agent_processor.py # Strands Agent + MCP lifecycle | ||
| │ │ └── strands_client.py # MCP client + Bedrock model factories | ||
| │ └── shared/ | ||
| │ ├── models.py # UserContext, AgentRequest, AgentResponse | ||
| │ ├── jwt_utils.py # JWT validation (Cognito ID tokens) | ||
| │ ├── error_utils.py # Error response formatting | ||
| │ └── logging_utils.py # Structured logging | ||
| ├── tests/ | ||
| │ ├── unit/ # Unit tests | ||
| │ └── property/ # Property-based tests (Hypothesis) | ||
| ├── requirements.txt # Lambda runtime dependencies | ||
| └── README.md | ||
| ``` | ||
|
|
||
| ## Changing the Outer Agent Model | ||
|
|
||
| The outer agent model is set in `src/agent/strands_client.py`: | ||
|
|
||
| ```python | ||
| # Default Bedrock model ID for Claude Sonnet 4.6 | ||
| DEFAULT_MODEL_ID = "us.anthropic.claude-sonnet-4-6" | ||
| ``` | ||
|
|
||
| To use a different model, update `DEFAULT_MODEL_ID` to any Bedrock cross-region inference profile ID you have access to. For example: | ||
|
|
||
| ```python | ||
| DEFAULT_MODEL_ID = "us.anthropic.claude-3-5-sonnet-20241022-v2:0" | ||
| ``` | ||
|
|
||
| Make sure the model is enabled in your AWS account under **Bedrock → Model access** before deploying. | ||
|
|
||
| > **Note:** The inner model (the one the agent calls as a tool) is separate — it's configured in the `SYSTEM_PROMPT` string in `src/agent/agent_processor.py`. See [Changing the Inner Model](#changing-the-inner-model) and [Key Design Decisions](#key-design-decisions) for more detail. | ||
|
|
||
| ## Changing the Inner Model | ||
|
|
||
| The inner model is the one the outer agent calls *as a tool* via the AgentCore Gateway. It's configured inside the `SYSTEM_PROMPT` string in `src/agent/agent_processor.py`: | ||
|
|
||
| ```python | ||
| SYSTEM_PROMPT = """You have access to Bedrock Runtime tools via MCP. Use the Converse tool (not InvokeModel) to call another model. | ||
|
|
||
| When using bedrock-runtime-target___Converse: | ||
| - Set modelId to: anthropic.claude-haiku-4-5-20251001-v1:0 | ||
| ... | ||
| ``` | ||
|
|
||
| To use a different model, replace the `modelId` value in that instruction: | ||
|
|
||
| ```python | ||
| - Set modelId to: anthropic.claude-haiku-4-5-20251001-v1:0 | ||
| ``` | ||
|
|
||
| A few things to keep in mind: | ||
|
|
||
| - The model ID here is passed as a parameter in the MCP tool call, not used directly by the Lambda — so it must be a valid Bedrock `modelId` (not a cross-region inference profile). | ||
| - Make sure the model supports the **Converse API** (most Claude models do, but check the [Bedrock docs](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html)). | ||
| - Enable the model in your AWS account under **Bedrock → Model access** before deploying. | ||
|
|
||
| ## Key Design Decisions | ||
|
|
||
| ### Official Smithy Model (not custom) | ||
| Custom Smithy models don't work for AWS service targets — the gateway needs the full endpoint resolution metadata (`aws.api#service` trait, partition data, FIPS/DualStack logic) that only official models have. The deploy script downloads the official model from the [AWS API Models repo](https://github.com/aws/api-models-aws). | ||
|
|
||
| ### Converse API (not InvokeModel) | ||
| The `InvokeModel` operation uses a `Blob` with `httpPayload` trait for the request body, which doesn't map to MCP tool input parameters. `Converse` has structured `messages` and `modelId` fields that map cleanly. | ||
|
|
||
| ### GATEWAY_IAM_ROLE (not API_KEY) | ||
| Since the target is an AWS service (Bedrock Runtime), the gateway uses its IAM execution role to sign requests. No API keys, no Secrets Manager secrets, no credential provider CLI commands. | ||
|
|
||
| ### Agent code is target-agnostic | ||
| All code in `src/agent/` and `src/shared/` connects to the gateway via MCP and doesn't reference Bedrock, DynamoDB, or any specific target. The same code works with any gateway target type. | ||
|
|
||
| ## Teardown | ||
|
|
||
| ```bash | ||
| aws cloudformation delete-stack --stack-name agentcore-smithy-bedrock --region us-east-1 | ||
| aws s3 rb s3://agentcore-smithy-bedrock-smithy-models --force --region us-east-1 | ||
| ``` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Lambda function's CloudWatch Logs log group is not deleted automatically. Remove it to avoid ongoing storage charges: |
||
|
|
||
| ## Run Tests | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we testing again after teardown? |
||
|
|
||
| ```bash | ||
| python -m pytest tests/ -v | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
| SPDX-License-Identifier: MIT-0 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| { | ||
| "title": "Strands Agent on Lambda with AgentCore Smithy Bedrock Target", | ||
| "description": "Strands SDK agent on Lambda calls Bedrock Runtime via AgentCore Gateway using a Smithy model target, with Cognito JWT auth and IAM role signing.", | ||
| "language": "Python", | ||
| "level": "300", | ||
| "framework": "SAM", | ||
| "introBox": { | ||
| "headline": "How it works", | ||
| "text": [ | ||
| "The user authenticates with Amazon Cognito and receives a JWT token.", | ||
| "The JWT is passed to an Agent Lambda which uses the Strands Agents SDK to create an AI agent backed by Amazon Bedrock (Claude Sonnet 4.6 cross-region inference profile).", | ||
| "The Strands Agent connects to an AgentCore Gateway MCP endpoint, dynamically discovering available Bedrock Runtime operations via the MCP tools/list protocol.", | ||
| "The AgentCore Gateway validates the JWT token using a CUSTOM_JWT authorizer backed by Cognito, then routes MCP tool calls to the Smithy model target.", | ||
| "The gateway target uses the official Bedrock Runtime Smithy model (loaded from S3) to expose Bedrock Runtime operations as MCP tools.", | ||
| "The gateway uses its IAM execution role (GATEWAY_IAM_ROLE) to sign requests to Bedrock Runtime — no API keys or Secrets Manager secrets are required.", | ||
| "The outer agent (Claude Sonnet 4.6) calls the Converse tool, which invokes Claude Haiku 4.5 as the inner model to generate the response.", | ||
| "The Strands SDK handles the full agentic loop: tool discovery, Claude tool selection, MCP tool execution, and response formatting — all in a single agent() call." | ||
| ] | ||
| }, | ||
| "gitHub": { | ||
| "template": { | ||
| "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/strands-agentcore-smithy", | ||
| "templateURL": "serverless-patterns/strands-agentcore-smithy", | ||
| "projectFolder": "strands-agentcore-smithy", | ||
| "templateFile": "infrastructure/template.yaml" | ||
| } | ||
| }, | ||
| "resources": { | ||
| "bullets": [ | ||
| { | ||
| "text": "Strands Agents SDK", | ||
| "link": "https://github.com/strands-agents/sdk-python" | ||
| }, | ||
| { | ||
| "text": "Amazon Bedrock AgentCore Gateway", | ||
| "link": "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html" | ||
| }, | ||
| { | ||
| "text": "AgentCore Gateway Smithy Model Target", | ||
| "link": "https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-building-smithy-targets.html" | ||
| }, | ||
| { | ||
| "text": "Model Context Protocol (MCP)", | ||
| "link": "https://modelcontextprotocol.io/" | ||
| }, | ||
| { | ||
| "text": "Amazon Cognito JWT Authentication", | ||
| "link": "https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html" | ||
| }, | ||
| { | ||
| "text": "Amazon Bedrock Converse API", | ||
| "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html" | ||
| }, | ||
| { | ||
| "text": "Amazon Bedrock Cross-Region Inference", | ||
| "link": "https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html" | ||
| } | ||
| ] | ||
| }, | ||
| "deploy": { | ||
| "text": [ | ||
| "bash scripts/deploy.sh" | ||
| ] | ||
| }, | ||
| "testing": { | ||
| "text": [ | ||
| "./scripts/test.sh", | ||
| "./scripts/test.sh 'Ask Haiku to write a short poem about the Beatles'" | ||
| ] | ||
| }, | ||
| "cleanup": { | ||
| "text": [ | ||
| "aws cloudformation delete-stack --stack-name agentcore-smithy-bedrock --region us-east-1", | ||
| "aws s3 rb s3://agentcore-smithy-bedrock-smithy-models --force --region us-east-1" | ||
| ] | ||
| }, | ||
| "authors": [ | ||
| { | ||
| "name": "Mike Hume", | ||
| "image": "https://serverlessland.com/assets/images/contributors/mike-hume.jpg", | ||
| "bio": "AWS Senior Solutions Architect & UKPS Serverless Lead.", | ||
| "linkedin": "michael-hume-4663bb64", | ||
| "twitter": "" | ||
| } | ||
| ] | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lambda --> AWS Lambda
AgentCore --> Amazon Bedrock AgentCore
Bedrock --> Amazon Bedrock
First mention should be full service name