AWS Builder Center

Giving an AI Agent a Tool Through AWS Lambda

Learn how to give an AI agent a tool through AWS Lambda. Write a small Lambda function, describe it with a tool schema, connect it directly or through AgentCore Gateway, and lock down permissions with least privilege.

Series: Amazon Bedrock (13 articles)

  1. …
  2. 10
    Giving an AI Agent a Tool Through AWS Lambda This article
Write one small Lambda function, describe it so the model knows when to use it, and connect it to your AgentCore agent, with permissions kept as narrow as possible.
In the last article we built an agent and gave it a tool written inside its own code. That works, but it ties the tool to that one agent. If you want several agents to share a capability, or you want to update, secure and monitor it separately, the tool belongs in its own service. On AWS, the natural home is a Lambda function.
In this article we'll build a small read-only tool in Lambda, look at two ways to connect it to an agent, and lock down who can call it.


What you need

  • The agent project from the previous article, or any AgentCore agent you can deploy
  • An AWS account where you can create Lambda functions and IAM policies
  • The AgentCore CLI installed, as in the previous article
  • About forty-five minutes
We'll use a deliberately boring example: looking up the status of an order by its ID. It only reads data, which makes it a safe first tool.

Step 1: Write the Lambda function

Create a function in the Lambda console with a current Python runtime. For this article it uses fake data held in the code. In real life you'd read from a database or an API.
Python
ORDERS = {
"1001": {"status": "shipped", "eta": "2 days"},
"1002": {"status": "processing", "eta": "5 days"},
}

def lambda_handler(event, context):
order_id = str(event.get("order_id", "")).strip()

# Validate the input. The model chose this value, so don't trust it.
if not order_id.isdigit() or len(order_id) > 12:
return {"found": False, "message": "Order IDs are numbers only."}

order = ORDERS.get(order_id)
if order is None:
return {"found": False, "message": "No order with that ID."}

return {"found": True, **order}
Two habits are already baked in. The function validates its input, because a model decides what to send and can get it wrong, and it returns a clear message instead of crashing when something doesn't match. Test it in the Lambda console with  {"order_id": "1001"}  before going further.
Set a short timeout, a few seconds, so a stuck call can't hang your agent.

Step 2: Describe the tool

The model never sees your code. It only sees a name, a description and the inputs the tool accepts. This description is the single biggest factor in whether the agent uses the tool correctly, so write it carefully.
Here's a tool definition in the format AgentCore Gateway expects for Lambda targets:
JSON
{
"name": "get_order_status",
"description": "Look up the shipping status and estimated delivery of an order by its order ID.",
"inputSchema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "The numeric order ID, for example 1001"
}
},
"required": ["order_id"]
}
}
Save it as  tools.json  in your project. The  name ,  description  and  inputSchema  fields are required. The  outputSchema  is optional. Check the AgentCore CLI reference for whether the schema file should hold a single definition or a list of them, and adjust to match.

Step 3: Connect it to your agent

There are two ways to do this. Start with the first, and move to the second when you need something shared and managed.

Option A: the agent calls Lambda directly

This is the simplest approach. Your agent has a Python tool that invokes the function with the AWS SDK.
Python
import json
import boto3
from strands import tool

lambda_client = boto3.client("lambda")

@tool
def get_order_status(order_id: str) -> str:
"""Look up the shipping status and estimated delivery of an order by its order ID."""
response = lambda_client.invoke(
FunctionName="order-status", # your function's name
Payload=json.dumps({"order_id": order_id}).encode(),
)
return response["Payload"].read().decode()
Add the function to your agent's tool list in  app/agent/main.py , following the pattern already there, as we did in the previous article. Then the agent's role needs permission to invoke that one function, which we'll cover in Step 4.
This option has very little to set up and is easy to understand. The downside is that every agent that wants this tool needs its own copy of the wrapper code and its own permission.

Option B: expose it through AgentCore Gateway

AgentCore Gateway is a managed layer that turns your functions and APIs into tools agents can discover and call, through the Model Context Protocol. It's the better fit when several agents will share tools, or when you want central control over who can call what.
With the AgentCore CLI, the steps are:
Bash
agentcore add gateway --name support-gateway

agentcore add gateway-target \
--name order-status \
--type lambda-function-arn \
--lambda-arn arn:aws:lambda:us-east-1:123456789012:function:order-status \
--tool-schema-file tools.json \
--gateway support-gateway

agentcore deploy
Some points from the AWS documentation that matter here:
  • The gateway's execution role needs  lambda:InvokeFunction  on your function. Without it, calls will fail.
  • The gateway needs inbound authorization, so that only callers you allow can use it. AWS's own demo disables it with an  --authorizer-type NONE  setting, which is fine for a throwaway test and not for anything real. Run  agentcore add gateway --help  or use the interactive wizard to choose a proper option.
  • Tool names get a prefix. The tool name your function receives is in the form  target___tool , with three underscores between them. If your function handles several tools, strip the prefix. The AWS docs include a short snippet for this using the context object. With one tool, you can ignore it.
  • Your function must return valid JSON, and its timeout should suit the work it does.
  • The event your function receives is simply the map of input properties, such as  {"order_id": "1001"} .
How your agent then finds and uses the gateway's tools depends on your template version and how the gateway is linked to your runtime. The AWS policy tutorial, for example, links them using a  --runtimes  option when adding the gateway. Follow the current AgentCore Gateway documentation for the exact wiring, and use  agentcore add gateway --help  to see the options your CLI version offers.

Step 4: Keep the permissions narrow

An agent that can invoke any function in your account is an accident waiting to happen. Give it exactly one.
For Option A, the agent's execution role needs a policy like this, with your own Region, account ID and function name:
JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:order-status"
}
]
}
For Option B, grant the same single-function permission to the gateway's execution role instead.
Also look at the Lambda function's own role. It should be able to write its logs and read only the data it needs, and nothing more. If the function reads from one table, allow that table, not every table.

Step 5: Test it end to end

Deploy and ask the agent a question that needs the tool:
Plain text
agentcore invoke --runtime agent --payload '{"prompt":"Where is order 1001?"}'
Then check three things:
  1. Did the agent call the tool? Look at the trace to see the tool call and its input.
  1. Did it use the result correctly? The answer should match your data.
  1. What happens on bad input? Ask about an order that doesn't exist, and about something unrelated like the weather. The agent should say it can't find the order and shouldn't invent one.
Check the function's logs in CloudWatch as well, to confirm what it received.

Design tips for good tools

  • Make tools small and specific. "Get order status" is easier for a model to use correctly than "manage orders."
  • Describe them well. Say what the tool does and what each input means. Include an example value.
  • Start read-only. Add tools that change things, such as refunds or cancellations, only with strict limits and, where it matters, a person approving the action.
  • Validate every input inside the function.
  • Return short, clear results. Long or messy output costs tokens and confuses the model.
  • Never return secrets. Anything a tool returns can end up in the model's answer.

Common problems

The agent never calls the tool. The description is probably unclear. Rewrite it to say exactly when the tool should be used.
Access denied. Check the role that's doing the invoking, and confirm the function ARN in the policy matches exactly.
The function receives unexpected input. Log the event. With Gateway, remember the event holds only the input properties, and the tool name arrives in the context object.
Timeouts. Raise the function timeout a little, or make the function faster. Keep agent-facing tools quick.
A malformed response error from the gateway. Make sure the function returns valid JSON.

Clean up

When you're done, delete the Lambda function and any test data. If you created a gateway, remove it through the CLI and redeploy, as in the previous article, and then check your account for leftovers.

Where we go next

We now have an agent with a real tool. In the next article we'll tighten security properly, with IAM least privilege in practice: the exact policies an agent needs and why.
AgentCore Gateway features, CLI options, schema formats and pricing change often. Check the current Amazon Bedrock AgentCore documentation before you follow any step here.

Series: Amazon Bedrock (13 articles)

  1. …
  2. 10
    Giving an AI Agent a Tool Through AWS Lambda This article
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article