Skip to content

ClineToolSet:工具定义与 schema 转换

源码版本v4.0.10

职责

ClineToolSet 是 Cline 把「工具 (tool)」这个概念在三个层面统一管理的中枢。第一层是「注册」:每个工具按模型家族 (ModelFamily) 注册一份 ClineToolSpec,Generic 家族兜底(register:19);第二层是「选择」:getEnabledTools 按 variant 声明的 tool id 列表 + 上下文要求(contextRequirements)过滤出当前该启用的工具(getEnabledTools:87);第三层是「转换」:getNativeConverter 按 providerId 选不同的转换器,把 ClineToolSpec 翻译成 Anthropic Tool / OpenAI ChatCompletionTool / Google FunctionDeclaration(getNativeConverter:151).

它的输出有两条路:一条是「文本路径」,工具 schema 被拼成 <tool_name>...<param>...</param></tool_name> 风格的 XML 段塞进系统提示,适用于不支持 native tool calling 的模型(PromptBuilder.tool:146);另一条是「原生路径」,getNativeTools 把转换后的 schema 数组作为 createMessagetools 参数传给 ApiHandler(getNativeTools:170).

设计动机

  • 双 schema 出口:同一份 ClineToolSpec 既能转成 XML 文本又能转成原生 tool schema,因为 Cline 支持的模型里有的支持 native function calling(Anthropic / OpenAI / Gemini),有的不支持(很多本地模型),后者只能靠在系统提示里塞 XML 让模型按格式输出。
  • variant fallback 链:getToolByNameWithFallback 先找精确 family → 再找 GENERIC → 再遍历所有 variant(fallback:48),保证某个工具只在 next-gen 注册了但 generic 没注册时也能被 generic variant 用上。
  • contextRequirements 动态过滤:工具可以声明 contextRequirements: (ctx) => boolean,比如 browser_action 要求 supportsBrowserUse=true,上下文不满足就过滤掉(ctx filter:101),比硬编码 variant 列表更灵活。
  • 动态 subagent 工具:getDynamicSubagentToolSpecs 在运行时根据 AgentConfigLoader 加载的 subagent 配置生成 USE_SUBAGENTS 工具的多个变体(dynamic subagent:108),每个 subagent 一个名字,所以工具集不是静态的。
  • MCP 工具统一进 toolset:mcpToolToClineToolSpec 把 MCP server 暴露的 tools 转成 ClineToolSpec 格式,id 都是 use_mcp_tool,name 是 serverUid__mcp__toolName(mcp name:238),MCP 工具和内置工具用同一套机制管理。
  • name 长度兜底:MCP 工具 name 超过 64 字符直接 skip 不注册(length check:243),因为 provider API 会拒绝过长的 tool name。

关键文件

数据流

工具从「注册」到「最终被 ApiHandler 使用」要经过四步:注册 → variant 声明 → 启用过滤 → schema 转换。注册阶段在 PromptRegistry 构造时触发:

typescript
// apps/vscode/src/core/prompts/system-prompt/tools/init.ts
export function registerClineToolSets(): void {
	const allToolVariants = [
		...access_mcp_resource_variants,
		...act_mode_respond_variants,
		// ...所有 22 个工具的 variants 数组
		...apply_patch_variants,
	]

	allToolVariants.forEach((v) => {
		ClineToolSet.register(v)
	})
}

每个工具文件导出 _variants 数组,里面是针对不同 ModelFamily 写的 ClineToolSpec(allToolVariants:34). 注册后,variant 自己声明要用哪些工具 id:

typescript
// apps/vscode/src/core/prompts/system-prompt/variants/generic/config.ts
.tools(
	ClineDefaultTool.BASH,
	ClineDefaultTool.FILE_READ,
	ClineDefaultTool.FILE_NEW,
	ClineDefaultTool.FILE_EDIT,
	ClineDefaultTool.SEARCH,
	ClineDefaultTool.LIST_FILES,
	ClineDefaultTool.LIST_CODE_DEF,
	ClineDefaultTool.BROWSER,
	ClineDefaultTool.MCP_USE,
	ClineDefaultTool.MCP_ACCESS,
	ClineDefaultTool.ASK,
	ClineDefaultTool.ATTEMPT,
	ClineDefaultTool.PLAN_MODE,
	ClineDefaultTool.MCP_DOCS,
	ClineDefaultTool.TODO,
	ClineDefaultTool.GENERATE_EXPLANATION,
	ClineDefaultTool.USE_SKILL,
	ClineDefaultTool.USE_SUBAGENTS,
)

generic variant 列了 18 个工具 id(generic tools:58),getEnabledTools 拿这个列表去 getToolByNameWithFallback 逐个解析,再过 contextRequirements 过滤。最后如果要走原生 tool calling,转换器把 ClineToolSpec 转成对应 provider 的 schema:

typescript
// apps/vscode/src/core/prompts/system-prompt/spec.ts
export function toolSpecInputSchema(tool: ClineToolSpec, context: SystemPromptContext): AnthropicTool {
	// ...
	const toolInputSchema: AnthropicTool = {
		name: tool.name,
		description: replacer(tool.description, context),
		input_schema: {
			type: "object",
			properties,
			required,
		},
	}
	return toolInputSchema
}

注意 replacer 函数会替换 / / 等模板变量(replacer:404),所以工具的 description 里可以写这些占位符,运行时按 context 填充。

边界与失败

  • 工具重复注册:同一 family + 同 id 不会重复加进 Set,代码里显式 some(t => t.config.id === config.id) 检查(dedup:25).
  • MCP 工具 name 过长:mcpToolName.length > 64 直接 skip(length check:243),不在工具集里出现,模型就调用不到这个 MCP 工具。
  • variant 没声明 tools:getEnabledToolsvariant.tools 为空时返回空数组(empty tools:89),意味着该 variant 走纯文本对话,不带任何工具。
  • subagent 嵌套:getDynamicSubagentToolSpecscontext.isSubagentRun 为 true 时直接返回空(subagent guard:109),避免 subagent 再生成 subagent 工具,防止无限递归。
  • use_native_tools label 未设:getNativeTools 默认 variant.labels.use_native_tools !== 1 时返回 undefined(label gate:175),即使 context.enableNativeToolCalls 也不生效,variant 必须显式开启。
  • converter 抛错:toolSpecInputSchema 等转换器在 tool.contextRequirements 不满足时抛 "Tool X does not meet context requirements"(contextRequirements throw:55),理论上 getEnabledTools 已经过滤过,这里是兜底。
  • MCP server.disabled:MCP 工具获取时先 filter(s => s.disabled !== true)(disabled filter:183),禁用的 server 的工具完全不进 toolset。

小结

ClineToolSet 是工具的「中央注册表 + 选择器 + schema 翻译器」三合一,关键设计是同一份 ClineToolSpec 能同时走「文本 XML 段」和「原生 tool schema」两条出口,让 Cline 既能服务支持 native function calling 的模型,也能服务只能输出文本的本地模型。MCP 工具被翻译成和内置工具一样的格式塞进同一套管理,subagent 工具按运行时配置动态生成,这些都让 toolset 不是静态表而是动态构造的。

  • 工具提示怎么拼成 XML 段:/prompts/prompt-builder
  • plan / act 模式各自能用的工具差异:/prompts/mode
  • handler 怎么消费 tools 参数:/providers/anthropic

对照官方资料:Cline 文档 · README