Claude Code 桥接与插件系统源码解读
Contents
Claude Code 桥接与插件系统源码解读
分析版本: Claude Code Main Branch 源码路径:
/mnt/e/code/cc/claude-code-main/src
目录
1. MCP (Model Context Protocol) 集成
1.1 MCP 客户端架构
Claude Code 实现了完整的 MCP 客户端,支持多种传输协议:
核心文件:
src/services/mcp/client.ts- MCP 客户端主实现 (1500+ 行)src/services/mcp/types.ts- MCP 类型定义
支持的传输类型:
// src/services/mcp/types.ts
export const TransportSchema = z.enum([
'stdio', // 标准输入输出
'sse', // Server-Sent Events
'sse-ide', // IDE 专用 SSE
'http', // HTTP Streamable
'ws', // WebSocket
'ws-ide', // IDE 专用 WebSocket
'sdk', // SDK 模式
])
客户端连接流程 (connectToServer):
// src/services/mcp/client.ts:595-961
export const connectToServer = memoize(
async (name, serverRef, serverStats) => {
// 1. 根据传输类型创建 Transport
let transport
if (serverRef.type === 'sse') {
transport = new SSEClientTransport(new URL(serverRef.url), options)
} else if (serverRef.type === 'http') {
transport = new StreamableHTTPClientTransport(new URL(serverRef.url), options)
} else if (serverRef.type === 'ws') {
transport = new WebSocketTransport(wsClient)
} else if (serverRef.type === 'stdio') {
transport = new StdioClientTransport({ command, args, env })
}
// 2. 创建 MCP Client
const client = new Client({
name: 'claude-code',
version: MACRO.VERSION,
}, {
capabilities: { roots: {}, elicitation: {} },
})
// 3. 设置请求处理器
client.setRequestHandler(ListRootsRequestSchema, async () => ({
roots: [{ uri: `file://${getOriginalCwd()}` }],
}))
// 4. 连接并设置错误处理
await client.connect(transport)
}
)
1.2 MCP 服务器实现
Claude Code 可以作为 MCP 服务器运行,暴露其工具给其他 MCP 客户端:
核心文件: src/entrypoints/mcp.ts
// src/entrypoints/mcp.ts:35-57
export async function startMCPServer(cwd: string, debug: boolean, verbose: boolean) {
const server = new Server({
name: 'claude/tengu',
version: MACRO.VERSION,
}, {
capabilities: { tools: {} },
})
// 暴露工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => {
const tools = getTools(toolPermissionContext)
return {
tools: tools.map(tool => ({
name: tool.name,
description: await tool.prompt(...),
inputSchema: zodToJsonSchema(tool.inputSchema),
})),
}
})
// 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async ({ params }) => {
const tool = findToolByName(tools, params.name)
const result = await tool.call(params.arguments, toolUseContext, ...)
return { content: [{ type: 'text', text: jsonStringify(result) }] }
})
const transport = new StdioServerTransport()
await server.connect(transport)
}
1.3 MCP 认证机制
支持 OAuth 2.0 和自定义认证提供者:
// src/services/mcp/client.ts:619-641
if (serverRef.type === 'sse') {
const authProvider = new ClaudeAuthProvider(name, serverRef)
const transportOptions: SSEClientTransportOptions = {
authProvider,
fetch: wrapFetchWithTimeout(
wrapFetchWithStepUpDetection(createFetchWithInit(), authProvider)
),
}
}
1.4 MCP 工具规范化
工具名称经过规范化处理以避免冲突:
// src/services/mcp/mcpStringUtils.ts
export function buildMcpToolName(serverName: string, toolName: string): string {
return `mcp__${normalizeNameForMCP(serverName)}__${normalizeNameForMCP(toolName)}`
}
2. 插件扩展点设计
2.1 插件清单 (Plugin Manifest)
插件通过 plugin.json 声明扩展点:
核心文件: src/utils/plugins/schemas.ts
// src/utils/plugins/schemas.ts:884-898
export const PluginManifestSchema = lazySchema(() =>
z.object({
...PluginManifestMetadataSchema().shape,
...PluginManifestHooksSchema().partial().shape, // 钩子扩展
...PluginManifestCommandsSchema().partial().shape, // 命令扩展
...PluginManifestAgentsSchema().partial().shape, // Agent 扩展
...PluginManifestSkillsSchema().partial().shape, // Skill 扩展
...PluginManifestOutputStylesSchema().partial().shape, // 输出样式扩展
...PluginManifestMcpServerSchema().partial().shape, // MCP 服务器
...PluginManifestLspServerSchema().partial().shape, // LSP 服务器
}),
)
2.2 插件目录结构
my-plugin/
├── .claude-plugin/
│ └── plugin.json # 插件清单
├── commands/ # 自定义斜杠命令
│ ├── build.md
│ └── deploy.md
├── agents/ # 自定义 Agent
│ └── reviewer.md
├── skills/ # 自定义 Skill
├── hooks/ # 钩子配置
│ └── hooks.json
├── output-styles/ # 输出样式
└── mcpServers/ # MCP 服务器配置
2.3 插件类型定义
核心文件: src/types/plugin.ts
// src/types/plugin.ts:48-70
export type LoadedPlugin = {
name: string
manifest: PluginManifest
path: string
source: string
repository: string
enabled?: boolean
isBuiltin?: boolean
sha?: string // Git commit SHA for version pinning
commandsPath?: string
agentsPath?: string
skillsPath?: string
hooksConfig?: HooksSettings
mcpServers?: Record<string, McpServerConfig>
lspServers?: Record<string, LspServerConfig>
settings?: Record<string, unknown>
}
2.4 插件来源类型
// src/utils/plugins/schemas.ts:1062-1161
export const PluginSourceSchema = z.union([
RelativePath(), // 本地相对路径
// 远程来源
z.object({ source: 'npm', package: NpmPackageNameSchema(), version?: string }),
z.object({ source: 'pip', package: string, version?: string }), // Python
z.object({ source: 'github', repo: string, ref?: string, sha?: string }),
z.object({ source: 'url', url: string, ref?: string, sha?: string }),
z.object({ source: 'git-subdir', url: string, path: string, ... }),
])
2.5 内置插件 (Builtin Plugins)
内置插件随 CLI 一起发布:
// src/plugins/builtinPlugins.ts
export type BuiltinPluginDefinition = {
name: string
description: string
version?: string
skills?: BundledSkillDefinition[]
hooks?: HooksSettings
mcpServers?: Record<string, McpServerConfig>
isAvailable?: () => boolean
defaultEnabled?: boolean
}
2.6 插件市场 (Marketplace)
// src/utils/plugins/schemas.ts:1293-1326
export const PluginMarketplaceSchema = lazySchema(() =>
z.object({
name: MarketplaceNameSchema(),
owner: PluginAuthorSchema(),
plugins: z.array(PluginMarketplaceEntrySchema()),
forceRemoveDeletedPlugins?: boolean,
metadata?: { pluginRoot?: string, version?: string },
allowCrossMarketplaceDependenciesOn?: string[],
}),
)
3. API 桥接机制
3.1 桥接主循环 (Bridge Loop)
核心文件: src/bridge/bridgeMain.ts
Claude Code 的 Bridge 是远程控制的核心,负责:
- 与后端服务器保持长连接
- 管理多个并发会话
- 处理工作分发和结果回收
// src/bridge/bridgeMain.ts:141-151
export async function runBridgeLoop(
config: BridgeConfig,
environmentId: string,
environmentSecret: string,
api: BridgeApiClient,
spawner: SessionSpawner,
logger: BridgeLogger,
signal: AbortSignal,
backoffConfig: BackoffConfig = DEFAULT_BACKOFF,
) {
const activeSessions = new Map<string, SessionHandle>()
while (!loopSignal.aborted) {
// 1. 轮询工作
const work = await api.pollForWork(environmentId, environmentSecret, ...)
// 2. 处理工作类型
switch (work.data.type) {
case 'healthcheck':
await ackWork()
break
case 'session':
// 启动新会话
const handle = spawner.spawn({ sessionId, sdkUrl, accessToken, ... })
activeSessions.set(sessionId, handle)
break
}
// 3. 心跳保活
await heartbeatActiveWorkItems()
}
}
3.2 会话生命周期
// src/bridge/bridgeMain.ts:442-591
function onSessionDone(sessionId, startTime, handle) {
return (status: SessionDoneStatus) => {
// 清理会话
activeSessions.delete(sessionId)
// 通知服务器工作完成
stopWorkWithRetry(api, environmentId, workId, ...)
// 清理关联的工作树
removeAgentWorktree(wt.worktreePath, wt.worktreeBranch, ...)
// 多会话模式: 存档会话; 单会话模式: 关闭环境
if (config.spawnMode !== 'single-session') {
api.archiveSession(compatId)
} else {
controller.abort() // 关闭桥接循环
}
}
}
3.3 多会话与工作树隔离
Bridge 支持多会话模式,每个会话可在独立的工作树中运行:
// src/bridge/bridgeMain.ts:976-1015
if (spawnModeAtDecision === 'worktree' && ...) {
const wt = await createAgentWorktree(`bridge-${safeFilenameId(sessionId)}`)
sessionWorktrees.set(sessionId, {
worktreePath: wt.worktreePath,
worktreeBranch: wt.worktreeBranch,
gitRoot: wt.gitRoot,
})
sessionDir = wt.worktreePath
}
3.4 Token 刷新机制
// src/bridge/bridgeMain.ts:279-313
const tokenRefresh = createTokenRefreshScheduler({
getAccessToken,
onRefresh: (sessionId, oauthToken) => {
const handle = activeSessions.get(sessionId)
if (v2Sessions.has(sessionId)) {
// v2: 调用 reconnectSession 触发服务器重新分发
api.reconnectSession(environmentId, sessionId)
} else {
// v1: 直接更新 access token
handle.updateAccessToken(oauthToken)
}
},
})
3.5 桥接配置
// src/bridge/types.ts
export type BridgeConfig = {
apiBaseUrl: string
sessionIngressUrl: string
spawnMode: 'single-session' | 'worktree' | 'same-dir'
maxSessions: number
sessionTimeoutMs?: number
debugFile?: string
}
4. 第三方工具接入
4.1 工具系统架构
核心文件:
src/Tool.ts- 工具基类src/tools.ts- 工具注册表src/services/tools/toolExecution.ts- 工具执行
// src/Tool.ts
export abstract class Tool {
name: string
description: string
inputSchema: z.ZodType
outputSchema?: z.ZodType
abstract prompt(params: ToolParams): string | Promise<string>
abstract call(params: unknown, context: ToolUseContext, ...): Promise<ToolResult>
validateInput?(params: unknown, context: ToolUseContext): ValidationResult
isEnabled?(): boolean
}
4.2 内置工具类型
| 工具 | 文件 | 功能 |
|---|---|---|
| BashTool | src/tools/BashTool/BashTool.tsx | 执行 shell 命令 |
| FileReadTool | src/tools/FileReadTool/FileReadTool.ts | 读取文件 |
| FileWriteTool | src/tools/FileWriteTool/FileWriteTool.ts | 写入文件 |
| FileEditTool | src/tools/FileEditTool/FileEditTool.ts | 编辑文件 |
| MCPTool | src/tools/MCPTool/MCPTool.ts | MCP 工具代理 |
| GrepTool | src/tools/GrepTool/GrepTool.ts | 文本搜索 |
| WebFetchTool | src/tools/WebFetchTool/WebFetchTool.ts | HTTP 请求 |
| WebSearchTool | src/tools/WebSearchTool/WebSearchTool.ts | 网络搜索 |
| AgentTool | src/tools/AgentTool/AgentTool.tsx | Agent 代理 |
| TaskCreateTool | src/tools/TaskCreateTool/TaskCreateTool.ts | 创建任务 |
| TodoWriteTool | src/tools/TodoWriteTool/TodoWriteTool.ts | 待办事项 |
4.3 MCP 工具桥接
MCP 工具通过 MCPTool 接入:
// src/tools/MCPTool/MCPTool.ts
export class MCPTool extends Tool {
constructor(
private serverName: string,
private toolName: string,
private client: Client,
) {
super({
name: buildMcpToolName(serverName, toolName),
description: tool.description,
inputSchema: tool.inputSchema,
})
}
async call(params: unknown, context: ToolUseContext) {
const result = await this.client.callTool({
name: this.toolName,
arguments: params,
})
return this.formatResult(result)
}
}
4.4 工具权限系统
// src/utils/permissions/permissions.ts
export function hasPermissionsToUseTool(
tool: Tool,
context: ToolUseContext,
): Promise<PermissionResult> {
// 检查工具是否需要权限
// 检查用户是否已授权
// 返回允许/拒绝/询问结果
}
4.5 工具执行流程
// src/services/tools/toolExecution.ts
export async function executeTool(
tool: Tool,
params: unknown,
context: ToolUseContext,
): Promise<ToolResult> {
// 1. 权限检查
const permission = await hasPermissionsToUseTool(tool, context)
if (permission.behavior === 'deny') {
return { success: false, reason: 'Permission denied' }
}
// 2. 输入验证
if (tool.validateInput) {
const validation = await tool.validateInput(params, context)
if (!validation.result) {
throw new Error(validation.message)
}
}
// 3. 执行工具
const result = await tool.call(params, context, hasPermissionsToUseTool)
// 4. 触发后置钩子
runPostToolUseHooks(tool.name, result)
return result
}
5. 插件生命周期管理
5.1 生命周期操作
核心文件: src/services/plugins/pluginOperations.ts
| 操作 | 函数 | 功能 |
|---|---|---|
| 安装 | installPluginOp() | 从市场安装插件 |
| 卸载 | uninstallPluginOp() | 卸载插件 |
| 启用 | enablePluginOp() | 启用插件 |
| 禁用 | disablePluginOp() | 禁用插件 |
| 更新 | updatePluginOp() | 更新到新版本 |
5.2 插件安装流程
// src/services/plugins/pluginOperations.ts:321-418
export async function installPluginOp(
plugin: string,
scope: InstallableScope = 'user',
): Promise<PluginOperationResult> {
// 1. 解析插件标识符
const { name: pluginName, marketplace: marketplaceName } =
parsePluginIdentifier(plugin)
// 2. 搜索市场获取插件信息
let foundPlugin: PluginMarketplaceEntry | undefined
if (marketplaceName) {
pluginInfo = await getPluginById(plugin)
} else {
// 在所有已知市场中搜索
const marketplaces = await loadKnownMarketplacesConfig()
for (const [mktName, mktConfig] of Object.entries(marketplaces)) {
const marketplace = await getMarketplace(mktName)
const pluginEntry = marketplace.plugins.find(p => p.name === pluginName)
// ...
}
}
// 3. 执行安装
const result = await installResolvedPlugin({
pluginId,
entry: foundPlugin,
scope,
marketplaceInstallLocation,
})
// 4. 写入配置 (设置优先原则)
updateSettingsForSource(settingSource, {
enabledPlugins: { [pluginId]: true },
})
// 5. 缓存插件
copyPluginToVersionedCache(sourcePath, pluginId, version)
}
5.3 插件加载流程
核心文件: src/utils/plugins/pluginLoader.ts
// src/utils/plugins/pluginLoader.ts:1348-1587
export async function createPluginFromPath(
pluginPath: string,
source: string,
enabled: boolean,
fallbackName: string,
): Promise<{ plugin: LoadedPlugin; errors: PluginError[] }> {
// 1. 加载插件清单
const manifestPath = join(pluginPath, '.claude-plugin', 'plugin.json')
const manifest = await loadPluginManifest(manifestPath, fallbackName, source)
// 2. 创建基础插件对象
const plugin: LoadedPlugin = {
name: manifest.name,
manifest,
path: pluginPath,
source,
repository: source,
enabled,
}
// 3. 自动检测组件目录
const [commandsDirExists, agentsDirExists, skillsDirExists, outputStylesDirExists] =
await Promise.all([
pathExists(join(pluginPath, 'commands')),
pathExists(join(pluginPath, 'agents')),
pathExists(join(pluginPath, 'skills')),
pathExists(join(pluginPath, 'output-styles')),
])
// 4. 注册组件路径
if (commandsDirExists) plugin.commandsPath = commandsPath
if (agentsDirExists) plugin.agentsPath = agentsPath
if (skillsDirExists) plugin.skillsPath = skillsPath
if (outputStylesDirExists) plugin.outputStylesPath = outputStylesPath
// 5. 加载钩子配置
if (manifest.hooks) {
plugin.hooksConfig = await loadPluginHooks(hooksConfigPath, plugin.name)
}
return { plugin, errors }
}
5.4 版本化缓存系统
// src/utils/plugins/pluginLoader.ts:139-177
export function getVersionedCachePath(
pluginId: string,
version: string,
): string {
return join(
getPluginsDirectory(),
'cache',
sanitizedMarketplace,
sanitizedPlugin,
sanitizedVersion,
)
}
// 缓存目录格式:
// ~/.claude/plugins/cache/{marketplace}/{plugin}/{version}/
5.5 插件设置作用域
// src/services/plugins/pluginOperations.ts:71-75
export const VALID_INSTALLABLE_SCOPES = ['user', 'project', 'local'] as const
// 优先级: local > project > user
5.6 插件钩子系统
核心文件: src/utils/plugins/loadPluginHooks.ts
// src/utils/plugins/loadPluginHooks.ts:28-86
function convertPluginHooksToMatchers(
plugin: LoadedPlugin,
): Record<HookEvent, PluginHookMatcher[]> {
const pluginMatchers: Record<HookEvent, PluginHookMatcher[]> = {
PreToolUse: [],
PostToolUse: [],
PostToolUseFailure: [],
PermissionDenied: [],
Notification: [],
UserPromptSubmit: [],
SessionStart: [],
SessionEnd: [],
Stop: [],
// ... 更多事件
}
if (!plugin.hooksConfig) return pluginMatchers
for (const [event, matchers] of Object.entries(plugin.hooksConfig)) {
const hookEvent = event as HookEvent
for (const matcher of matchers) {
pluginMatchers[hookEvent].push({
matcher: matcher.matcher,
hooks: matcher.hooks,
pluginRoot: plugin.path,
pluginName: plugin.name,
})
}
}
return pluginMatchers
}
5.7 钩子事件类型
// src/types/hooks.ts
export type HookEvent =
| 'PreToolUse'
| 'PostToolUse'
| 'PostToolUseFailure'
| 'PermissionDenied'
| 'Notification'
| 'UserPromptSubmit'
| 'SessionStart'
| 'SessionEnd'
| 'Stop'
| 'StopFailure'
| 'SubagentStart'
| 'SubagentStop'
| 'PreCompact'
| 'PostCompact'
| 'PermissionRequest'
| 'Setup'
| 'TeammateIdle'
| 'TaskCreated'
| 'TaskCompleted'
| 'Elicitation'
| 'ElicitationResult'
| 'ConfigChange'
| 'WorktreeCreate'
| 'WorktreeRemove'
| 'InstructionsLoaded'
| 'CwdChanged'
| 'FileChanged'
5.8 插件依赖管理
// src/utils/plugins/dependencyResolver.ts
export function findReverseDependents(
pluginId: string,
plugins: LoadedPlugin[],
): string[] {
// 查找依赖该插件的其他插件
}
export function verifyAndDemote(
pluginId: string,
plugins: LoadedPlugin[],
): void {
// 验证依赖链,禁用无法满足依赖的插件
}
5.9 热重载支持
// src/utils/plugins/loadPluginHooks.ts:255-286
export function setupPluginHookHotReload(): void {
settingsChangeDetector.subscribe(source => {
if (source === 'policySettings') {
// 检测插件相关设置的变更
const newSnapshot = getPluginAffectingSettingsSnapshot()
if (newSnapshot !== lastPluginSettingsSnapshot) {
lastPluginSettingsSnapshot = newSnapshot
// 清除缓存并重新加载
clearPluginCache(...)
clearPluginHookCache()
void loadPluginHooks()
}
}
})
}
6. 总结与架构图
6.1 系统架构总览
┌─────────────────────────────────────────────────────────────────┐
│ Claude Code CLI │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────┐ │
│ │ Bridge │ │ Plugin │ │ MCP │ │
│ │ System │ │ System │ │ Client │ │
│ └─────────────┘ └─────────────┘ └──────────────────────┘ │
│ │ │ │ │
│ │ │ │ │
│ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │
│ │ Remote │ │ Market- │ │ Stdio │ │
│ │ Server │ │ place │ │ SSE │ │
│ │ Sessions │ │ Manager │ │ HTTP │ │
│ └───────────┘ └───────────┘ │ WS │ │
│ └───────────┘ │
│ │ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Tool System │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │
│ │ │ Bash │ │ File │ │ Git │ │ MCP │ │ │
│ │ │ Tool │ │ Tools │ │ Tools │ │ Tools │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Hook System │ │
│ │ PreToolUse │ PostToolUse │ SessionStart │ ... │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
6.2 关键设计模式
| 模式 | 应用场景 | 实现方式 |
|---|---|---|
| 传输抽象 | 支持多种 MCP 传输协议 | Transport 接口 + 具体实现 (StdioClientTransport, SSEClientTransport, etc.) |
| 设置优先 | 插件启用/禁用状态 | 写入 settings.json → 触发缓存重建 |
| 版本化缓存 | 插件多版本管理 | ~/.claude/plugins/cache/{market}/{plugin}/{version}/ |
| 热插拔 | 插件动态加载 | memoize 缓存 + 显式清除机制 |
| 事件驱动 | 钩子系统 | HookEvent 联合类型 + HookCallback 回调 |
| 错误联合 | 插件错误处理 | PluginError discriminated union |
| 工作树隔离 | 多会话隔离 | git worktree + 工作目录分离 |
6.3 核心文件索引
| 功能模块 | 关键文件 |
|---|---|
| MCP 客户端 | src/services/mcp/client.ts, src/services/mcp/types.ts |
| MCP 服务器 | src/entrypoints/mcp.ts |
| 插件清单 | src/utils/plugins/schemas.ts |
| 插件加载 | src/utils/plugins/pluginLoader.ts |
| 插件操作 | src/services/plugins/pluginOperations.ts |
| 插件钩子 | src/utils/plugins/loadPluginHooks.ts |
| 桥接主循环 | src/bridge/bridgeMain.ts |
| 工具系统 | src/Tool.ts, src/tools.ts, src/services/tools/toolExecution.ts |
| 类型定义 | src/types/plugin.ts, src/types/hooks.ts |
6.4 安全考虑
插件来源验证
- 官方市场名称保护 (
ALLOWED_OFFICIAL_MARKETPLACE_NAMES) - 同形异义攻击防护 (Unicode 字符检查)
- GitHub 组织验证
- 官方市场名称保护 (
MCP 认证
- OAuth 2.0 支持
- Bearer Token 刷新
- 401 错误自动重试
工具权限
- 权限分类器
- 文件系统访问控制
- 命令执行白名单
路径安全
- 版本化缓存防止路径遍历
- 符号链接解析与验证
- 插件目录隔离
文档生成时间: 2026-04-03 分析深度: P9 研究级