Skip to content

jritsema/aws-lambda-mcp-tools

Repository files navigation

aws-lambda-mcp-tools

Build MCP tools for Amazon Bedrock AgentCore Gateway without the boilerplate.

Normally, exposing a Lambda function as an MCP gateway tool means wiring up tool schema JSON, uploading it to S3, registering a gateway target, handling the AgentCore context format, and routing multi-tool invocations — all before you write a single line of business logic.

This template eliminates that overhead. You write a decorated Python function, and make deploy handles the rest: schema generation, S3 upload, gateway registration, and Lambda deployment in one command.

From In-Process Tool to Out-of-Process MCP

This project uses the same @tool decorator from Strands Agents. If you already have tools running in-process on a Strands agent in AgentCore Runtime, you can copy them directly into this project as-is and deploy them as an out-of-process Lambda behind AgentCore Gateway, exposed via MCP - same decorator, same docstring, same code:

from strands import tool

@tool
def get_weather(location: str) -> dict:
    """Get the current weather for a location.

    Args:
        location: City name or zip code
    """
    return {"location": location, "temperature": "72F", "condition": "sunny"}

No schema files to write. The tool name, description, parameter types, and descriptions are all inferred from your code and docstring.

Quick Start

Setup

These commands setup a python virtual environment and install dependencies. This is optional if you want to do it another way.

make init
make install

Test locally

make start

Deploy

# First time: creates Lambda + registers as gateway target
make create function=my-tools gateway_id=gw-abc123

# Test the deployed function
make invoke function=my-tools tool=get_weather

# After code changes: redeploys + updates gateway target
make deploy function=my-tools gateway_id=gw-abc123

Clean up

make delete function=my-tools gateway_id=gw-abc123

Adding a Tool

Edit tools.py and add a decorated function:

@tool
def search_products(query: str, limit: int = 10) -> dict:
    """Search the product catalog.

    Args:
        query: Search query
        limit: Max results to return
    """
    results = do_search(query, limit)
    return {"results": results}

That's it. The tool name, description, parameter types, required/optional, and parameter descriptions are all inferred from the function. Run make deploy to push the changes.

Architecture

tools.py (define tools here — same @tool decorator as Strands)
    |
    ├──> lambda_function.py   (routes tool calls at runtime)
    └──> registry.py          (collects @tool functions)
              |
    deploy.py (boto3 CLI: build, deploy, schema, gateway registration)
              |
              └──> tools.json → S3 → AgentCore Gateway
  • One Lambda handles multiple tools, routed by tool name from the gateway context
  • Tool schemas are auto-generated from your function signatures and docstrings
  • deploy.py automates the full lifecycle (role, image, Lambda, schema, gateway target); the Makefile is a thin set of aliases over it

Command Reference

The Makefile is a thin wrapper over deploy.py (a boto3 CLI). Use either.

  init              create a python virtualenv
  install           install dependencies
  start             run the tools locally with a fake gateway context
  start-container   run the packaged image locally
  schema            generate tools.json from the @tool functions
  create            create lambda + register gateway target - make create function=X gateway_id=G
  deploy            update code + gateway target        - make deploy function=X gateway_id=G
  delete            delete lambda + role + gateway target - make delete function=X gateway_id=G
  invoke            invoke a tool on the lambda          - make invoke function=X [tool=get_weather]

Equivalent direct calls: python deploy.py create --function X --gateway-id G, etc. Run python deploy.py --help for all options.

Parameters

Parameter Default Description
arch arm64 arm64 or x86_64
function (required) Lambda function name
gateway_id (required for create/deploy) AgentCore Gateway ID
target (function name) Gateway target name
role <function>-role Execution role name
packaging container container or zip
tool get_weather Tool name for make invoke

What happens behind the scenes

make create / make deploy:

  1. Ensure a per-function IAM role (<function>-role) with basic execution + your policy.json (if present)
  2. Build & push the container image (or build the zip) and create/update the Lambda
  3. Apply env.json (if present) to the Lambda, and grant the gateway invoke permission
  4. Generate tools.json, upload it to s3://agentcore-tools-{account}-{region}/{function}/tools.json
  5. Register or update the gateway target (waiting for the Lambda to be ready)

Configuration: environment variables & permissions

Two optional files let a tool declare what it needs. Both support ${VAR} expansion from the current shell environment, so dynamic values (ARNs resolved by direnv/.envrc, CI variables, etc.) flow straight through. Deploy fails loudly if a referenced variable is unset, so you never ship a literal ${...}.

env.json — Lambda environment variables (AWS format). Applied on every create/deploy. See env.json.example:

{
  "Variables": {
    "LOG_LEVEL": "INFO",
    "TABLE_ARN": "${MY_TABLE_ARN}"
  }
}

policy.json — an IAM policy document attached inline to the function's role, so each tool grants exactly the AWS access it needs. See policy.json.example:

{
  "Version": "2012-10-17",
  "Statement": [
    { "Effect": "Allow",
      "Action": ["dynamodb:GetItem", "dynamodb:Query"],
      "Resource": "${MY_TABLE_ARN}" }
  ]
}

Delete either file to drop the corresponding configuration on the next deploy (the inline policy is removed automatically when policy.json is absent).

Requirement Notes
AWS_REGION Region for deployment (defaults to us-east-1)
AWS credentials Via environment, profile, or IAM role
Docker Required for the default container packaging

Local Testing

Python (fastest iteration)

make start

Runs main.py which invokes each tool with test parameters using a fake AgentCore context.

Container (matches Lambda runtime)

make start-container
# In another terminal
curl -X POST "http://localhost:8080/2015-03-31/functions/function/invocations" \
  -d '{"location": "Seattle, WA"}'

Note: Container invocation doesn't pass client context, so the handler will raise an error about missing context. For full local testing with routing, use make start.

Notes

  • Gateway expects raw JSON responses (not {"statusCode": 200, ...} envelope)

About

Build MCP tools for Amazon Bedrock AgentCore Gateway without the boilerplate

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages