什么是 MCP:从工具调用到标准化上下文协议

6678 字
33 分钟
什么是 MCP:从工具调用到标准化上下文协议

最近 MCP(Model Context Protocol)越来越常见。很多产品都在宣传“支持 MCP”,但实际讨论中经常把几件不同的事情混在一起:MCP 是不是一种 Agent,MCP 里的 tool 和模型 API 里的 tool 有什么区别,远程 MCP 服务为什么会要求登录,以及客户端到底怎么知道应该打开哪个授权地址。

这些问题可以用一句话先概括:

MCP 是连接 LLM 应用与外部上下文、资源和能力的一套开放协议。它规定了客户端和服务端如何发现能力、交换消息、调用工具以及完成 HTTP 场景下的授权,但它本身不是 Agent Runtime,也不是某一家模型厂商的工具 API。

本文按协议分层说明 MCP,再重点拆开认证授权流程。

MCP 解决的是什么问题#

一个 Agent 应用通常同时面对三类外部能力:

  • 读取资料,例如文件、数据库记录、订单详情或知识库内容;
  • 使用可执行能力,例如查询天气、创建工单、提交退款申请;
  • 使用结构化的提示模板或工作流入口。

在没有统一协议时,每个 AI 应用都要单独适配一套 SDK、请求格式、工具描述、鉴权方式和错误处理。一个系统接入了某种工具,并不意味着另一个 Agent 也能直接复用。

MCP 试图把这层连接标准化。它借鉴了 Language Server Protocol 的思路:不是为每个编辑器和每种语言写两两适配,而是让编辑器和语言服务分别遵守一套协议。

在 MCP 中,角色大致如下:

Host -> 运行 LLM 或 Agent 的应用,例如 IDE、聊天应用、自动化平台
MCP Client -> Host 内部负责连接某个 MCP Server 的连接器
MCP Server -> 对外提供资源、提示模板和工具的服务

因此,用户看到的“Agent 调用 MCP”,通常不是 Agent 直接和 MCP Server 说话,而是 Agent Runtime 通过 Host 中的 MCP Client 完成连接、能力发现和调用。

MCP Server 也不一定是远程 HTTP 服务。它可以由 Host 通过 stdio 启动为本地子进程,也可以通过 Streamable HTTP 访问远程服务。

MCP 里有哪些协议和标准#

严格来说,MCP 不是“很多个协议的集合”,而是一套分层规范。不同层解决不同问题。

1. 消息层:JSON-RPC 2.0#

MCP 的请求、响应和通知使用 JSON-RPC 2.0 表达。一个最简单的请求类似这样:

{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}

这里的 tools/list 是 MCP 定义的方法名,id 用于把响应和请求对应起来。通知没有 id,也不需要响应。

JSON-RPC 只解决“消息长什么样、如何对应请求和响应”,它不负责 HTTP、授权、工具审批或模型推理。

2. 生命周期和版本协商#

客户端和服务端需要协商协议版本以及双方支持的能力。早期和当前不同日期版本的生命周期细节可能不同:较早版本通过 initialize 请求完成初始化;较新的规范强调每个请求携带必要的元数据,并保留对旧初始化模型的兼容策略。

这意味着 MCP 的版本号不能被当作普通的产品版本号。MCP 规范使用日期版本,例如 2025-06-182025-11-252026-07-28。客户端和服务端应该在连接时明确协商或声明版本,不要把某个 SDK 支持的字段直接当成所有 MCP Server 都支持。

能力也需要协商。服务端可以声明自己支持:

  • resources:可读取的上下文和数据;
  • prompts:可复用的提示模板;
  • tools:可调用的操作;
  • logging、进度、取消、补全等辅助能力。

客户端也可以声明自己支持的能力,例如 roots、sampling、elicitation 等。MCP 只规定能力如何表达和交互,不代表每个客户端都必须实现所有能力。

3. 传输层:stdio 和 Streamable HTTP#

当前 MCP 定义的主要标准传输有两种。

stdio#

客户端启动 MCP Server 子进程,通过标准输入和标准输出交换 JSON-RPC 消息:

Host / MCP Client
| stdin/stdout
MCP Server 子进程

这种方式适合本机工具,例如文件系统、Git、开发环境脚本或桌面应用插件。服务端的日志应该写到 stderr,不能把普通日志混入 stdout,否则客户端会把日志误认为协议消息。

stdio 场景通常由 Host 负责提供环境变量、配置文件或本机凭据。MCP 的 HTTP OAuth 授权规范不要求 stdio 按同样方式跳转浏览器授权。

Streamable HTTP#

远程 MCP Server 使用 HTTP 传输。客户端向一个 MCP endpoint 发送 JSON-RPC 请求,服务端可以返回普通 JSON,也可以返回 Server-Sent Events(SSE)流来承载流式结果或通知。

典型地址可能是:

https://mcp.example.com/mcp

当前规范要求客户端以 POST 发送 JSON-RPC 消息,并携带相应的协议版本信息;服务端还必须校验 Origin,以降低 DNS rebinding 对本地服务造成的风险。旧的 HTTP+SSE 传输仍可能出现在兼容实现中,但不应把它和当前 Streamable HTTP 混为一谈。

传输层只解决“消息怎么到达”,不是授权协议。HTTP MCP 可以使用规范定义的 OAuth 流程,也可以在双方明确约定时使用其他安全的认证机制。

4. 服务能力层:resources、prompts 和 tools#

能力谁通常决定使用典型返回典型业务场景
resourcesHost 或用户文本、JSON、文件内容等上下文用户选中的合同、当前页面对应的订单详情、项目文档、数据库 schema、日志文件
prompts用户一组带角色的消息模板“审查这个 PR”“分析这份客服投诉”“按公司格式生成周报”
tools模型决定,Host 可要求确认文本、结构化结果或错误查询订单、搜索工单、创建工单、发起退款、执行计算、调用第三方 API

可以把它们分别理解成:资料、模板、动作。以“退款助手”为例:订单详情可以作为 resource 提供给模型阅读;客服可以选择一个“退款原因分析” prompt;模型判断需要查询订单时调用 query_order,用户确认后再调用 create_refund 这个 tool

这也对应 MCP 文档中常见的控制边界:resources 偏向 application-controlled,由 Host 或用户决定何时加载;prompts 偏向 user-controlled,由用户选择或显式触发;tools 偏向 model-controlled,由模型根据描述决定是否提出调用。它们描述的是交互控制权,不是最终的安全权限;真正的权限判断仍然必须在 MCP Server 执行时完成。

Resources:把业务上下文交给模型#

resource 代表一个 MCP Server 能提供的可读取对象,通常通过 URI 标识。它适合表达“这份资料是什么”,而不是表达“请帮我执行一个动作”。Host 可以通过 resources/list 发现资源,通过 resources/read 读取内容;某些实现还会使用资源模板或订阅来表达参数化、会变化的资源。

例如,一个项目管理 MCP Server 可以提供:

  • project://acme/schemas/ticket:工单字段和状态定义;
  • project://acme/tickets/10086:用户已经打开的工单详情;
  • file:///workspace/README.md:当前项目文档。

用户打开工单页面时,Host 可以自动把对应 resource 读出来作为上下文;用户选择一份合同后,Host 也可以把合同内容附加给模型。这里的重点是“应用知道要给模型哪份资料”,模型不必先猜一个工具名再发起查询。

因此,resources 特别适合:

  • 已经确定对象的资料读取,例如当前页面、当前项目、当前会话的上下文;
  • 需要被多个提示或工具重复引用的文档、schema、配置和知识材料;
  • 不希望模型直接修改的只读内容。

它不等于数据库表,也不要求所有数据都预先暴露为 resource。比如“根据自然语言筛选出符合条件的订单”通常仍然更适合设计为 search_orders tool,因为查询条件和查询时机需要由模型决定;查询结果也可以在调用后作为工具结果返回。

Prompts:把经验固化成可复用入口#

prompt 不是一次模型回复,也不是让 MCP Server 替模型完成推理。它是服务端提供的模板,客户端通过 prompts/list 发现、通过 prompts/get 获取,必要时传入参数,最后得到一组消息,由 Host 放入当前对话。

例如,“生成售后处理建议”这个 prompt 可以要求传入 orderNocustomerMessage,返回一套固定结构的 system/user 消息:先判断问题类型,再检查订单状态,最后给出符合公司政策的建议。模板本身不会创建工单、退款或发送消息;这些动作仍然要由模型后续调用 tools 完成。

prompts 适合以下场景:

  • 把公司 SOP、输出格式和专业角色固化下来,避免每个用户自己复制一大段提示词;
  • 给用户提供明确的命令或菜单入口,例如 /review-pr/weekly-report
  • 让同一套提示模板可以组合当前选中的 resource,并引导模型按固定步骤使用 tools。

它的触发方式通常是用户选择或应用菜单触发,所以不能把 prompt 当成“模型自动可调用的工具”。如果需求是“模型发现天气需要就调用天气服务”,应该提供 get_weather tool,而不是提供一个 prompt。

Tools:让模型使用外部系统的能力#

tool 是最接近传统函数调用的能力。客户端通过 tools/list 发现工具及其 inputSchema,模型根据工具名称、描述和参数 schema 形成调用意图,Host 再通过 tools/call 请求 MCP Server 执行。

工具既可以是读操作,也可以是写操作:

  • 读操作:query_ordersearch_knowledge_baseget_inventory
  • 写操作:create_ticketupdate_addresscreate_refund
  • 外部动作:send_emailrun_deploymentbook_meeting

工具调用可能访问实时系统、消耗资源或产生业务副作用,所以服务端必须在 tools/call 时重新做身份、权限、参数和业务状态校验。Host 也通常会对写操作显示确认界面。tools/list 中没有显示某个工具,不能当作唯一的安全边界;真正的拒绝必须发生在服务端执行阶段。

为什么大多数 MCP 主要提供 tools#

这是因为目前 Agent 的主要交互闭环是“模型看到工具描述 → 选择工具 → 传参 → 获得结果”。tools 很容易映射到已有的 REST API、SDK 方法、数据库查询和业务命令,也最容易被不同 Host 转换为各家模型 API 的 function calling 格式。

相比之下:

  • resources 要求 Host 理解 URI、选择时机和上下文装载策略;很多 Host 直接把数据读取封装成 tool,接入成本更低;
  • prompts 的价值在于统一经验和用户入口,只有需要共享 SOP 或交互模板的服务才有必要提供;
  • 许多 MCP Server 的核心目标本来就是“让 Agent 操作一个系统”,例如 GitHub、工单、数据库和浏览器,因此自然以 tools 为主。

所以“多数 MCP 都有 tools”是实际生态的侧重点,不代表 resourcesprompts 是可有可无的别名。一个实用的选型规则是:

需求优先选择
应用或用户已经知道要把哪份资料交给模型resource
用户想按一套固定方法开始任务prompt
模型需要根据当前上下文决定查询或执行什么tool
需要改变外部系统状态tool,并在 Host 侧考虑确认

同一业务系统可以同时提供三者:resource 提供当前数据,prompt 提供标准作业模板,tool 提供查询和变更能力。三者不是互斥的 API 类型,而是同一个业务上下文在“资料、模板、动作”三个层面的不同表达。

5. 数据结构:JSON Schema#

MCP 工具通过 inputSchema 描述参数,通常使用 JSON Schema。这样客户端可以在把工具交给模型前,知道工具叫什么、有什么说明、需要哪些参数,以及参数是什么类型。

例如:

{
"name": "query_order",
"description": "查询当前用户有权限查看的订单状态",
"inputSchema": {
"type": "object",
"properties": {
"orderNo": {
"type": "string",
"description": "业务订单号"
}
},
"required": ["orderNo"]
}
}

Schema 解决的是参数契约和校验问题,不能替代服务端的权限校验。即使模型传入的参数符合 JSON Schema,服务端仍然必须检查用户是否有权查询这个订单。

6. 授权层:OAuth 相关标准#

MCP 并没有重新发明一套登录协议。当前 HTTP 授权规范主要组合了这些标准:

标准在 MCP 授权流程中的作用
OAuth 2.1 草案授权码、令牌和安全最佳实践的整体框架
RFC 6750Bearer access token 的 HTTP 使用方式
RFC 8414Authorization Server Metadata,发现授权端点和令牌端点
RFC 9728Protected Resource Metadata,让资源服务器声明对应的授权服务器
RFC 8707Resource Indicators,通过 resource 参数把令牌绑定到目标 MCP Server
RFC 9207在授权响应中携带并校验 iss,防止授权服务器混淆
OpenID Connect Discovery 1.0当授权服务器通过 OIDC 提供发现文档时,发现授权端点
RFC 7591Dynamic Client Registration;当前规范保留它用于兼容旧服务
OAuth Client ID Metadata Document 草案新实现可使用 HTTPS 文档 URL 作为 client_id

这里的“标准”有两个边界需要注意:OAuth 2.1 在 MCP 当前规范中仍以 IETF 草案形式被引用;Client ID Metadata Document 也仍是草案。实现时应以目标 MCP Server 实际支持的规范版本和元数据为准。

MCP 里的 tools 和以前说的 tools 有什么区别#

它们不是两种完全不同的东西。MCP 的 tool 仍然是一个工具函数,只是它被放进了统一的发现、描述、调用和返回结果的协议里。

以前的 tools 通常是什么#

在很多模型 API 中,应用会把函数定义直接放到一次模型请求中:

{
"type": "function",
"function": {
"name": "query_order",
"description": "查询订单",
"parameters": {
"type": "object"
}
}
}

模型返回一个函数调用意图,应用自己执行函数,再把执行结果放回下一轮模型请求。这个 tool 的生命周期、执行进程、鉴权、错误重试和结果格式都由应用自行约定。

MCP tool 增加了什么#

MCP 把下面这些事情标准化了:

  1. 服务发现:客户端可以通过 tools/list 获取当前可用工具,而不是把所有工具硬编码在 Host 中;
  2. 能力变更:服务端可以通知工具列表发生变化,客户端重新拉取列表;
  3. 调用协议:使用 tools/call,传递工具名称和参数,返回统一的内容结构;
  4. 跨进程和跨网络:同一套工具契约可以通过 stdio 或 HTTP 提供;
  5. 上下文组合:工具可以和 resources、prompts 放在同一个服务能力模型中;
  6. 权限边界:远程服务可以根据请求中的访问令牌,只返回当前授权允许的工具,并在调用时再次校验权限。

可以把两者的关系写成这样:

模型 API 的 tool = 一次模型调用中的函数描述
MCP tool = 通过 MCP Server 提供、可发现、可调用的远端能力

MCP tool 最终仍然可能被 Host 转换成模型 API 所需的 tool schema。因此,MCP 并没有替代模型厂商的 tool calling;它解决的是“工具从哪里来、如何被多个 Host 复用、如何统一连接和治理”。

还有一个容易误解的点:MCP 规范不会替 Agent 决定什么时候调用工具。模型是否选择某个工具、Host 是否要求人工确认、Agent Runtime 如何重试和保存任务状态,仍然是应用层的职责。MCP 也不是完整的 Agent Runtime,它不负责模型循环、记忆、工作流编排、审批中心或业务幂等。

MCP 认证和授权是怎么开始的#

先区分两个概念:

  • 认证(authentication):确认用户或客户端是谁;
  • 授权(authorization):确认它能访问哪些 MCP 资源或工具。

MCP 的 HTTP 规范主要定义“客户端如何获得访问受保护资源的授权凭据”。真正的用户登录页面由 Authorization Server 提供,可能使用密码、企业 SSO、多因素认证或 OIDC。MCP Server 本身未必负责显示登录页面。

完整关系是:

MCP Client -- Bearer access token --> MCP Server
| |
| | 资源服务器 Resource Server
+-- OAuth 授权码流程 --> Authorization Server

MCP Server 是资源服务器,Authorization Server 负责和用户交互并签发 token。两者可以是同一个系统,也可以是不同系统。

Agent 怎么知道应该跳转到哪个授权地址#

答案不是“把 MCP URL 拼上 /login”。客户端应该遵循标准化的发现链路:先找到 Protected Resource Metadata,再找到 Authorization Server Metadata,最后读取其中的 authorization_endpoint

第一步:MCP Server 返回 401 和 WWW-Authenticate#

客户端先向配置好的 MCP endpoint 发起请求。如果没有访问令牌,服务端返回类似:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"

这里的 resource_metadata 不是登录地址,而是“受保护资源元数据”的地址。客户端应该解析 WWW-Authenticate,不能把它当作普通字符串拼接。

如果服务端没有在响应头中给出地址,客户端还可以按 RFC 9728 规则尝试 well-known 地址。MCP endpoint 是 https://mcp.example.com/mcp 时,常见顺序是:

https://mcp.example.com/.well-known/oauth-protected-resource/mcp
https://mcp.example.com/.well-known/oauth-protected-resource

第二步:读取 Protected Resource Metadata#

元数据文档大致会包含:

{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": [
"https://auth.example.com"
],
"scopes_supported": [
"orders:read",
"tickets:write"
]
}

authorization_servers 告诉客户端应该向哪个授权服务器继续发现。一个 MCP Server 可以声明多个授权服务器,客户端需要根据自己的用户、组织和支持能力选择其中一个,并且不能把在授权服务器 A 获得的 client credentials 或 token 拿去授权服务器 B 使用。

第三步:读取 Authorization Server Metadata#

客户端拿到授权服务器 issuer 后,按照 RFC 8414 和 OIDC Discovery 的规则读取授权服务器元数据。

没有路径的 issuer,例如:

https://auth.example.com

客户端会尝试:

https://auth.example.com/.well-known/oauth-authorization-server
https://auth.example.com/.well-known/openid-configuration

如果 issuer 带路径,例如 https://auth.example.com/tenant-a,则还要遵循规范规定的路径插入或路径追加规则。元数据中会给出类似字段:

{
"issuer": "https://auth.example.com",
"authorization_endpoint": "https://auth.example.com/oauth2/authorize",
"token_endpoint": "https://auth.example.com/oauth2/token",
"registration_endpoint": "https://auth.example.com/oauth2/register",
"code_challenge_methods_supported": ["S256"]
}

这一步才得到真正的 authorization_endpoint。所以 Agent 不是猜测跳转地址,而是从经过校验的授权服务器元数据中读取地址。客户端还必须校验返回文档中的 issuer 是否与它用于构造发现地址的 issuer 完全一致,发现文档不匹配时不能继续使用。

第四步:确定 OAuth client_id#

在跳转前,客户端还需要有 client_id。当前 MCP 规范支持三种方式,优先级大致如下:

  1. 预注册:服务提供方提前给客户端分配 client_id;
  2. Client ID Metadata Document:客户端使用一个 HTTPS URL 作为 client_id,授权服务器访问这个 URL,读取客户端名称和 redirect URI 等元数据;
  3. Dynamic Client Registration:通过 RFC 7591 的注册端点动态注册。当前规范把它主要作为旧服务的兼容方案;
  4. 如果上述方式都不支持,由用户在客户端配置中输入服务方提供的 client 信息。

Client ID Metadata Document 可能长这样:

{
"client_id": "https://client.example.com/oauth/mcp-client.json",
"client_name": "Example MCP Client",
"redirect_uris": [
"http://127.0.0.1:3000/callback"
],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}

这不是把 client secret 放到 MCP Server 上,而是让授权服务器能够发现和校验一个公共客户端的元数据。桌面应用和 CLI 常见的是本机 loopback redirect URI;授权服务器仍然必须严格校验 redirect URI,不能接受客户端任意传入的回调地址。

第五步:使用 PKCE 打开授权页面#

客户端生成一次性的 code_verifier,计算出 code_challenge,然后拼出授权请求。核心参数包括:

https://auth.example.com/oauth2/authorize
?response_type=code
&client_id=https%3A%2F%2Fclient.example.com%2Foauth%2Fmcp-client.json
&redirect_uri=http%3A%2F%2F127.0.0.1%3A3000%2Fcallback
&code_challenge=<base64url-sha256-of-code-verifier>
&code_challenge_method=S256
&state=<random-state>
&resource=https%3A%2F%2Fmcp.example.com%2Fmcp
&scope=orders%3Aread

这里最关键的是:

  • 跳转的 host 来自授权服务器元数据中的 authorization_endpoint
  • resource 是目标 MCP Server 的规范 URI,不是 Authorization Server 的地址;
  • state 用于防止授权响应被串到另一个请求;
  • PKCE 防止授权码被截获后被其他客户端兑换;
  • scope 应遵循最小权限,服务端在 WWW-Authenticate 中给出的当前请求所需 scope 应被优先考虑。

用户在浏览器中完成登录和同意后,Authorization Server 将 authorization code 重定向到客户端登记的 redirect_uri

第六步:兑换 token 并访问 MCP#

客户端收到回调后,需要校验 state,校验授权服务器 issuer(如果响应包含 iss),再把 code 和原始 code_verifier 发送到元数据中的 token_endpoint

POST https://auth.example.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=<authorization-code>&
client_id=<client-id>&
redirect_uri=http%3A%2F%2F127.0.0.1%3A3000%2Fcallback&
code_verifier=<original-code-verifier>&
resource=https%3A%2F%2Fmcp.example.com%2Fmcp

拿到 access token 后,客户端调用 MCP endpoint:

POST /mcp HTTP/1.1
Host: mcp.example.com
Authorization: Bearer <access-token>
Content-Type: application/json
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }

MCP Server 不能只检查“这个 token 能不能被某个授权服务器签发”。它还应该确认 token 是发给自己的,检查 issuer、签名、有效期、scope,以及 audience 或 RFC 8707 的资源绑定。MCP Server 如果继续调用上游系统,还必须使用发给上游系统的独立 token,不能把客户端发给 MCP Server 的 token 直接透传给上游。

把整个授权过程画出来#

BrowserAuthorization ServerResource MetadataMCP ServerAgent / MCP ClientBrowserAuthorization ServerResource MetadataMCP ServerAgent / MCP Client请求 MCP endpoint(无 token)401 + WWW-Authenticate(resource_metadata)GET Protected Resource Metadataauthorization_servers + scopes_supportedGET OAuth/OIDC Server Metadataauthorization_endpoint + token_endpoint + issuer预注册 / Client ID Metadata / 动态注册client_id 或注册结果打开 authorization_endpoint + PKCE + state + resource用户登录并同意redirect_uri?code=...&state=...&iss=...授权回调code + code_verifier + resourceaccess_tokenBearer access_token + MCP 请求MCP 响应
BrowserAuthorization ServerResource MetadataMCP ServerAgent / MCP ClientBrowserAuthorization ServerResource MetadataMCP ServerAgent / MCP Client请求 MCP endpoint(无 token)401 + WWW-Authenticate(resource_metadata)GET Protected Resource Metadataauthorization_servers + scopes_supportedGET OAuth/OIDC Server Metadataauthorization_endpoint + token_endpoint + issuer预注册 / Client ID Metadata / 动态注册client_id 或注册结果打开 authorization_endpoint + PKCE + state + resource用户登录并同意redirect_uri?code=...&state=...&iss=...授权回调code + code_verifier + resourceaccess_tokenBearer access_token + MCP 请求MCP 响应

如果把客户端状态简化为状态机,则是:

401 / well-known

读取 authorization_servers

无可用 client_id

已有 client_id

回调 code + 校验 state/iss

token 成功

token 有效

401 或 scope 不足

未认证

发现资源元数据

发现授权服务器

注册客户端

等待用户授权

兑换令牌

已授权

调用

Bearer token

401 / well-known

读取 authorization_servers

无可用 client_id

已有 client_id

回调 code + 校验 state/iss

token 成功

token 有效

401 或 scope 不足

未认证

发现资源元数据

发现授权服务器

注册客户端

等待用户授权

兑换令牌

已授权

调用

Bearer token

这个状态机也说明了为什么“401 后直接跳到 /login”不是一个可靠的 MCP 客户端实现:它跳过了资源发现、授权服务器发现、issuer 校验、客户端注册和安全参数生成。

实际落地时最容易踩的坑#

把 MCP 当成 Agent 平台#

MCP 规定的是连接和能力交换,不会自动提供记忆、规划、重试、任务持久化、人工审批、业务幂等和审计。企业需要的 Agent Runtime 仍然要在 MCP 之上建设。

以为拿到一个 token 就能访问所有 MCP Server#

不同 MCP Server 是不同的受保护资源。客户端应使用 resource 指明目标 MCP Server,服务端也要校验 token 的目标受众。不能因为 token 来自同一个企业身份平台,就默认它可以跨系统使用。

只在 tools/list 时做权限检查#

按权限过滤工具列表可以改善用户体验,但不能替代 tools/call 时的服务端授权判断。工具列表会变化,调用参数也可能决定实际访问范围。

把工具描述当成可信代码#

工具名称、描述、注解和返回内容都可能来自外部服务。Host 应该展示工具来源和风险,写操作最好保留人工确认;服务端不能因为描述中写了“只读”就跳过真实的权限和业务校验。

忽略本地 stdio Server 的安全边界#

stdio 没有远程 OAuth 跳转,不代表本地工具天然安全。启动命令、环境变量、文件系统 roots、子进程权限和工具参数仍然需要限制。远程网页通过 DNS rebinding 访问本地 HTTP MCP Server,也是需要特别防护的场景。

混用不同日期版本#

MCP 规范是按日期发布的。连接失败时不能只看“是不是 MCP”,还要确认双方支持的版本、传输方式、生命周期模型和认证规范。尤其是旧版 HTTP+SSE、旧版初始化流程和新版 Streamable HTTP 之间,不能只替换一个 URL 就认为兼容。

总结#

MCP 的价值不只是“给模型增加几个函数”。它把 LLM 应用与外部系统之间的连接拆成了可复用的协议层:

  • JSON-RPC 规定消息交互;
  • stdio 和 Streamable HTTP 规定消息如何传输;
  • resources、prompts、tools 规定服务可以提供什么能力;
  • JSON Schema 规定工具参数契约;
  • OAuth、RFC 9728、RFC 8414、RFC 8707 等标准规定远程 HTTP 服务如何发现授权端点、获取令牌并限制令牌的目标资源。

MCP tool 仍然是 tool,但它不再只是某个应用内部的一段函数描述,而是一个可以被发现、协商、跨进程或跨网络调用的能力。

对认证流程最重要的记忆点是:

MCP 401
-> Protected Resource Metadata
-> Authorization Server Metadata
-> authorization_endpoint
-> OAuth authorization code + PKCE
-> token_endpoint
-> Bearer token 调用 MCP

Agent 不应该猜授权地址,也不应该把用户直接交给一个未经发现和校验的登录 URL。标准化发现链路的意义,就是让一个事先不知道具体身份平台的 MCP Client,也能在安全边界内连接新的 MCP Server。

参考资料#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

什么是 MCP:从工具调用到标准化上下文协议
https://blog.sephy.top/posts/what-is-mcp-and-authorization/
作者
虾米
发布于
2026-08-09
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
虾米
coder
分类
标签
站点统计
文章
73
分类
12
标签
81
总字数
100,355
运行时长
0
最后活动
0 天前
站点信息
构建平台
GitHub Actions
博客版本
Firefly v6.14.3
文章许可
CC BY-NC-SA 4.0