MCP 认证与 OAuth:连接远程能力的“钥匙”
对于需要访问 Google Calendar、GitHub 或内部系统的远程 MCP 服务,Claude Code 提供了一套标准化的认证流程。它支持工业级的 OAuth 2.0 授权码流,同时提供了灵活的 headersHelper 来处理自定义认证。
先搞清楚这是什么
MCP 认证本质上是 跨进程的凭证交换与令牌生命周期代理 (OAuth Proxy & Token Broker)。
它解决了子进程(MCP Server)无法直接弹出 UI 授权的问题。Claude Code 扮演了“守门人”的角色:它拦截 Server 抛出的认证请求,启动本地回调监听器(OAuth Port),引导人类在浏览器中完成操作,最后将换取的令牌安全地注入 Server 进程。其目标不是让面板显示“已登录”,而是让一个 needs-auth 状态的连接恢复成真实的工具集。
实现细节
认证流程被拆分为配置、执行和重连三个阶段:
OAuth 核心流程 (src/services/mcp/auth.ts):
- metadata 发现:通过
authServerMetadataUrl识别远程服务的授权端点。 - 本地回调服务:利用
src/services/mcp/oauthPort.ts开启127.0.0.1上的临时端口。 - 令牌管理:
saveTokens将 Access Token 和 Refresh Token 写入安全存储(Secure Storage),而不是普通的.mcp.json配置文件。 - 自动续期:
ClaudeAuthProvider.tokens()会定期检查并自动刷新即将过期的令牌。
- metadata 发现:通过
伪工具占位与恢复 (src/tools/McpAuthTool/McpAuthTool.ts):
- 当 Server 尚未认证时,Claude 会在模型侧暴露一个
mcp__<server>__authenticate伪工具。 - 一旦用户完成 OAuth,该工具会调用
reconnect(),把这个 Server 真正的 tools/commands 整体“热替换”进来。
- 当 Server 尚未认证时,Claude 会在模型侧暴露一个
动态请求头 (src/services/mcp/headersHelper.ts):
- 如果远程服务不走 OAuth,而是需要短期令牌或内部 SSO,可以使用
headersHelper字段。 - 它是受 Workspace Trust 约束的 shell 命令。在连接建立前,Claude 会运行此命令并捕获其 stdout 中的 JSON,将其作为 HTTP Headers 并入 SSE 连接请求中。
- 源码逻辑确保了每次连接都会执行,无本地缓存,这正好适配了“每次请求换取新 token”的短寿命认证场景。
- 如果远程服务不走 OAuth,而是需要短期令牌或内部 SSO,可以使用
XAA 一键登录 (src/services/mcp/xaaIdpLogin.ts):
- 针对高度集成的合作伙伴(如 claude.ai 本身),系统支持 XAA IdP 流程,实现无缝的身份识别与令牌交换。
实战注意事项
- 存储隔离:
clientId和authUrl进.mcp.json,但clientSecret和具体tokens永远留在安全存储中。 - Transport 限制:OAuth 机制目前主要服务于 HTTP 和 SSE 连接。对于 Stdio 类型的本地进程,它默认被禁用。
- Helper 容错:
headersHelper的执行超时被硬编码为 10 秒。如果命令执行失败,Claude 会记录日志但可能尝试带空头继续连接,而不是立刻抛出 fatal error。 - 无图形化环境:在纯 SSH 会话中,由于无法自动打开本地浏览器,OAuth 流程会退化为“手动粘贴回调 URL”模式。
相关主题
- 如果你想了解认证后的工具是如何被延迟加载的,请看 MCP 注册表与服务发现。
- 如果你更关心资源面的交互逻辑,请看 MCP 资源与 Prompt 命令。
- 如果你想了解 elicitation 交互(除了 URL 外的表单请求),请看 MCP elicitation 交互。
源码锚点
claude-code-opensource/src/services/mcp/auth.ts— OAuth 授权、发现与令牌交换的中枢。
📄 src/services/mcp/auth.ts — OAuth 授权、发现与令牌交换的中枢。
typescript
discoverOAuthServerInfo,
type OAuthClientProvider,
type OAuthDiscoveryState,
auth as sdkAuth,
refreshAuthorization as sdkRefreshAuthorization,
} from '@modelcontextprotocol/sdk/client/auth.js'
import {claude-code-opensource/src/services/mcp/headersHelper.ts— 动态 Headers 获取与 shell 命令执行入口。
📄 src/services/mcp/headersHelper.ts — 动态 Headers 获取与 shell 命令执行入口。
typescript
* @returns Headers object or null if not configured or failed
*/
export async function getMcpHeadersFromHelper(
serverName: string,
config: McpSSEServerConfig | McpHTTPServerConfig | McpWebSocketServerConfig,
): Promise<Record<string, string> | null> {
if (!config.headersHelper) {
return null
}
// Security check for project/local settings
// Skip trust check in non-interactive mode (e.g., CI/CD, automation)
if (
'scope' in config &&
isMcpServerFromProjectOrLocalSettings(config as ScopedMcpServerConfig) &&
!getIsNonInteractiveSession()
) {
// Check if trust has been established for this project
const hasTrust = checkHasTrustDialogAccepted()
if (!hasTrust) {
const error = new Error(
`Security: headersHelper for MCP server '${serverName}' executed before workspace trust is confirmed. If you see this message, post in ${MACRO.FEEDBACK_CHANNEL}.`,
)
logAntError('MCP headersHelper invoked before trust check', error)
logEvent('tengu_mcp_headersHelper_missing_trust', {})
return null
}
}
try {claude-code-opensource/src/services/mcp/oauthPort.ts— 本地回调服务器管理。
📄 src/services/mcp/oauthPort.ts — 本地回调服务器管理。
typescript
const REDIRECT_PORT_RANGE =
getPlatform() === 'windows'
? { min: 39152, max: 49151 }claude-code-opensource/src/tools/McpAuthTool/McpAuthTool.ts— 衔接模型与认证流程的伪工具实现。
📄 src/tools/McpAuthTool/McpAuthTool.ts — 衔接模型与认证流程的伪工具实现。
typescript
const inputSchema = lazySchema(() => z.object({}))claude-code-opensource/src/services/mcp/types.ts— 远程 Server 的 OAuth 配置模型。
📄 src/services/mcp/types.ts — 远程 Server 的 OAuth 配置模型。
typescript
ServerCapabilities,
} from '@modelcontextprotocol/sdk/types.js'
import { z } from 'zod/v4'