MultiServerMCPClient利用注册的回调可以帮助我们处理由服务端发送给客户端的如下三种请求和通知进度报告订阅长时间运行工具执行的进度更新日志回传MC协议支持记录来自服务器的通知信息征询信息征询Elicitation机制允许MCP服务器在工具执行期间向用户请求额外输入。服务器无需预先获取所有输入而是可以根据需要以交互方式请求信息上述三种回调对应Callbacks类型如下所示的三个字段on_progress、on_logging_message和on_elicitation它的to_mcp_format方法会将它们统一转换称MCP规范定义的标准形式。我们利用__init__方法的callbacks参数将Callbacks对象注册到MultiServerMCPClient对象上。dataclassclassCallbacks:on_logging_message:LoggingMessageCallback|NoneNoneon_progress:ProgressCallback|NoneNoneon_elicitation:ElicitationCallback|NoneNonedefto_mcp_format(self,*,context:CallbackContext)-_MCPCallbacksclassMultiServerMCPClient:def__init__(self,connections:dict[str,Connection]|NoneNone,*,callbacks:Callbacks|NoneNone,tool_interceptors:list[ToolCallInterceptor]|NoneNone,tool_name_prefix:boolFalse,)-None1. 进度报告客户端用于处理进度报告的ProgressCallback类型是一个具有唯一__call__方法的协议所以我们可以将其此回调定义成函数。runtime_checkableclassProgressCallback(Protocol):asyncdef__call__(self,progress:float,total:float|None,message:str|None,context:CallbackContext,)-NonedataclassclassCallbackContext:server_name:strtool_name:str|NoneNone__call__方法参数说明progress表示完成工作量的数值total表示总工作量的数值message描述进度的文本消息context提供MCP服务器和当前调用工具名称的上下文我们通过一个简答的实例来演示如何利用注册到MultiServerMCPClient的ProgressCallback回调来实时显示服务端回传的进度。如下所示的是由FastMCP构建的MCP服务器。注册的long_running_task工具模拟一个长耗时5秒的操作我们每隔一秒调用Context对象的report_progress方法报告一次进度。importasynciofromfastmcpimportFastMCPfromfastmcp.server.contextimportContext mcpFastMCP(Server,tasksTrue)mcp.toolasyncdeflong_running_task(context:Context)-int:foriinrange(1,6):awaitcontext.report_progress(progressi,total5,messagefStep{i}completed)awaitasyncio.sleep(1)return5mcp.run(transportstreamable-http,host0.0.0.0)在如下所示的客户端程序中我们定义了用于处理进度报告的handle_progress函数它会实时输出接收到的进度通知。我们针对这个函数创建了Callbacks对象并将其注册到连接上面这个MCP服务器的MultiServerMCPClient对象上。fromlangchain_mcp_adapters.clientimportMultiServerMCPClientfromlangchain_mcp_adapters.callbacksimportCallbacks,CallbackContextimportasyncio,datetimeasyncdefhandle_progress(progress:float,total:float|None,message:str|None,context:CallbackContext,)-None:percentage(progress/(totalor100))*100print(f[{datetime.datetime.now()}] Progress:{percentage:.1f}% -{messageor})callbacksCallbacks(on_progresshandle_progress,)asyncdefmain():clientMultiServerMCPClient(connections{server:{transport:streamable_http,# 或者 streamable_httpurl:http://localhost:8000/mcp}},callbackscallbacks)tool(awaitclient.get_tools(server_nameserver))[0]resultawaittool.ainvoke(input{})print(fResult:{result})asyncio.run(main())我们调用MultiServerMCPClient的get_tools方法得到工具对象并直接调用的ainvoke调用此工具。handle_progress函数输出的进度和最终的结果将会以如下的形式输出来[2026-04-11 23:33:42.309599] Progress: 20.0% - Step 1 completed [2026-04-11 23:33:43.315791] Progress: 40.0% - Step 2 completed [2026-04-11 23:33:44.317458] Progress: 60.0% - Step 3 completed [2026-04-11 23:33:45.328593] Progress: 80.0% - Step 4 completed [2026-04-11 23:33:46.344349] Progress: 100.0% - Step 5 completed Result: [{type: text, text: 5, id: lc_eeb16e28-6729-4c30-b316-a6a8385970f1}]2. 日志回传用于处理服务端回传日志的回调类型LoggingMessageCallback定义如下这个协议同样只定义了唯一的__call__方法日志等级、logger表示由谁写入和数据可以通过params参数表示的LoggingMessageNotificationParams对象提取出来。classLoggingMessageCallback(Protocol):asyncdef__call__(self,params:LoggingMessageNotificationParams,context:CallbackContext,)-NoneclassLoggingMessageNotificationParams(NotificationParams):level:LoggingLevel logger:str|NoneNonedata:Any model_configConfigDict(extraallow)在上面演示的例子中服务端指定的工具通过调用Context对象的report_progress方法报告进度我们也可以按照如下的方式将其改写成利用日志报告进度Context的info方法表示采用Information等级写入日志。importasynciofromfastmcpimportFastMCPfromfastmcp.server.contextimportContext mcpFastMCP(Server,tasksTrue)mcp.toolasyncdeflong_running_task(context:Context)-int:foriinrange(1,6):awaitcontext.info(messagef{i*20}% completed)awaitasyncio.sleep(1)return5mcp.run(transportstreamable-http,host0.0.0.0)那么客户端程序注册到MultiServerMCPClient上的Callbacks只需按照如下的方式定义就可以了asyncdefhandle_logging(params:LoggingMessageNotificationParams,context:CallbackContext,)-None:print(f[{datetime.datetime.now()}] [{params.level}]{params.data})callbacksCallbacks(on_logging_messagehandle_logging)工具执行的进度将以如下的形式输出[2026-04-11 23:53:00.563647] [info]{msg: 20% completed, extra: None} [2026-04-11 23:53:01.569508] [info]{msg: 40% completed, extra: None} [2026-04-11 23:53:02.584512] [info]{msg: 60% completed, extra: None} [2026-04-11 23:53:03.595626] [info]{msg: 80% completed, extra: None} [2026-04-11 23:53:04.604788] [info]{msg: 100% completed, extra: None} Result: [{type: text, text: 5, id: lc_d3862595-0977-4001-9a2d-6f921c56357b}]3. 信息征询Elicitation是我们可以在工具执行期间请求用户的结构化输入。它允许工具无需预先要求所有输入而是可以根据需要交互式地询问缺失的参数、澄清说明或补充上下文信息。启发功能使工具能够暂停执行并向用户请求特定信息如下是几种典型的应用场景参数缺失询问用户是否提供了初始未提供的必要信息澄清请求针对模糊不清的情况获取用户的确认或选择渐进披露逐步收集复杂信息动态工作流程根据用户响应调整工具行为如下所示的协议ElicitationCallback代表客户端处理Elicitation的回调。从__call__方法的定义可以看出我们可以从mcp_context和params参数中获取Elicitation请求的相关信息针对请求的处理结果体现在返回的MCPElicitResult对象上。classElicitationCallback(Protocol):asyncdef__call__(self,mcp_context:MCPRequestContext,params:ElicitRequestParams,context:CallbackContext,)-MCPElicitResultmcp_context参数类型为RequestContext。这是一个相对复杂的泛型数据类它是整个请求生命周期的“内存快照”存储了处理一个请求所需的所有环境信息。泛型设计是为了高度解耦以适配不同类型的Session如 Webhook, WebSocket, SSE 等和不同类型的生命周期管理。SessionTTypeVar(SessionT,boundBaseSession[Any,Any,Any,Any,Any])LifespanContextTTypeVar(LifespanContextT)RequestTTypeVar(RequestT,defaultAny)dataclassclassRequestContext(Generic[SessionT,LifespanContextT,RequestT]):request_id:RequestId meta:RequestParams.Meta|Nonesession:SessionT lifespan_context:LifespanContextT experimental:Anyfield(defaultNone)request:RequestT|NoneNoneclose_sse_stream:CloseSSEStreamCallback|NoneNoneclose_standalone_sse_stream:CloseSSEStreamCallback|NoneNoneCloseSSEStreamCallbackCallable[[],Awaitable[None]]字段成员说明如下request_id请求的标识meta请求相关元数据session当前会话lifespan_context存储随请求生命周期存在的数据例如数据库连接、临时缓存experimental提供一些支持的实验性的特性request原始请求对象的引用如 HTTP Request 对象方便底层调试。close_sse_stream/close_standalone_sse_stream提供一个可执行对象关闭SSE连接Elicitation是服务器向用户索取额外信息的机制。根据信息的敏感程度和交互方式它分为Form表单和URL两种模式Form模式这是一种 “在客户端内” 完成的交互方式。服务器发送一个JSON Schema给客户端客户端根据这个架构在聊天窗口或UI中直接渲染一个输入框或表单如单选、多选、文本框。这种模式用于收集非敏感的结构化信息URL模式这是一种需要 “跳转到外部” 完成的交互方式。服务器向客户端发送一个外部URL客户端引导用户在外部浏览器中打开该链接进行操作。这种模式用于处理高敏感度或必须绕过AI客户端的操作确保敏感数据如密码不会流经LLM或MCP 客户端。典型的例子包括OAuth授权、支付处理和用户凭证收集等params参数的类型ElicitRequestParams是由ElicitRequestURLParams和ElicitRequestFormParams它们分别代表针对上面两种模式的请求参数。ElicitRequestParams:TypeAliasElicitRequestURLParams|ElicitRequestFormParamsclassElicitRequestFormParams(RequestParams):mode:Literal[form]formmessage:strrequestedSchema:ElicitRequestedSchema model_configConfigDict(extraallow)classElicitRequestURLParams(RequestParams):mode:Literal[url]urlmessage:strurl:strelicitationId:strmodel_configConfigDict(extraallow)作为针对Elicitation请求响应结果的ElicitResult它利用action字段表达对应接收到的请求它是接受accept、拒绝decline还是无视cancel。并在接受的前提下利用content提供所需的数据。classElicitResult(Result):action:Literal[accept,decline,cancel]content:dict[str,str|int|float|bool|list[str]|None]|NoneNone我们通过一个简单的实例演示工具在执行过程中如何利用信息征询向客户端请求所需的信息。在如下这个使用FastMCP构建的MCP服务器中我们注册了一个collect_user_info工具它在执行过程中会调用Context的elicit方法向客户端请求提供用户信息。fromfastmcpimportFastMCP,Contextfromdataclassesimportdataclass serverFastMCP(Server)dataclassclassUserInfo:name:strage:intserver.toolasyncdefcollect_user_info(ctx:Context)-str:resultawaitctx.elicit(messagePlease provide your information,response_typeUserInfo)ifresult.actionaccept:userresult.datareturnfHello{user.name}, you are{user.age}years old.elifresult.actiondecline:returnInformation not providedelse:returnOperation cancelledserver.run(transportstreamable-http,host0.0.0.0)在如下所示的客户端程序中我们将ElicitationCallback定义为如下这个handle_elicitation函数。在它利用返回的ElicitResult提供用户信息之前它还会将params参数承载的请求信息打印出来。我们利用handle_elicitation函数创建了Callbacks对象并将其注册到连接上面这个MCP服务器的MultiServerMCPClient对象上。我们最终调用此对象的get_tools方法得到代表collect_user_info工具的BaseTool对象调用ainvoke方法执行此工具并输出返回的结果。fromtypingimportcastfromlangchain_mcp_adapters.clientimportMultiServerMCPClientfromlangchain_mcp_adapters.callbacksimportCallbacks,CallbackContextfrommcp.shared.contextimportRequestContextfrommcp.typesimportElicitRequestParams,ElicitResult,ElicitRequestFormParamsimportasyncioasyncdefhandle_elicitation(mcp_context:RequestContext,params:ElicitRequestParams,context:CallbackContext,)-ElicitResult:Handle elicitation requests from MCP servers.formParams:ElicitRequestFormParamscast(ElicitRequestFormParams,params)print(f\ Request: mode:{formParams.mode}message:{formParams.message}response_format:{formParams.requestedSchema})returnElicitResult(actionaccept,content{name:Jayden,age:18},)asyncdefmain():clientMultiServerMCPClient({server:{url:http://localhost:8000/mcp,transport:streamable_http,}},callbacksCallbacks(on_elicitationhandle_elicitation),)tool(awaitclient.get_tools(server_nameserver))[0]resultawaittool.ainvoke(input{})content:dictresult[0]print(f\ Result: type:{content[type]}content:{content[text]})asyncio.run(main())输出Request: mode: form message: Please provide your information response_format: {properties: {name: {title: Name, type: string}, age: {title: Age, type: integer}}, required: [name, age], title: UserInfo, type: object} Result: type: text content: Hello Jayden, you are 18 years old.