エージェントの発見、ルーティング、アクセス制御のためのサーバーレス A2A ゲートウェイの構築
AWS は、AI エージェント間の通信を管理するサーバーレス型 A2A ゲートウェイの構築パターンとアーキテクチャを公開し、分散した接続やセキュリティリスクを解消する解決策を提示しました。
キーポイント
エージェント間通信の課題とゲートウェイの役割
エンタープライズ環境における AI エージェントの増加に伴い、点対点接続や認証管理が複雑化・二重化する課題に対し、単一のエントリーポイントを持つゲートウェイパターンで解決を図ります。
3 層アーキテクチャによる包括的管理
エージェントの登録・発見を行う「管理層」、JWT スコープを用いた細粒度アクセス制御を行う「制御層」、および OAuth と SSE サポートを持つ実行層から構成されるサーバーレス設計を採用します。
A2A プロトコル標準化とランタイム非依存
Agent-to-Agent (A2A) プロトコルに基づき、ECS、Lambda、Bedrock AgentCore などの特定のランタイムやクラウド環境に縛られずに、単一ドメインで経路制御を可能にします。
Terraform を活用した迅速なデプロイ
既存の A2A クライアントを変更することなく接続可能なゲートウェイを、Terraform によるインフラコードで即座に構築・展開できる具体的な手順を提供しています。
Backend Trust Model and Agent Vetting
The gateway operates on a trust-after-authentication model where backend agents must handle their own input validation, necessitating an approval workflow integrated with CI/CD pipelines for production safety.
Rate Limiting via DynamoDB TTL
Per-user and per-agent rate limits are enforced using atomic counters in DynamoDB that automatically expire via Time-To-Live (TTL), eliminating cleanup overhead while providing granular control over consumption.
Private Deployment with VPC Integration
The gateway supports private infrastructure deployment within an Amazon VPC, keeping AWS service traffic internal and allowing access to on-premises or multi-cloud agents via Direct Connect while maintaining OAuth connectivity through NAT gateways.
影響分析・編集コメントを表示
影響分析
本記事は、AI エージェントが分散化する現代のアーキテクチャにおいて、セキュリティと運用効率を両立させるための標準的なパターンを示すものです。特に、A2A プロトコルの普及に伴い、個別接続の管理から中央集権型ゲートウェイへの移行が業界の次のステップとして重要であることを示唆しています。
編集コメント
AI エージェントが実社会で本格的に連携し始める中で、セキュリティとスケーラビリティを担保するインフラ設計の指針として極めて重要です。AWS の具体的な実装例は、開発者がすぐに適用可能な実践的な価値を持っています。
企業が AI エージェントをチーム、ベンダー、インフラストラクチャ全体に展開するにつれ、エージェント間通信の管理が運用上の負担として増大しています。集中化されたレイヤーがない場合、新しいエージェント統合ごとにポイントツーポイント接続、個別の認証情報、カスタムルーティングロジックが追加されます。チームはエンジニアリングサイクルを接続性の構築に費やし、エージェント機能の開発に充てることができません。アクセス制御も断片化され、どのクライアントがどのエージェントに到達できるかを強制する単一の場所が存在しません。その結果、新しいエージェントワークフローの市場投入までの時間が遅延し、一貫性のない認証ポリシーによるセキュリティリスクが増大し、ネットワークに追加される各新しいエージェントに対して四乗級数的に増加する運用オーバーヘッドが生じます。
ゲートウェイパターンは、エージェントが Amazon Elastic Container Service (Amazon ECS)、AWS Lambda、Amazon Bedrock AgentCore Runtime、非 AWS クラウド、またはハイブリッド環境のいずれで実行されていても、エージェントの前に単一のエントリーポイントを配置することでこの課題に対処します。これは、チームを特定のランタイム、フレームワーク、オーケストレーション層に縛り付けることなく、ルーティングを処理し、細粒度の権限を中央で強制します。このパターンは、エージェント間の通信方法を標準化する Agent-to-Agent (A2A) プロトコルに基づいています。集中化されたオーケストレーターがない場合、20 個のエージェントのデプロイには最大で 190 のポイントツーポイント接続が必要になります。
この記事では、AWS でパスベースのルーティング(/agents/{agentId})を使用して単一ドメインの背後に複数のエージェントをホストするサーバーレス A2A ゲートウェイの構築方法について学びます。標準的な A2A クライアントは修正なしで動作します。このソリューションには 3 つのレイヤーがあります:
- マネージメント層:発見とセマンティック検索を備えた集中型エージェントレジストリ。
- コントロール層:JSON Web Token (JWT) スコープおよび Lambda オーザライザーを使用したきめ細かいアクセス制御。
- 実行層:OAuth バックエンド認証および Server-Sent Events (SSE) ストリーミングサポートを備えた単一ドメインルーティング。
このガイドに従っていただければ、A2A に準拠したエージェントが接続できる Terraform でプロビジョニングされたゲートウェイをデプロイできます。
アーキテクチャ
以下の図は、ゲートウェイの構成要素とリクエストがシステム内をどのように流れるかを示しています。

Amazon API Gateway (REST API) が単一のエントリーポイントとして機能します。このアーキテクチャでは REST API を使用しています。これは、レスポンスストリーミングをサポートしているためです。SSE ベースのリアルタイムエージェント応答にはストリーミングが必要です。Lambda オーザライザーは JWT スコープを検査し、特定のエージェントパス(/agents/agent-a/*)へのアクセスを許可しつつ、他のパスへのアクセスを拒否する AWS Identity and Access Management (IAM) ポリシーを生成します。
Lambda 関数がゲートウェイロジックを実装します:
- オーソライザー:JWT の検証を行い、スコープとエージェントのマッピングに基づいて IAM ポリシーを生成します。
- レジストリ:呼び出し元がアクセスできるエージェントの一覧を表示し、URL をゲートウェイを指すように書き換えます。
- 検索:Amazon Bedrock 内の Amazon Titan Text Embeddings を使用した意味的なエージェントの発見を行います。
- プロキシ:OAuth 認証を伴ってバックエンドのエージェントへリクエストをルーティングし、Lambda Web Adapter を介して SSE ストリーミングをサポートします。
- 管理:エージェントの登録とライフサイクル管理を行います。
Amazon DynamoDB は 3 つのテーブルを格納しています。エージェントレジストリは、エージェント ID をバックエンド URL、認証設定、キャッシュされたエージェントカードにマッピングします。権限テーブルは JWT スコープを許可されたエージェントにマッピングします。レート制限カウンターテーブルは 1 分あたりのリクエスト数をカウントします。
Amazon Cognito は、OAuth 2.0 クライアント認証情報フローを使用して認証を処理します。トークン内のスコープが呼び出し元がアクセスできるエージェントを決定します。クライアントが認証すると、billing:read や support:write などのスコープを含む JWT を受け取ります。オーソライザーはこれらのスコープを権限テーブルで照会し、クライアントが到達可能なエージェントを決定します。
AWS Secrets Manager はバックエンドの認証情報を格納しています。プロキシ Lambda がバックエンドのエージェントと認証を行う必要がある場合、Amazon Resource Name (ARN) を介して OAuth クライアントシークレットを取得します。シークレットは DynamoDB には保存されません。
セマンティック検索のため、エージェントの説明は Amazon Titan Text Embeddings を用いて埋め込み表現に変換され、Amazon S3 Vectors に保存されます。これにより、クライアントは正確な名前一致ではなく、自然言語によるクエリを使用してエージェントを検出できるようになります。
ゲートウェイ設計
A2A ネイティブエンドポイントは A2A プロトコル仕様に従い、バックエンドのエージェントへルーティングします。ゲートウェイは仕様で定義された両方のプロトコルバインディングをサポートしています。JSON-RPC では、各エージェントに対して 1 つのエンドポイントが用意され、メソッドはリクエスト本文に含まれます:
- GET /agents/{agentId}/.well-known/agent-card.json – エージェントの機能を取得。
- POST /agents/{agentId} with {"method": "SendMessage", ...} (バッファレスポンス用)。
- POST /agents/{agentId} with {"method": "SendStreamingMessage", ...} (SSE ストリーミング用)。
HTTP+JSON/REST バインディングも、RESTful な URL を好むクライアント向けにサポートされています。
これらのエンドポイントは A2A プロトコルと完全に整合しています。クライアントは個別のバックエンド URL ではなく、ゲートウェイ URL を指すようになります。ただし、A2A ネイティブエンドポイントだけでは管理上の課題は解決されません。クライアントには依然として、存在するエージェントの検出、機能によるエージェント検索、およびエージェントライフサイクルの管理手段が必要です。
ゲートウェイエンドポイントはこのレイヤーを提供します:
- GET /agents – 呼び出し元がアクセスできるエージェントの一覧を取得します。
- POST /search – エージェントに対する意味検索を実行します。
- POST /admin/agents/register – 新しいバックエンドエージェントを登録します。
- POST /admin/agents/{agentId}/sync – キャッシュされたエージェントカードを更新します。
- POST /admin/agents/{agentId}/status – エージェントのアクティブ化または非アクティブ化を行います。
すべてのリクエストは同じパスに従います。クライアントは Authorization ヘッダーに JWT を含めてリクエストを送信します。API Gateway は Lambda 認証者(authorizer)を呼び出し、JWT の検証と、Permissions テーブルにおける呼び出し元のスコープの照合を行います。認証者は、特定のエージェントへのアクセスを許可または拒否する IAM ポリシーを返します。許可された場合、リクエストは適切な Lambda にルーティングされます。具体的には、A2A トラフィック用の Proxy、エージェント発見用の Registry、意味検索クエリ用の Search、管理操作用の Admin です。A2A リクエストの場合、Proxy Lambda はバックエンドと OAuth で認証を行い、リクエストを転送します。未承認のリクエストは API Gateway で拒否され、バックエンドの Lambda には到達しません。
3 レイヤーモデル
エージェントの展開規模が大きくなるにつれ、チームは利用可能なリソースを把握する必要があります。管理層は、エージェントがその機能、バックエンド URL、ステータスとともに登録される中央集権型のレジストリを提供します。新しいエージェントが展開されると、ゲートウェイで一度だけ登録され、すぐに認証されたクライアントから検出可能になります。また、レジストリはエージェントカードをキャッシュするため、クライアントは各バックエンドから個別に機能を取得する必要がありません。キャッシュされたカードの URL は書き換えられ、ゲートウェイを経由して指し示されるため、クライアントは単一のゲートウェイドメインとやり取りするだけで済み、バックエンド URL を直接検出する必要がなくなります。大規模な展開においては、セマンティック検索により、クライアントは正確な名前を知るのではなく、必要なものを記述することでエージェントを検索できます。
企業環境では、すべてのクライアントがすべてのエージェントにアクセスできるわけではありません。制御層は JWT スコープに基づいて細粒度の権限を強制します。クライアントが認証されると、そのトークンには billing:read や support:admin といったスコープが含まれます。Lambda オーザライザーはこれらのスコープを Permissions テーブル内の特定のエージェントにマッピングし、API Gateway レベルでアクセスを許可または拒否する IAM ポリシーを生成します。権限のないリクエストはバックエンドの Lambda に到達しません。さらに、レート制限もプロキシレベルでユーザーごと、エージェントごとに適用されます。プロキシは自動的な有効期限(TTL)機能を持つ原子性の DynamoDB カウンターを使用してリクエスト数を追跡し、クライアントがクォータを超えた場合は 429 ステータスコードと Retry-After ヘッダーを返します。権限とレート制限は中央管理されています:アクセスの付与・取り消しやクォータの調整を行うには、各エージェントを変更するのではなく、Permissions テーブルを更新するだけで済みます。
実行層は、バックエンドエージェントへのリクエストの実際のルーティングを処理します。クライアントは単一のドメインに接続し、ゲートウェイはパスに基づいて適切なバックエンドへルーティングします。これによりネットワーク構成が簡素化されます:すべてのエージェントへの接続を開く代わりに、クライアントはゲートウェイに到達するだけで済みます。プロキシ Lambda はバックエンドとの OAuth 認証を処理するため、クライアントはバックエンドの資格情報を管理する必要がありません。シークレットマネージャーから機密情報を取得し、アクセストークンをフェッチして、リクエストを透過的に転送します。リアルタイムユースケースでは、プロキシは SSE ストリーミングをサポートしており、エージェントが生成されるたびにインクリメンタルなレスポンスをクライアントへ送信できます。
ソリューションのデプロイ
ゲートウェイは Terraform だけで完全にデプロイされます。まず、以下の要件を満たしていることを確認してください。
前提条件
- Terraform >= 1.5.0.
- Python 3.12.
- 有効な資格情報で設定された AWS Command Line Interface (AWS CLI)。
- Docker(プロキシ Lambda コンテナのビルド用)。
- Terraform の状態保存用の Amazon Simple Storage Service (Amazon S3) バケット(リモート状態用、オプション)。
ゲートウェイコード は aws-samples GitHub リポジトリで利用可能です。
リポジトリをクローンして変数を設定してください:
cp terraform/terraform.tfvars.example terraform/terraform.tfvars
terraform/terraform.tfvars を編集し、リージョンと命名の好みを変更してください:
aws_region = "us-east-1"
project_name = "a2a-gateway"
environment = "poc"
Lambda パッケージを構築してデプロイします:
./scripts/build_lambda_package.sh
cd terraform
terraform init
terraform plan
terraform apply
Terraform は、DynamoDB テーブル、Cognito ユーザープール、Amazon Elastic Container Registry (Amazon ECR) リポジトリ、Lambda 関数、API Gateway、IAM ロールを一度に作成します。Terraform はデプロイの一部としてプロキシ Lambda コンテナを構築してプッシュします。
ソリューションのテスト
Terraform の出力からゲートウェイ認証情報を取得します:
GATEWAY_URL=$(terraform output -raw api_gateway_url)
TOKEN_ENDPOINT=$(terraform output -raw cognito_token_endpoint)
CLIENT_ID=$(terraform output -raw cognito_client_id)
CLIENT_SECRET=$(terraform output -raw cognito_client_secret)
PERMISSIONS_TABLE=$(terraform output -raw permissions_table_name)
JWT を取得します:
TOKEN_RESPONSE=$(curl -s -X POST "$TOKEN_ENDPOINT" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET")
export JWT=$(echo $TOKEN_RESPONSE | jq -r .access_token)
このリポジトリには、examples/ ディレクトリにデプロイ可能な A2A サンプルエージェント(Weather Agent と Calculator Agent)が含まれています。これらはそれぞれ独自の Terraform 設定を使用してデプロイできます。オプションとして、cd examples/terraform && terraform apply でデプロイし、その出力をキャプチャします:
例の Terraform デプロイ後、例のエージェントをデプロイした後の例から
WEATHER_BACKEND=$(terraform output -raw weather_agent_backend_url)
WEATHER_CARD=$(terraform output -raw weather_agent_card_url)
AGENT_TOKEN_ENDPOINT=$(terraform output -raw cognito_token_endpoint)
AGENT_CLIENT_ID=$(terraform output -raw cognito_client_id)
AGENT_CLIENT_SECRET=$(terraform output -raw cognito_client_secret)
次に、ゲートウェイにエージェントを登録します(前述の $GATEWAY_URL と $JWT を使用):
curl -X POST "$GATEWAY_URL/admin/agents/register" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"agentId": "weather-agent",
"name": "Weather Agent",
"backendUrl": "'"$WEATHER_BACKEND"'",
"agentCardUrl": "'"$WEATHER_CARD"'",
"authConfig": {
"type": "oauth2_client_credentials",
"tokenUrl": "'"$AGENT_TOKEN_ENDPOINT"'",
"clientId": "'"$AGENT_CLIENT_ID"'",
"clientSecret": "'"$AGENT_CLIENT_SECRET"'",
"scopes": ["a2a-gateway/weather:read"]
}
}'
ゲートウェイは、きめ細かいアクセス制御を適用します。各 OAuth スコープ(権限範囲)は、DynamoDB の権限制御テーブルにおいて、特定のエージェントへのアクセスが明示的に付与されている必要があります。
登録されたエージェントにスコープからアクセスできるように権限を更新してください:
aws dynamodb put-item \
--table-name "$PERMISSIONS_TABLE" \
--item '{
"scope": {"S": "gateway:admin"},
"allowedAgents": {"L": [{"S": "weather-agent"}]},
"description": {"S": "Admin scope with access to weather agent"
}'
登録されたエージェントを発見し、ゲートウェイを介してメッセージを送信します:
curl "$GATEWAY_URL/agents" -H "Authorization: Bearer $JWT"
curl -X POST "$GATEWAY_URL/agents/weather-agent/message:send" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"message": {
"messageId": "msg-001",
"role": "user",
"parts": [{"text": "What is the weather in New York"}]
}
}'
クリーンアップ
ソリューションをクリーンアップするには、/terraform フォルダから terraform destroy を実行します。Terraform はリソースの削除に対する許可を求めます。
terraform destroy
セキュリティに関する考慮事項
このゲートウェイは参照実装です。本番環境へ移行する前に、組織のセキュリティ姿勢に基づいて強化が必要な以下の領域を確認してください。
バックエンド信頼モデル
ゲートウェイは、認証後の信頼モデルに基づいて動作します。バックエンドエージェントが登録され、OAuth 資格情報が検証された後、ゲートウェイはコンテンツの検査を行わずにレスポンスをクライアントに直接プロキシします。A2A メッセージは変更なしでプロキシされるため、バックエンドエージェント側で独自のプロンプトインジェクション対策と入力検証を実装する責任があります。本番環境では、管理者がバックエンドエージェントへのアクセス可能にする前にレビューを行う、エージェント登録用の承認ワークフローを実装してください。これにより、デプロイプロセスの一部としてエージェントを検証し、デプロイ後にチェックするのではなく、継続的インテグレーションおよび継続的デリバリー(CI/CD)パイプラインと統合します。
レート制限とクォータ
ゲートウェイは、プロキシ層でユーザーごと、エージェントごとのレート制限を適用します。各リクエストは、ユーザー、エージェント、および 1 分ウィンドウをキーとする DynamoDB の原子カウンターを増加させます。クライアントがクォータを超えた場合、プロキシは 429 ステータスコードと retry-after ヘッダーを返してレスポンスし、そのリクエストはバックエンドに到達しません。カウンターは DynamoDB の TTL(Time To Live)機能により自動的に期限切れとなるため、クリーンアップのオーバーヘッドはありません。制限は、Permissions テーブル内のアクセス制御設定と共に構成され、デフォルトのリクエスト数/分として、またはエージェントごとの上書きルールとして設定可能です。これにより、管理者は消費量に対して細粒度の制御を行使できます。
プライベートデプロイメント
プライベートインフラストラクチャを必要とする環境において、ゲートウェイはオプションの Amazon Virtual Private Cloud (Amazon VPC) デプロイモードをサポートしています。このモードを有効にすると、Lambda 関数はプライベートサブネット内で実行されます。API Gateway は VPC の内部のみからアクセス可能なプライベートエンドポイントに切り替わります。VPC エンドポイントは、インターネットを経由せずに AWS サービスへのトラフィックを処理します。
ゲートウェイは、新しい VPC の作成と、既存の VPC の利用(Bring Your Own)の両方をサポートしています。既存の VPC へデプロイする場合は、terraform.tfvars にて VPC ID、サブネット ID、ルートテーブル ID、およびセキュリティグループ ID を指定してください。
enable_private_deployment = true
existing_vpc_id = "vpc-0123456789abcdef0"
existing_subnet_ids = ["subnet-aaa", "subnet-bbb", "subnet-ccc"]
existing_route_table_ids = ["rtb-aaa"]
existing_lambda_security_group_id = "sg-aaa"
existing_vpc_endpoint_security_group_id = "sg-bbb"
このモードではゲートウェイのインフラストラクチャがプライベートになりますが、Cognito やその他の外部アイデンティティプロバイダとの OAuth トークン交換には VPC からのアウトバウンドインターネット接続が必要です。これは通常、ネットワークアドレス変換 (NAT) ゲートウェイまたは AWS Transit Gateway を使用した共有エグレス VPC へのルーティング によって処理されます。AWS サービスのトラフィック (DynamoDB、Amazon S3、Secrets Manager、S3 Vectors) は VPC エンドポイントを通じてプライベートなまま維持されます。アウトバウンド接続が必要なのは OAuth トークン交換のみです。Amazon Bedrock でセマンティック検索が必要な場合は、enable_bedrock_endpoint = true を設定して、Amazon Bedrock Runtime の VPC エンドポイントも追加してください。
オンプレミス環境や他のクラウドで実行されているエージェントに対しては、プライベートゲートウェイを AWS Direct Connect または AWS Interconnect (プレビュー) を経由して到達可能にできます。これにより、ゲートウェイはパブリックインターネットへのトラフィック露出を避けながら、環境全体のエージェントを統制することができます。
A2A サーバー認証
ゲートウェイは、OAuth 2.0 クライアント資格情報フローを使用してバックエンドエージェントと相互認証を行います。登録された各エージェントにはトークン URL と認証情報が含まれており、Proxy Lambda がトークンの取得を透明性を持って処理します。これにより、バックエンドエージェント
原文を表示
As enterprises deploy AI agents across teams, vendors, and infrastructure, managing agent-to-agent communication becomes a growing operational burden. Without a centralized layer, each new agent integration adds point-to-point connections, separate credentials, and custom routing logic. Teams spend engineering cycles wiring up connectivity instead of building agent capabilities. Access control becomes fragmented, with no single place to enforce which clients can reach which agents. The result is slower time-to-market for new agent workflows, increased security risk from inconsistent auth policies, and operational overhead that scales quadratically with each new agent added to the network.
The gateway pattern addresses this by placing a single entry point in front of your agents, regardless of whether they run on Amazon Elastic Container Service (Amazon ECS), AWS Lambda, Amazon Bedrock AgentCore Runtime, a non-AWS cloud, or a hybrid environment. It handles routing and enforces fine-grained permissions centrally, without binding teams to a particular runtime, framework, or orchestration layer. This pattern builds on the Agent-to-Agent (A2A) protocol, which standardizes how agents communicate with each other. Without a central orchestrator, a deployment of 20 agents requires up to 190 point-to-point connections.
In this post, you will learn how to build a serverless A2A gateway on AWS that hosts multiple agents behind a single domain using path-based routing (/agents/{agentId}). Standard A2A clients work without modification. The solution has three layers:
- Management layer: Centralized agent registry with discovery and semantic search.
- Control layer: Fine-grained access control using JSON Web Token (JWT) scopes and a Lambda authorizer.
- Execution layer: Single-domain routing with OAuth backend authentication and Server-Sent Events (SSE) streaming support.
Follow along, and you will deploy a Terraform-provisioned gateway that A2A-adherent agents can connect to.
Architecture
The following diagram shows the gateway’s components and how requests flow through the system.

Amazon API Gateway (REST API) serves as the single-entry point. The architecture uses a REST API because REST APIs support response streaming. Streaming is required for SSE-based real-time agent responses. The Lambda authorizer inspects JWT scopes and generates AWS Identity and Access Management (IAM) policies that allow access to specific agent paths (/agents/agent-a/*) while denying others.
Lambda functions implement the gateway logic:
- Authorizer: Validates JWTs and generates IAM policies based on scope-to-agent mappings.
- Registry: Lists agents the caller can access, with URLs rewritten to point to the gateway.
- Search: Semantic agent discovery using Amazon Titan Text Embeddings in Amazon Bedrock.
- Proxy: Routes requests to backend agents with OAuth authentication, supports SSE streaming via Lambda Web Adapter.
- Admin: Agent registration and lifecycle management.
Amazon DynamoDB stores three tables. The Agent Registry maps agent IDs to backend URLs, auth configs, and cached agent cards. The Permissions table maps JWT scopes to allowed agents. A RateLimitCounters table counts requests per minute.
Amazon Cognito handles authentication using OAuth 2.0 client credentials flow. Scopes in the token determine which agents the caller can access. When a client authenticates, they receive a JWT containing scopes like billing:read or support:write. The authorizer looks up these scopes in the Permissions table to determine which agents the client can reach.
AWS Secrets Manager stores backend credentials. When the Proxy Lambda needs to authenticate with a backend agent, it retrieves the OAuth client secret by Amazon Resource Name (ARN). Secrets aren’t stored in DynamoDB.
For semantic search, agent descriptions are embedded using Amazon Titan Text Embeddings and stored in Amazon S3 Vectors. This allows clients to discover agents using natural language queries rather than exact name matches.
Gateway design
A2A native endpoints follow the A2A protocol specification and route to backend agents. The gateway supports both protocol bindings defined in the spec. JSON-RPC uses a single endpoint per agent with the method in the request body:
- GET /agents/{agentId}/.well-known/agent-card.json – Fetch agent capabilities.
- POST /agents/{agentId} with {"method": "SendMessage", ...} (for buffered response).
- POST /agents/{agentId} with {"method": "SendStreamingMessage", ...} (for SSE streaming).
The HTTP+JSON/REST binding is also supported for clients that prefer RESTful URLs.
These endpoints are fully A2A protocol aligned. Clients point to the gateway URL instead of individual backend URLs. However, A2A native endpoints alone don’t solve the management problem. Clients still need a way to discover which agents exist, search for agents by capability, and manage the agent lifecycle.
Gateway endpoints provide this layer:
- GET /agents – List agents the caller can access.
- POST /search – Semantic search for agents.
- POST /admin/agents/register – Register a new backend agent.
- POST /admin/agents/{agentId}/sync – Refresh cached agent card.
- POST /admin/agents/{agentId}/status – Activate or deactivate an agent.
Every request follows the same path. The client sends a request with a JWT in the Authorization header. API Gateway invokes the Lambda authorizer, which validates the JWT and looks up the caller’s scopes in the Permissions table. The authorizer returns an IAM policy allowing or denying access to specific agents. If allowed, the request routes to the appropriate Lambda: Proxy for A2A traffic, Registry for agent discovery, Search for semantic queries, or Admin for management operations. For A2A requests, the Proxy Lambda authenticates with the backend using OAuth and forwards the request. Unauthorized requests are rejected at API Gateway and don’t reach the backend Lambdas.
The three-layer model
As agent deployments grow, teams need visibility into what’s available. The management layer provides a centralized registry where agents are cataloged with their capabilities, backend URLs, and status. When a new agent is deployed, it’s registered once in the gateway and immediately discoverable by authorized clients. The registry also caches agent cards, so clients don’t need to fetch capabilities from each backend individually. Cached cards have their URLs rewritten to point through the gateway, so clients interact with the single gateway domain rather than discovering backend URLs. For larger deployments, semantic search lets clients find agents by describing what they need rather than knowing exact names.
In an enterprise, not every client should access every agent. The control layer enforces fine-grained permissions based on JWT scopes. When a client authenticates, their token contains scopes like billing:read or support:admin. The Lambda authorizer maps these scopes to specific agents in the Permissions table and generates IAM policies that allow or deny access at the API Gateway level. Unauthorized requests don’t reach backend Lambdas. Additionally, rate limiting is enforced per-user, per-agent at the proxy level. The proxy tracks request counts using atomic DynamoDB counters with automatic time-to-live (TTL) expiration, returning a 429 with a Retry-After header when a client exceeds its quota. Permissions and rate limits are centrally managed: to grant or revoke access or adjust quotas, you update the Permissions table rather than modifying each agent.
The execution layer handles the actual routing of requests to backend agents. Clients connect to a single domain, and the gateway routes to the appropriate backend based on the path. This simplifies network configuration: instead of opening connectivity to every agent, clients only need to reach the gateway. The Proxy Lambda handles OAuth authentication with backends, so clients don’t manage backend credentials. It retrieves secrets from Secrets Manager, fetches access tokens, and forwards requests transparently. For real-time use cases, the proxy supports SSE streaming, allowing agents to send incremental responses back to clients as they’re generated.
Deploy the solution
The gateway deploys entirely with Terraform. First, verify that you have the following.
Prerequisites
- Terraform >= 1.5.0.
- Python 3.12.
- AWS Command Line Interface (AWS CLI) configured with valid credentials.
- Docker (for building the proxy Lambda container).
- An Amazon Simple Storage Service (Amazon S3) bucket for Terraform state (optional, for remote state).
The gateway code is available on the aws-samples GitHub repository.
Clone the repository and configure your variables:
cp terraform/terraform.tfvars.example terraform/terraform.tfvarsEdit terraform/terraform.tfvars with your region and naming preferences:
aws_region = "us-east-1"
project_name = "a2a-gateway"
environment = "poc"Build the Lambda package and deploy:
./scripts/build_lambda_package.sh
cd terraform
terraform init
terraform plan
terraform applyTerraform creates the resources in one go: DynamoDB tables, Cognito user pool, Amazon Elastic Container Registry (Amazon ECR) repository, Lambda functions, API Gateway, and IAM roles. Terraform builds and pushes the proxy Lambda container as part of the deployment.
Test the solution
Get your gateway credentials from Terraform outputs:
GATEWAY_URL=$(terraform output -raw api_gateway_url)
TOKEN_ENDPOINT=$(terraform output -raw cognito_token_endpoint)
CLIENT_ID=$(terraform output -raw cognito_client_id)
CLIENT_SECRET=$(terraform output -raw cognito_client_secret)
PERMISSIONS_TABLE=$(terraform output -raw permissions_table_name)Obtain a JWT:
TOKEN_RESPONSE=$(curl -s -X POST "$TOKEN_ENDPOINT" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET")
export JWT=$(echo $TOKEN_RESPONSE | jq -r .access_token)This repository includes deployable A2A example agents in the examples/ directory: a Weather Agent and a Calculator Agent. They can be deployed using their own Terraform configuration. Optionally, deploy them with cd examples/terraform && terraform apply, then capture their outputs:
# From examples/terraform after deploying the example agents
WEATHER_BACKEND=$(terraform output -raw weather_agent_backend_url)
WEATHER_CARD=$(terraform output -raw weather_agent_card_url)
AGENT_TOKEN_ENDPOINT=$(terraform output -raw cognito_token_endpoint)
AGENT_CLIENT_ID=$(terraform output -raw cognito_client_id)
AGENT_CLIENT_SECRET=$(terraform output -raw cognito_client_secret)Then register the agent with the gateway (using the $GATEWAY_URL and $JWT captured earlier):
curl -X POST "$GATEWAY_URL/admin/agents/register" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"agentId": "weather-agent",
"name": "Weather Agent",
"backendUrl": "'"$WEATHER_BACKEND"'",
"agentCardUrl": "'"$WEATHER_CARD"'",
"authConfig": {
"type": "oauth2_client_credentials",
"tokenUrl": "'"$AGENT_TOKEN_ENDPOINT"'",
"clientId": "'"$AGENT_CLIENT_ID"'",
"clientSecret": "'"$AGENT_CLIENT_SECRET"'",
"scopes": ["a2a-gateway/weather:read"]
}
}'The gateway enforces fine-grained access control. Each OAuth scope must be explicitly granted access to specific agents in the DynamoDB permissions table.
Update permissions to allow your scope to access the registered agent:
aws dynamodb put-item \
--table-name "$PERMISSIONS_TABLE" \
--item '{
"scope": {"S": "gateway:admin"},
"allowedAgents": {"L": [{"S": "weather-agent"}]},
"description": {"S": "Admin scope with access to weather agent"}
}'Discover registered agents and send a message through the gateway:
curl "$GATEWAY_URL/agents" -H "Authorization: Bearer $JWT"
curl -X POST "$GATEWAY_URL/agents/weather-agent/message:send" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"message": {
"messageId": "msg-001",
"role": "user",
"parts": [{"text": "What is the weather in New York"}]
}
}'Clean up
To clean up the solution, run terraform destroy from the /terraform folder. Terraform will ask for your permission to delete the resources.
terraform destroySecurity considerations
This gateway is a reference implementation. Before moving to production, review the following areas that require hardening based on your organization’s security posture.
Backend trust model
The gateway operates on a trust-after-authentication model. After a backend agent is registered and OAuth credentials are validated, the gateway proxies the responses directly to clients without content inspection. A2A messages are proxied without modification, so backend agents are responsible for implementing their own prompt injection defenses and input validation. In production, implement an approval workflow for agent registration where administrators review backend agents before they become accessible. Integrate this with your continuous integration and continuous delivery (CI/CD) pipeline so agents are vetted as part of the deployment process, not after.
Rate limiting and quotas
The gateway enforces per-user, per-agent rate limits at the proxy layer. Each request increments an atomic counter in DynamoDB keyed by user, agent, and minute window. When a client exceeds its quota, the proxy returns a 429 with a retry-after header and the request does not reach the backend. Counters expire automatically via DynamoDB TTL, so there’s no cleanup overhead. Limits are configured alongside access controls in the Permissions table, either as a default requests-per-minute or as per-agent overrides, giving administrators granular control over consumption.
Private deployment
For environments that require private infrastructure, the gateway supports an optional Amazon Virtual Private Cloud (Amazon VPC) deployment mode. When you enable this mode, the Lambda functions run inside private subnets. The API Gateway switches to a private endpoint accessible only within the VPC. VPC endpoints handle traffic to AWS services without traversing the internet.
The gateway supports both creating a new VPC and bringing your own. To deploy into an existing VPC, provide your VPC ID, subnet IDs, route table IDs, and security group IDs in terraform.tfvars:
enable_private_deployment = true
existing_vpc_id = "vpc-0123456789abcdef0"
existing_subnet_ids = ["subnet-aaa", "subnet-bbb", "subnet-ccc"]
existing_route_table_ids = ["rtb-aaa"]
existing_lambda_security_group_id = "sg-aaa"
existing_vpc_endpoint_security_group_id = "sg-bbb"Note that this mode makes the gateway’s infrastructure private, but your VPC still needs outbound internet connectivity for OAuth token exchange with Cognito or other external identity providers. This is typically handled through a network address translation (NAT) gateway or AWS Transit Gateway routing to a shared egress VPC. AWS service traffic (DynamoDB, Amazon S3, Secrets Manager, S3 Vectors) stays private through VPC endpoints. Only the OAuth token exchange requires outbound connectivity. If you need semantic search with Amazon Bedrock, set enable_bedrock_endpoint = true to add an Amazon Bedrock Runtime VPC endpoint as well.
For agents running on-premises or on other clouds, the private gateway is reachable over AWS Direct Connect or AWS Interconnect (preview). This lets the gateway govern agents across environments without exposing traffic to the public internet.
A2A server authentication
The gateway authenticates with backend agents using OAuth 2.0 client credentials flow. Each registered agent includes its token URL and credentials, and the Proxy Lambda handles token acquisition transparently. This means backend agent
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み