使用 Gateway

AgentCore Gateway 是一个托管的 MCP 兼容代理,使多个 agent 能够通过统一端点发现、认证和调用工具。

我们将从直接在 agent 代码中定义工具迁移到通过 Gateway 管理的集中式企业级工具。

为什么使用 AgentCore Gateway

image-20260401142521666

优秀的 agent 需要能够充分利用专有和第三方 API 及数据的工具。然而,构建、保护和扩展 agent 工具非常困难,成为客户从原型转向生产的重大障碍。

在大多数组织中,有价值的业务逻辑已经存在——作为 Lambda 函数、REST API 或内部服务——但它们并非为 AI agent 而构建。AgentCore Gateway 让我们可以将这些现有资源"MCPify”,将其转变为任何 agent 都可以通过标准 MCP 协议发现和使用的工具,而无需修改原始代码。

AgentCore Gateway 支持多种目标类型:

  • AWS Lambda functions — 将现有 serverless 函数包装为 MCP 工具
  • Amazon API Gateway REST API stages — 直接暴露托管的 REST API
  • OpenAPI schema targets — 指向任何 OpenAPI 描述的 HTTP 服务
  • Smithy model targets — 使用 Smithy 服务模型作为工具定义
  • MCP server targets — 代理并保护现有的 MCP 兼容端点

AgentCore Gateway 提供:

  • 简化工具开发和集成 — 将现有 Lambda 函数、API 和服务转变为 agent 就绪的 MCP 工具,无需重写代码
  • 通过统一访问加速 agent 开发 — 单个 MCP 端点让每个 agent 都能访问所有已注册的工具
  • 通过智能工具发现实现可信扩展 — Agent 通过 MCP 协议自动发现可用工具及其功能
  • 全面的身份验证 — 基于 JWT 的入站认证,以及用于上游服务的出站凭证(API 密钥、OAuth)
  • 框架兼容性 — 适用于任何 MCP 兼容的 agent 框架(Strands、LangChain、CrewAI 等)
  • Serverless 基础设施 — 完全托管,无需配置或扩展服务器

架构变更

原先(本地工具):
  Agent → [get_return_policy(), get_product_info()](在代码中)
  Agent → [Exa AI MCP](直接连接)

目标(Gateway + 本地):
  Agent → Gateway → [Lambda: check_warranty]
  Agent → [get_return_policy(), get_product_info()](保留本地)
  Agent → [Exa AI MCP](保留为直接 MCP 客户端)

AWS Lambda 函数

我们使用一个 Lambda 函数(workshop-warranty-check),它模拟由我们组织中另一个团队维护的企业级 warranty check API。这是一个常见的现实场景:有用的业务逻辑已经作为 Lambda 函数存在,但它并非为 AI agent 设计。AgentCore Gateway 让我们可以对这些现有函数进行 MCPify,使其可被任何 agent 发现和调用, 而无需修改原始 Lambda 代码。

函数代码如下,这是一个针对硬编码 warranty 数据库的简单查询:

import json

WARRANTIES = {
    "PROD-001": {"product": "Wireless Headphones", "warranty_months": 12, "status": "active", "expires": "2027-03-01"},
    "PROD-002": {"product": "Smart Watch", "warranty_months": 24, "status": "active", "expires": "2028-01-15"},
    "PROD-003": {"product": "Laptop Stand", "warranty_months": 6, "status": "expired", "expires": "2026-01-01"},
    "PROD-004": {"product": "USB-C Hub", "warranty_months": 12, "status": "active", "expires": "2027-06-20"},
}

def handler(event, context):
    product_id = event.get("product_id", "").upper()
    if product_id in WARRANTIES:
        return {"statusCode": 200, "body": json.dumps(WARRANTIES[product_id])}
    return {"statusCode": 404, "body": json.dumps({"error": f"No warranty found for {product_id}"})}

Gateway 如何调用 Lambda: AgentCore Gateway 直接在 Lambda event 中传递工具参数(而不是在 event["body"] 中)。工具名称可在 context.client_context.custom["bedrockAgentCoreToolName"] 中获取,格式为 <TargetName>___<tool_name>。由于每个 Lambda 只有一个工具,我们只需从 event 中读取参数。

现在从 Parameter Store 获取 Lambda ARN(cloudformation stack创建的):

WARRANTY_LAMBDA_ARN=$(aws ssm get-parameter \
  --name /app/customersupport/agentcore/warranty_check_lambda_arn \
  --query 'Parameter.Value' --output text)

echo "Lambda ARN: $WARRANTY_LAMBDA_ARN"

image-20260401142708539

创建工具 Schema

为了让 AI agent 使用工具,它需要了解工具的功能、期望的参数以及返回内容——所有这些都用自然语言描述。这正是 MCP 协议提供的:一种让工具宣传其功能的标准方式,使 agent 能够推理何时以及如何调用它们。

然而,Lambda 函数不携带这些元数据。Lambda 只是带有输入事件和输出的代码——没有内置方式让 agent 知道 workshop-warranty-check 的功能或需要传递什么参数。

这就是 AgentCore Gateway 弥合差距的地方。它将我们的 Lambda(或 API,或 MCP 服务器)包装在 MCP 兼容端点后面,但仍然需要我们提供工具描述——名称、自然语言描述和输入 schema。这就是我们在这里创建的内容。

创建 schema 文件:

mkdir -p app/CustomerSupport/tool
touch app/CustomerSupport/tool/__init__.py
touch app/CustomerSupport/tool/warranty_schema.json

在编辑器中打开 app/CustomerSupport/tool/warranty_schema.json 并添加以下内容:

[
  {
    "name": "check_warranty",
    "description": "Check the warranty status of a product by its product ID (e.g. PROD-001). Returns warranty duration, status (active/expired), and expiration date.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "product_id": {
          "type": "string",
          "description": "The product ID to check warranty for (e.g. PROD-001)"
        }
      },
      "required": ["product_id"]
    }
  }
]

注意 description 字段如何使用自然语言——这是 agent 读取以决定是否调用工具以及如何填写参数的内容。没有这些,Gateway 将无法向 agent 呈现有意义的 Lambda 工具。

通过 CLI 添加 Gateway 和 Target

现在我们将所有内容连接在一起。我们将创建一个 Gateway(托管的 MCP 端点)并将 Lambda 函数注册为目标。Lambda 只是众多目标类型之一,我们同样可以轻松地将 API Gateway REST API stage、OpenAPI 端点、Smithy 模型、现有 MCP 服务器或内置提供商模板添加到同一个 Gateway。

在terminal中:

# 创建链接到现有 CustomerSupport agent 的 gateway
agentcore add gateway --name my-gateway --runtimes CustomerSupport

# 将 warranty check Lambda 添加为目标(使用之前从 Parameter Store 获取的 ARN)
agentcore add gateway-target \
  --type lambda-function-arn \
  --name WarrantyCheck \
  --lambda-arn $WARRANTY_LAMBDA_ARN \
  --tool-schema-file app/CustomerSupport/tool/warranty_schema.json \
  --gateway my-gateway

我们应该看到:

Added gateway 'my-gateway'
Added gateway target 'WarrantyCheck'

更新 Agent 以使用 Gateway 工具

首先,在编辑器中更新 app/CustomerSupport/mcp_client/client.py 以添加 gateway MCP 客户端:

这在现有 Exa AI 客户端旁边添加了一个新的 get_gateway_mcp_client() 函数。它从 AGENTCORE_GATEWAY_MY_GATEWAY_URL 环境变量(部署后由 AgentCore Runtime 注入)读取 gateway URL,并创建一个连接到我们的 gateway 端点的 MCP 客户端。如果 URL 未设置(例如,在本地开发期间),它会优雅地返回 None

import os
import logging
from mcp.client.streamable_http import streamablehttp_client
from strands.tools.mcp.mcp_client import MCPClient

logger = logging.getLogger(__name__)

# ExaAI MCP endpoint for web search
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"


def get_streamable_http_mcp_client() -> MCPClient:
    """Returns an MCP Client for Exa AI web search"""
    return MCPClient(lambda: streamablehttp_client(EXAMPLE_MCP_ENDPOINT))


def get_gateway_mcp_client() -> MCPClient | None:
    """Returns an MCP Client for AgentCore Gateway, if configured"""
    url = os.environ.get("AGENTCORE_GATEWAY_MY_GATEWAY_URL")
    if not url:
        logger.warning("Gateway URL not set — gateway tools unavailable")
        return None
    return MCPClient(lambda: streamablehttp_client(url))

然后更新 app/CustomerSupport/main.py 以导入并使用 gateway 客户端。关键变化是将 get_gateway_mcp_client 添加到 MCP 客户端列表中:

唯一的变化是导入 get_gateway_mcp_client 并将其添加到 mcp_clients 列表中。这使我们的 agent 能够访问 gateway 工具(如 order lookup Lambda),同时保留现有的 Exa AI 网络搜索和本地工具。Agent 自动从两个 MCP 客户端发现所有可用工具。

from strands import Agent, tool
from bedrock_agentcore.runtime import BedrockAgentCoreApp
from model.load import load_model
from mcp_client.client import get_streamable_http_mcp_client, get_gateway_mcp_client
from memory.session import get_memory_session_manager

app = BedrockAgentCoreApp()
log = app.logger

# MCP clients: Exa AI (web search) + AgentCore Gateway (Lambda tools)
mcp_clients = [get_streamable_http_mcp_client(), get_gateway_mcp_client()]

# --- Customer Support Tools ---

RETURN_POLICIES = {
    "electronics": {"window": "30 days", "condition": "Original packaging required, must be unused or defective", "refund": "Full refund to original payment method"},
    "accessories": {"window": "14 days", "condition": "Must be in original packaging, unused", "refund": "Store credit or exchange"},
    "audio": {"window": "30 days", "condition": "Defective items only after 15 days", "refund": "Full refund within 15 days, replacement after"},
}

PRODUCTS = {
    "PROD-001": {"name": "Wireless Headphones", "price": 79.99, "category": "audio", "description": "Noise-cancelling Bluetooth headphones with 30h battery life", "warranty_months": 12},
    "PROD-002": {"name": "Smart Watch", "price": 249.99, "category": "electronics", "description": "Fitness tracker with heart rate monitor, GPS, and 5-day battery", "warranty_months": 24},
    "PROD-003": {"name": "Laptop Stand", "price": 39.99, "category": "accessories", "description": "Adjustable aluminum laptop stand for ergonomic desk setup", "warranty_months": 6},
    "PROD-004": {"name": "USB-C Hub", "price": 54.99, "category": "accessories", "description": "7-in-1 USB-C hub with HDMI, USB-A, SD card reader, and ethernet", "warranty_months": 12},
    "PROD-005": {"name": "Mechanical Keyboard", "price": 129.99, "category": "electronics", "description": "RGB mechanical keyboard with Cherry MX switches", "warranty_months": 24},
}

@tool
def get_return_policy(product_category: str) -> str:
    """Get return policy information for a specific product category.

    Args:
        product_category: Product category (e.g., 'electronics', 'accessories', 'audio')

    Returns:
        Formatted return policy details including timeframes and conditions
    """
    category = product_category.lower()
    if category in RETURN_POLICIES:
        policy = RETURN_POLICIES[category]
        return f"Return policy for {category}: Window: {policy['window']}, Condition: {policy['condition']}, Refund: {policy['refund']}"
    return f"No specific return policy found for '{product_category}'. Please contact support for details."

@tool
def get_product_info(query: str) -> str:
    """Search for product information by name, ID, or keyword.

    Args:
        query: Product name, ID (e.g., 'PROD-001'), or search keyword

    Returns:
        Product details including name, price, category, and description
    """
    query_lower = query.lower()
    # Search by ID
    if query.upper() in PRODUCTS:
        p = PRODUCTS[query.upper()]
        return f"{p['name']} ({query.upper()}): ${p['price']}, Category: {p['category']}, {p['description']}, Warranty: {p['warranty_months']} months"
    # Search by keyword
    results = [f"{pid}: {p['name']} - ${p['price']} - {p['description']}" for pid, p in PRODUCTS.items()
               if query_lower in p['name'].lower() or query_lower in p['description'].lower() or query_lower in p['category'].lower()]
    if results:
        return "Found products:\n" + "\n".join(results)
    return f"No products found matching '{query}'."

tools = [get_return_policy, get_product_info]

# Add MCP client (Exa AI web search) to tools
for mcp_client in mcp_clients:
    if mcp_client:
        tools.append(mcp_client)

# Agent factory — creates one agent per session/user combination
def agent_factory():
    cache = {}
    def get_or_create_agent(session_id, user_id):
        key = f"{session_id}/{user_id}"
        if key not in cache:
            cache[key] = Agent(
                model=load_model(),
                session_manager=get_memory_session_manager(session_id, user_id),
                system_prompt="""You are a helpful and professional customer support assistant for an e-commerce company.
Your role is to:
- Provide accurate information using the tools available to you
- Be friendly, patient, and understanding with customers
- Always offer additional help after answering questions
- If you can't help with something, direct customers to the appropriate contact

You have access to the following tools:
1. get_return_policy() - For return policy questions
2. get_product_info() - To look up product information and specifications
3. Web search - To search the web for troubleshooting help

Always use the appropriate tool to get accurate, up-to-date information rather than guessing.""",
                tools=tools
            )
        return cache[key]
    return get_or_create_agent

get_or_create_agent = agent_factory()

@app.entrypoint
async def invoke(payload, context):
    log.info("Invoking Agent.....")

    session_id = getattr(context, 'session_id', 'default-session')
    user_id = getattr(context, 'user_id', 'default-user')
    agent = get_or_create_agent(session_id, user_id)

    stream = agent.stream_async(payload.get("prompt"))
    async for event in stream:
        if "data" in event and isinstance(event["data"], str):
            yield event["data"]


if __name__ == "__main__":
    app.run()

本地工具(get_return_policyget_product_info)保留在 agent 代码中。Gateway 工具(check_warranty)在运行时通过 MCP 客户端自动发现。

工作原理: 部署后,AgentCore Runtime 将 AGENTCORE_GATEWAY_MY_GATEWAY_URL 作为环境变量注入。get_gateway_mcp_client() 函数读取此 URL 并创建连接到 gateway 的 MCP 客户端。Gateway 将请求路由到 Lambda 函数。

部署

agentcore deploy -y -v

注意: 首次 gateway 部署需要约 2 分钟。

测试 Gateway 工具

测试 warranty check 工具:

agentcore invoke "Check the warranty for product PROD-003" --stream

预期响应:

The warranty for PROD-003 (Laptop Stand) is:
- Warranty Duration: 6 months
- Status: Expired
- Expiration Date: January 1, 2026

image-20260401144224601

测试有效的 warranty:

agentcore invoke "Is the warranty still valid for PROD-002?" --stream

预期:Agent 通过 Gateway 调用 check_warranty 并返回 Smart Watch warranty 有效至 2028 年。

image-20260401144256538

aws控制台查看gateway:

image-20260401144444250

image-20260401144513592

Gateway 的工作原理

agentcore invoke "Check warranty for PROD-003"
    ↓
AgentCore Runtime(CustomerSupport)
    ↓
Agent 从环境变量读取 AGENTCORE_GATEWAY_MY_GATEWAY_URL
    ↓
MCP Client 连接到 Gateway
    ↓
Gateway 路由到 WarrantyCheck Lambda 目标
    ↓
Lambda 执行并返回结果
    ↓
Agent 综合响应