Claude Code 的服务层(src/services/)包含约 130 个文件,涵盖 MCP 协议集成、Anthropic API 客户端、OAuth 认证、插件系统、技能系统等核心功能。本文档基于源码最大深度分析,覆盖 services/mcp/(23 文件)、services/api/(20 文件)、services/oauth/、services/plugins/、skills/、tools/MCPTool/ 等全部相关模块。
MCP 类型系统通过 Zod schema 定义了完整的传输联合类型:
// types.ts — 传输类型枚举
export const TransportSchema = lazySchema(() =>
z.enum(['stdio', 'sse', 'sse-ide', 'http', 'ws', 'sdk']),
)加上代码中实际处理的 ws-ide 和 claudeai-proxy,共 8 种传输类型。每种传输都有独立的 Zod schema 验证配置:
| 传输类型 | Schema | 连接方式 | OAuth 支持 | 适用场景 | 关键限制 |
|---|---|---|---|---|---|
| stdio | McpStdioServerConfigSchema |
StdioClientTransport 子进程 |
无 | 本地命令行 MCP 服务器 | 需 spawn 进程,env 通过 subprocessEnv() 注入 |
| sse | McpSSEServerConfigSchema |
SSEClientTransport + EventSource |
完整(OAuth + XAA) | 远程 SSE 服务器 | EventSource 长连接不加超时;POST 请求 60s 超时 |
| sse-ide | McpSSEIDEServerConfigSchema |
SSEClientTransport(无 auth) |
无 | IDE 扩展内部连接 | 仅允许 mcp__ide__executeCode 和 mcp__ide__getDiagnostics |
| http | McpHTTPServerConfigSchema |
StreamableHTTPClientTransport |
完整(OAuth + XAA) | 远程 Streamable HTTP 服务器 | Accept: application/json, text/event-stream 必须设置 |
| ws | McpWebSocketServerConfigSchema |
WebSocketTransport(自定义) |
无(headersHelper 支持) | WebSocket 远程服务器 | Bun/Node 双路径适配;支持 mTLS |
| ws-ide | McpWebSocketIDEServerConfigSchema |
WebSocketTransport + authToken |
无 | IDE WebSocket 连接 | 通过 X-Claude-Code-Ide-Authorization 认证 |
| sdk | McpSdkServerConfigSchema |
SdkControlClientTransport |
无 | SDK 进程内 MCP 服务器 | 通过 stdout/stdin 控制消息桥接 |
| claudeai-proxy | McpClaudeAIProxyServerConfigSchema |
StreamableHTTPClientTransport |
Claude.ai OAuth | claude.ai 组织管理的 MCP 连接器 | 通过 MCP_PROXY_URL 代理;自动 401 重试 |
// InProcessTransport.ts — 进程内链式传输对
class InProcessTransport implements Transport {
private peer: InProcessTransport | undefined
async send(message: JSONRPCMessage): Promise<void> {
// 通过 queueMicrotask 异步传递,避免同步请求/响应导致栈溢出
queueMicrotask(() => { this.peer?.onmessage?.(message) })
}
}
export function createLinkedTransportPair(): [Transport, Transport] {
const a = new InProcessTransport()
const b = new InProcessTransport()
a._setPeer(b); b._setPeer(a)
return [a, b]
}用于两种场景:
- Chrome MCP 服务器:
isClaudeInChromeMCPServer(name)时启用,避免 spawn ~325MB 子进程 - Computer Use MCP 服务器:
feature('CHICAGO_MCP')门控下的计算机使用功能
SDK 传输桥接实现了 CLI 进程与 SDK 进程间的 MCP 通信:
CLI → SDK: SdkControlClientTransport.send() → 控制消息(stdout) → SDK StructuredIO → 路由到对应server
SDK → CLI: MCP server → SdkControlServerTransport.send() → callback → 控制消息解析 → onmessage
关键设计:SdkControlClientTransport 通过 sendMcpMessage 回调将 JSONRPC 消息包装为控制请求(含 server_name 和 request_id),SDK 端的 StructuredIO 负责路由和响应关联。
┌─────────┐
│ pending │ ←──── 初始 / 重连
└────┬─────┘
│ connectToServer()
┌─────────┼──────────┬──────────────┐
▼ ▼ ▼ ▼
┌─────────┐ ┌────────┐ ┌──────────┐ ┌──────────┐
│connected│ │ failed │ │needs-auth│ │ disabled │
└────┬────┘ └───┬────┘ └────┬─────┘ └──────────┘
│ │ │
│ 401/ │ auto- │ performMCPOAuthFlow()
│ expired │ reconnect │ performMCPXaaAuth()
│ │ │
▼ ▼ ▼
┌──────────┐ ┌─────────┐ ┌─────────┐
│needs-auth│ │ pending │ │connected│
└──────────┘ └─────────┘ └─────────┘
五种状态通过 TypeScript 联合类型严格定义:
export type MCPServerConnection =
| ConnectedMCPServer // client + capabilities + cleanup
| FailedMCPServer // error message
| NeedsAuthMCPServer // 等待 OAuth
| PendingMCPServer // reconnectAttempt / maxReconnectAttempts
| DisabledMCPServer // 用户主动禁用重连策略(useManageMCPConnections.ts):
- 最大重连次数:
MAX_RECONNECT_ATTEMPTS = 5 - 指数退避:
INITIAL_BACKOFF_MS = 1000→MAX_BACKOFF_MS = 30000 - 连接超时:
getConnectionTimeoutMs()默认 30s,可通过MCP_TIMEOUT环境变量覆盖
// 本地服务器(stdio/sdk):并发 3 个
export function getMcpServerConnectionBatchSize(): number {
return parseInt(process.env.MCP_SERVER_CONNECTION_BATCH_SIZE || '', 10) || 3
}
// 远程服务器(sse/http/ws 等):并发 20 个
function getRemoteMcpServerConnectionBatchSize(): number {
return parseInt(process.env.MCP_REMOTE_SERVER_CONNECTION_BATCH_SIZE || '', 10) || 20
}本地和远程服务器分开批处理,远程并发更高以利用网络 I/O。
services/api/client.ts 的 getAnthropicClient() 是 API 访问的统一入口,通过环境变量选择后端:
export async function getAnthropicClient({ apiKey, maxRetries, model, fetchOverride, source }) {
// 公共参数
const ARGS = { defaultHeaders, maxRetries, timeout: 600_000, dangerouslyAllowBrowser: true, ... }
if (isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK)) {
// 1. AWS Bedrock — AnthropicBedrock SDK
// 支持 awsRegion / awsAccessKey / awsSecretKey / awsSessionToken
// ANTHROPIC_SMALL_FAST_MODEL_AWS_REGION 可为 Haiku 指定独立 region
return new AnthropicBedrock(bedrockArgs) as unknown as Anthropic
}
if (isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY)) {
// 2. Azure Foundry — AnthropicFoundry SDK
// 支持 ANTHROPIC_FOUNDRY_API_KEY 或 Azure AD DefaultAzureCredential
return new AnthropicFoundry(foundryArgs) as unknown as Anthropic
}
if (isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX)) {
// 3. Google Vertex AI — AnthropicVertex SDK
// GoogleAuth scopes: cloud-platform
// 项目ID回退链: 环境变量 → 凭证文件 → ANTHROPIC_VERTEX_PROJECT_ID
return new AnthropicVertex(vertexArgs) as unknown as Anthropic
}
// 4. 直接 API — 标准 Anthropic SDK
// apiKey(外部) vs authToken(Claude.ai 订阅者)
return new Anthropic({
apiKey: isClaudeAISubscriber() ? null : apiKey || getAnthropicApiKey(),
authToken: isClaudeAISubscriber() ? getClaudeAIOAuthTokens()?.accessToken : undefined,
...ARGS,
})
}关键细节:
- 所有后端的 maxRetries 在 SDK 层设为 0,重试逻辑由
withRetry.ts统一管理 - 自定义 headers:
ANTHROPIC_CUSTOM_HEADERS环境变量注入任意 header(支持 HFI 调试场景) - 代理支持:
getProxyFetchOptions({ forAnthropicAPI: true })对 Anthropic API 启用代理
services/api/claude.ts 中的 queryModel 是核心查询函数。流式和非流式模式的差异:
流式模式(主路径):
// claude.ts 中 createStream() 使用 withStreamingVCR 包装
for await (const message of withStreamingVCR(messages, async function* () {
yield* queryModel(messages, /* ... streaming: true */)
}))非流式回退:
- 当流式请求遇到 529 overloaded 错误时,
withRetry触发FallbackTriggeredError - 回退到 Sonnet 模型(
options.fallbackModel)
cache_control 标记的放置策略极为精细(claude.ts):
- 每请求仅一个标记点:Mycro 的 KV 页面驱逐机制要求单一
cache_control标记 - 标记位置:最后一条消息的最后一个 content block
- 缓存作用域:
function getCacheControl({ scope, querySource }): { type: string } { // 'global' scope: type = 'ephemeral_1h'(1小时全局缓存) // 默认: type = 'ephemeral'(5分钟短暂缓存) }
- cache_reference:在
cache_control标记之前的tool_resultblocks 上添加cache_reference,避免重复传输已缓存内容 - 1h 缓存资格:通过 GrowthBook feature flag
tengu_prompt_cache_1h+ 允许列表双重门控
withRetry 是一个 AsyncGenerator,可通过 yield 向调用方报告重试状态:
| 错误类型 | 重试策略 | 降级策略 |
|---|---|---|
| 401 Unauthorized | 刷新 OAuth token / API key 缓存 | 重建 client 实例 |
| 403 Token Revoked | handleOAuth401Error 强制刷新 |
同 401 |
| 429 Rate Limit | 指数退避(base 500ms,max 32s) | Fast mode: 切换到标准速度 |
| 529 Overloaded | 最多 3 次 → FallbackTriggeredError |
Opus → Sonnet 模型降级 |
| 400 Context Overflow | 调整 maxTokensOverride |
保留 >=3000 output tokens |
| AWS/GCP Auth Error | 清除凭证缓存后重试 | 重建 client |
| ECONNRESET/EPIPE | disableKeepAlive() 后重试 |
禁用连接池 |
Persistent Retry 模式(CLAUDE_CODE_UNATTENDED_RETRY):
- 无人值守场景,429/529 无限重试
- 退避上限 5 分钟,重置窗口上限 6 小时
- 每 30 秒发送心跳(SystemAPIErrorMessage yield),防止会话被标记为空闲
Fast Mode 降级:
- 短 retry-after(<20s):保持 fast mode 重试(保护 prompt cache)
- 长 retry-after(>=20s):进入冷却期(至少 10 分钟),切换到标准速度
用户发起 /mcp 认证
│
▼
[1] 检查 oauth.xaa → 是 → 走 XAA 流程(见下节)
│ 否
▼
[2] clearServerTokensFromLocalStorage (清除旧 token)
│
▼
[3] fetchAuthServerMetadata
RFC 9728 PRM → authorization_servers[0] → RFC 8414 AS 元数据
回退: RFC 8414 直接对 MCP URL (path-aware)
│
▼
[4] new ClaudeAuthProvider(serverName, serverConfig, redirectUri)
│
▼
[5] 启动本地 HTTP server (127.0.0.1:{port}/callback)
- port: oauth.callbackPort 或 findAvailablePort()
- 监听 code + state 参数
│
▼
[6] sdkAuth() → 浏览器打开授权 URL (PKCE: code_challenge_method=S256)
│
▼
[7] 用户在浏览器授权 → 回调到 localhost
- 验证 state 防 CSRF
- 提取 authorization code
│
▼
[8] sdkAuth() 交换 code → tokens (access_token + refresh_token)
│
▼
[9] ClaudeAuthProvider.saveTokens() → keychain (SecureStorage)
存储结构: mcpOAuth[serverKey] = {
serverName, serverUrl, accessToken, refreshToken,
expiresAt, scope, clientId, clientSecret, discoveryState
}
ClaudeAuthProvider 实现 OAuthClientProvider 接口,tokens() 方法在每次 MCP 请求时被调用:
ClaudeAuthProvider.tokens()
│
├── 检查 accessToken 是否过期
│ ├── 未过期 → 返回 { access_token, refresh_token }
│ └── 已过期 → _doRefresh()
│ ├── fetchAuthServerMetadata() → 获取 token_endpoint
│ ├── sdkRefreshAuthorization() → POST /token (grant_type=refresh_token)
│ ├── 成功 → saveTokens() → 返回新 tokens
│ └── 失败 →
│ ├── invalid_grant → invalidateCredentials('tokens') + 删除旧 token
│ ├── 5xx/transient → 重试最多 2 次,间隔 2s
│ └── 其他 → 抛出错误,标记 needs-auth
│
└── XAA 路径: xaaRefresh()
├── 检查 IdP id_token 缓存
├── performCrossAppAccess() (不弹浏览器)
└── 保存新 tokens
// auth.ts: wrapFetchWithStepUpDetection
export function wrapFetchWithStepUpDetection(baseFetch, provider): FetchLike {
return async (url, init) => {
const response = await baseFetch(url, init)
if (response.status === 403) {
const wwwAuth = response.headers.get('WWW-Authenticate')
// 解析 scope 和 resource_metadata 参数
// 持久化到 keychain (stepUpScope + discoveryState.resourceMetadataUrl)
// 设置 forceReauth → tokens() 下次省略 refresh_token → 触发 PKCE 重新授权
}
return response
}
}XAA (Cross-App Access / SEP-990) 实现了一次 IdP 登录,N 个 MCP 服务器静默认证的能力。核心在 xaa.ts 和 xaaIdpLogin.ts。
[配置] settings.xaaIdp = { issuer, clientId, callbackPort? }
[配置] server.oauth = { clientId, xaa: true }
[配置] keychain: mcpOAuthClientConfig[serverKey].clientSecret
performMCPXaaAuth(serverName, serverConfig)
│
▼
[1] acquireIdpIdToken(idpIssuer, idpClientId)
├── getCachedIdpIdToken() → 命中缓存 → 直接返回
└── 缓存未命中 →
├── discoverOidc(issuer) → .well-known/openid-configuration
├── startAuthorization() (PKCE: code_challenge_method=S256)
├── openBrowser(authorizationUrl) ← 唯一的浏览器弹出
├── waitForCallback(port, state, abortSignal)
├── exchangeAuthorization() → { id_token, access_token, ... }
└── saveIdpIdToken(issuer, id_token, expiresAt) → keychain
│
▼
[2] performCrossAppAccess(serverUrl, xaaConfig)
│
├── [Layer 2] discoverProtectedResource(serverUrl) → RFC 9728 PRM
│ 验证: prm.resource === serverUrl (mix-up protection)
│
├── [Layer 2] discoverAuthorizationServer(asUrl) → RFC 8414
│ 验证: meta.issuer === asUrl (mix-up protection)
│ 验证: token_endpoint 必须 HTTPS
│ 检查: grant_types_supported 包含 jwt-bearer
│
├── [Layer 2] requestJwtAuthorizationGrant()
│ RFC 8693 Token Exchange: id_token → ID-JAG
│ POST IdP_token_endpoint:
│ grant_type = urn:ietf:params:oauth:grant-type:token-exchange
│ requested_token_type = urn:ietf:params:oauth:token-type:id-jag
│ subject_token = id_token
│ subject_token_type = urn:ietf:params:oauth:token-type:id_token
│ audience = AS_issuer, resource = PRM_resource
│
└── [Layer 2] exchangeJwtAuthGrant()
RFC 7523 JWT Bearer: ID-JAG → access_token
POST AS_token_endpoint:
grant_type = urn:ietf:params:oauth:grant-type:jwt-bearer
assertion = ID-JAG
认证方式: client_secret_basic (默认) 或 client_secret_post
│
▼
[3] 保存 tokens 到 keychain (mcpOAuth[serverKey])
包含 discoveryState.authorizationServerUrl 用于后续刷新
// XaaTokenExchangeError 携带 shouldClearIdToken 标记
// 4xx / invalid_grant → id_token 无效,清除缓存
// 5xx → IdP 宕机,id_token 可能仍有效,保留
// 200 + 非法 body → 协议违规,清除XAA 对敏感信息(token、assertion、client_secret)在日志中做了严格脱敏:
const SENSITIVE_TOKEN_RE =
/"(access_token|refresh_token|id_token|assertion|subject_token|client_secret)"\s*:\s*"[^"]*"/g
function redactTokens(raw) {
return s.replace(SENSITIVE_TOKEN_RE, (_, k) => `"${k}":"[REDACTED]"`)
}export type ConfigScope = 'local' | 'user' | 'project' | 'dynamic' | 'enterprise' | 'claudeai' | 'managed'配置加载优先级(getAllMcpConfigs):
- Enterprise (
managed-mcp.json):存在时禁用 claude.ai 连接器 - User (
~/.claude/settings.json中的mcpServers) - Project (
.mcp.json或.claude/settings.local.json) - Plugin:通过
getPluginMcpServers()提供 - Claude.ai:通过
fetchClaudeAIMcpConfigsIfEligible()API 获取 - Dynamic:运行时动态注入(SDK 等)
三层去重防止同一 MCP 服务器重复连接:
// 1. 插件 vs 手动配置去重
dedupPluginMcpServers(pluginServers, manualServers)
// 签名比较: stdio → "stdio:" + JSON(commandArray)
// remote → "url:" + unwrapCcrProxyUrl(url)
// 2. Claude.ai 连接器 vs 手动配置去重
dedupClaudeAiMcpServers(claudeAiServers, manualServers)
// 仅用启用的手动服务器作为去重目标
// 3. CCR 代理 URL 解包
unwrapCcrProxyUrl(url) // 提取 mcp_url 查询参数中的原始供应商 URL// Denylist 绝对优先 — 三种匹配方式
isMcpServerDenied(name, config)
├── isMcpServerNameEntry(entry) // 按名称
├── isMcpServerCommandEntry(entry) // 按命令数组(stdio)
└── isMcpServerUrlEntry(entry) // 按 URL 通配符模式
// Allowlist — allowManagedMcpServersOnly 时仅用 policySettings
isMcpServerAllowedByPolicy(name, config)services/plugins/
PluginInstallationManager.ts — 后台安装管理器
pluginOperations.ts — 增删改操作
pluginCliCommands.ts — CLI 命令接口
启动时:
loadAllPluginsCacheOnly() ← 仅从缓存加载(不阻塞启动)
后台:
performBackgroundPluginInstallations()
├── getDeclaredMarketplaces() → settings 中声明的 marketplace
├── loadKnownMarketplacesConfig() → 已物化的 marketplace 配置
├── diffMarketplaces() → 计算 missing / sourceChanged
└── reconcileMarketplaces() → clone/update Git 仓库
└── onProgress: installing → installed | failed
安装完成后:
├── refreshActivePlugins() → 重新加载插件
└── 或 needsRefresh → 显示通知提示 /reload-plugins
插件通过 getPluginMcpServers() 注入 MCP 服务器配置。插件服务器的命名空间为 plugin:<name>:<server>,不会与手动配置键冲突。但内容去重(dedupPluginMcpServers)会检测相同 command/url 的重复。
每个插件 MCP 服务器配置上附带 pluginSource 字段(如 'slack@anthropic'),用于 channel 权限控制时的快速查找,无需等待 AppState.plugins.enabled 异步加载完成。
| 来源 | 目录 | LoadedFrom | 加载时机 |
|---|---|---|---|
| 内置(Bundled) | skills/bundled/ |
'bundled' |
initBundledSkills() 启动时同步注册 |
| 目录(Disk) | .claude/skills/, ~/.claude/skills/ |
'skills' |
loadSkillsDir() 扫描 Markdown 文件 |
| MCP | 远程 MCP 服务器 prompts | 'mcp' |
fetchMcpSkillsForClient() 连接时获取 |
// skills/bundled/index.ts — initBundledSkills()
registerUpdateConfigSkill() // /update-config
registerKeybindingsSkill() // /keybindings-help
registerVerifySkill() // /verify
registerDebugSkill() // /debug
registerLoremIpsumSkill() // /lorem-ipsum
registerSkillifySkill() // /skillify
registerRememberSkill() // /remember
registerSimplifySkill() // /simplify
registerBatchSkill() // /batch
registerStuckSkill() // /stuck
// Feature-gated:
registerDreamSkill() // KAIROS / KAIROS_DREAM
registerHunterSkill() // REVIEW_ARTIFACT
registerLoopSkill() // AGENT_TRIGGERS
registerScheduleRemoteAgentsSkill() // AGENT_TRIGGERS_REMOTE
registerClaudeApiSkill() // CLAUDE_API
registerClaudeInChromeSkill() // auto-enable conditionregisterBundledSkill 将 BundledSkillDefinition 转换为 Command 对象并推入全局 bundledSkills 数组。支持 files 字段延迟解压到磁盘(getBundledSkillExtractDir),通过 O_NOFOLLOW|O_EXCL 标志防符号链接攻击。
// mcpSkillBuilders.ts — 依赖图叶节点,无导入
export type MCPSkillBuilders = {
createSkillCommand: typeof createSkillCommand
parseSkillFrontmatterFields: typeof parseSkillFrontmatterFields
}
let builders: MCPSkillBuilders | null = null
export function registerMCPSkillBuilders(b: MCPSkillBuilders): void {
builders = b // 写一次
}
export function getMCPSkillBuilders(): MCPSkillBuilders {
if (!builders) throw new Error('MCP skill builders not registered')
return builders
}这个模式解决了循环依赖问题:client.ts → mcpSkills.ts → loadSkillsDir.ts → ... → client.ts。通过将 builders 注册延迟到模块初始化时(loadSkillsDir.ts 通过 commands.ts 的静态导入在启动时被 eagerly 求值),保证 MCP 服务器连接时 builders 已就绪。
---
description: 技能描述文本
when-to-use: 触发条件描述
argument-hint: 参数提示
allowed-tools: Bash, Read, Edit
model: claude-sonnet-4-20250514
context: inline | fork
hooks:
preToolUse:
- pattern: "*"
command: echo "pre-hook"
---
### 技能 Prompt 内容
实际的 system prompt 文本...前置数据由 parseFrontmatter() 解析,支持:
allowed-tools:限制技能可用的工具列表model:覆盖默认模型context: fork:在子 agent 中运行hooks:技能级别的 pre/post 钩子
export const MCPTool = buildTool({
isMcp: true,
name: 'mcp', // 在 client.ts 中被覆盖为实际 MCP 工具名
maxResultSizeChars: 100_000,
// description, prompt, call, userFacingName 都在 client.ts 中覆盖
async checkPermissions(): Promise<PermissionResult> {
return { behavior: 'passthrough', message: 'MCPTool requires permission.' }
},
})MCPTool 是一个模板对象,在 client.ts 的 fetchToolsForClient() 中为每个 MCP 服务器暴露的工具创建定制副本,设置:
name:mcp__{normalizedServerName}__{normalizedToolName}(双下划线分隔)description: 截断到MAX_MCP_DESCRIPTION_LENGTH = 2048字符call: 封装client.callTool()+ 超时 + 结果格式化 + 图片处理
LLM 输出 tool_use(name="mcp__github__create_issue", input={...})
│
▼
MCPTool.call(input)
│
├── 查找对应 ConnectedMCPServer
├── client.callTool({ name: originalToolName, arguments: input })
│ ├── 超时: getMcpToolTimeoutMs() 默认 ~27.8 小时
│ ├── 401 → McpAuthError → 标记 needs-auth
│ └── 404 + -32001 → McpSessionExpiredError → 清除缓存 → 重建连接
│
├── 结果处理:
│ ├── isError: true → McpToolCallError
│ ├── 图片: maybeResizeAndDownsampleImageBuffer()
│ ├── 大型输出: truncateMcpContentIfNeeded()
│ └── 二进制: persistBinaryContent() → 保存到磁盘
│
└── 返回格式化文本结果
Channel 通知让 MCP 服务器(如 Discord/Slack/Telegram 机器人)向对话推送消息:
// channelNotification.ts
export const ChannelMessageNotificationSchema = lazySchema(() =>
z.object({
method: z.literal('notifications/claude/channel'),
params: z.object({
content: z.string(),
meta: z.record(z.string(), z.string()).optional(),
}),
}),
)通知处理流程:
- MCP 服务器发送
notifications/claude/channel通知 - 内容被包装为
<channel source="..." chat_id="..." ...>XML 标签 - 通过
enqueue()推入消息队列 SleepTool的hasCommandsInQueue()检测到新消息,1 秒内唤醒- 模型看到
<channel>标签后决定如何响应
权限安全:ChannelPermissionNotificationSchema 支持结构化权限回复({request_id, behavior}),避免文本消息意外匹配权限确认。
// 预取 Anthropic 官方 MCP 注册表
export async function prefetchOfficialMcpUrls(): Promise<void> {
// GET https://api.anthropic.com/mcp-registry/v0/servers?version=latest&visibility=commercial
// 用于 isOfficialMcpUrl() 判断 — 影响信任等级和 UI 显示
}// MCP 名称标准化: ^[a-zA-Z0-9_-]{1,64}$
export function normalizeNameForMCP(name: string): string {
let normalized = name.replace(/[^a-zA-Z0-9_-]/g, '_')
if (name.startsWith('claude.ai ')) {
normalized = normalized.replace(/_+/g, '_').replace(/^_|_$/g, '')
}
return normalized
}动态 header 注入机制 — 通过执行外部脚本生成 header:
- 项目/本地设置中的
headersHelper需通过信任检查 - 脚本超时执行,结果解析为 JSON 对象
- 与静态
headers合并后用于所有 MCP 请求
环境变量展开:MCP 配置中 ${VAR} 风格的引用在连接时被展开为实际值。
MCP 服务器可通过 Elicitation 协议向用户收集信息:
- Form 模式:结构化表单
- URL 模式:重定向到外部 URL 后等待完成通知
- 通过
ElicitationCompleteNotification实现异步完成通知
Claude Code 的 MCP 集成是一个完整的协议客户端实现,包含 8 种传输、完整的 OAuth/XAA 认证链、企业级策略控制和弹性重试机制。API 客户端统一了 4 种云后端的访问方式,prompt 缓存策略在 token 级别精细控制。Skills 系统通过 bundled + disk + MCP 三源汇聚,write-once registry 模式优雅地解决了循环依赖问题。插件系统以 marketplace 为分发单元,后台安装不阻塞启动。