Skip to main content
最终版标准轨道
字段
SEP1036
标题用于安全带外交互的 URL 模式征询
状态最终版
类型标准轨道
创建日期2025-07-22
作者Nate Barbettini (@nbarbettini) 和 Wils Dawson (@wdawson)
赞助者
PR#1036

摘要

本 SEP 为现有的征询客户端能力引入了一种新的 url 模式,支持绕过 MCP 客户端的安全带外交互。URL 模式征询解决了表单模式征询无法处理的敏感用例,例如收集敏感凭证、为外部(第三方)授权执行 OAuth 流程以及处理支付,同时将敏感数据暴露给 MCP 客户端。通过将用户引导至浏览器中受信任的 URL,此模式在保持安全边界的同时,实现了与第三方服务的丰富集成。

动机

当前的 MCP 规范(2025-06-18)提供了一种征询机制,用于通过结构化的带内请求从用户那里收集非敏感信息(通常想象为 MCP 客户端渲染一个表单以从最终用户那里收集数据)。然而,有几个关键用例需要不能通过 MCP 客户端的交互:
  1. 敏感数据收集:API 密钥、密码和其他凭证绝不能通过中介系统传输。
  2. 外部授权:MCP 服务器通常需要代表用户访问第三方 API。MCP 授权规范仅涵盖客户端到服务器的授权,而不涵盖服务器到第三方的授权。安全最佳实践 文档明确禁止令牌传递,要求有一种安全的机制用于外部(第三方)OAuth 流程。这是从 #234 和 #284 的讨论中出现的一个特别重要的动机因素。
  3. 支付和订阅流程:金融交易需要 PCI 合规性和安全支付处理,这无法通过带内数据收集实现。
如果没有用于这些交互的标准化机制,MCP 服务器必须诉诸非标准的变通方法或不安全的做法,例如通过带内表单式征询请求 API 密钥。本 SEP 通过引入一种利用既定 Web 安全模式来安全处理敏感交互的 URL 征询模式来解决这些差距。 URL 征询与 MCP 授权 根本不同。URL 征询不是用于授权 MCP 客户端访问 MCP 服务器(这由 MCP 授权直接处理)。相反,当 MCP 服务器需要代表用户获取敏感信息或第三方授权时使用它。MCP 客户端的 Bearer 令牌保持不变,客户端的唯一责任是为用户提供有关服务器希望他们打开的征询 URL 的上下文。

规范

概述

征询更新为支持两种模式:
  • 表单模式(带内):服务器可以使用可选的 JSON 架构向用户请求结构化数据以验证响应(此处无变化,除了为现有能力添加一个名称)
  • URL 模式(带外):服务器可以将用户引导至外部 URL 进行敏感交互,这些交互绝不能通过 MCP 客户端

能力

支持征询的客户端必须在初始化期间声明 elicitation 能力:
{
  "capabilities": {
    "elicitation": {
      "form": {},
      "url": {}
    }
  }
}
为了向后兼容性,空能力对象等同于仅声明支持 form 模式:
{
  "capabilities": {
    "elicitation": {},
  },
}
声明 elicitation 能力的客户端必须支持至少一种模式(formurl)。

表单征询请求

与现有规范的唯一变化是在 elicitation/create 请求中添加了一个 mode 字段:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "elicitation/create",
  "params": {
    "mode": "form", // 新字段
    "message": "Please provide your GitHub username",
    "requestedSchema": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        }
      },
      "required": ["name"]
    }
  }
}

URL 征询请求

URL 征询请求必须指定 mode: "url" 并包含以下参数:
名称类型描述
urlstring用户应该导航到的 URL。
elicitationIdstring征询的唯一标识符。
messagestring解释为何需要交互的人类可读消息。

示例:OAuth 授权流程

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "elicitation/create",
  "params": {
    "mode": "url",
    "elicitationId": "550e8400-e29b-41d4-a716-446655440000",
    "url": "https://github.com/login/oauth/authorize?client_id=abc123&state=xyz789&scope=repo",
    "message": "Please authorize access to your GitHub repositories to continue."
  }
}

响应操作

URL 征询响应使用与表单征询相同的三操作模型:
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "action": "accept" // 或 "decline" 或 "cancel"
  }
}
带有 action: "accept" 的响应表示用户已同意交互。交互发生在带外,除非服务器发送完成通知,否则客户端不知道结果。

完成通知

当由 URL 模式征询启动的带外交互完成时,服务器应该发送 notifications/elicitation/complete 通知。这允许客户端在适当时以编程方式做出反应。
  • 该通知必须仅发送给发起征询请求的客户端。
  • 该通知必须包含原始 elicitation/create 请求中建立的 elicitationId
  • 客户端必须忽略引用未知或已完成 ID 的通知。
  • 如果从未收到完成通知,客户端应该为用户提供一种手动继续交互的方式。
客户端可以使用该通知自动重试收到需要 URL 征询错误的请求,更新用户界面,或以其他方式继续交互。但是,由于通知的交付不保证,客户端不得无限期等待来自服务器的通知。
{
  "jsonrpc": "2.0",
  "method": "notifications/elicitation/complete",
  "params": {
    "elicitationId": "550e8400-e29b-41d4-a716-446655440000"
  }
}

需要 URL 征询错误

当请求在完成征询之前无法处理时,服务器可以返回 URLElicitationRequiredError(代码 -32042)以指示需要 URL 模式征询。除非用户交互需要 URL 模式征询,否则服务器不得返回此错误。
{
  "jsonrpc": "2.0",
  "id": 2,
  "error": {
    "code": -32042,
    "message": "This request requires more information.",
    "data": {
      "elicitations": [
        {
          "mode": "url",
          "elicitationId": "550e8400-e29b-41d4-a716-446655440000",
          "url": "https://oauth.example.com/authorize?client_id=abc123&response_type=code&...",
          "message": "Authorization is required to access your Example Co files."
        }
      ]
    }
  }
}
错误中返回的任何征询必须是 URL 模式征询,并包含 elicitationId 返回 URLElicitationRequiredError 等同于发送 elicitation/create 请求。服务器可以返回错误(而不是发送单独的 elicitation/create 请求),作为一种对客户端的便利,以明确特定征询与失败的客户端请求直接相关。 客户端必须将 URLElicitationRequiredError 响应视为等同于 elicitation/create 请求。客户端可以在征询成功完成后自动重试失败的请求,例如在收到完成通知后。

理由

设计决策

为什么扩展信息收集机制而不是创建新机制? 最初,我们考虑为带外交互创建一个单独的机制(在 #475 中讨论)。然而,在与 MCP 维护者讨论后,我们决定扩展现有的信息收集规范,因为:
  1. 两种机制服务于相同的基本目的:从用户那里收集信息
  2. 拥有两个相似但独立的机制来实现相同目的会造成混淆且容易出错
  3. mode 参数清晰地分离了两种交互模式
为什么客户端不能自己执行交互? 有人可能会建议 MCP 客户端自己执行交互,例如作为第三方授权服务器的 OAuth 客户端。然而,有几个原因说明这不是一个好主意:
  • 如果 MCP 客户端从第三方授权服务器获取用户令牌,MCP 服务器将成为 令牌透传 服务器,这是明确禁止的。
  • 同样,对于支付类流程,MCP 客户端需要执行符合 PCI 标准的支付处理,这不是 MCP 客户端所需的要求。
为什么服务器不在信息收集完成时阻塞(等待)? URL 模式信息收集请求在设计上是异步或“断开连接”的流程,因为它们启发的交互类型本质上是异步的。支付流程、外部授权等可能需要几分钟或更长时间才能完成,在某些情况下甚至永远无法完成(如果被最终用户放弃)。 为什么不允许在表单模式中使用 URL? 非常明确地指出何时可以在信息收集请求中发送(以及何时不能发送)URL,可以改善客户端的安全状况。通过在规范中明确声明 URL 允许在 URL 模式信息收集请求的 url 字段中,客户端实现者可以实现与安全模型一致的 UX 模式。例如,客户端可以拒绝在表单模式信息收集请求中将 URL 渲染为可点击的超链接,从而降低用户点击恶意服务器发送的恶意 URL 的可能性。

考虑的替代方案

  1. 令牌透传:简单地将 MCP 客户端的令牌传递给外部服务因安全最佳实践中记录的安全问题而被拒绝。让 MCP 客户端获取额外令牌并将其传递给 MCP 服务器也因同样原因被拒绝。
  2. 特定于 OAuth 的能力:曾考虑创建专门用于外部(第三方)授权且基于 OAuth 的能力,但为了支持多种用例的更通用的 URL 模式信息收集方法而被拒绝。

社区反馈

本提案结合了来自 #475、#234 和 #284 讨论以及 Discord 上 #auth-wg 工作组的广泛社区反馈。社区确定了以下需求:
  • 安全的凭证收集而无需客户端暴露
  • 独立于 MCP 授权的外部授权模式
  • 支付和订阅流程支持
  • 清晰的安全边界和信任模型

向后兼容性

本 SEP 引入了以下破坏性变更:
  1. 能力声明:客户端现在必须指定它们支持哪些信息收集模式:
    {
      "capabilities": {
        "elicitation": {
          "form": {},
          "url": {}
        }
      }
    }
    
    此前,客户端仅声明 "elicitation": {} 而不指定模式。
  2. 模式参数:所有 elicitation/create 请求现在必须包含一个 mode 参数("form""url")。

迁移路径

为了便于迁移:
  • 服务器在发送特定模式请求之前 检查客户端能力
  • 客户端 可以 最初仅支持表单模式以保持兼容性
  • 现有的表单信息收集实现在添加模式参数后继续工作

参考实现

TypeScript 中的客户端/服务器实现:feat/url-elicitation 解释视频:https://drive.google.com/file/d/1llCFS9wmkK_RUgi5B-zHfUUgy-CNb0n0/view?usp=sharing

安全影响

本 SEP 引入了几个安全考虑因素:

URL 安全要求

  1. SSRF 防护:客户端必须验证 URL 以防止服务器端请求伪造攻击
  2. 协议限制:URL 信息收集仅允许 HTTPS URL
  3. 域名验证:客户端必须向用户清晰显示目标域名

信任边界

URL 信息收集明确创建了清晰的信任边界:
  • MCP 客户端永远看不到 MCP 服务器通过 URL 信息收集获得的敏感数据
  • MCP 服务器必须独立验证用户身份
  • 第三方服务通过安全的浏览器上下文直接与用户交互

身份验证

服务器必须验证完成 URL 信息收集的用户与发起请求的用户是同一用户。验证用户身份不得依赖于来自客户端的不可信输入(例如用户输入)。

实现要求

  1. 客户端必须
    • 使用防止检查用户输入的安全浏览器上下文
    • 验证 URL 以进行 SSRF 防护
    • 在打开 URL 之前获得明确的用户同意
    • 清晰显示目标域名
  2. 服务器必须
    • 将信息收集状态绑定到经过身份验证的用户会话
    • 在 URL 信息收集流程的开始和结束时验证用户身份
    • 实施适当的速率限制
  3. 双方都应
    • 记录安全事件以供审计
    • 为信息收集请求实施超时机制
    • 为安全失败提供清晰的错误消息

与现有安全措施的关系

本提案建立在现有 MCP 安全措施之上并对其进行补充:
  • 在现有 MCP 授权框架内工作(本提案不影响 MCP 授权)
  • 遵循有关令牌处理的安全最佳实践
  • 保持客户端 - 服务器与服务器 - 第三方授权之间的关注点分离