Skip to content

ClineToolSet: tool definitions and schema conversion

源码版本v4.0.10

Responsibilities

ClineToolSet is the hub where Cline unifies the concept of "tool" across three layers. The first is "registration": every tool is registered as a ClineToolSpec per model family (ModelFamily), with the Generic family as a fallback (register:19). The second is "selection": getEnabledTools filters the currently enabled tools by the variant's declared tool id list plus context requirements (getEnabledTools:87). The third is "conversion": getNativeConverter picks a converter by providerId and translates the ClineToolSpec into an Anthropic Tool / OpenAI ChatCompletionTool / Google FunctionDeclaration (getNativeConverter:151).

Its output has two paths. The "text path" stitches the tool schema into an XML-style section <tool_name>...<param>...</param></tool_name> and embeds it in the system prompt, for models that do not support native tool calling (PromptBuilder.tool:146). The "native path" passes the converted schema array as the tools argument of createMessage to the ApiHandler (getNativeTools:170).

Design motivation

  • Dual schema outlets: the same ClineToolSpec can be converted into either XML text or a native tool schema, because some models Cline supports have native function calling (Anthropic / OpenAI / Gemini) while others do not (many local models); the latter can only be made to emit structured output by injecting XML into the system prompt.
  • Variant fallback chain: getToolByNameWithFallback first looks up the exact family, then GENERIC, then iterates over all variants (fallback:48), so a tool registered only in next-gen can still be used by the generic variant.
  • Dynamic contextRequirements filtering: a tool can declare contextRequirements: (ctx) => boolean; for example browser_action requires supportsBrowserUse=true, and if the context does not satisfy it the tool is filtered out (ctx filter:101). This is more flexible than hardcoding variant lists.
  • Dynamic subagent tools: getDynamicSubagentToolSpecs generates multiple variants of the USE_SUBAGENTS tool at runtime based on subagent configs loaded by AgentConfigLoader (dynamic subagent:108), one name per subagent. The toolset is therefore not static.
  • MCP tools fold into the toolset: mcpToolToClineToolSpec converts tools exposed by an MCP server into the ClineToolSpec format. The id is always use_mcp_tool and the name is serverUid__mcp__toolName (mcp name:238). MCP tools are managed through the same mechanism as built-in tools.
  • Name length fallback: MCP tool names longer than 64 characters are skipped and not registered (length check:243), because provider APIs reject tool names that are too long.

Key files

  • ClineToolSet class:8 — Holds the static registry variants: Map<ModelFamily, Set<ClineToolSet>>.
  • register:19 — Static method; creates an instance and stores it in the Map by family, deduplicating by id.
  • getToolByNameWithFallback:48 — Three-level lookup: exact family → GENERIC → full table scan.
  • getToolsForVariantWithFallback:73 — Batch resolves by id list, deduplicating.
  • getEnabledTools:87 — variant.tools list + contextRequirements filter.
  • getDynamicSubagentToolSpecs:108 — When subagents are enabled and this is not itself a subagent run, generates USE_SUBAGENTS variants from AgentConfigLoader.
  • getEnabledToolSpecs:136 — Merges static tools with dynamic subagent tools; removes the static USE_SUBAGENTS when subagents are generated dynamically.
  • getNativeConverter:151 — Picks a converter by providerId: anthropic/bedrock/minimax → inputSchema, OpenAI-compatible → functionDefinition, gemini → functionDeclarations.
  • getNativeTools:170 — Returns the native schema array only when variant.labels.use_native_tools === 1 and context.enableNativeToolCalls is true.
  • mcpToolToClineToolSpec:198 — Translates an MCP server's inputSchema into ClineToolSpec.parameters, preserving extra fields like enum / format.
  • ClineDefaultTool enum:8 — Enumeration of all built-in tool ids: execute_command / read_file / write_to_file / replace_in_file / search_files etc.
  • ClineToolSpec:10 — Tool spec type: id / name / description / parameters / contextRequirements.
  • toolSpecFunctionDefinition:52 — Converts to OpenAI ChatCompletionTool, with strict: false and additionalProperties: false.
  • toolSpecInputSchema:145 — Converts to Anthropic Tool; the field name is input_schema rather than parameters.
  • toolSpecFunctionDeclarations:243 — Converts to Google FunctionDeclaration; types use uppercase constants like STRING / NUMBER / BOOLEAN.
  • registerClineToolSets:32 — Registers all tool variants into ClineToolSet once at startup.

Data flow

A tool goes through four steps from "registration" to "used by ApiHandler": register → variant declaration → enable filter → schema conversion. Registration happens during PromptRegistry construction:

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,
		// ...all 22 tools' variants arrays
		...apply_patch_variants,
	]

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

Each tool file exports a _variants array containing ClineToolSpecs written for different ModelFamilies (allToolVariants:34). After registration, each variant declares which tool ids it wants:

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,
)

The generic variant lists 18 tool ids (generic tools:58). getEnabledTools takes this list, resolves each id via getToolByNameWithFallback, and applies the contextRequirements filter. Finally, if native tool calling is used, the converter turns the ClineToolSpec into the corresponding provider's 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
}

Note that the replacer function substitutes template variables like / / (replacer:404); a tool's description can carry these placeholders and they are filled from context at runtime.

Boundaries and failure modes

  • Duplicate tool registration: the same family + id is not added to the Set twice; the code explicitly checks some(t => t.config.id === config.id) (dedup:25).
  • MCP tool name too long: mcpToolName.length > 64 is skipped outright (length check:243); the tool never appears in the toolset, so the model cannot call it.
  • Variant declares no tools: when variant.tools is empty, getEnabledTools returns an empty array (empty tools:89), meaning that variant runs pure text conversation with no tools.
  • Subagent nesting: getDynamicSubagentToolSpecs returns empty when context.isSubagentRun is true (subagent guard:109), preventing a subagent from spawning further subagent tools and avoiding infinite recursion.
  • use_native_tools label not set: by default, when variant.labels.use_native_tools !== 1, getNativeTools returns undefined (label gate:175), even if context.enableNativeToolCalls is true; the variant must opt in explicitly.
  • Converter throws: converters like toolSpecInputSchema throw "Tool X does not meet context requirements" when tool.contextRequirements is not satisfied (contextRequirements throw:55). In theory getEnabledTools has already filtered these out, so this is a safety net.
  • MCP server.disabled: when fetching MCP tools, the code first runs filter(s => s.disabled !== true) (disabled filter:183); tools from disabled servers never enter the toolset.

Summary

ClineToolSet is a three-in-one "central registry + selector + schema translator" for tools. The key design is that a single ClineToolSpec can serve both outlets — the "text XML section" and the "native tool schema" — so Cline can serve models with native function calling as well as local models that can only emit text. MCP tools are converted into the same format as built-in tools and managed through the same pipeline; subagent tools are generated dynamically from runtime config. Together these make the toolset a dynamically constructed structure rather than a static table.

  • How tool prompts become XML sections: /prompts/prompt-builder
  • Tool-availability differences between plan / act modes: /prompts/mode
  • How the handler consumes the tools argument: /providers/anthropic

See official docs: Cline 文档 · README