7月17日、AWSが「Open Protocols with the Strands Agents SDK」と題した記事を公開した。Strands Agents SDK(Apache 2.0ライセンスのOSSエージェントフレームワーク)を通じて、MCP・A2Aをはじめとするオープンなエージェントプロトコルを実装する方法が詳しく解説されている。
AIエージェントを実用的なシステムに組み込もうとすると、プロトコルの選択と実装が最初の壁になる。記事ではMCP・A2A・x402・AG-UI・UTCPといった略語が登場する。それぞれを簡単に整理しておくと、x402はHTTPベースの機械ネイティブ決済プロトコル、AG-UIはエージェントとフロントエンドをつなぐストリーミング接続仕様、UTCP(Universal Tool Calling Protocol)はツール呼び出しのスキーマを統一する仕様だ。本稿ではこのうちMCPとA2Aに絞って紹介する。
題材として使われるのが「Pantry」というグローサリー配達コンシェルジュのエージェントだ。在庫確認・代替品提案・課金・配送スケジューリングをまとめて担う例として、各プロトコルがどこに登場するかが明示される。
MCP:エージェントにツールを持たせる
Model Context Protocol(MCP)は、エージェントが外部ツールやデータにアクセスする際の標準インターフェースだ。エージェント側は「どんなツールが使えるか」だけを知ればよく、相手のAPIがどう実装されているかは関知しない。Anthropicが主導して策定し、現在は多くのエージェントフレームワークが採用するデファクトスタンダードになりつつある。
Strands SDKではMCPClientという単一オブジェクトがこの境界を担う。MCPClientはToolProviderインターフェースを実装しており、Agentのコンストラクタのtools引数にそのまま渡せる。ローカルの@tool関数とMCPサーバー経由のツールを同じリストに混在させられる設計だ。
Pantryの例では、食料品カタログをMCPサーバーとして公開する。サーバー側はこう書く:
# catalog_server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("pantry-catalog")
@mcp.tool(description="Search the grocery catalog under a price cap.")
def search_catalog(query: str, max_price: float) -> list[dict]:
catalog = [
{"sku": "OAT-001", "name": "Oatly Oat Milk 1L", "price": 4.49},
{"sku": "OAT-002", "name": "Store Brand Oat Milk 1L", "price": 2.99},
{"sku": "MLK-010", "name": "Whole Milk 1L", "price": 1.89},
]
return [
item for item in catalog
if query.lower() in item["name"].lower()
and item["price"] <= max_price
]
@mcp.tool(description="Check whether a SKU is in stock at the user's store.")
def check_inventory(sku: str) -> dict:
out_of_stock = {"OAT-001"}
return {
"sku": sku,
"in_stock": sku not in out_of_stock,
"qty": 0 if sku in out_of_stock else 12,
}
if __name__ == "__main__":
mcp.run(transport="stdio")
@mcp.toolのdescriptionがモデルの意思決定に使われる点が重要だ。モデルはこの説明文をもとに「いつこのツールを呼ぶか」を判断する。エージェント側の接続は以下のとおり:
# pantry_agent.py
from mcp import StdioServerParameters, stdio_client
from strands import Agent
from strands.tools.mcp import MCPClient
catalog_client = MCPClient(
lambda: stdio_client(
StdioServerParameters(command="python", args=["catalog_server.py"])
)
)
pantry = Agent(
system_prompt="You are Pantry, a grocery concierge.",
tools=[catalog_client],
)
pantry("Find me oat milk under $5 and tell me if your top pick is in stock.")
MCPClientがコネクションの「ファクトリ関数」を受け取る設計になっている。接続済みのオブジェクトではなく「接続の作り方」を渡すことで、再接続やクレデンシャルのリフレッシュが自然に扱える。
A2A:別エージェントへの委譲
MCPが「ツールの呼び出し」ならば、Agent2Agent(A2A)は「判断ごと別エージェントに任せる」委譲のプロトコルだ。A2AはGoogleが主導して策定したオープン仕様であり、2025年に入って急速に注目を集めている。異なるフレームワーク・ベンダーのエージェント同士が相互運用できる点が業界での採用を後押ししており、AWSがStrands SDKでA2Aを正式サポートしたことは、エージェント間通信の標準化という大きな流れへの明確な追随といえる。
記事ではMCPとA2Aの違いを明確に言語化している。MCPによるツール呼び出しはinvocation(呼び出し元が制御を持ち続ける)、A2Aによる委譲はdelegation(受け取ったエージェントがタスクを所有する)と区別する。在庫切れ時の代替品提案のように、複数ターンの会話・独自のツール呼び出し・判断が絡む処理はA2Aで専門エージェントに委ねるのが適切だ。
Strands SDKはA2Aのサーバー側・クライアント側の両方をサポートしており、クライアント側には3段階の抽象レベルが用意されている:
| 抽象 | 制御モデル | 使いどころ |
|---|---|---|
A2AAgent |
コード駆動 | 呼ぶ相手が決まっている場合 |
A2AClientToolProvider |
モデル駆動 | モデルが実行時に委譲先を選ぶ場合 |
生のa2a-sdk |
プロトコル直接操作 | 高レベル抽象では不十分な場合 |
代替品専門エージェントをA2Aサーバーとして公開する例:
# substitutions_server.py
from strands import Agent
from strands.multiagent.a2a import A2AServer
substitutions = Agent(
name="Substitutions Agent",
description="Suggests a replacement when a grocery item is out of stock.",
system_prompt=(
"You are a grocery substitutions specialist. "
"Given an out-of-stock item and a price cap, "
"propose the closest in-stock alternative under that cap."
),
)
A2AServer(agent=substitutions, port=9001).serve()
nameとdescriptionがA2Aにおいてはエージェントの公開コントラクトになる。/.well-known/agent-card.jsonとして公開されるこの情報をもとに、他のエージェントが委譲先を選択する。system_promptは外部には公開されない。
Pantry側がモデルに委譲先を選ばせる場合はA2AClientToolProviderを使う:
# pantry_orchestrator.py
from strands import Agent
from strands_tools.a2a_client import A2AClientToolProvider
provider = A2AClientToolProvider(
known_agent_urls=[
"http://127.0.0.1:9001", # substitutions
"http://127.0.0.1:9002", # deals
"http://127.0.0.1:9003", # delivery slots
]
)
pantry = Agent(
system_prompt=(
"You are Pantry, a grocery concierge. "
"Delegate to specialist agents when a request needs substitutions, "
"deals, or delivery scheduling."
),
tools=provider.tools,
)
pantry("Oatly Oat Milk is out of stock. Find me a replacement under $4.49.")
ルーティングテーブルをハードコードするのではなく、モデルが各エージェントのカードを読んで最適な委譲先を判断する。
設計思想:MCPとA2Aの統一感
記事が強調するのは、MCPとA2Aが同じ設計パターンを異なるレイヤーに適用したものだという点だ。MCPではリモートのツールをローカルのツールと同様に扱えるようにする。A2Aではリモートのエージェントをローカルの呼び出しと同様に扱えるようにする。Strandsはプロトコル間の変換を境界で吸収するため、アプリケーションコードはStrands固有の概念のまま書ける。
記事本文にはこのほかにも、x402(機械ネイティブな決済プロトコル)・AG-UI(フロントエンドとのストリーミング接続)・UTCP(ツール呼び出しスキーマの統一仕様)に関するセクションが含まれており、それぞれStrands SDKでの実装例とともに解説されている。
詳細はOpen Protocols with the Strands Agents SDKを参照していただきたい。