19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS LambdaをAmazon Bedrock AgentCore GatewayでMCP互換ツールに変換して、Strands Agentsから呼び出してみた

Last updated at Posted at 2025-08-24

こんにちは!
株式会社キャピタル・アセット・プランニングの岡田です。

本記事は、「2025 Japan AWS Jr. Champions Qiitaリレー夏」52日目の記事です。
過去の投稿はこちらからご覧ください!

はじめに

企業システムやサービスのビジネスロジックは、しばしば REST API や AWS Lambda 関数 として提供されています。
これらをそのままではエージェントから直接呼び出すことはできません。
そこで登場するのが MCP (Model Context Protocol)
既存のAPIを MCP互換ツール に変換すれば、ClaudeやStrands AgentsのようなAIエージェントから安全に利用できるようになります。

これにより、

  • 社内のAPIやLambdaを 「ツール」として再利用 できる
  • エージェントにより 複数ツールを連鎖的に利用 させられる
  • 開発者は 既存ロジックをほぼ変更せず エージェント対応が可能になる

といった価値が生まれると考えています。

Amazon Bedrock AgentCore Gatewayとは

AgentCore Gateway は、AIエージェントが外部ツールやサービスへ安全にアクセスするためのゲートウェイ機能です。
これまでMCP非対応だったサービスを、最小限のコード変更で「エージェント対応ツール」として公開できます。

  • 対象リソース: REST API、AWS Lambda、データベース、SaaSなど
  • 対応プロトコル: MCP(Model Context Protocol)、A2A(Agent-to-Agent)
  • セキュリティ: 認証・認可を備えた二重認証モデル、アクセス制御の一元化
  • 機能: リクエスト/レスポンス変換、スロットリング、ツール選択機能、OAuthラッパーなど

これにより、
エージェントはCRM・注文管理システム・外部APIなどとセキュアに接続でき、
最新データを取り込んだ回答や高度なタスク実行が可能になります。

今回やってみること

今回は「既存のビジネスロジック(Lambda関数)を AgentCore Gateway を通じて MCP互換ツール に変換し、
それを Strands Agents から実際に呼び出して動作させる」という一連の流れを試してみます。

シナリオ

旅行計画を例にします。

「渋谷から箱根神社まで明日の朝9時に電車で行く予定。
運賃をUSDに換算し、手持ちの15 USDで足りるかどうかをチェックしたい。」

実装するLambda関数(ツール化する対象)

  1. get_travel_time_and_fare
    • 出発地・目的地・出発時刻 → 所要時間(分)と運賃(JPY)を返す
  2. convert_currency
    • 運賃(JPY) → USD換算する
  3. check_budget
    • USD換算額と予算を比較して「予算内/オーバー」を判定する

やることの全体像

  1. Lambda関数を用意(ビジネスロジックをAPI化したものと考えてよい)
  2. AgentCore GatewayでそれらをMCPツール化する
  3. Strands AgentsからMCP経由でツールを呼び出し、実際に質問を解かせる

Lambda関数の実装

すべての関数は ランタイム: python3.13 で実装しました。

get_travel_time_and_fare

import json
def lambda_handler(event, context):
    tool = (getattr(context,"client_context",None) or {}).custom.get("bedrockAgentCoreToolName","")
    if "___" in tool: tool = tool.split("___",1)[1]
    if tool != "get_travel_time_and_fare":
        return {"statusCode":400,"body":json.dumps({"error":"wrong tool"})}

    origin = event.get("origin"); destination = event.get("destination")
    depart = event.get("departure_time") 
    duration = 90 if ("渋谷" in (origin or "") and "箱根" in (destination or "")) else 120
    fare_jpy = 2000
    return {"statusCode":200,"body":json.dumps({
        "origin":origin,"destination":destination,"departure_time":depart,
        "duration_minutes":duration,"fare_jpy":fare_jpy
    }, ensure_ascii=False)}

convert_currency

import json
MOCK = {("JPY","USD"):0.0065}
def lambda_handler(event, context):
    tool = (context.client_context.custom or {}).get('bedrockAgentCoreToolName','')
    if '___' in tool: tool = tool.split('___',1)[1]
    if tool != 'convert_currency':
        return {"statusCode": 400, "body": json.dumps({"error":"wrong tool"})}

    amount = float(event.get("amount", 10000))
    base   = (event.get("base","JPY")).upper()
    target = (event.get("target","USD")).upper()
    rate = MOCK.get((base,target))
    res = {"base": base, "target": target, "rate": rate,
           "base_amount": amount, "converted_amount": round(amount*rate,2)}
    return {"statusCode": 200, "body": json.dumps(res, ensure_ascii=False)}

check_budget

import json
def lambda_handler(event, context):
    tool = (getattr(context,"client_context",None) or {}).custom.get("bedrockAgentCoreToolName","")
    if "___" in tool: tool = tool.split("___",1)[1]
    if tool != "check_budget":
        return {"statusCode":400,"body":json.dumps({"error":"wrong tool"})}

    trip_usd = float(event["converted_amount"])
    budget_usd = float(event["budget_usd"])
    within = trip_usd <= budget_usd
    gap = round(budget_usd - trip_usd, 2)
    msg = "予算内です" if within else "予算オーバーです"
    return {"statusCode":200,"body":json.dumps({
        "trip_usd":trip_usd,"budget_usd":budget_usd,
        "within_budget": within, "delta_usd": gap, "message": msg
    }, ensure_ascii=False)}

AgentCoreの設定

今回は AgentCore Gateway を作成し、その中に3つのLambda関数をターゲットとして登録します。
これにより、それぞれのLambdaが MCP互換ツール としてエージェントから呼び出せるようになります。


1. Gatewayの作成

コンソールのメニューから 「Gateways」 を選択します。
オレンジ色の 「Create gateway」 をクリックします。
image.png
→ この部分の設定はデフォルトのままです。


2. ターゲットの追加(1つ目:get_travel_time_and_fare)

  1. 「Target」 セクションに移動します。image.png
  2. Target type: 「Lambda ARN」を選択し、get_travel_time_and_fare 関数のARNをペーストします。
  3. Target Schema: 「Define an inline schema」を選び、以下を貼り付けます。
{
  "name": "get_travel_time_and_fare",
  "description": "出発地・目的地・出発時刻から所要時間と運賃(JPY)を返す",
  "inputSchema": {
    "type":"object",
    "properties":{
      "origin":{"type":"string"},
      "destination":{"type":"string"},
      "departure_time":{"type":"string","description":"ISO8601日時"}
    },
    "required":["origin","destination","departure_time"]
  }
}

3.ターゲットの追加(2つ目:convert_currency)

同じく 「Targets」 画面で 「Add」 をクリック
Target type: 「Lambda ARN」を選択し、convert_currency 関数のARNをペースト
Target Schema に以下を入力

{
  "name": "convert_currency",
  "description": "金額を指定通貨へ換算する。get_travel_time_and_fare の fare_jpy を受け取る",
  "inputSchema": {
    "type":"object",
    "properties":{
      "amount":{"type":"number"},
      "base":{"type":"string"},
      "target":{"type":"string"}
    },
    "required":["amount","base","target"]
  }
}

4. ターゲットの追加(3つ目:check_budget)

再度 「Add」 をクリック
Target type: 「Lambda ARN」を選び、check_budget 関数のARNをペースト
Target Schema に以下を入力

{
  "name": "check_budget",
  "description": "USD換算額と予算を比較して、予算内かどうかを返す",
  "inputSchema": {
    "type":"object",
    "properties":{
      "converted_amount":{"type":"number","description":"USD換算済みの運賃"},
      "budget_usd":{"type":"number"}
    },
    "required":["converted_amount","budget_usd"]
  }
}

5. 完了

これで3つのLambda関数がAgentCore Gateway経由で MCPツール化 されました 🎉
Strands Agentからは list_tools を通じてこれらが取得でき、エージェントの思考の中で連鎖的に呼び出されます。

さらに、Gatewayを作成すると Amazon Cognito が自動的に作成されます。
ここで取得するクライアント情報が、後ほど MCPツールを呼び出すための認証情報 になります。

  1. Cognitoのサイドメニューから 「アプリケーションクライアント」 を選択します
    スクリーンショット(アプリケーションクライアント一覧)

  2. 作成されているアプリケーションクライアントを選択し、「ログインページ」タブ を開きます
    スクリーンショット(クライアント詳細)

  3. ここで以下をコピーしておきます

    • クライアントID
    • クライアントシークレット
    • 「ログインページ」タブにある カスタムスコープ

認証情報の整理

今回のケースでは以下の認証情報が必要になります。

  • Strands Agents → MCPツール取得用
    • 認証方式: Cognito Client Credentials
    • 環境変数例:
       - COGNITO_CLIENT_ID
       - COGNITO_CLIENT_SECRET
       - COGNITO_DISCOVERY_URL
       - COGNITO_CUSTOM_SCOPE

COGNITO_DISCOVERY_URL は、AgentCore Gatewayを作成したあとに表示される
「Gateway details」画面 → 下部の「Inbound Identity」セクション → 「Discovery URL」 をコピーします。
image.png

  • Bedrock Agents → モデル呼び出し用
    • 認証方式: AWS IAM
      IAMユーザーまたはIAMロールを作成し、必要な権限を付与します。
    • 必要なIAMポリシー
       Strandsのドキュメントでは、Bedrockを使うために最低限必要なアクションとしてbedrock:InvokeModelWithResponseStream と bedrock:InvokeModel を挙げています
      サンプルポリシーは以下のようになります
      (本番環境ではResourceを特定のモデルARNに絞ることが推奨されています)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModelWithResponseStream",
        "bedrock:InvokeModel"
      ],
      "Resource": "*"
    }
  ]
}
  • 環境変数例:
    • AWS_ACCESS_KEY_ID
    • AWS_SECRET_ACCESS_KEY
    • AWS_DEFAULT_REGION

IAMユーザー/ロールの作成とアクセスキーの取得

IAMで上記ポリシーをアタッチしたユーザーまたはロールを作成

AWSコンソールかAWS CLIでアクセスキーID (AWS_ACCESS_KEY_ID) とシークレットアクセスキー (AWS_SECRET_ACCESS_KEY) を作成

Bedrockを使用するリージョンを AWS_DEFAULT_REGION に設定

StrandsではAWS SDK (boto3) を利用してBedrockにアクセスするため、開発環境では環境変数で以下のように設定する方法が一般的です

export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export AWS_DEFAULT_REGION=us-east-1

モデルへのアクセス要求
Bedrockを初めて使う場合は、Bedrockコンソールから利用したいモデルにアクセス権を申請しておく必要があります

実行例(Strands Agentsで呼び出し)

ここまで設定したGatewayとLambda関数を、実際に Strands Agents から呼び出してみます。
実行コードは以下です。

from dotenv import load_dotenv
import base64
import os
import requests
from mcp.client.streamable_http import streamablehttp_client
from strands import Agent
from strands.tools.mcp import MCPClient
from strands.models import BedrockModel

load_dotenv()

def get_access_token():
    client_id = os.getenv("COGNITO_CLIENT_ID")
    client_secret = os.getenv("COGNITO_CLIENT_SECRET")
    discovery_url = os.getenv("COGNITO_DISCOVERY_URL")
    custom_scope = os.getenv("COGNITO_CUSTOM_SCOPE")

    missing = [k for k,v in {
        "COGNITO_CLIENT_ID": client_id,
        "COGNITO_CLIENT_SECRET": client_secret,
        "COGNITO_DISCOVERY_URL": discovery_url,
        "COGNITO_CUSTOM_SCOPE": custom_scope,
    }.items() if not v]
    if missing:
        raise ValueError(f"Missing env vars: {', '.join(missing)}")

    # Basic 認証ヘッダ
    auth_header = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()

    # OIDC Discovery から token_endpoint を取得
    discovery_resp = requests.get(discovery_url, timeout=10)
    discovery_resp.raise_for_status()
    token_endpoint = discovery_resp.json()["token_endpoint"]

    # Client Credentials でアクセストークン取得
    token_headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "Authorization": f"Basic {auth_header}",
    }
    token_data = {"grant_type": "client_credentials", "scope": custom_scope}
    token_resp = requests.post(token_endpoint, headers=token_headers, data=token_data, timeout=10)
    token_resp.raise_for_status()
    return token_resp.json()["access_token"]


def main():
    access_token = get_access_token()

    gateway_endpoint = os.getenv("AGENTCORE_GATEWAY_ENDPOINT")  # 例: https://xxxxx.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
    model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")

    if not gateway_endpoint:
        raise ValueError("Missing env var: AGENTCORE_GATEWAY_ENDPOINT")

    # MCPクライアント(AgentCore GatewayのMCPエンドポイントへ)
    mcp_client = MCPClient(
        lambda: streamablehttp_client(
            gateway_endpoint,
            headers={"Authorization": f"Bearer {access_token}"},
        )
    )

    # 使えるツールの確認 → エージェントで実行
    with mcp_client:
        tools = mcp_client.list_tools_sync()

        # Bedrockを直接指定する場合は文字列でOK(Strandsが内部で呼び分け)
        agent = Agent(tools=tools, model=model_id)

        user_prompt = "渋谷から箱根神社まで明日の朝9時に電車で行く。手持ちは 15 USD。所要時間と運賃を確認して、USDに換算し、15 USD の予算で足りるか教えて。"
        result = agent(user_prompt)
        print(result)


if __name__ == "__main__":
    main()

.envサンプル

# AWS (Bedrock用)
AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION=us-east-1

# Bedrock モデルID
BEDROCK_MODEL_ID=us.anthropic.claude-3-7-sonnet-20250219-v1:0

# AgentCore Gateway (MCPエンドポイント)
AGENTCORE_GATEWAY_ENDPOINT=https://<your-gateway-id>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp

# Cognito (Client Credentials)
COGNITO_CLIENT_ID=YOUR_APP_CLIENT_ID
COGNITO_CLIENT_SECRET=YOUR_APP_CLIENT_SECRET
COGNITO_DISCOVERY_URL=https://cognito-idp.us-east-1.amazonaws.com/<user-pool-id>/.well-known/openid-configuration
COGNITO_CUSTOM_SCOPE=gateway-quick-start-xxxxxx/<gateway-name>:invoke

期待する流れ

今回のシナリオでは、ユーザーが以下のように質問します:

「渋谷から箱根神社まで明日の朝9時に電車で行く。手持ちは15 USD。所要時間と運賃を確認して、USDに換算し、15 USD の予算で足りるか教えて。」

エージェントが呼び出すことを期待するツールチェーンは以下です。

  1. get_travel_time_and_fare

    • 入力: 出発地, 目的地, 出発時刻
    • 出力: {fare_jpy:2000, duration_minutes:90}
  2. convert_currency

    • 入力: amount=fare_jpy
    • 出力: {converted_amount:13.0}
  3. check_budget

    • 入力: converted_amount=13.0, budget_usd=15
    • 出力: {within_budget:true, message:"予算内です"}

このように、1つ目のツールの出力を2つ目の入力に渡し、さらに2つ目の出力を3つ目に渡すという依存関係のあるフローが構築されます。

実行結果

実行すると期待通り、以下のようなツール呼び出しの連鎖が走りました。

  1. get_travel_time_and_fare{fare_jpy:2000, duration_minutes:90}
  2. convert_currency{converted_amount:13.0}
  3. check_budget{within_budget:true, message:"予算内です"}

コンソール出力例

渋谷から箱根神社までの所要時間と運賃を調べ、15 USDの予算で足りるか確認しますね。

まずは渋谷から箱根神社までの所要時間と運賃(日本円)を調べます。
Tool #1: get-travel-time-and-fare___get_travel_time_and_fare

次に、この運賃(2,000円)をUSDに換算します。
Tool #2: convert-currency___convert_currency

最後に、換算した金額(13.0 USD)が予算(15 USD)内に収まるか確認します。
Tool #3: check-budget___check_budget

渋谷から箱根神社までの情報をまとめました:

所要時間:90分(1時間30分)

運賃:2,000円(約13 USD)

出発時間:明日の朝9時

あなたの予算15 USDで運賃13 USDは十分に賄えます。予算内に2 USD余裕がありますので、安心して旅行できますね。

まとめ

  • Lambda関数をAgentCore Gateway経由でMCPツール化し、Strands Agentsから呼び出せることを確認しました
  • ツール同士に依存関係を持たせたシナリオを作ることで、エージェントが複数の関数を順番に呼び出し、最終的な答えを導き出す流れを実現できました
  • 認証やアクセス制御をAgentCore Gatewayに任せることで、既存のAPIやLambdaを大きく書き換えずにエージェント対応できる点が強力です

今回は旅行計画というシンプルな例でしたが、応用すれば:

  • 社内の見積りAPI+為替API+在庫システムを組み合わせて自然文で問い合わせ
  • 顧客管理システムや注文管理システムをMCPツール化して自動化
  • 外部SaaSをMCP経由で安全に統合

など、現実のビジネスロジックをAIエージェントに取り込む基盤として活用できます。

この記事が、AWS Lambda / AgentCore Gateway / MCP を組み合わせたエージェント開発の参考になれば幸いです。

19
8
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
19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?