主题
征询
Model Context Protocol (MCP) 为服务端提供了一种标准化方式,使其能够在交互过程中通过客户端向用户请求额外信息。该流程允许客户端保持对用户交互和数据共享的控制,同时使服务端能够动态收集必要信息。
征询支持两种模式:
- 表单模式:服务端可以向用户请求结构化数据,并可选地使用 JSON schema 来校验响应
- URL 模式:服务端可以将用户引导至外部 URL,用于那些不得经过 MCP 客户端的敏感交互
用户交互模型
MCP 中的征询允许服务端通过让用户输入请求嵌套在其他 MCP 服务端功能内部发生,从而实现交互式工作流。
实现方可以自由地通过任何适合其需求的界面模式来暴露征询——协议本身并不强制规定任何特定的用户交互模型。
WARNING
出于信任与安全以及安全性的考虑:
- 服务端 MUST NOT(不得)使用表单模式征询来请求敏感信息,例如密码、API 密钥、访问令牌或支付凭证
- 服务端 MUST(必须)对涉及此类敏感信息的交互使用 URL 模式
此上下文中的“敏感信息”指的是授予访问权限或授权交易的机密和凭证。一般联系信息或个人资料信息(例如姓名、电子邮件地址或用户名)并非被绝对禁止;是否通过表单模式请求此类数据由服务端自行决定,并取决于用户审查和拒绝的能力。
MCP 客户端 MUST(必须):
- 提供明确的 UI,让用户清楚是哪个服务端在请求信息
- 尊重用户隐私,并提供明确的拒绝和取消选项
- 对于表单模式,允许用户在发送前审查和修改其响应
- 对于 URL 模式,清晰显示目标域名/主机,并在导航到目标 URL 之前获取用户同意
能力
支持征询的客户端 MUST(必须)在每个请求的 _meta.io.modelcontextprotocol/clientCapabilities 中声明 elicitation 能力:
json
{
"_meta": {
"io.modelcontextprotocol/clientCapabilities": {
"elicitation": {
"form": {},
"url": {}
}
}
}
}为保持向后兼容,空的能力对象等同于仅声明支持 form 模式:
jsonc
{
"_meta": {
"io.modelcontextprotocol/clientCapabilities": {
"elicitation": {}, // Equivalent to { "form": {} }
},
},
}声明 elicitation 能力的客户端 MUST(必须)至少支持一种模式(form 或 url)。
服务端 MUST NOT(不得)发送客户端不支持的模式所对应的征询请求。
协议消息
征询请求
服务端 MAY(可以)在处理客户端请求期间,通过发送包含 elicitation/create 请求的 InputRequiredResult 来向用户请求信息。
所有征询请求 MUST(必须)包含以下参数:
| Name | Type | Options | Description |
|---|---|---|---|
mode | string | form, url | 征询的模式。对于表单模式为可选(省略时默认为 "form")。 |
message | string | 一条人类可读的消息,说明为何需要该交互。 |
mode 参数指定征询的类型:
"form":带内结构化数据收集,可选 schema 校验。数据会暴露给客户端。"url":通过 URL 导航进行的带外交互。数据(URL 本身除外)不会暴露给客户端。
为保持向后兼容,服务端 MAY(可以)在表单模式征询请求中省略 mode 字段。客户端 MUST(必须)将没有 mode 字段的请求视为表单模式。
表单模式征询请求
表单模式征询允许服务端直接通过 MCP 客户端收集结构化数据。
表单模式征询请求 MUST(必须)指定 mode: "form" 或省略 mode 字段,并包含以下附加参数:
| Name | Type | Description |
|---|---|---|
requestedSchema | object | 一个 JSON Schema,定义预期响应的结构。 |
请求的 Schema
requestedSchema 参数允许服务端使用 JSON Schema 的一个受限子集来定义预期响应的结构。
为简化客户端用户体验,表单模式征询的 schema 仅限于具有原始类型属性的扁平对象。
该 schema 仅限于以下原始类型:
字符串 Schema
json{ "type": "string", "title": "Display Name", "description": "Description text", "minLength": 3, "maxLength": 50, "format": "email", "default": "user@example.com" }支持的格式:
email、uri、date、date-time数字 Schema
json{ "type": "number", // or "integer" "title": "Display Name", "description": "Description text", "minimum": 0, "maximum": 100, "default": 50 }布尔 Schema
json{ "type": "boolean", "title": "Display Name", "description": "Description text", "default": false }枚举 Schema
单选枚举(不带标题):
json{ "type": "string", "title": "Color Selection", "description": "Choose your favorite color", "enum": ["Red", "Green", "Blue"], "default": "Red" }单选枚举(带标题):
json{ "type": "string", "title": "Color Selection", "description": "Choose your favorite color", "oneOf": [ { "const": "#FF0000", "title": "Red" }, { "const": "#00FF00", "title": "Green" }, { "const": "#0000FF", "title": "Blue" } ], "default": "#FF0000" }多选枚举(不带标题):
json{ "type": "array", "title": "Color Selection", "description": "Choose your favorite colors", "minItems": 1, "maxItems": 2, "items": { "type": "string", "enum": ["Red", "Green", "Blue"] }, "default": ["Red", "Green"] }多选枚举(带标题):
json{ "type": "array", "title": "Color Selection", "description": "Choose your favorite colors", "minItems": 1, "maxItems": 2, "items": { "anyOf": [ { "const": "#FF0000", "title": "Red" }, { "const": "#00FF00", "title": "Green" }, { "const": "#0000FF", "title": "Blue" } ] }, "default": ["#FF0000", "#00FF00"] }
客户端可以使用此 schema 来:
- 生成适当的输入表单
- 在发送前校验用户输入
- 为用户提供更好的引导
所有原始类型都支持可选的默认值,以提供合理的起始点。支持默认值的客户端 SHOULD(应该)用这些值预填充表单字段。
请注意,为简化客户端用户体验,复杂嵌套结构、对象数组(枚举之外)以及其他高级 JSON Schema 特性被有意地不予支持。
示例:简单文本请求
输入请求(在 InputRequiredResult.inputRequests 内传递):
json
{
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Please provide your GitHub username",
"requestedSchema": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
},
"required": ["name"]
}
}
}客户端结果(在重试请求的 inputResponses 内返回):
json
{
"action": "accept",
"content": {
"name": "octocat"
}
}示例:结构化数据请求
输入请求(在 InputRequiredResult.inputRequests 内传递):
json
{
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "Please provide your contact information",
"requestedSchema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Your full name"
},
"email": {
"type": "string",
"format": "email",
"description": "Your email address"
},
"age": {
"type": "number",
"minimum": 18,
"description": "Your age"
}
},
"required": ["name", "email"]
}
}
}客户端结果(在重试请求的 inputResponses 内返回):
json
{
"action": "accept",
"content": {
"name": "Monalisa Octocat",
"email": "octocat@github.com",
"age": 30
}
}URL 模式征询请求
NOTE
新特性: URL 模式征询在 MCP 规范的 2025-11-25 版本中引入。其设计和实现可能会在未来的协议修订版中发生变化。
URL 模式征询使服务端能够将用户引导至外部 URL,以进行不得经过 MCP 客户端的带外交互。这对于认证流程、支付处理以及其他敏感或安全操作至关重要。
URL 模式征询请求 MUST(必须)指定 mode: "url"、一个 message,并包含以下附加参数:
| Name | Type | Description |
|---|---|---|
url | string | 用户应导航到的 URL。 |
url 参数 MUST(必须)包含一个有效的 URL。
NOTE
重要:URL 模式征询并非用于授权 MCP 客户端对 MCP 服务端的访问(这由 MCP 授权处理)。相反,它用于 MCP 服务端需要代表用户获取敏感信息或第三方授权的情况。MCP 客户端的 bearer token 保持不变。客户端唯一的职责是为用户提供关于服务端希望其打开的征询 URL 的上下文。
示例:请求敏感数据
此示例展示了一个 URL 模式征询请求,将用户引导至一个安全 URL,用户可在其中提供敏感信息(例如 API 密钥)。 同一请求也可以将用户引导至 OAuth 授权流程或支付流程。唯一的区别在于 URL 和消息。
输入请求(在 InputRequiredResult.inputRequests 内传递):
json
{
"method": "elicitation/create",
"params": {
"mode": "url",
"url": "https://mcp.example.com/ui/set_api_key",
"message": "Please provide your API key to continue."
}
}客户端结果(在重试请求的 inputResponses 内返回):
json
{
"action": "accept"
}带有 action: "accept" 的响应表示用户已同意该交互。它并不意味着交互已完成。该交互在带外发生,客户端不会直接获知结果。当客户端重试原始请求时,服务端根据回传的 requestState(或其自身存储的状态)判断带外交互是否已完成,并返回最终结果或再次以另一个 InputRequiredResult 响应。客户端 SHOULD(应该)提供手动控制,让用户可以重试或取消原始请求(或以其他方式恢复与客户端的交互)。
消息流程
表单模式流程
sequenceDiagram
participant User
participant Client
participant Server
Client->>Server: tools/call(id: 1)
note over Server: Server needs more info
Server-->>Client: InputRequiredResult(elicitation/create (mode: form))
Note over User,Client: Present elicitation UI
User-->>Client: Provide requested information
Note over Server,Client: Retry request with new information
Client->>Server: tools/call(id: 2, user response)
Server-->>Client: Result(id: 2, result)
URL 模式流程
sequenceDiagram
participant UserAgent as User Agent (Browser)
participant User
participant Client
participant Server
Client->>Server: tools/call(id: 1)
Note over Server: Server needs more info <br/> Server creates requestState encoding url info.
Server-->>Client: InputRequiredResult(elicitation/create (mode: url), requestState)
Client->>User: Present consent to open URL
User-->>Client: Provide consent
Client->>UserAgent: Open URL
Client->>Server: tools/call(id: 2, Accept Response, requestState))
Note over Server: Server uses requestState to discover url info. <br/> It may need to block until the request is fulfilled.
Note over User,UserAgent: User interaction
UserAgent-->>Server: Interaction complete
Note over Server: Continue processing with new information
Server-->Client: Result(id: 2, result)
响应动作
征询响应使用三动作模型,以清晰区分不同的用户动作。这些动作同时适用于表单和 URL 征询模式。
json
{
"action": "accept", // or "decline" or "cancel"
"content": {
"propertyName": "value",
"anotherProperty": 42
}
}三种响应动作为:
Accept(
action: "accept"):用户明确批准并提交了数据- 对于表单模式:
content字段包含与请求 schema 匹配的已提交数据 - 对于 URL 模式:省略
content字段 - 示例:用户点击了“Submit”、“OK”、“Confirm”等
- 对于表单模式:
Decline(
action: "decline"):用户明确拒绝了该请求- 通常省略
content字段 - 示例:用户点击了“Reject”、“Decline”、“No”等
- 通常省略
Cancel(
action: "cancel"):用户未做出明确选择即关闭了该请求- 通常省略
content字段 - 示例:用户关闭了对话框、点击了外部、按下了 Escape、浏览器加载失败等
- 通常省略
服务端应适当地处理每种状态:
- Accept:处理已提交的数据
- Decline:处理明确拒绝(例如提供替代方案)
- Cancel:处理关闭(例如稍后再次提示)
实现注意事项
状态性
征询不要求服务端通过多轮往返请求机制维护关于用户的状态。
然而,如果存储了状态,实现征询的服务端 MUST(必须)按照安全最佳实践文档中的指南,安全地将该状态与各个用户关联。具体而言:
- 状态存储 MUST(必须)受到保护,防止未经授权的访问
- 对于远程 MCP 服务端,在可能的情况下,用户身份 MUST(必须)从通过 MCP 授权获取的凭证中派生(例如
sub声明)
NOTE
本节中的示例是非规范性的,用于说明征询的潜在用途。实现者应在保持安全最佳实践的同时,将这些模式适配到其特定需求。
用于敏感数据的 URL 模式征询
对于需要与要求敏感信息(例如凭证、支付信息)的外部 API 交互的服务端,URL 模式征询提供了一种安全机制,让用户可以安全地提供这些信息,而不会将其暴露给 MCP 客户端。
在此模式中:
- 服务端将用户引导至一个安全网页(通过 HTTPS 提供)
- 该页面在用户信任的域名上呈现品牌化的表单 UI
- 用户直接将敏感凭证输入安全表单
- 服务端安全地存储凭证,并将其绑定到用户身份
- 后续 MCP 请求使用这些存储的凭证进行 API 访问
此方法确保敏感凭证永远不会经过 LLM 上下文、MCP 客户端或任何中间 MCP 服务端,从而降低通过客户端日志或其他攻击途径泄露的风险。
用于 OAuth 流程的 URL 模式征询
URL 模式征询支持一种模式,其中 MCP 服务端充当第三方资源服务器的 OAuth 客户端。 由 URL 模式征询启用的与外部 API 的授权,独立于 MCP 授权。MCP 服务端 MUST NOT(不得)依赖 URL 模式征询来为用户授权其自身。
理解二者的区别
- MCP 授权:MCP 客户端与 MCP 服务端之间必需的 OAuth 流程(在授权规范中涵盖)
- 外部(第三方)授权:MCP 服务端与第三方资源服务器之间可选的授权,通过 URL 模式征询发起
在外部授权中,服务端同时充当:
- OAuth 资源服务器(对 MCP 客户端而言)
- OAuth 客户端(对第三方资源服务器而言)
示例场景:
- 一个 MCP 客户端连接到 MCP 服务端
- MCP 服务端集成了各种不同的第三方服务
- 当 MCP 客户端调用需要访问第三方服务的工具时,MCP 服务端需要该服务的凭证
关键的安全要求是:
- 第三方凭证 MUST NOT(不得)经过 MCP 客户端:客户端绝不能看到第三方凭证,以保护安全边界
- MCP 服务端 MUST NOT(不得)将客户端的凭证用于第三方服务:那将是 Token 透传,这是被禁止的
- 用户 MUST(必须)直接授权 MCP 服务端:该交互发生在 MCP 协议之外,不涉及 MCP 客户端
- MCP 服务端负责令牌:MCP 服务端负责存储和管理通过 URL 模式征询获取的第三方令牌(换言之,MCP 服务端必须是有状态的)。 通过 URL 模式征询获取的凭证,与 MCP 客户端使用的 MCP 服务端凭证不同。MCP 服务端 MUST NOT(不得)将通过 URL 模式征询获取的凭证传输给 MCP 客户端。
NOTE
如需更多背景信息,请参阅安全最佳实践文档中的 Token 透传部分,以理解为何 MCP 服务端不能充当透传代理。
实现模式
在通过 URL 模式征询实现外部授权时:
- MCP 服务端生成一个授权 URL,充当第三方服务的 OAuth 客户端
- MCP 服务端存储内部状态,将征询请求与用户身份关联(绑定)。
- MCP 服务端向客户端发送一个 URL 模式征询请求,其中包含一个可启动授权流程的 URL,以及一个可选的
requestState,用于编码关于征询请求和用户的信息(如需要)。 - 用户直接与第三方授权服务器完成 OAuth 流程
- 第三方授权服务器重定向回 MCP 服务端
- MCP 服务端安全地存储第三方令牌,并将其绑定到用户身份
- 后续 MCP 请求可以利用这些存储的令牌对第三方资源服务器进行 API 访问
以下是如何实现此模式的非规范性示例:
sequenceDiagram
participant User
participant UserAgent as User Agent (Browser)
participant 3AS as 3rd Party AS
participant 3RS as 3rd Party RS
participant Client as MCP Client
participant Server as MCP Server
Client->>Server: tools/call
Note over Server: Needs 3rd-party authorization for user
Note over Server: Store state (bind the elicitation request to the user)
Note over Server: generate requestState that encodes information about the original request and user.
Server->>Client: InputRequiredResult<br/>(mode: "url", url: "https://mcp.example.com/connect?...", requestState)
Client->>User: Present consent to open URL
User->>Client: Provide consent
Client->>UserAgent: Open URL
Client->>Server: Accept response
UserAgent->>Server: Load connect route
Note over Server: Confirm: user is logged into MCP Server or MCP AS<br>Confirm: elicitation user matches session user
Server->>UserAgent: Redirect to third-party authorization endpoint
UserAgent->>3AS: Load authorize route
Note over 3AS,User: User interaction (OAuth flow):<br>User consents to scoped MCP Server access
3AS->>UserAgent: redirect to MCP Server's redirect_uri
UserAgent->>Server: load redirect_uri page
Note over Server: Confirm: redirect_uri belongs to MCP Server
Server->>3AS: Exchange authorization code for OAuth tokens
3AS->>Server: Grants tokens
Note over Server: Bind tokens to MCP user identity
Client->>Server: tools/call (ElicitResults, requestState)
Note over Server: Retrieve token bound to user identity
Server->>3RS: Call 3rd-party API
此模式在保持清晰安全边界的同时,支持与需要用户授权的第三方服务进行丰富的集成。
错误处理
服务端 SHOULD NOT(不应)假设征询请求总会成功,并且 MUST(必须)处理用户拒绝或取消征询,或客户端处理请求失败的情况。
安全注意事项
- 服务端 MUST(必须)将征询请求绑定到客户端和用户身份
- 客户端 MUST(必须)清晰指明是哪个服务端在请求信息
- 客户端 SHOULD(应该)实现用户批准控制
- 客户端 SHOULD(应该)允许用户随时拒绝征询请求
- 客户端 SHOULD(应该)以清晰表明正在请求什么信息以及为何请求的方式呈现征询请求
安全的 URL 处理
请求征询的 MCP 服务端:
- MUST NOT(不得)在 URL 征询请求中发送给客户端的 URL 里包含关于最终用户的敏感信息,包括凭证、个人可识别信息等。
- MUST NOT(不得)提供预先通过认证即可访问受保护资源的 URL,因为恶意客户端可能利用该 URL 冒充用户。
- SHOULD NOT(不应)在表单模式征询请求的任何字段中包含意图可点击的 URL。
- SHOULD(应该)在非开发环境中使用 HTTPS URL。
这些服务端要求确保客户端实现对于何时向用户呈现 URL 有明确的规则,从而使客户端侧规则(见下文)能够一致地应用。
实现 URL 模式征询的客户端 MUST(必须)谨慎处理 URL,以防止用户在不知情的情况下点击恶意链接。
在处理 URL 模式征询请求时,MCP 客户端:
- MUST NOT(不得)自动预取该 URL 或其任何元数据。
- MUST NOT(不得)在未经用户明确同意的情况下打开该 URL。
- MUST(必须)在征得同意之前向用户展示完整 URL 以供检查。
- MUST(必须)以安全的方式打开服务端提供的 URL,使客户端或 LLM 无法检查内容或用户输入。 例如,在 iOS 上,SFSafariViewController 是好的,但 WkWebView 则不是。
- SHOULD(应该)突出显示 URL 的域名,以缓解子域名欺骗。
- SHOULD(应该)对含糊/可疑的 URI(即包含 Punycode 的 URI)给出警告。
- SHOULD NOT(不应)在征询请求的任何字段中将 URL 渲染为可点击,URL 征询请求中的
url字段除外(并受上述限制约束)。
识别用户
服务端 MUST NOT(不得)在未经服务端验证的情况下依赖客户端提供的用户身份,因为这可能被伪造。 相反,服务端 SHOULD(应该)遵循安全最佳实践。 非规范性示例:
- 错误:将诸如“我是 joe@example.com”之类的用户输入视为权威
- 正确:依赖授权来识别用户
表单模式安全
- 服务端 MUST NOT(不得)通过表单模式请求敏感信息(密码、API 密钥等)
- 客户端 SHOULD(应该)根据所提供的 schema 校验所有响应
- 服务端 SHOULD(应该)校验收到的数据与请求的 schema 匹配
钓鱼
URL 模式征询会返回一个 URL,攻击者可以利用它发送给受害者。MCP 服务端 MUST(必须)在接受信息之前验证打开该 URL 的用户的身份。
通常,身份验证通过利用 MCP 授权服务器来识别用户,在浏览器中通过会话 cookie 或等效机制完成。
例如,URL 模式征询可用于执行 OAuth 流程,其中服务端充当另一个资源服务器的 OAuth 客户端。如果没有适当的缓解措施,就可能发生以下钓鱼攻击:
- 恶意用户(Alice)连接到良性服务端并触发一个征询请求
- 良性服务端生成一个授权 URL,充当第三方授权服务器的 OAuth 客户端
- Alice 的客户端显示该 URL 并请求同意
- Alice 没有点击该链接,而是诱骗同一良性服务端的受害用户(Bob)点击它
- Bob 打开该链接并完成授权,以为自己在授权自己与该良性服务端的连接
- 良性服务端收到来自第三方授权服务器的回调/重定向,并认为这是 Alice 的请求
- 第三方服务器的令牌被绑定到 Alice 的会话和身份,而非 Bob 的,从而导致账户接管
为防止此攻击,服务端 MUST(必须)确保发起征询请求的用户(通过 MCP 客户端访问服务端的最终用户)与完成授权流程的用户是同一用户。
实现此目标的方法有很多,最佳方式将取决于具体实现。
作为一个常见的非规范性示例,考虑 MCP 服务端可通过 Web 访问并希望执行第三方授权码流程的情况。 为防止钓鱼攻击,服务端会创建一个指向 https://mcp.example.com/connect?... 而非第三方授权端点的 URL 模式征询。 这个“connect URL”必须确保打开页面的用户与生成征询时所针对的用户是同一用户。 例如,它会检查用户是否拥有有效的会话 cookie,以及该会话 cookie 是否属于使用 MCP 客户端生成 URL 模式征询的同一用户。 这可以通过比较 MCP 服务端授权服务器中的权威主体(sub 声明)与会话 cookie 中的主体来实现。 一旦该页面确保是同一用户,它就可以将用户发送到 https://example.com/authorize?... 处的第三方授权服务器,在那里完成正常的 OAuth 流程。
在其他情况下,服务端可能无法通过 Web 访问,也可能无法使用会话 cookie 来识别用户。 在这种情况下,服务端必须使用不同的机制来识别打开征询 URL 的用户与生成征询时所针对的用户是同一用户。
在所有实现中,服务端 MUST(必须)确保用于确定用户身份的机制能够抵御攻击者修改征询 URL 的攻击。