ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

MCP Python SDK 处理器(Handler)能力全景:从 Context 注入到多轮往返请求

MCP Python SDK 处理器(Handler)能力全景:从 Context 注入到多轮往返请求 人工智能MCP 服务MCP Clients【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址https://gitcode.com/gh_mirrors/pythonsd/python-sdk点击查看免费下载导读在 MCPModel Context ProtocolPython SDK 中处理器handler是一个工具tool、资源resource或提示prompt背后的普通 Python 函数其参数由客户端模型传入。而除此之外的一切——请求本身、服务器共享状态、向客户端反馈的通道——则集中在这份《Inside your handler》指南所覆盖的能力中处理器能读取什么Context、依赖、生命周期状态运行中能做什么elicitation 交互、采样与 roots、进度汇报、日志、订阅通知。读完本文你将掌握在mcp.tool()、mcp.resource()与mcp.prompt()处理器中正确使用这八类能力的完整路线图并理解 2026-07-28 协议时代下多轮往返multi-round-trip请求对它们的影响。处理器能力的全景地图本指南docs/handlers/index.md把处理器的能力划分为两大阵营能读取什么来自服务器框架本身而非模型能力一句话说明详细文档Context任意处理器唯一可要求注入的附加参数携带当前请求、请求头、会话以及进度与变更通知的操作docs/handlers/context.md依赖Dependencies模型永远看不到的参数由你自己的函数通过Resolve填充docs/handlers/dependencies.md生命周期Lifespan服务器启动时只构建一次的状态处理器通过Context访问docs/handlers/lifespan.md运行中能做什么向客户端方向发出的动作能力一句话说明详细文档Elicitation / 多轮往返中途向用户索要额外输入表单或跳转 URLdocs/handlers/elicitation.md、docs/handlers/multi-round-trip.mdSampling 与 Roots请求客户端执行 LLM 补全、获取其工作区目录已弃用但仍提供服务docs/handlers/sampling-and-roots.md进度Progress为耗时操作上报进度通知docs/handlers/progress.md日志Logging面向服务器运维者输出日志写到标准错误docs/handlers/logging.md订阅Subscriptions告知已订阅的客户端列表发生了变更docs/handlers/subscriptions.md如果你还没有注册过任何处理器请先阅读 docs/servers/tools.md上述每篇页面都假设你已经有了一个可运行的处理器。处理器能读取什么三项只进不出的注入Context唯一一个任意处理器都能要求的附加参数工具的普通参数来自模型而其余一切正在服务的请求、所在的服务器、与客户端对话的通道都来自同一个对象Context。你不需要构造它、也不需要配置它——只需要要求它在任意工具函数上添加一个标注为Context的参数即可示例见 docs_src/context/tutorial001.py。SDK 为每次请求构建全新的Context并注入参数名无关紧要ctx、context、c都可以SDK 按类型标注识别资源与提示处理器可以用同样方式声明ctx.request_id即当前所服务请求的 id。在源码层面Context定义于 src/mcp/server/mcpserver/context.py其核心能力request_id、report_progress、read_resource、elicit、elicit_url均在此实现。对模型不可见是必须内化的关键点。tools/list为search_books上报的输入 schema 只有一个属性query{ type: object, properties: { query: {title: Query, type: string} }, required: [query], title: search_booksArguments }ctx不是参数它永远不会出现在 schema 中模型永远不会被告知它的存在客户端也无法填充它。这是你与 SDK 之间的契约在线上对协议不可见。Context提供的主要能力还包括await ctx.read_resource(uri)从工具内部读取服务器自己的资源与resources/read走同一注册表返回ReadResourceContents的可迭代对象await ctx.report_progress(progress, total, message)长调用期间向调用方流式上报进度await ctx.elicit(message, schema)/ctx.elicit_url(...)暂停工具并向用户提问ctx.session与当前客户端对话的服务端一侧发往客户端的通知都在这里ctx.headers传输层携带的请求头stdio 下为None可用(ctx.headers or {}).get(x-...)读取自定义头——注意头部属于客户端提供的输入适合做语言区域或特性开关永远不能当作身份凭证ctx.request_context原始请求记录最常用字段是lifespan_context即生命周期启动代码 yield 出的对象。日志刻意不在这个清单里服务器应当像任何普通 Python 程序一样使用标准库logging。另需注意注入只发生在你注册的那个函数上工具内部调用的辅助函数不会获得自己的Context需要把ctx当作普通参数向下传递——不存在可以从别处取用的隐式当前上下文。依赖Dependencies模型不能编造的参数有些值永远不该由模型来填从你自己的记录里查出的价格、只有真人能给出的确认。依赖正是由你自己的函数填充的参数——你标注参数、指明函数SDK 会在工具运行前调用它def check_stock(title: str) - Stock: ... # 参数声明处 stock: Annotated[Stock, Resolve(check_stock)]check_stock是一个resolver解析器SDK 在reserve_book之前运行它的普通函数返回值成为stock参数。resolver 可以声明自己的依赖Resolve套ResolveSDK 按依赖图顺序执行且每次调用每个 resolver 最多运行一次——多个消费者共享一次库存查询。依赖图在注册时而非调用时被分析无法归类的参数、resolver 之间的循环依赖都会在启动时抛出InvalidSignature服务器在客户端连接之前就失败并指明违规参数。实现位于 src/mcp/server/mcpserver/resolve.pyResolve类与 src/mcp/server/mcpserver/exceptions.pyInvalidSignature。与Context一样resolver 填充的参数对模型不可见reserve_book的输入 schema 只有title一个属性。模型无法供应的参数就是模型无法搞错的参数。resolver 还可以返回Elicit(message, Model)在必要时向用户提问见后文或返回Sample(...)、ListRoots()转而向客户端请求见采样与 roots 一节。完整的依赖机制含一次调用去重、依赖图的InvalidSignature校验、resolver 中Context的使用见 docs/handlers/dependencies.md。生命周期Lifespan一次构建、全程共享的状态真实服务器大多持有贯穿整个生命周期的对象数据库连接池、HTTP 客户端、加载好的模型。你不想每次调用都重建它也希望它能被干净地关闭——这就是 lifespan 的用途。生命周期是一个asynccontextmanager接收服务器并yield一个对象示例见 docs_src/lifespan/tutorial001.py。yield之前的代码是启动startupfinally中的代码是关闭shutdown整个接线只有一行MCPServer(Bookshop, lifespanapp_lifespan)。处理器通过ctx.request_context.lifespan_context取用 yield 出的对象。三个要点它真的只运行一次服务器启动时进入第一个请求之前停止时退出期间所有请求共享同一个状态对象。类型化访问在工具中写ctx: Context[AppContext]类型检查器就能确认ctx.request_context.lifespan_context是AppContext字段自动补全、拼写错误提前报错。但这个写法仅限工具放在mcp.resource()或mcp.prompt()上会导致每次调用失败报错Context is not available outside of a request——资源与提示处理器请写裸的ctx: Context。永远存在一个 lifespan不传时 SDK 默认 yield 一个空dict所以lifespan_context是{}而非None。验证生命周期时序的方式见 docs_src/lifespan/tutorial002.py给Database一个connected标志服务器启动前为False运行中调用工具返回connected停止后finally执行又回到False——工作确实发生在yield周围而不是导入时或每次请求时。详见 docs/handlers/lifespan.md。处理器运行中能做什么六类向客户端反馈的动作提问用户Elicitation 与承载它的多轮往返一个进行到一半的工具缺一个答案不必失败。Elicitation诱导让它在工具调用中途向用户发问答案回到同一个函数调用里。有两种模式表单模式form mode需要某个值确认、日期、数量你描述字段客户端渲染表单URL 模式url mode需要用户去别处OAuth 授权页、支付页用户在那边做的事不经过协议。提问有两种方式。首选是resolver把问题挂在参数上SDK 在任何连接、任何协议时代下替你发问。直接方式await ctx.elicit(...)是服务器到客户端的请求该通道只对 legacy 连接2025-11-25 及更早的客户端存在。在 2026-07-28 连接上服务器返回问题而非推送问题客户端下一次尝试携带答案——这正是 docs/handlers/multi-round-trip.md 讲述的机制详见下文。resolver 形式两种时代都可用你的 resolver 代码无需区分。表单模式中ctx.elicit(message, schemaModel)接受一条消息和一个 Pydantic 模型示例见 docs_src/elicitation/tutorial001.py。客户端收到消息和由模型生成的 JSON Schema字段Field(description...)即表单标签default预填输入并使字段可选。答案有且仅有三种accept提交了表单result.data是已验证的模型实例、decline用户拒绝、cancel用户未选择就关闭。拒绝不是错误——由工具决定拒绝意味着什么并正常答复模型。一个重要的约束elicitation schema 不如工具的输入 schema 表达力强——只支持扁平的原始字段str、int、float、bool或字符串Literal模型里嵌模型会在发送前直接抛错TypeError: Elicitation schema field ... is not a valid PrimitiveSchemaDefinition。因为你是在打断一个正在做任务的人答案如果需要嵌套它本应是工具的参数。URL 模式用ctx.elicit_url(message, url, elicitation_id)示例见 docs_src/elicitation/tutorial002.py凭证、卡号、OAuth 同意等必须经过模型或客户端的事情交给用户在浏览器侧带外完成accept只表示用户同意打开 URL不代表另一端流程已完成。当服务器从 webhook 或轮询得知带外流程结束时调用ctx.session.send_elicit_complete(elicitation_id)发送notifications/elicitation/complete客户端才能停止显示等待支付…。客户端一侧通过给Client(...)传入一个elicitation_callback来应答示例见 docs_src/elicitation/tutorial003.py一个回调同时处理两种模式params是ElicitRequestFormParams与ElicitRequestURLParams的联合类型用isinstance分支。传入回调同时也是能力声明——服务器据此知道这个客户端可以被提问。注意 legacy 连接才需要这个 server→client 通道所以示例客户端传入modelegacy。若客户端没有注册回调就调用需要提问的工具整个调用会以协议错误失败Elicitation not supported。多轮往返Multi-round-trip2026-07-28 时代的返回而非回调有时工具一个来回完不成它需要只有用户有的东西。2026-07-28 之前的做法是服务器回拨——在处理原请求中途向客户端开一个新请求elicitation、sampling。2026-07-28 规范废除了这条反向通道改为服务器返回服务器用InputRequiredResult应答tools/call而非CallToolResult两个字段起作用input_requests服务器还缺什么一个按服务器自选键名组织的 dict每个值是一个ElicitRequest、CreateMessageRequest或ListRootsRequestrequest_state不透明令牌客户端在重试时原样回显只有你的服务器能读它。客户端满足每个请求后再次调用同一个工具在input_responses中携带答案、在request_state中携带令牌服务器拿到缺的东西返回正常的CallToolResult。协议的每一段都是客户端到服务器的普通请求没有任何反向流量。在mcp.tool()上你很少手工构建它声明一个会提问的依赖Elicit、采样客户端 LLMSample或列出其 rootsListRootsSDK 就替你返回InputRequiredResult。两种形式不能混用一个调用只有一条input_responses/request_state通道所以使用Resolve(...)参数的工具体内不能再返回InputRequiredResult注册时报InvalidSignature。手工形式是底层Server其on_call_tool处理器返回类型为CallToolResult | InputRequiredResult返回后者就是完整的服务端 API示例见 docs_src/mrtr/tutorial001.py。tools/call并不特殊2026-07-28 下服务器同样可以用InputRequiredResult应答prompts/get和resources/read。mcp.prompt()函数或mcp.resource()模板函数返回InputRequiredResult本身并在重试时从ctx.input_responses读取答案。静态mcp.resource()函数不参与——它们不接收Context无法读取重试。在 2026-07-28 连接上URL 模式的 elicitation 正是乘坐这个机制input_requests中的条目是携带ElicitRequestURLParams的ElicitRequest用户完成带外流程后客户端重试即可。客户端侧由Client替你运行循环注册服务器可能用到的回调elicitation_callback、sampling_callback、list_roots_callback然后调用工具即可。收到InputRequiredResult时Client把input_requests的每条分发给匹配回调用答案和回显的request_state重试直到拿到CallToolResultcall_tool最终返回普通的CallToolResult中间轮次对调用方不可见。get_prompt与read_resource驱动同一个循环。循环有界Client(..., input_required_max_rounds10)是默认上限如果某一轮只携带request_state而无input_requests服务器在说还没好Client会短暂休眠50ms 起、翻倍至 250ms 封顶再重试避免忙轮询。分布式客户端可自行驱动循环当渲染问题的进程不是调用call_tool的进程时使用底层会话client.session.call_tool(..., allow_input_requiredTrue)拿到联合返回类型自行while isinstance(result, InputRequiredResult)循环——request_state是你可以跨进程持久化的令牌input_responses是对端随它送回的东西。每条input_requests都要在input_responses的同一个键下放一条InputResponse工具名与arguments每一轮都相同重试是原调用的再次执行而非新方法。保护requestState是部署前必须知道的最后一件事。客户端在轮次之间持有它跨进程写下正是上一节所提倡的所以回传的它是客户端提供的输入可能被篡改、过期或从别的调用中取出。MCPServer默认保护它每个服务器在进程启动时生成密钥对发出的requestState加密密封并验证每一次回显——resolver 状态与手工构建状态一视同仁。你配置什么都不需要写明文、读明文线上只携带不透明的加密令牌。默认密钥随进程生灭因此多进程部署必须显式配置from mcp.server.mcpserver import MCPServer, RequestStateSecurity # 多实例或重启后仍需验证一个或多个共享密钥每个 32 字节。 mcp MCPServer(fleet, request_state_securityRequestStateSecurity(keys[key]))密封不仅保证完整性每个令牌还绑定到时间窗口RequestStateSecurity(ttl...)默认 600 秒限制的是单轮思考时间而非整个流程、已认证主体请求携带经 SDK 验证的 OAuth 访问令牌时绑定其 client、issuer、subject、发起请求方法、工具/提示名或资源 URI 及参数摘要、被问的确切问题每个 resolver 答案都钉在客户端所看到的问题渲染上。验证失败的统一答复是冻结的错误{code: -32602, message: Invalid or expired requestState}无论篡改、过期还是密钥不符都是这一个答复线上不暴露任何校验细节。密钥轮换分三阶段[OLD, NEW]所有人学会验证 NEW→[NEW, OLD]NEW 铸造、在途 OLD 仍可验证→[NEW]一个 ttl 后淘汰 OLD切不可先提升铸造者。也可以自带加密RequestStateSecurity(codec...)接受任何实现seal(bytes) - str与unseal(str) - bytes且对未铸造令牌抛InvalidRequestState的对象——典型形态是 KMS 信封加密示例见 docs_src/mrtr/tutorial005.py。TTL、主体绑定、请求绑定都不是 codec 的职责SDK 在seal之前把它们盖进载荷、在unseal之后重新验证。InputRequiredResult只存在于协议版本2026-07-28。Client默认modeauto在任何连接上自动发现连接后client.protocol_version告诉你得到了什么。在 legacy 会话上返回它客户端只会得到-32603Handler returned an invalid result——同时服务两个时代的服务器必须先检查ctx.protocol_version。完整机制见 docs/handlers/multi-round-trip.md它取代了服务器发起的采样等推送式反向通道弃用清单见 docs/deprecated.md。采样与 Roots借用客户端的模型与目录已弃用处理器还可以向已连接的客户端索要两样东西客户端自己模型的补全sampling与客户端的工作区目录roots。两者在所有协议版本上仍可工作但已被 2026-07-28 规范弃用SEP-2577至少十二个月内保持完整功能但新实现不应在此基础上构建。建议的迁移方向直接集成你的 LLM 提供商 API 取代 sampling用工具参数、资源 URI 或服务器配置传递目录取代 roots。采样resolver 返回Sample(messages, max_tokens...)镜像sampling/createMessage参数示例见 docs_src/sampling_and_roots/tutorial001.py工具通过依赖机制收到客户端的CreateMessageResult传入tools或tool_choice时为CreateMessageResultWithTools。客户端必须声明过sampling能力传tools/tool_choice时需要sampling.tools否则调用以-32021协议错误失败。include_context除none之外的值本身也已被弃用SEP-2596不要动它。Rootsresolver 返回ListRoots()示例见 docs_src/sampling_and_roots/tutorial002.py注入的ListRootsResult携带Root列表file://URI 与可选显示名。roots 是信息性指引不是访问控制机制。未声明roots能力同样以-32021失败。两条路径的传输时代选择与 elicitation 一致2026-07-28 下在多轮往返流程内送达2025-11-25 下是独立的服务器→客户端请求。注意多轮往返的规则请求必须跨重试轮次渲染一致所以只能用工具参数和其他稳定数据构建。客户端用已有的sampling_callback与list_roots_callback应答见 docs/client/callbacks.md。ctx.session.create_message(...)与ctx.session.list_roots()仍然存在以驱动旧代码但只在存在反向通道的 2025 时代连接上工作且调用会触发弃用警告。详见 docs/handlers/sampling-and-roots.md。进度Progress让三十秒的工具看起来还活着一个要跑三十秒却三十秒一言不发的工具看起来像坏了。进度通知解决这个问题工具上报进度到哪了客户端决定画成进度条、旋转指示器还是日志行。服务端只需接收一个Context参数并调用report_progress示例见 docs_src/progress/tutorial001.pyawait ctx.report_progress(progress, totalNone, messageNone)三个参数的含义由你决定progress表示进行到哪规范要求每次上报递增不要重复或回退、total表示总量可选只有你知道时才给、message是关于当前这一步的一行人类可读文本可选。字节、行数、页数——选用户能识别的单位并且只承诺你能兑现的total。客户端每次调用通过progress_callback选择加入call_tool的参数不是Client构造参数——不同调用想要不同的回调一个驱动下载条下一个驱动日志行。回调是async (progress, total, message) - None在工具仍在运行时逐个触发每次通知独立送达与响应并列因此慢回调可能在call_tool返回后仍在运行只有进程内测试连接是内联执行的async def show(progress: float, total: float | None, message: str | None) - None: print(f{message} ({progress}/{total})) result await client.call_tool( import_catalog, {urls: [...]}, progress_callbackshow, )关键的宽容设计没有回调时report_progress是 no-op——不报错、不警告、结果相同。所以你在服务端无条件上报永远不必担心有没有人在听。当不知道总量时正在排空一个 feed、游标扫描、无长度头的下载省略total回调收到None客户端仍可显示已导入 3 个…之类的活动信息但无法显示百分比——不要为了好看的进度条编造一个 total。完整细节见 docs/handlers/progress.md。日志Logging用标准库写给运维者从工具里记录日志和从任何其他 Python 函数里记录一样用标准库。MCP 协议曾有一个协议级 logging 能力——服务器通过Context上的方法把日志消息作为通知推给客户端——但 2026-07-28 修订版弃用了该能力且未提供替代品因此本指南不教它完整弃用清单见 docs/deprecated.md。替代方案就是你在每个 Python 程序里做的标准库。import logging logger logging.getLogger(__name__) mcp.tool() async def search_books(query: str, ctx: Context) - ...: logger.info(Searching for %r, query) ...logging.getLogger(__name__)给你一个以模块命名的 logger在顶部创建一次工具内调用logger.info(...)与任何函数无异——无需注入、无需await、没有任何 MCP 特有的东西。日志行永远不出现在工具结果里result.content与result.structured_content都没有它——日志是写给你服务器运维者的模型永远看不到如果模型应该读到什么请return它。去向问题对stdio服务器尤其重要宿主编译你的服务器为子进程从stdout读取 MCP 消息——标准错误是你的地盘。标准库默认就把日志输出到sys.stderr协议流保持干净。不要在 stdio 服务器里用print()print写 stdout而 stdout 属于协议。虽然服务期间 SDK 会把真正 flush 的游离 stdout 转向 stderr 以免污染线路但块缓冲进程中的print()通常滞留在sys.stdout缓冲区直到解释器退出时才排空直落协议流即使被转向那行文本也是无级别、无 logger 名、无法过滤的裸文本。logger.debug(got here)同样一行工作量却去了正确的地方。你也不必自己调用logging.basicConfig()构造MCPServer时已经调用过带一个指向标准错误的 handler级别就是你传入的log_level。因此MCPServer(Bookshop, log_levelDEBUG)一条就够了默认是INFO。logging.basicConfig()从不替换已存在的 handler——如果你在创建服务器之前自己配置了日志你的配置胜出。处理器抛异常时 SDK 也会替你记录见 docs/servers/handling-errors.md。如果你的真正需求是追踪每个请求、耗时、是否失败你要的不是日志行而是 spanSDK 已经用 OpenTelemetry 内置追踪每条消息见 docs/run/opentelemetry.md。详见 docs/handlers/logging.md。订阅Subscriptions告诉已订阅客户端有变化服务器的目录不是固定的工具在运行时出现资源 URI 背后的内容会变。订阅是客户端获知这些变化的方式客户端发送一个subscriptions/listen请求而该请求的响应本身就是流——它保持打开承载客户端要求的变更通知。服务端一侧只是一行发布变更示例见 docs_src/subscriptions/tutorial001.py。await ctx.notify_resource_updated(board://sprint) # 只到达订阅该 URI 的流 await ctx.notify_tools_changed() # 到达所有请求了工具列表变更的流兄弟方法是notify_prompts_changed()与notify_resources_changed()。没有订阅者就没有工作向空闲服务器发布是 no-op所以你从不需要检查有没有人在听——你只陈述发生了什么。MCPServer替你服务subscriptions/listen线上的确认首帧、按流过滤、每帧上的订阅 id 都是 SDK 的职责。线上形态每个帧都携带 listen 请求的 JSON-RPC id 于_meta之下那个 id 就是订阅 id——由客户端铸造PythonClient用listen-1这样的字符串其他客户端可能用整数。变更通知不携带内容更新的不是看板本身只是看板变了因此订阅是线索cue而非载荷两端都要重新拉取。过滤是契约只请求了工具列表变更和一个资源 URI 的流只收到这两种通知其余保持沉默。MCPServer将资源 URI 按精确字符串匹配订阅board://sprint的流听不到board://sprint/tasks/1的变化。还要明确流不是什么它不是重放日志断开的流就没了无人连接时发布的事件不会排队客户端重新 listen 并重新拉取它也不是 2025 的路径调用过resources/subscribe的客户端由ctx.session.send_resource_updated(uri)服务notify_*方法只到达subscriptions/listen流。谁可以看默认是开放的——任何调用者可以监视你发布的任何 URI不会咨询你的读取处理器。默认宽但窄看客永远学不到内容也无法探测存在性未知 URI 也被接受只是永不触发。但从多租户服务器发布按用户区分的 URI 前请务必加一道中间件闸门示例见 docs_src/subscriptions/tutorial006.py中间件在 SDK 确认subscriptions/listen之前看到请求ctx.params是原始请求校验为SubscriptionsListenRequestParams后读取客户端要求的过滤条件拒绝就是在call_next(ctx)之前抛MCPError客户端收到错误且没有流。一个can_access(user, uri)同时回答两个问题资源处理器在resources/read上问它中间件在subscriptions/listen上问它。决策在流的整个生命周期内有效没有逐事件复查——如果调用者的访问权限可能中途失效如令牌过期到期时就结束该调用者的连接。中间件完整契约见 docs/advanced/middleware.md。客户端一侧进入client.listen(...)就发送请求并等待确认因此块开始时流已经活跃每个类型化事件都是重新拉取的提示完整客户端故事见 docs/client/subscriptions.md。跨进程扩展发布经由一个SubscriptionBus从处理器到达打开的流。默认是内存实现——单进程内所有流。负载均衡后的多副本部署中客户端的流被钉在某一个副本上另一个副本上的发布必须能到达它。这个接缝由你实现两个方法publish与subscribe对接你的 pub/sub 后端from mcp.server.mcpserver import MCPServer from mcp.server.subscriptions import ServerEvent # SubscriptionBus 是 Protocol无基类 class RedisSubscriptionBus: def __init__(self, redis): ... # 持有 Redis 客户端与本地 listener 注册表 async def publish(self, event: ServerEvent) - None: ... # 发布到每个副本 def subscribe(self, listener) - Callable[[], None]: ... # 返回注销函数 mcp MCPServer(Sprint Board, subscriptionsRedisSubscriptionBus(redis))总线携带类型化的ServerEvent值四个小型 dataclass从不携带 JSON-RPC盖章、过滤、流生命周期都留在 SDK 内所以总线实现不可能破坏协议只能把事件在进程间搬运。要在请求之外发布lifespan 任务、webhook自行构造总线并持有引用即可MCPServer在你不传时内部构建一个且不暴露它。底层Server上无预接线同样部件三行组装自持总线直接await bus.publish(ResourceUpdated(uri...))、ListenHandler(bus)是MCPServer注册的同一个处理器、on_subscriptions_listen是普通处理器槽位ListenHandler.close()优雅结束每条打开的流每流收到 listen 请求的结果作为最后一帧。详见 docs/handlers/subscriptions.md。源码层面的能力印证上述能力在仓库源码中均有对应实现可作为深入阅读的入口Context注入机制定义于 src/mcp/server/mcpserver/context.py包含report_progress第 113 行、read_resource第 151 行、elicit/elicit_url第 216/222 行、request_id第 292 行等核心方法request_context属性按需从ServerRequestContext解析未注入时给出明确报错。依赖解析器Resolve、Elicit、Sample、ListRoots标记类定义于 src/mcp/server/mcpserver/resolve.py依赖图在注册时分析无法分类的参数或循环依赖抛InvalidSignature同一模块第 235、301 行。订阅总线SubscriptionBus协议与InMemorySubscriptionBus位于 src/mcp/server/subscriptions.pyListenHandler亦在其附近。教学示例源码本文引用的全部可运行示例位于 docs_src/ 下如 docs_src/context/tutorial001.py、docs_src/elicitation/tutorial001.py、docs_src/mrtr/tutorial001.py、docs_src/subscriptions/tutorial001.py对应测试见 tests/docs_src/test_context.py、test_elicitation.py、test_mrtr.py、test_subscriptions.py等。从哪开始处理器能力的两大阵营有一条清晰的规则来自模型之外的输入走注入Context、依赖、lifespan去往客户端的动作走Context提供的通道进度、elicitation、订阅而在 2026-07-28 协议时代向用户/客户端要东西的统一载体是返回InputRequiredResult的多轮往返机制resolver 则是它在高层 API 上的声明式形态。如果你还没有注册过任何处理器请先以 docs/servers/tools.md 为起点注册第一个工具本系列每篇页面都假设你已经有一个可运行的处理器。之后按需深入docs/handlers/context.md注入基础→ docs/handlers/dependencies.md模型不可见的参数→ docs/handlers/lifespan.md共享状态→ 再按业务需要选择 docs/handlers/elicitation.md、docs/handlers/multi-round-trip.md、docs/handlers/progress.md、docs/handlers/logging.md、docs/handlers/subscriptions.md 中的对应篇章。赞分享人工智能MCP 服务MCP Clients【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址https://gitcode.com/gh_mirrors/pythonsd/python-sdk点击查看免费下载相关推荐深入 MCP Python SDK 的多轮往返请求InputRequiredResult 与 requestState 安全机制深入 MCP Python SDK 的多轮往返请求InputRequiredResult 与 requestState 安全机制 多轮往返multi rou人工智能MCP 服务MCP ClientsMCP Python SDK 多轮往返请求Multi-round-trip实战从 InputRequiredResult 到 requestState 安全机制MCP Python SDK 多轮往返请求Multi round trip实战从 InputRequiredResult 到 requestState 安人工智能MCP 服务MCP ClientsPython SDK 多轮往返Multi-Round-Trip请求实战从 InputRequiredResult 到 requestState 保护Python SDK 多轮往返Multi Round Trip请求实战从 InputRequiredResult 到 requestState 保护 导读人工智能MCP 服务MCP Clients上一篇Human库入门5分钟搭建你的第一个AI人体分析应用下一篇使用OpenMind库加载BiomedNLP-BiomedBERT完整代码示例与常见问题解决创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表