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/)则提供自己的实现。

统一的接口:HookAdapterPlatformCapabilities

HookAdapter 接口是适配器系统的基石。它要求每个平台适配器必须声明自己的 nameparadigm(范式)以及最重要的 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:该平台是否允许通过 PreToolUse Hook 修改工具的输入参数。Claude Code、Gemini CLI、Cursor 支持此功能,而 Codex CLI 不支持。
  • canModifyOutput: boolean:该平台是否允许通过 PostToolUse Hook 修改工具的输出。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";
  1. 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: ... }
  2. TypeScript 插件函数 (ts-plugin):此范式专用于 OpenCode 平台。Hook 不是通过标准输入输出传递 JSON,而是通过在插件(TypeScript 文件)中暴露特定的函数(如 tool.execute.before)来拦截。因此,OpenCode 的 formatPreToolUseResponse 方法需要在适配器层完成逻辑判断,并可能通过抛出 Error 来阻止工具执行,或返回一个包含修改后参数的对象。

  3. 纯 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)中都能准确识别。

  1. 最高优先级:MCP clientInfo:当 Context Mode 作为 MCP 服务器被连接时,客户端(如 Cursor、VS Code)会在初始化握手时提供 clientInfo。如果 clientInfo.name 能直接映射到已知平台(通过 CLIENT_NAME_TO_PLATFORM 查找),则以“高置信度”立即确定平台。这是最准确、零配置的方式。

  2. 高优先级:平台特定环境变量:如果 clientInfo 不可用或不匹配,则检查一组精心定义的环境变量。每个平台都有专属的“标识”环境变量。例如:

    • Claude Code:检查 CLAUDE_CODE_ENTRYPOINTCLAUDE_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)不参与平台身份检测,仅用于解析项目目录,防止误判。

  3. 中等优先级:配置目录存在性:如果环境变量检测失败,则回退到检查用户主目录下是否存在平台特定的配置目录。检测顺序也经过优化:CLI 代理工具优先于宿主 IDE。例如,会先检查 ~/.kiro/~/.omp/~/.pi/ 等,再检查 ~/.cursor/~/.vscode/,避免因为 Cursor 安装广泛而错误拦截了运行在其终端中的其他代理。

  4. 低置信度回退:如果以上所有方法都失败,则默认回退到 claude-code,因为它是使用最广泛的平台之一。

如何验证适配器配置:validateHooksgetHealthChecks

每个适配器都实现了诊断方法,供 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_ENTRYPOINTCLAUDE_PLUGIN_ROOT 这两个专有变量,只有当它们存在时,才能高置信度地判定为 Claude Code 环境,避免被通用的 VS Code 变量误导。