7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amazon Bedrock AgentCore Gateway のターゲットに Amazon API Gateway が追加されたので Claude Code から呼び出してみた

7
Last updated at Posted at 2025-12-30

はじめに & 内容

2025 年最後の記事!

AWS re:Invent 2025 で発表があった Amazon Bedrock AgentCore Gateway のターゲットに Amazon API Gateway が追加されたことについてシンプルに纏めるのと、実際に触ってみることを主としています。

スクリーンショット 2025-12-17 1.44.38.png

スクリーンショット 2025-12-17 1.46.18.png

Amazon Bedrock AgentCore Gateway とは何かをざっくり勉強したい方はよければ過去の記事を参考にご覧ください。

考慮事項

Amazon Bedrock AgentCore Gateway のターゲットに Amazon API Gateway を指定する場合、AWS ドキュメントの考慮事項を読んでいると、押さえておきたい点がいくつかあったので予め確認しておいた方が良さそうです。

  • パブリック REST API のみをサポート
  • API のデフォルトエンドポイントの無効化は不可
  • API のすべてのメソッドにオペレーション名が必要
  • Amazon Cognito ユーザープールや Lambda オーソライザーは未サポート
  • API と AgentCore Gateway は同じアカウント・リージョンに存在する必要がある

最終的な想定の構成図

Bedrock AgentCore Gateway のターゲットに Amazon API Gateway を指定し試してみる

API Gateway + Lambda の準備

get_location は都市の位置情報を取得する Lambda 関数です。デモ実装としてモックデータを使用しています。
get_weather は、都市の天気情報を取得する Lambda 関数です。こちらもデモ実装としてモックデータを使用しています。

get_location
src/get_location/app.py
"""
Lambda function to get location information for a city.
This is a demo implementation with mock data.
"""
import json
import logging
from typing import Dict, Any

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Mock location data
MOCK_LOCATION_DATA = {
    "tokyo": {
        "city": "Tokyo",
        "country": "Japan",
        "region": "Kanto",
        "latitude": 35.6762,
        "longitude": 139.6503,
        "timezone": "Asia/Tokyo",
        "population": 13960000,
        "area_km2": 2194,
        "currency": "JPY",
        "languages": ["Japanese"]
    },
    "newyork": {
        "city": "New York",
        "country": "USA",
        "region": "New York",
        "latitude": 40.7128,
        "longitude": -74.0060,
        "timezone": "America/New_York",
        "population": 8336817,
        "area_km2": 783,
        "currency": "USD",
        "languages": ["English"]
    },
    "seattle": {
        "city": "Seattle",
        "country": "USA",
        "region": "Washington",
        "latitude": 47.6062,
        "longitude": -122.3321,
        "timezone": "America/Los_Angeles",
        "population": 753675,
        "area_km2": 217,
        "currency": "USD",
        "languages": ["English"]
    },
    "london": {
        "city": "London",
        "country": "UK",
        "region": "England",
        "latitude": 51.5074,
        "longitude": -0.1278,
        "timezone": "Europe/London",
        "population": 9002488,
        "area_km2": 1572,
        "currency": "GBP",
        "languages": ["English"]
    }
}


def handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
    """
    Lambda handler for getting location information.

    Args:
        event: API Gateway event
        context: Lambda context

    Returns:
        API Gateway response
    """
    logger.info(f"Received event: {json.dumps(event)}")

    try:
        # Get city from path parameters
        city = event.get('pathParameters', {}).get('city', '').lower()

        if not city:
            return {
                'statusCode': 400,
                'headers': {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                },
                'body': json.dumps({
                    'error': 'City parameter is required'
                })
            }

        # Get location data
        location_data = MOCK_LOCATION_DATA.get(city)

        if not location_data:
            return {
                'statusCode': 404,
                'headers': {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                },
                'body': json.dumps({
                    'error': f'Location data not found for city: {city}',
                    'available_cities': list(MOCK_LOCATION_DATA.keys())
                })
            }

        # Add source
        response_data = {
            **location_data,
            'source': 'mock-location-service'
        }

        logger.info(f"Returning location data for {city}: {response_data}")

        return {
            'statusCode': 200,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': json.dumps(response_data)
        }

    except Exception as e:
        logger.error(f"Error processing request: {str(e)}", exc_info=True)
        return {
            'statusCode': 500,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': json.dumps({
                'error': 'Internal server error',
                'message': str(e)
            })
        }
get_weather
src/get_weather/app.py
"""
Lambda function to get weather information for a city.
This is a demo implementation with mock data.
"""
import json
import logging
from datetime import datetime, timezone
from typing import Dict, Any

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Mock weather data
MOCK_WEATHER_DATA = {
    "tokyo": {
        "city": "Tokyo",
        "country": "Japan",
        "temperature": 22,
        "unit": "celsius",
        "condition": "Partly Cloudy",
        "humidity": 65,
        "wind_speed": 12,
        "wind_unit": "km/h"
    },
    "newyork": {
        "city": "New York",
        "country": "USA",
        "temperature": 18,
        "unit": "celsius",
        "condition": "Sunny",
        "humidity": 55,
        "wind_speed": 10,
        "wind_unit": "km/h"
    },
    "seattle": {
        "city": "Seattle",
        "country": "USA",
        "temperature": 15,
        "unit": "celsius",
        "condition": "Rainy",
        "humidity": 80,
        "wind_speed": 15,
        "wind_unit": "km/h"
    },
    "london": {
        "city": "London",
        "country": "UK",
        "temperature": 12,
        "unit": "celsius",
        "condition": "Cloudy",
        "humidity": 70,
        "wind_speed": 18,
        "wind_unit": "km/h"
    }
}


def handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
    """
    Lambda handler for getting weather information.

    Args:
        event: API Gateway event
        context: Lambda context

    Returns:
        API Gateway response
    """
    logger.info(f"Received event: {json.dumps(event)}")

    try:
        # Get city from path parameters
        city = event.get('pathParameters', {}).get('city', '').lower()

        if not city:
            return {
                'statusCode': 400,
                'headers': {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                },
                'body': json.dumps({
                    'error': 'City parameter is required'
                })
            }

        # Get weather data
        weather_data = MOCK_WEATHER_DATA.get(city)

        if not weather_data:
            return {
                'statusCode': 404,
                'headers': {
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                },
                'body': json.dumps({
                    'error': f'Weather data not found for city: {city}',
                    'available_cities': list(MOCK_WEATHER_DATA.keys())
                })
            }

        # Add timestamp
        response_data = {
            **weather_data,
            'timestamp': datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
            'source': 'mock-weather-service'
        }

        logger.info(f"Returning weather data for {city}: {response_data}")

        return {
            'statusCode': 200,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': json.dumps(response_data)
        }

    except Exception as e:
        logger.error(f"Error processing request: {str(e)}", exc_info=True)
        return {
            'statusCode': 500,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': json.dumps({
                'error': 'Internal server error',
                'message': str(e)
            })
        }

これらを SAM でサクッとデプロイしておきます。ポイントとしては、OpenAPI 仕様で API を定義していないと Gateway 化できないことに注意が必要です。

◼️ API Gateway ターゲットの要件

ドキュメント: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-target-api-gateway.html

"The exported API is subject to the same considerations and limitations as the OpenAPI target type."

API Gateway から GetExport で取得した OpenAPI 仕様は、OpenAPI ターゲットタイプと同じ制限に従うと明記されています。

なので、一から API Gateway を構築するというよりもすでに OpenAPI 仕様に従った既存の API Gateway を Amazon Bedrock AgentCore Gateway と統合できるという点で活躍します。

SAMテンプレート
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Amazon API Gateway + Lambda for AgentCore Gateway Demo

Globals:
  Function:
    Runtime: python3.12
    Timeout: 30
    MemorySize: 256
    Environment:
      Variables:
        LOG_LEVEL: INFO

Resources:
  # API Gateway with OpenAPI Definition
  AgentCoreApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: AgentCore Gateway Demo API
      StageName: prod
      TracingEnabled: true
      Cors:
        AllowOrigin: "'*'"
        AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
        AllowMethods: "'GET,POST,OPTIONS'"
      MethodSettings:
        - ResourcePath: '/*'
          HttpMethod: '*'
          LoggingLevel: INFO
          DataTraceEnabled: true
          MetricsEnabled: true
      DefinitionBody:
        openapi: 3.0.1
        info:
          title: AgentCore Gateway Demo API
          description: Demo REST API for AgentCore Gateway integration with weather and location endpoints
          version: 1.0.0
        paths:
          /weather/{city}:
            get:
              summary: Get weather information for a city
              description: Returns current weather conditions including temperature, humidity, and wind speed for the specified city
              operationId: getWeather
              parameters:
                - name: city
                  in: path
                  required: true
                  description: City name (tokyo, newyork, seattle, london)
                  schema:
                    type: string
              responses:
                '200':
                  description: Successful response with weather data
                  content:
                    application/json:
                      schema:
                        type: object
                        properties:
                          city:
                            type: string
                            description: City name
                          country:
                            type: string
                            description: Country name
                          temperature:
                            type: number
                            description: Temperature value
                          unit:
                            type: string
                            description: Temperature unit
                          condition:
                            type: string
                            description: Weather condition
                          humidity:
                            type: number
                            description: Humidity percentage
                          wind_speed:
                            type: number
                            description: Wind speed value
                          wind_unit:
                            type: string
                            description: Wind speed unit
                          timestamp:
                            type: string
                            description: Timestamp of the data
                          source:
                            type: string
                            description: Data source
                '400':
                  description: Bad request - city parameter missing
                  content:
                    application/json:
                      schema:
                        type: object
                        properties:
                          error:
                            type: string
                '404':
                  description: City not found
                  content:
                    application/json:
                      schema:
                        type: object
                        properties:
                          error:
                            type: string
                          available_cities:
                            type: array
                            items:
                              type: string
                '500':
                  description: Internal server error
                  content:
                    application/json:
                      schema:
                        type: object
                        properties:
                          error:
                            type: string
                          message:
                            type: string
              x-amazon-apigateway-integration:
                type: aws_proxy
                httpMethod: POST
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetWeatherFunction.Arn}/invocations
                responses:
                  default:
                    statusCode: '200'
          /location/{city}:
            get:
              summary: Get location information for a city
              description: Returns detailed location information including coordinates, population, timezone, and more for the specified city
              operationId: getLocationInfo
              parameters:
                - name: city
                  in: path
                  required: true
                  description: City name (tokyo, newyork, seattle, london)
                  schema:
                    type: string
              responses:
                '200':
                  description: Successful response with location data
                  content:
                    application/json:
                      schema:
                        type: object
                        properties:
                          city:
                            type: string
                            description: City name
                          country:
                            type: string
                            description: Country name
                          region:
                            type: string
                            description: Region or state
                          latitude:
                            type: number
                            description: Latitude coordinate
                          longitude:
                            type: number
                            description: Longitude coordinate
                          timezone:
                            type: string
                            description: Timezone identifier
                          population:
                            type: integer
                            description: Population count
                          area_km2:
                            type: number
                            description: Area in square kilometers
                          currency:
                            type: string
                            description: Currency code
                          languages:
                            type: array
                            items:
                              type: string
                            description: Spoken languages
                          source:
                            type: string
                            description: Data source
                '400':
                  description: Bad request - city parameter missing
                  content:
                    application/json:
                      schema:
                        type: object
                        properties:
                          error:
                            type: string
                '404':
                  description: City not found
                  content:
                    application/json:
                      schema:
                        type: object
                        properties:
                          error:
                            type: string
                          available_cities:
                            type: array
                            items:
                              type: string
                '500':
                  description: Internal server error
                  content:
                    application/json:
                      schema:
                        type: object
                        properties:
                          error:
                            type: string
                          message:
                            type: string
              x-amazon-apigateway-integration:
                type: aws_proxy
                httpMethod: POST
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetLocationFunction.Arn}/invocations
                responses:
                  default:
                    statusCode: '200'

  # Lambda Function: Get Weather
  GetWeatherFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: agentcore-demo-get-weather
      Description: Get weather information for a city
      CodeUri: src/get_weather/
      Handler: app.handler

  # Lambda Permission for API Gateway
  GetWeatherFunctionPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref GetWeatherFunction
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${AgentCoreApi}/*/*/*'

  # Lambda Function: Get Location
  GetLocationFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: agentcore-demo-get-location
      Description: Get location information for a city
      CodeUri: src/get_location/
      Handler: app.handler

  # Lambda Permission for API Gateway
  GetLocationFunctionPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref GetLocationFunction
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${AgentCoreApi}/*/*/*'

Outputs:
  ApiUrl:
    Description: API Gateway URL
    Value: !Sub 'https://${AgentCoreApi}.execute-api.${AWS::Region}.amazonaws.com/prod/'
    Export:
      Name: AgentCoreGatewayApiUrl

  ApiId:
    Description: API Gateway ID
    Value: !Ref AgentCoreApi
    Export:
      Name: AgentCoreGatewayApiId

  ApiArn:
    Description: API Gateway ARN
    Value: !Sub 'arn:aws:apigateway:${AWS::Region}::/restapis/${AgentCoreApi}'
    Export:
      Name: AgentCoreGatewayApiArn

  GetWeatherFunctionArn:
    Description: Get Weather Lambda Function ARN
    Value: !GetAtt GetWeatherFunction.Arn

  GetLocationFunctionArn:
    Description: Get Location Lambda Function ARN
    Value: !GetAtt GetLocationFunction.Arn

Amazon Bedrock AgentCore Gateway のデプロイ

▼ ゲートウェイの詳細
適当な「ゲートウェイ名」を入力。
追加設定 - optional で細かい設定もできますが、本題とか異なり掘り下げていきたいなと思ったので、今後別の記事で書いていこうかなと思っています。
スクリーンショット 2025-12-31 1.10.42.png

▼ インバウンド認証設定
「IAM 許可を使用」を選択。
スクリーンショット 2025-12-29 21.49.14.png

▼ Policy
後で検証するので、一旦何も設定せずにいきます。
スクリーンショット 2025-12-29 21.57.36.png

ちなみに、Policy も AWS re:Invent 2025 で発表があった新しい Amazon Bedrock AgentCore の新しいコンポーネントです。

一応、Policy のざっくりとした概要を説明しておきます。

Policy 機能は、AI エージェントが「やっていいこと」と「やってはいけないこと」を明確に制御するための仕組みです。

AI エージェントは便利な反面、自律的に動くからこそ以下のリスクがあります:

  • 機密データへの不正アクセス
  • 許可されていない操作の実行
  • 予期しない行動

Policy 機能は、エージェントが実行する前に「この操作をしていいか?」をチェックするガードレールの役割を果たします。

ざっくりとした特徴:

  1. 自然言語でルールを書ける
    専門的なコードを書かなくても、日本語や英語で「こういう条件のときだけ許可する」と書けば、システムが自動でポリシーコードを生成してくれます。
  2. 事前チェックで安全性を確保
    ツールが実際に動く前にリクエストを検証します。問題のある操作は実行される前にブロックされます。
  3. どんなエージェントでも使える
    使用する AI モデルやフレームワークに関係なく、一貫したポリシーを適用できます。

▼ 許可
「新しいサービスロールを作成して使用」を選択し、適当な「サービスロール名」を入力。
スクリーンショット 2025-12-29 21.57.58.png

▼ KMS キー
今回は何も設定しません。
スクリーンショット 2025-12-29 21.59.09.png

▼ ターゲット
適当な「ターゲット名」を入力し、ターゲットタイプに今回メインとなる「API Gateway」を選択。デプロイ済みの API Gateway の API と Stage を選択します。
スクリーンショット 2025-12-29 22.01.42.png

ツールとして公開する API Operetion を選択します。
Operation ID がある場合は、エージェントはその値をツール名として使用します。
Operation ID がない場合は、Name overide を入力する必要があります。
アウトバウンド認証設定では「IAM ロール」としておきます。
スクリーンショット 2025-12-29 22.12.52.png

「ゲートウェイを作成」
スクリーンショット 2025-12-29 22.13.18.png

ClaudeCode から呼び出す

まず、Amazon Bedrock AgentCore > ゲートウェイ > bedrock-agentcore-gateway から、「ゲートウェイリソース URL」をコピーしておきます。
スクリーンショット 2025-12-30 1.41.36.png

ClaudeCode から MCP Proxy for AWS を使って接続するので、次の通り MCP の設定を行います。<ゲートウェイリソース URL> は先程確認した自身のものに置き換えてください。

MCP Proxy for AWS については過去に以下の記事でも詳しく紹介してますので、参考にしてください。

.mcp.json
{
  "mcpServers": {
    "test-mcp": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "mcp-proxy-for-aws@latest",
        "<ゲートウェイリソース URL>",
        "--service",
        "bedrock-agentcore",
        "--profile",
        "default",
        "--region",
        "us-west-2",
        "--log-level",
        "INFO"
      ]
    }
  }
}

ClaudeCode を起動し、/mcp コマンドでツールとして認識しているか確認しておきます。
【ターゲット】に登録した、get_location_city と get_weathet_city が登録されていることが確認できました。
スクリーンショット 2025-12-30 23.10.11.png
スクリーンショット 2025-12-30 23.10.24.png
スクリーンショット 2025-12-30 23.10.44.png

では最後に次の通り質問し、各ツールが使われていることが確認できれば検証は完了です。

tokyoの天気を教えてください。

get_weathet_city が使われること。

tokyoの場所を教えてください。

get_location_city が使われること。

スクリーンショット 2025-12-30 23.16.54.png

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?