本地与远程 MCP 服务端对比:模型上下文协议连接原理详解
- 作者

- 姓名
- Nino
- 职业
- Senior Tech Editor
模型上下文协议(Model Context Protocol,简称 MCP)已快速成为连接 AI 客户端(如 Claude Code、Cursor 或自定义大模型应用)与外部工具及数据源的开放标准。无论你是调用本地文件评估工具,还是与 Upwork、Notion、Todoist 等远程云服务交互,深入理解其底层的连接与认证机制,对于系统架构设计、安全加固及生产级扩展都至关重要。
在构建复杂 AI Agent 工作流或通过 n1n.ai 等高性能 API 聚合平台进行模型调度时,开发者通常需要在两种 MCP 传输模式之间做出选择:运行在本地宿主机器上的 本地服务端(stdio),以及部署在云端并通过授权校验的 远程服务端(HTTP/SSE + OAuth 2.0)。
本文结合实际测试数据与 MCP 官方规范,详细拆解这两种模式的通信协议、进程行为、鉴权握手以及令牌校验流程。
1. 本地 MCP 服务端:Stdio 传输与进程生命周期
本地 MCP 服务端作为宿主 AI 应用直接启动的子进程运行。双方通过标准输入(stdin)和标准输出(stdout)管道传输 JSON-RPC 消息进行通信。
+-----------------------+ +-----------------------+
| 宿主 AI 应用程序 | --- stdin ->| 本地 MCP 服务端 |
| (例如 Claude Code) | <- stdout --| (子进程) |
+-----------------------+ +-----------------------+
进程生命周期的实测分析
为了观察本地 MCP 服务端在实际运行中的表现,我们在 Claude Code(claude -p)运行本地工具服务端(如 doceval)时,以 250ms 为间隔对进程树进行了连续监控:
- 会话启动即初始化:当客户端会话建立时,宿主程序会立即将其配置的所有本地 MCP 服务端作为子进程启动——即使当前 Prompt 尚未调用任何工具。如果配置文件中定义了 3个本地服务端,系统会立刻创建 3个独立的子进程。
- 跨调用复用进程:在同一个会话中,后续的所有工具调用都会复用已启动的进程。在单次会话中连续调用 10次工具,服务端进程只会被创建 1次。
- 会话间隔离:每个独立的客户端会话都会启动属于自己的子进程集合。如果同时打开 3个运行 Claude Code 的终端窗口,系统将运行 3 份完全独立的本地服务端副本。
- 进程销毁与崩溃恢复:当客户端正常退出时,宿主程序会向子进程发送终止信号以完成清理。若对宿主程序执行强制杀进程(
kill -9),子进程的stdin管道会立即关闭。符合规范的本地 MCP 服务端在检测到stdin的EOF(文件结束符)后,会在 2秒内自动退出,避免产生孤儿进程。
本地模式下的密钥注入
根据 MCP 官方规范,本地 stdio 服务端不应使用 OAuth 2.0 等复杂授权流程。相反,敏感凭证与 API 密钥应通过宿主应用配置中的环境变量进行传递。
为了避免将明文密钥直接硬编码在磁盘配置文件中,主流客户端支持 shell 环境变量动态替换:
{
"mcpServers": {
"local-evaluator": {
"command": "node",
"args": ["/path/to/server.js"],
"env": {
"API_KEY": "${MY_CUSTOM_API_SECRET}"
}
}
}
}
在运行时,宿主程序会自动读取当前 shell 中的 ${MY_CUSTOM_API_SECRET} 变量值并注入子进程的环境变量块中,既保障了安全,又无需繁琐的浏览器登录流程。
2. 远程 MCP 服务端:HTTP、OAuth 2.0 与 PKCE 握手
远程 MCP 服务端部署在公网 HTTPS 地址上(例如 ai.todoist.net 或 mcp.upwork.com/mcp)。由于远程接口暴露在公网上,必须采用严格的身份认证与授权机制。
+-------------+ +---------------+ +-------------------+
| AI 客户端 | -----> | MCP 服务端 | -----> | 授权服务器 |
| (Claude Code)| | (资源提供方) | | (OAuth 2.0 Provider)|
+-------------+ +---------------+ +-------------------+
与简单的静态 API Key 不同,MCP 规范要求远程服务端采用带有 PKCE(带密钥交换的验证码,RFC 7636)的 OAuth 2.0 授权机制。
远程连接的 9 步完整时序
以下是 AI 客户端连接远程 MCP 服务端(如 Upwork、Notion 或 Todoist)时的完整交互流程:
AI 客户端 (Claude Code) MCP 服务端 (资源) 授权服务器 (OAuth) 浏览器 / 用户
| | | |
1. 未带 Token 发起请求 -------->| | |
|<--- 返回 401 Unauthorized-| | |
| (包含元数据链接) | | |
2. 获取资源元数据 ------------->| | |
3. 查询授权服务器端点 ---------------------------------->| |
4. 解析 Client ID URL ---------------------------------->| |
5. 生成 PKCE 随机串与哈希 | | |
6. 在浏览器中打开登录页面 -------------------------------------------------------->| 用户登录授权
7. 授权码重定向返回客户端 <-------------------------------------------------------| 同意授权
8. 提交授权码与 PKCE 原始验证串 ------------------------->| |
|<--- 返回 Access Token & Refresh Token -----------| |
9. 存储 Token 并携带 Bearer 请求->| | |
步骤 1:无令牌探测(401 响应)
客户端首先在不携带 Bearer 令牌的情况下向远程 MCP 服务端发起请求。服务端返回 401 Unauthorized 响应,并在 WWW-Authenticate 响应头中提供公开元数据地址(例如 /.well-known/oauth-protected-resource)。
步骤 2:资源元数据发现
客户端请求该元数据文件,从中提取出规范的授权服务器地址。例如,Todoist 的 MCP 服务端(ai.todoist.net)指向的授权服务器为 todoist.com。
步骤 3:授权端点解析
客户端访问授权服务器的 /.well-known/oauth-authorization-server 文件,获取登录地址、Token 兑换地址、撤销地址以及支持的 PKCE 哈希算法(S256)。
步骤 4:基于 URL 的客户端身份标识
为避免每个开发者的本地机器都需要手动注册 App Secret,MCP 规范采用了客户端元数据 URL。Claude Code 使用统一的公开 URL 作为其 Client ID:https://claude.ai/oauth/claude-code-client-metadata。授权服务器会拉取该配置文件以确认客户端属性。由于本地 CLI 工具无法安全保存 App Secret,配置文件显式标明 "token_endpoint_auth_method": "none"。
步骤 5:生成 PKCE 密钥
由于客户端没有固定 App Secret,必须借助 PKCE 来证明“完成登录的程序就是发起登录的程序”。客户端在本地生成一个高熵随机字符串(Code Verifier),并计算其 SHA256 哈希值(Code Challenge)。
步骤 6:用户浏览器登录
客户端自动打开用户的默认浏览器,跳转至授权服务器的登录页面,URL 参数中包含:
client_id:https://claude.ai/oauth/claude-code-client-metadataredirect_uri:http://localhost/callbackcode_challenge: Base64URL(SHA256(Verifier))code_challenge_method:S256resource: 目标 MCP 服务端地址
用户直接在服务商官方页面输入账号密码。客户端完全无法接触用户的明文密码。
步骤 7:授权码回调
用户同意授权后,授权服务器将浏览器重定向至 http://localhost/callback?code=AUTH_CODE。客户端在本地监听的回调服务接收到该请求,并校验防重放状态。
步骤 8:Token 兑换
客户端向授权服务器的 Token 端点发送 POST 请求,提交:
- 获取到的授权码(Authorization Code)
- 步骤 5 中在本地保留的 Code Verifier 原始字符串
授权服务器对传入的 Verifier 进行哈希计算并与步骤 6 存留的 Challenge 进行比对。校验一致后,颁发 Access Token(短效访问令牌)和 Refresh Token(长效刷新令牌)。
步骤 9:Token 存储与请求携带
客户端将获得的令牌安全保存在本地仅限当前用户读写的文件中(例如 ~/.claude/.credentials.json)。在此之后,发往该 MCP 服务端的每个 HTTP 请求都会携带以下 Header: Authorization: Bearer <access_token>
在构建连接大语言模型与外部系统的高效桥梁时,接入如 n1n.ai 提供的多模型 API 服务,能够帮助开发者在统一管理底层模型调用的同时,保持上层工具链认证逻辑的高效与规范。
3. 远程 MCP 服务端如何校验访问令牌
当 Access Token 抵达远程 MCP 服务端后,服务端必须在执行任何工具逻辑前完成令牌合法性校验。校验方式主要分为以下两种策略:
不透明令牌(Opaque Token)与 JWT 签名校验对比
| 令牌机制 | 数据结构 | 校验方式 | 网络开销 | 撤销生效时效 |
|---|---|---|---|---|
| 不透明令牌 | 随机字符串 | 数据库查询或 RFC 7662 Introspection 接口请求 | 每次请求都需要发起内部网络调用 | 实时生效 |
| 签名 JWT | Header.Payload.Signature | 使用公钥(JWKS)进行本地密码学签名校验 | 零额外网络开销 | 依赖 Token 过期时间 |
JWT 本地验签代码实现逻辑
若 MCP 服务端接收的是 JWT 格式的令牌,可以通过公钥在本地完成快速验签:
import jwt
import requests
def verify_mcp_jwt(token_string, trusted_issuer_url):
# 1. 解析 Header 获取 Key ID (kid)
header = jwt.get_unverified_header(token_string)
kid = header.get("kid")
# 2. 仅从受信任的发号服务器拉取公钥集 (JWKS)
jwks_url = f"{trusted_issuer_url}/.well-known/jwks.json"
jwks = requests.get(jwks_url).json()
# 3. 匹配对应的公钥
public_key = get_public_key_by_kid(jwks, kid)
# 4. 校验签名及标准 Claim(aud, exp, iss)
decoded = jwt.decode(
token_string,
public_key,
algorithms=["RS256"],
audience="https://mcp.target-service.com",
issuer=trusted_issuer_url
)
return decoded
安全专家建议:合规的 MCP 服务端必须仅从预先配置并信任的授权服务器 URL(
iss)获取公钥,绝不能直接信任 JWT Header 内部声明的外部 URL 地址,以防范公钥伪造攻击。
协议演进:无状态(Sessionless)架构
在最新的 MCP 协议修订中(包括面向 2026年的标准演进),官方废弃了早期繁琐的 initialize 握手过程及 Mcp-Session-Id 请求头。
现在的远程 MCP 服务端完全基于 无状态 HTTP 运行。每个独立的 HTTP 请求都会完整携带协议版本、客户端能力声明及 Bearer 令牌。状态逻辑被彻底沉淀在两端:客户端本地的 Token 存储与授权服务器端的撤销记录。这种设计使得远程 MCP 服务端可以非常轻松地在负载均衡器后方实现水平扩展。
结合 n1n.ai 统一的大模型 API 聚合能力,开发者可以在横向扩展云端 Agent 架构的同时,保持高并发模型推理与远程工具调用的稳定连接。
4. 本地与远程 MCP 架构全面对比
| 维度 | 本地 MCP 服务端 (Stdio) | 远程 MCP 服务端 (HTTP / OAuth) |
|---|---|---|
| 传输层 | 标准输入输出管道 (stdin/stdout) | HTTPS / Server-Sent Events (SSE) |
| 进程模式 | 由 AI 客户端在本地作为子进程启动 | 运行在远程云端服务器或容器中 |
| 鉴权机制 | 环境变量注入 (${ENV_VAR}) | 带有 PKCE (S256) 的 OAuth 2.0 |
| 凭证安全 | 保存在本地环境或配置文件中 | 账号密码仅保留在服务商侧;本地仅保存 Token |
| 并发能力 | 每个会话独占 1个子进程 | 单服务端并发处理成千上万客户端连接 |
| 进程生命周期 | 随父进程管道关闭(EOF)自动销毁 | 无状态 / 无会话请求生命周期 |
| 适用场景 | 本地文件操作、Git 管理、代码执行环境 | SaaS 软件集成 (Notion, Upwork, Jira, 数据库) |
5. 安全分析与风险防御
在选择 MCP 服务端架构时,必须清晰评估不同传输模式的安全边界:
本地 MCP 的安全边界
- 子进程权限隐患:本地 MCP 服务端继承了宿主用户的系统权限。若引入了存在安全漏洞的本地 MCP 模块,可能会被恶意利用执行任意 shell 命令或读取敏感文件。
- 环境变量泄漏:将明文密钥直接写在配置文件中存在泄露风险。开发者在结合 n1n.ai 进行多模型调度与工具集成时,应始终坚持使用环境变量动态展开(
${SECRET_KEY})的方式管理密钥。
远程 MCP 的安全边界
- 签名密钥泄漏风险:若授权服务器的私钥泄漏(如历史上的 Storm-0558 安全事件),攻击者将能够伪造任意有效 Token。通过缩短 Access Token 的有效时长(如 15-60分钟)并建立完善的密钥轮换机制,可以有效规避该风险。
- 令牌撤销机制:当 Access Token 过期或被用户主动撤销后,Refresh Token 兑换流程将失效,系统会强制要求重新登录,从而确保了风险窗口的可控性。
凭证流转与存储总结表
| 凭证要素 | 生成方 | 持有/管理方 | 暴露等级 |
|---|---|---|---|
| App ID URL | 客户端开发者 | 公开可访问的元数据配置文件 | 完全公开 |
| PKCE Verifier | AI 客户端 | 登录流程中暂存于客户端内存 | 临时代码级机密 |
| 授权码 (Auth Code) | 授权服务器 | 浏览器重定向 URL 参数 | 一次性有效 |
| Access Token | 授权服务器 | 客户端本地文件 (credentials.json) | 短效机密 |
| Refresh Token | 授权服务器 | 客户端本地及授权服务器数据库 | 长效机密 |
| 用户密码 | 用户 | 仅保留在授权服务器侧 | 严格保密 |
| 本地服务端密钥 | 用户 | 宿主系统环境变量 (env) | 本地局部机密 |
合理选择传输协议——用本地 stdio 解决高性能本地文件与系统交互,用远程 HTTP/OAuth 解决云端 SaaS 服务安全集成——是构建高可用、生产级 AI 应用的核心要义。
在 n1n.ai 获取免费 API 密钥。