7.2 两种模式:表单与 URL


文档摘要

7.2 两种模式:表单与 URL 本节摘要:本节讲引导填写的两种实现模式——表单模式( 带 Schema,宿主弹结构化表单)与 URL 模式( ,宿主跳转外部页面)。两者都是「把用户引导到一个界面,拿到回答再继续」,但适用场景不同:表单适合简单确认与填几个字段,URL 适合 OAuth、富交互、复用现有 Web 流程。本节讲透两种模式的写法、何时用哪个,为下一节(现代协议下的 MRTR 实现)做好铺垫。 一、表单模式:结构化表单 表单模式让宿主弹出一个结构化表单,你用一个 Schema 描述「要问什么、什么类型」: 接受: :要问的问题文本 :表单结构(JSON Schema,描述字段与类型) 返回的 是用户填的答案(字典)。

7.2 两种模式:表单与 URL

本节摘要:本节讲引导填写的两种实现模式——表单模式(elicit 带 Schema,宿主弹结构化表单)与 URL 模式(elicit_url,宿主跳转外部页面)。两者都是「把用户引导到一个界面,拿到回答再继续」,但适用场景不同:表单适合简单确认与填几个字段,URL 适合 OAuth、富交互、复用现有 Web 流程。本节讲透两种模式的写法、何时用哪个,为下一节(现代协议下的 MRTR 实现)做好铺垫。

一、表单模式:结构化表单

表单模式让宿主弹出一个结构化表单,你用一个 Schema 描述「要问什么、什么类型」:

from mcp.server.mcpserver import Context @mcp.tool() async def transfer(from_id: str, to_id: str, amount: float, ctx: Context) -> bool: """Transfer money, asking confirmation for large amounts.""" if amount > 1000: # 中途反问:用 Schema 描述表单 result = await ctx.elicit( message=f"转账金额 {amount} 较大,确认执行吗?", schema={ "confirm": {"type": "boolean", "description": "确认转账"}, "note": {"type": "string", "description": "备注(可选)"} } ) if not result.data["confirm"]: raise PermissionError("用户取消了转账") return do_transfer(from_id, to_id, amount)

ctx.elicit 接受:

  • message:要问的问题文本
  • schema:表单结构(JSON Schema,描述字段与类型)

返回的 result.data 是用户填的答案(字典)。宿主据 schema 渲染表单(布尔变复选框、字符串变文本框),用户填完提交,答案回传。

表单模式的特点:

特点 说明
结构化 用 Schema 描述,宿主自动渲染表单
适合简单交互 确认、填几个字段、选选项
体验一致 跨宿主都用宿主原生 UI
无需外部页面 一切在宿主界面内完成

二、URL 模式:跳转外部页面

URL 模式让宿主跳转一个外部页面,用户在那里完成交互后回跳:

@mcp.tool() async def link_account(provider: str, ctx: Context) -> str: """Link an external account via OAuth.""" # 中途反问:跳转到 OAuth 授权页 result = await ctx.elicit_url( message=f"请在页面完成 {provider} 授权", url=f"https://auth.example.com/authorize?provider={provider}" ) if result.status != "completed": raise PermissionError("授权未完成") return result.data.get("account_id", "")

ctx.elicit_url 接受:

  • message:提示文本
  • url:要跳转的 URL

用户在浏览器打开 url,在那里完成交互(如 OAuth 授权),完成后页面回跳,宿主拿到结果。

URL 模式的特点:

特点 说明
富交互 外部页面可任意复杂(OAuth、富表单、多步流程)
复用现有 Web 不用重写已有 Web 流程
适合复杂场景 OAuth、第三方支付、SSO
体验离开宿主 用户要去浏览器/外部页面

三、两种模式的对照

把两种模式放一起对照,差异清晰:

维度 表单模式(elicit) URL 模式(elicit_url)
交互位置 宿主界面内 外部浏览器页面
描述方式 JSON Schema 一个 URL
渲染 宿主自动 外部页面自己渲染
适合 简单确认、填字段 OAuth、富交互、复用 Web
体验 原生、一致 离开宿主、跳转
实现成本 低(写 Schema) 中(要有外部页面)

判断口诀:

问:这个交互能在宿主界面内用简单表单完成吗? 能 → 表单模式 不能(需要 OAuth/富交互/复用 Web)→ URL 模式

四、表单模式的典型应用

表单模式适合「宿主内能搞定的简单交互」:

场景 Schema 说明
二次确认 {confirm: boolean} 一个复选框
填验证码 {code: string} 一个文本框
选环境 {env: enum(["dev","prod"])} 一个下拉框
补邮箱 {email: string} 一个文本框
多字段补充 {name, email, phone} 几个文本框

共同点:字段少、类型简单、宿主原生 UI 能渲染。这种场景用表单模式最顺,体验也最好(用户不离开宿主)。

五、URL 模式的典型应用

URL 模式适合「需要外部页面才能完成」的复杂交互:

场景 为什么用 URL
OAuth 授权 授权必须在 provider 的页面完成
第三方支付 支付页面由支付商提供
SSO 登录 身份验证在 IdP 页面
复杂多步表单 已有 Web 表单,不想重写
富交互(拖拽、上传) 宿主表单能力不够

共同点:交互复杂度超出宿主表单能力,或必须在外部页面完成

💡 技巧:URL 模式的前提是你有一个外部页面。如果你只是想问个简单问题,别为了「炫」用 URL——表单模式更快、体验更好。URL 是「表单搞不定时」的选择,不是首选。

六、返回值的处理

两种模式都返回一个结果对象,但要处理「用户没完成」的情况:

# 表单模式 result = await ctx.elicit(message="...", schema={...}) if result.action == "cancel": # 用户取消 raise PermissionError("用户取消") data = result.data # 用户填的答案 # URL 模式 result = await ctx.elicit_url(message="...", url="...") if result.status != "completed": # 用户没完成 raise PermissionError("未完成") data = result.data # 回跳带回的数据

关键实践:永远处理「用户取消/未完成」的情况。别假设用户一定会填——用户可能关掉表单、可能不授权。不处理这些情况,工具会卡住或基于空数据继续,导致问题。

七、两种模式的协议层差异(预告)

这里点一个下一节会详讲的差异——两种模式在协议层的实现不同:

表单模式(ctx.elicit): - legacy 协议:服务端直接发起请求 - 现代协议(2026-07-28):靠 Resolve 返回 Elicit + MRTR(或受限支持) URL 模式(elicit_url): - 主要靠跳转 + 回调,协议层相对简单 - 在两种协议下都可用

这个差异解释了为什么第 7.3 节要专门讲「现代路径」——表单模式在 2026-07-28 下的实现变了,而 URL 模式相对稳定。但写代码时,SDK 替你处理了协议差异,你只需关心「用表单还是 URL」这个业务选择。

本节要点回顾

  1. 表单模式(ctx.elicit):用 Schema 描述,宿主弹原生表单,适合简单确认/填字段。
  2. URL 模式(ctx.elicit_url):跳转外部页面,适合 OAuth、富交互、复用 Web。
  3. 判断口诀:宿主内能搞定的简单交互→表单;需要外部页面的复杂交互→URL。
  4. 表单适合:二次确认、验证码、选环境、补字段(字段少、类型简单)。
  5. URL 适合:OAuth、支付、SSO、复杂多步表单(超出宿主表单能力)。
  6. 永远处理「用户取消/未完成」,别假设用户一定填。
  7. 两种模式协议层实现不同(预告),但写代码时 SDK 替你处理差异。

两种模式清楚了,下一节讲 v2 最微妙的变化——现代协议下引导填写靠 Resolve + 多轮往返(MRTR)。


发布者: 作者: 灏天文库 转发
评论区 (0)
U