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