Context Mode 通过 HookAdapter 接口为 15 个 AI 编码平台抽象出统一的 Hook 拦截与路由逻辑。其核心是 PlatformCapabilities 能力标志和三种范式(JSON 标准输入输出、TypeScript 插件函数、纯 MCP)的适配。平台检测采用三级优先级链:clientInfo、平台特定环境变量、配置目录存在性。
Context Mode 适配器系统全解:如何为 15 个 AI 编码平台实现统一的 Hook 拦截与路由
Context Mode 作为一个 MCP 服务器插件,其核心价值在于将 AI 编码助手的上下文窗口占用降低约 98%。实现这一目标的关键在于其强大的 Hook 拦截与路由系统。该系统必须兼容 15 个技术栈各异的 AI 编码平台,从命令行工具(如 Claude Code、Gemini CLI)到 IDE 插件(如 Cursor、VS Code Copilot)。为了达成这一目标,Context Mode 设计了一套精巧的适配器系统。
适配器系统的目标与架构
适配器系统的目标是让 Context Mode 的 MCP 服务器核心逻辑保持 100% 可移植。核心的沙箱执行引擎、FTS5 知识库和会话管理不需要感知平台差异。只有负责与各个平台宿主环境交互的“Hook 层”需要平台特定的适配。
这个分层设计体现在源码结构中:src/adapters/types.ts 定义了所有适配器必须遵循的 HookAdapter 接口合约,而每个平台(如 src/adapters/claude-code/、src/adapters/gemini-cli/)则提供自己的实现。
统一的接口:HookAdapter 与 PlatformCapabilities
HookAdapter 接口是适配器系统的基石。它要求每个平台适配器必须声明自己的 name、paradigm(范式)以及最重要的 capabilities(能力)。
// src/adapters/types.ts (节选)
export interface HookAdapter {
readonly name: string;
readonly paradigm: HookParadigm;
readonly capabilities: PlatformCapabilities;
// ... 省略其他方法
}
export interface PlatformCapabilities {
preToolUse: boolean;
postToolUse: boolean;
preCompact: boolean;
sessionStart: boolean;
canModifyArgs: boolean;
canModifyOutput: boolean;
canInjectSessionContext: boolean;
}
PlatformCapabilities 像一个“能力清单”,清晰地定义了每个平台在 Hook 交互中能做什么。例如:
canModifyArgs: boolean:该平台是否允许通过PreToolUseHook 修改工具的输入参数。Claude Code、Gemini CLI、Cursor 支持此功能,而 Codex CLI 不支持。canModifyOutput: boolean:该平台是否允许通过PostToolUseHook 修改工具的输出。OpenCode 支持(尽管存在已知的 TUI Bug),而 Cursor 不支持。canInjectSessionContext: boolean:该平台是否支持在会话启动或压缩时注入上下文。这是实现跨压缩会话连续性的关键,绝大多数平台都支持。
这些能力标志直接决定了 formatPreToolUseResponse 等响应格式化方法的行为逻辑。
三种 Hook 范式:平台差异的抽象
不同平台的 Hook 实现机制截然不同。Context Mode 将其归纳为三种范式,用 HookParadigm 类型表示:
// src/adapters/types.ts
export type HookParadigm = "json-stdio" | "ts-plugin" | "mcp-only";
-
JSON 标准输入输出 (
json-stdio):这是最普遍的范式,适用于 Claude Code、Gemini CLI、VS Code Copilot、Cursor 等。平台宿主(如 Claude CLI)将 Hook 事件(如工具调用前)以 JSON 对象通过标准输入发送给 Context Mode 的适配器脚本,适配器处理后再将 JSON 响应通过标准输出发回。这从formatPreToolUseResponse的实现差异可见一斑:- Gemini CLI:返回
{ decision: "deny", reason: "..." }或{ hookSpecificOutput: { tool_input: ... } }。 - Cursor:返回
{ permission: "deny", user_message: "..." }或{ updated_input: ... }。 - Claude Code:返回
{ decision: "modify", updatedInput: ... }或{ decision: "context", additionalContext: ... }。
- Gemini CLI:返回
-
TypeScript 插件函数 (
ts-plugin):此范式专用于 OpenCode 平台。Hook 不是通过标准输入输出传递 JSON,而是通过在插件(TypeScript 文件)中暴露特定的函数(如tool.execute.before)来拦截。因此,OpenCode 的formatPreToolUseResponse方法需要在适配器层完成逻辑判断,并可能通过抛出Error来阻止工具执行,或返回一个包含修改后参数的对象。 -
纯 MCP (
mcp-only):Codex CLI 属于此范式。它没有原生的 Hook 机制来拦截工具调用。Context Mode 在此类平台上主要依赖 MCP 协议本身的能力(如工具定义)和在系统提示中注入指导信息来引导 LLM 行为,同时sessionStart等有限的事件仍可通过配置触发。
这种范式划分是设计决策的核心:它允许 Context Mode 在统一接口下,灵活适配从命令行到深度 IDE 集成的不同技术栈,而无需修改核心路由逻辑。
平台检测:detect.ts 的三级优先级链
在启动时,Context Mode 需要知道自己运行在哪个平台上,以便加载正确的适配器。src/adapters/detect.ts 中的 detectPlatform 函数实现了一个清晰的三级检测链,确保在各种混合环境(如从 VS Code 集成终端启动 Claude Code CLI)中都能准确识别。
-
最高优先级:MCP
clientInfo:当 Context Mode 作为 MCP 服务器被连接时,客户端(如 Cursor、VS Code)会在初始化握手时提供clientInfo。如果clientInfo.name能直接映射到已知平台(通过CLIENT_NAME_TO_PLATFORM查找),则以“高置信度”立即确定平台。这是最准确、零配置的方式。 -
高优先级:平台特定环境变量:如果
clientInfo不可用或不匹配,则检查一组精心定义的环境变量。每个平台都有专属的“标识”环境变量。例如:- Claude Code:检查
CLAUDE_CODE_ENTRYPOINT或CLAUDE_PLUGIN_ROOT。 - Gemini CLI:检查
GEMINI_CLI。 - Cursor:检查
CURSOR_TRACE_ID。
// src/adapters/detect.ts (简化逻辑) for (const [platform, vars] of PLATFORM_ENV_VARS) { if (vars.some(v => v.detect !== false && process.env[v.name])) { // 特别处理:VS Code 终端中的 Claude Code 会设置 VSCODE_PID, // 但如果检测到是 Claude Code 插件,则优先归类为 claude-code。 if (platform === "vscode-copilot" && claudeCodeHasContextModePlugin()) { return { platform: "claude-code", ... }; } return { platform, ... }; } }值得注意的是,
PlatformEnvEntry中的detect字段允许某些“工作区”环境变量(如PI_WORKSPACE_DIR)不参与平台身份检测,仅用于解析项目目录,防止误判。 - Claude Code:检查
-
中等优先级:配置目录存在性:如果环境变量检测失败,则回退到检查用户主目录下是否存在平台特定的配置目录。检测顺序也经过优化:CLI 代理工具优先于宿主 IDE。例如,会先检查
~/.kiro/、~/.omp/、~/.pi/等,再检查~/.cursor/、~/.vscode/,避免因为 Cursor 安装广泛而错误拦截了运行在其终端中的其他代理。 -
低置信度回退:如果以上所有方法都失败,则默认回退到
claude-code,因为它是使用最广泛的平台之一。
如何验证适配器配置:validateHooks 与 getHealthChecks
每个适配器都实现了诊断方法,供 ctx_doctor 命令使用,帮助用户验证配置是否正确。
validateHooks:检查平台设置文件(如 Claude Code 的settings.json、Gemini CLI 的~/.gemini/settings.json)中是否配置了必要的 Hook 条目,并确认其命令指向了 Context Mode 的脚本。getHealthChecks:这是一个更轻量的检查,直接验证 Hook 脚本文件是否存在且可执行。以 Claude Code 适配器为例,它会遍历HOOK_SCRIPTS映射表,直接使用existsSync检查pluginRoot下的脚本文件,避免了通过解析命令字符串可能带来的路径问题。// src/adapters/claude-code/index.ts (节选) getHealthChecks(pluginRoot: string): readonly HealthCheck[] { const hookChecks = Object.entries(HOOK_SCRIPTS).map(([hookType, scriptName]) => { const absolutePath = join(pluginRoot, "hooks", scriptName); return { name: `Hook script: ${hookType} (${scriptName})`, check: () => existsSync(absolutePath) ? { status: "OK" } : { status: "FAIL", detail: `not found at ${absolutePath}` }, }; }); // ... }
总结:统一合约下的灵活实现
Context Mode 的适配器系统展现了优秀的工程设计:通过定义严格的 HookAdapter 接口合约和 PlatformCapabilities 能力声明,为多样化的 AI 编码平台提供了一个统一的集成点。三级平台检测链确保了在复杂环境中的准确识别。三种 Hook 范式的抽象,则使得从 JSON 命令行交互到 TypeScript 插件拦截的各种技术细节都被妥善封装,对上层透明。
这套系统是 Context Mode 能够跨越 15 个平台、系统性解决 LLM 上下文窗口消耗问题的技术基石。它使得核心的上下文节省、会话连续性管理逻辑得以专注,而平台差异被有效隔离在适配器层。
欲了解如何为特定平台安装和配置 Context Mode,请参阅《Context Mode 安装与配置完全指南》。关于其核心工作原理,可阅读《Context Mode 全面解析》。
FAQ
Q: 三种 Hook 范式(json-stdio, ts-plugin, mcp-only)的主要优缺点是什么? A: json-stdio 跨平台兼容性好,是 CLI 工具的标准做法,但交互是同步阻塞的。ts-plugin(OpenCode)能深度集成,响应更灵活(可直接抛错),但绑定特定运行时。mcp-only(Codex CLI)无需平台原生 Hook 支持,但拦截能力最弱,主要依赖提示引导。
Q: 平台检测时,为什么有时需要检查多个环境变量?
A: 单个环境变量可能存在歧义。例如,VSCODE_PID 会被许多在 VS Code 中运行的进程继承。detect.ts 中为 Claude Code 定义了 CLAUDE_CODE_ENTRYPOINT 和 CLAUDE_PLUGIN_ROOT 这两个专有变量,只有当它们存在时,才能高置信度地判定为 Claude Code 环境,避免被通用的 VS Code 变量误导。