View a markdown version of this page

搭配 AgentCore 閘道使用引出 - Amazon Bedrock AgentCore

搭配 AgentCore 閘道使用引出

Elicitation 是一種 MCP 功能,可讓 MCP 伺服器在工具呼叫期間向用戶端請求其他資訊。當工具需要使用者確認、身分驗證或其他輸入才能繼續時,伺服器會將請求傳回給用戶端。AgentCore Gateway 會將請求從 MCP 伺服器目標轉送到您的用戶端,id以閘道產生的識別符取代請求。

先決條件

若要搭配閘道使用引動,您必須具有:

  • 工作階段已啟用 — 請求需要工作階段支援。請參閱搭配閘道使用 MCP 工作階段

  • 啟用回應串流 — 在開放連線期間,請求會以伺服器傳送事件 (SSE) 區塊的形式傳送。在閘道的 truestreamingConfiguration.enableResponseStreaming將 設定為 protocolConfiguration.mcp

  • MCP 伺服器目標類型 — 僅 MCP 伺服器目標支援請求。引出源自 MCP 伺服器,並透過閘道轉送至用戶端。

  • 用戶端宣告引出功能 — 用戶端必須在請求閘道轉送引出initialize請求期間宣告對引出的支援。

支援的引出模式

AgentCore Gateway 支援 MCP 規格定義的三種引出模式:

Mode 說明

表單模式

伺服器會傳送結構化表單,其中包含要由用戶端填寫的欄位。用於收集使用者確認、偏好設定或輸入資料。請求在等待回應時保持開啟狀態。

URL 模式 (以請求為基礎)

伺服器會傳送使用者必須造訪的 URL,以完成動作 (通常是身分驗證)。請求在等待動作完成時保持開啟狀態。

URL 模式 (例外類型)

伺服器擲回URLElicitationRequiredError包含 URL 的 。請求關閉,使用者在 URL 完成動作,用戶端會重試原始工具呼叫。

功能交涉

如果符合下列條件,閘道只會宣告對 MCP 伺服器目標的引出支援:

  1. 用戶端在 期間宣告引出支援initialize

  2. MCP 通訊協定版本支援引出模式 — form 模式需要版本 2025-03-26或更新版本,url模式需要版本 2025-11-25 或更新版本。

  3. 閘道符合用戶端宣告的特定引出功能 (形式、url 或兩者)。

表單模式引出流程

  1. 用戶端使用 Mcp-Session-Id標頭傳送tools/call請求。

  2. Gateway 會將工具呼叫轉送至 MCP 伺服器目標。

  3. 目標會開啟 SSE 串流,並以第一個事件傳送elicitation/create請求。

  4. Gateway 會將elicitation/create請求轉送至 SSE 串流上的用戶端,取代請求 id

  5. 用戶端會將表單呈現給使用者,並收集回應。

  6. 用戶端會使用相同的 傳送具有引出回應 (動作: acceptdecline) 的新請求Mcp-Session-Id

  7. Gateway 會將回應轉送至 MCP 伺服器目標。

  8. 目標確認 HTTP 202 已接受。

  9. 目標完成工具呼叫,並在原始 SSE 串流上傳送最終結果。

  10. Gateway 會將最終結果轉送至用戶端並關閉串流。

URL 模式 (例外型) 引出流程

  1. 用戶端使用 Mcp-Session-Id標頭傳送tools/call請求。

  2. Gateway 會將工具呼叫轉送至 MCP 伺服器目標。

  3. 目標擲回 URLElicitationRequiredError做為 JSON-RPC 錯誤,其中包含 URL 和引出 ID。

  4. Gateway 會將 轉送URLElicitationRequiredError到用戶端,取代請求 id

  5. 用戶端會將使用者重新導向至提供的 URL 以完成動作 (通常是 OAuth 身分驗證)。

  6. 使用者完成動作後,用戶端會重試原始tools/call請求。

  7. Gateway 會將重試轉送到目標。目標會在完成 URL 引出後完成工具呼叫。

  8. Gateway 會將最終工具結果轉送至用戶端。

具有引出的平行工具呼叫

即使正在等待引出,用戶端也可以在同一個工作階段中啟動多個tools/call請求。每個引動都會由其 獨立追蹤id。傳送引出回應時,用戶端必須包含閘道在elicitation/create請求中id傳送的相同 。

MCP 伺服器目標開發人員指南

重要

傳送引出請求的 MCP 伺服器目標應將引出呼叫包裝在 try-catch 區塊中,並處理用戶端不支援引出的情況。如果閘道的用戶端未宣告引出功能,閘道不會將其宣告至目標。如果目標仍然傳送引出,閘道會將 -32601(找不到方法) 錯誤傳回目標。

當引出不可用時,伺服器應實作備用路徑 (例如使用預設值或略過操作)。

錯誤處理

案例 錯誤 說明

用戶端在未等待引出時傳送引出回應

JSON-RPC -32600(無效的請求)

找不到此工作階段的相符引出。

用戶端使用不符合待定引出id的 傳送引出回應

JSON-RPC -32600(無效的請求)

id 必須與elicitation/create請求中閘道傳送的 相符。

閘道與 MCP 伺服器目標之間的連線中斷

具有 DependencyFailedException 的 JSON-RPC 錯誤

用戶端應重試原始工具呼叫請求。

用戶端和閘道之間的連線中斷

N/A

待定引出已清除。用戶端應重試工具呼叫。

MCP 伺服器傳送引出,但閘道未宣告支援

JSON-RPC -32601(找不到方法)

已傳回 MCP 伺服器目標。請參閱故障診斷

疑難排解

錯誤:「錯誤呼叫工具 'sample_tool':找不到方法:引出/建立」

當 MCP 伺服器目標傳送引出請求,但閘道的用戶端未在 期間宣告引出功能時,就會發生此錯誤initialize。閘道會將 -32601(找不到方法) 錯誤傳回至目標,而目標可能會將此做為工具執行錯誤傳回給用戶端。

若要解決問題:

  • 如果您是 MCP 伺服器開發人員:請新增引出呼叫的錯誤處理。不支援引出時實作備用路徑:

    try: result = await context.session.create_elicitation( message="Confirm this action?", requested_schema={"type": "object", "properties": {"confirm": {"type": "boolean"}}} ) except Exception as e: # Fallback when client doesn't support elicitation logger.warning(f"Elicitation not supported: {e}") result = default_action()
  • 如果您是閘道用戶端開發人員:確保您的用戶端在 期間宣告引出功能initialize

    { "capabilities": { "elicitation": { "form": {}, "url": {} } } }

程式碼範例

表單模式範例

在表單模式中,伺服器會傳送結構式結構描述供用戶端填寫。請求在等待回應時保持開啟狀態。

範例
Python requests package
  1. import requests import json import sseclient gateway_url = "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp" headers = { "Content-Type": "application/json", "Accept": "text/event-stream", "Authorization": "Bearer YOUR_ACCESS_TOKEN" } # Step 1: Initialize with elicitation capability init_response = requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": "init-request", "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {"elicitation": {"form": {}, "url": {}}}, "clientInfo": {"name": "my-agent", "version": "1.0.0"} } }) session_id = init_response.headers["Mcp-Session-Id"] headers["Mcp-Session-Id"] = session_id # Step 2: Call tool (streaming response) response = requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": "tool-call-1", "method": "tools/call", "params": { "name": "use_aws", "arguments": {"command": "aws s3 rm s3://my-bucket/important-file"} } }, stream=True) # Step 3: Process SSE events client = sseclient.SSEClient(response) for event in client.events(): data = json.loads(event.data) if data.get("method") == "elicitation/create": elicitation_id = data["id"] print(f"Form elicitation received: {data['params']['message']}") # Step 4: Send elicitation response requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": elicitation_id, "result": {"action": "accept", "content": {"confirm": True}} }) elif "result" in data: print(f"Tool result: {data['result']}") break
MCP Client
  1. from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client import asyncio async def elicitation_handler(request): """Handle form elicitation requests from the server.""" print(f"Elicitation: {request.params.message}") return {"action": "accept", "content": {"confirm": True}} async def use_elicitation(url, token): headers = {"Authorization": f"Bearer {token}"} async with streamablehttp_client(url=url, headers=headers) as ( read_stream, write_stream, _ ): async with ClientSession( read_stream, write_stream, elicitation_handler=elicitation_handler ) as session: await session.initialize() result = await session.call_tool( name="use_aws", arguments={"command": "aws s3 rm s3://my-bucket/important-file"} ) print(f"Tool result: {result}") return result asyncio.run(use_elicitation( url="https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp", token="YOUR_ACCESS_TOKEN" ))
Strands MCP Client
  1. from mcp.client.streamable_http import streamablehttp_client from mcp.types import ElicitResult from strands import Agent from strands.tools.mcp import MCPClient mcp_url = "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp" access_token = "YOUR_ACCESS_TOKEN" async def elicitation_callback(context, params): """Handle form elicitation requests from the MCP server target.""" print(f"Elicitation: {params.message}") user_response = get_user_input(params) # Your UI logic return ElicitResult(action="accept", content=user_response) mcp_client = MCPClient( lambda: streamablehttp_client( mcp_url, headers={"Authorization": f"Bearer {access_token}"} ), elicitation_callback=elicitation_callback, ) with mcp_client: agent = Agent(tools=mcp_client.list_tools_sync()) response = agent("Delete the file s3://my-bucket/important-file using AWS CLI") print(response)
LangGraph MCP Client
  1. from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_mcp_adapters.callbacks import Callbacks, CallbackContext from langchain.agents import create_agent from mcp.shared.context import RequestContext from mcp.types import ElicitRequestParams, ElicitResult async def on_elicitation( mcp_context: RequestContext, params: ElicitRequestParams, context: CallbackContext, ) -> ElicitResult: """Handle elicitation requests from MCP servers.""" print(f"[{context.server_name}] Elicitation: {params.message}") # Prompt user for input based on params.requestedSchema return ElicitResult( action="accept", content={"confirm": True}, ) client = MultiServerMCPClient( { "gateway": { "url": "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp", "transport": "http", "headers": {"Authorization": "Bearer YOUR_ACCESS_TOKEN"}, } }, callbacks=Callbacks(on_elicitation=on_elicitation), ) tools = await client.get_tools() agent = create_agent("claude-sonnet-4-20250514", tools) result = await agent.ainvoke( {"messages": [{"role": "user", "content": "Delete s3://my-bucket/important-file"}]} )

URL 模式範例

在 URL 模式中,伺服器會傳送使用者必須造訪的 URL,以完成動作 (通常是 OAuth 身分驗證)。請求在等待使用者在 URL 完成動作時保持開啟狀態。

範例
Python requests package
  1. import requests import json import sseclient import webbrowser gateway_url = "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp" headers = { "Content-Type": "application/json", "Accept": "text/event-stream", "Authorization": "Bearer YOUR_ACCESS_TOKEN", "Mcp-Session-Id": "session-abc123def456" } # Call tool that triggers URL elicitation response = requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": "tool-call-2", "method": "tools/call", "params": { "name": "access_github_repo", "arguments": {"repo": "my-org/my-repo"} } }, stream=True) # Process SSE events client = sseclient.SSEClient(response) for event in client.events(): data = json.loads(event.data) if data.get("method") == "elicitation/create": elicitation_id = data["id"] url = data["params"]["url"] print(f"URL elicitation: {data['params']['message']}") # Open browser for user to complete authentication webbrowser.open(url) input("Press Enter after completing authentication...") # Send elicitation response requests.post(gateway_url, headers=headers, json={ "jsonrpc": "2.0", "id": elicitation_id, "result": {"action": "accept"} }) elif "result" in data: print(f"Tool result: {data['result']}") break
MCP Client
  1. from mcp import ClientSession from mcp.client.streamable_http import streamablehttp_client import asyncio import webbrowser async def elicitation_handler(request): """Handle URL elicitation by opening the browser.""" if hasattr(request.params, 'url') and request.params.url: print(f"Opening URL for authentication: {request.params.url}") webbrowser.open(request.params.url) input("Press Enter after completing authentication...") return {"action": "accept"} # Form mode fallback return {"action": "accept", "content": {}} async def use_url_elicitation(url, token): headers = {"Authorization": f"Bearer {token}"} async with streamablehttp_client(url=url, headers=headers) as ( read_stream, write_stream, _ ): async with ClientSession( read_stream, write_stream, elicitation_handler=elicitation_handler ) as session: await session.initialize() result = await session.call_tool( name="access_github_repo", arguments={"repo": "my-org/my-repo"} ) print(f"Tool result: {result}") return result asyncio.run(use_url_elicitation( url="https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp", token="YOUR_ACCESS_TOKEN" ))
Strands MCP Client
  1. from mcp.client.streamable_http import streamablehttp_client from mcp.types import ElicitResult from strands import Agent from strands.tools.mcp import MCPClient import webbrowser mcp_url = "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp" access_token = "YOUR_ACCESS_TOKEN" async def elicitation_callback(context, params): """Handle URL elicitation by opening the browser.""" if hasattr(params, 'url') and params.url: print(f"Opening URL: {params.url}") webbrowser.open(params.url) input("Press Enter after completing authentication...") return ElicitResult(action="accept") return ElicitResult(action="accept", content={}) mcp_client = MCPClient( lambda: streamablehttp_client( mcp_url, headers={"Authorization": f"Bearer {access_token}"} ), elicitation_callback=elicitation_callback, ) with mcp_client: agent = Agent(tools=mcp_client.list_tools_sync()) response = agent("Access the my-org/my-repo GitHub repository") print(response)
LangGraph MCP Client
  1. from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_mcp_adapters.callbacks import Callbacks, CallbackContext from langchain.agents import create_agent from mcp.shared.context import RequestContext from mcp.types import ElicitRequestParams, ElicitResult import webbrowser async def on_elicitation( mcp_context: RequestContext, params: ElicitRequestParams, context: CallbackContext, ) -> ElicitResult: """Handle URL elicitation by opening the browser.""" if params.url: print(f"[{context.server_name}] Opening URL: {params.url}") webbrowser.open(params.url) input("Press Enter after completing authentication...") return ElicitResult(action="accept") return ElicitResult(action="accept", content={}) client = MultiServerMCPClient( { "gateway": { "url": "https://mygateway-abcdefghij.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp", "transport": "http", "headers": {"Authorization": "Bearer YOUR_ACCESS_TOKEN"}, } }, callbacks=Callbacks(on_elicitation=on_elicitation), ) tools = await client.get_tools() agent = create_agent("claude-sonnet-4-20250514", tools) result = await agent.ainvoke( {"messages": [{"role": "user", "content": "Access the my-org/my-repo GitHub repository"}]} )