Skip to content

ClineToolSet : définition des outils et conversion de schéma

源码版本v4.0.10

Responsabilités

ClineToolSet est le hub par lequel Cline unifie la notion d'« outil » (tool) à trois niveaux. Premier niveau, « enregistrement » : chaque outil est enregistré, par famille de modèles (ModelFamily), sous forme d'une ClineToolSpec, la famille Generic servant de filet de secours (register:19). Deuxième niveau, « sélection » : getEnabledTools filtre, à partir de la liste d'ids d'outils déclarée par la variante et des exigences de contexte (contextRequirements), les outils à activer pour le contexte courant (getEnabledTools:87). Troisième niveau, « conversion » : getNativeConverter choisit un convertisseur selon providerId et traduit la ClineToolSpec en Anthropic Tool, OpenAI ChatCompletionTool ou Google FunctionDeclaration (getNativeConverter:151).

Sa sortie suit deux chemins : la « voie texte », où le schéma de l'outil est assemblé en section XML <tool_name>...<param>...</param></tool_name> injectée dans le system prompt, destinée aux modèles qui ne supportent pas l'appel d'outil natif (PromptBuilder.tool:146) ; et la « voie native », où getNativeTools fournit le tableau de schémas convertis comme paramètre tools de createMessage à l'ApiHandler (getNativeTools:170).

Motivation de conception

  • Double sortie de schéma : une même ClineToolSpec peut se convertir à la fois en XML texte et en schéma d'outil natif, car parmi les modèles supportés par Cline certains acceptent le function calling natif (Anthropic / OpenAI / Gemini), d'autres non (notamment des modèles locaux), et ces derniers ne peuvent qu'être guidés par injection d'un XML dans le system prompt pour forcer un format de sortie.
  • chaîne de repli par variante : getToolByNameWithFallback cherche d'abord la famille exacte, puis GENERIC, puis parcourt toutes les variantes (fallback:48), pour qu'un outil enregistré uniquement en next-gen puisse tout de même être utilisé par la variante generic.
  • filtrage dynamique par contextRequirements : un outil peut déclarer contextRequirements: (ctx) => boolean ; par exemple browser_action exige supportsBrowserUse=true, faute de quoi il est filtré (ctx filter:101), plus souple que de coder en dur une liste de variantes.
  • outils subagent dynamiques : getDynamicSubagentToolSpecs génère, au runtime, plusieurs variantes de l'outil USE_SUBAGENTS à partir des configurations de subagent chargées par AgentConfigLoader (dynamic subagent:108), une par nom de subagent — la toolset n'est donc pas statique.
  • outils MCP intégrés au toolset : mcpToolToClineToolSpec convertit les tools exposés par un serveur MCP en ClineToolSpec, tous avec l'id use_mcp_tool et un name au format serverUid__mcp__toolName (mcp name:238). Outils MCP et outils internes sont gérés par le même mécanisme.
  • garde-fou sur la longueur du name : si le name d'un outil MCP dépasse 64 caractères, il est purement ignoré et non enregistré (length check:243), car les API des providers refusent les noms d'outil trop longs.

Fichiers clés

  • ClineToolSet class:8 — détient variants: Map<ModelFamily, Set<ClineToolSet>>, registre statique.
  • register:19 — méthode static ; crée une instance, la stocke par famille, déduplique par id.
  • getToolByNameWithFallback:48 — recherche à trois niveaux : famille exacte → GENERIC → parcours complet.
  • getToolsForVariantWithFallback:73 — résolution en lot par liste d'ids, avec déduplication.
  • getEnabledTools:87 — filtrage par liste variant.tools + contextRequirements.
  • getDynamicSubagentToolSpecs:108 — si subagents activés et qu'on n'est pas dans un run subagent, génère dynamiquement les variantes USE_SUBAGENTS depuis AgentConfigLoader.
  • getEnabledToolSpecs:136 — fusionne outils statiques et outils subagent dynamiques ; retire le USE_SUBAGENTS statique quand le dynamique est généré.
  • getNativeConverter:151 — sélection du convertisseur par providerId : anthropic/bedrock/minimax → inputSchema, compatible openai → functionDefinition, gemini → functionDeclarations.
  • getNativeTools:170 — ne renvoie le tableau de schémas natifs que si variant.labels.use_native_tools === 1 et que context.enableNativeToolCalls est vrai.
  • mcpToolToClineToolSpec:198 — traduit l'inputSchema d'un serveur MCP en ClineToolSpec.parameters, en préservant les champs supplémentaires comme enum / format.
  • ClineDefaultTool enum:8 — enumération de tous les ids d'outils internes : execute_command / read_file / write_to_file / replace_in_file / search_files, etc.
  • ClineToolSpec:10 — type de spec d'outil : id / name / description / parameters / contextRequirements.
  • toolSpecFunctionDefinition:52 — conversion en OpenAI ChatCompletionTool, avec strict: false et additionalProperties: false.
  • toolSpecInputSchema:145 — conversion en Anthropic Tool, le champ s'appelle input_schema et non parameters.
  • toolSpecFunctionDeclarations:243 — conversion en Google FunctionDeclaration, le type utilise des constantes majuscules STRING / NUMBER / BOOLEAN.
  • registerClineToolSets:32 — au démarrage, enregistre toutes les variantes d'outils en une fois dans ClineToolSet.

Flux de données

De l'enregistrement à l'utilisation finale par l'ApiHandler, l'outil traverse quatre étapes : enregistrement → déclaration par variante → filtrage d'activation → conversion de schéma. La phase d'enregistrement se déclenche à la construction de 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)
	})
}

Chaque fichier d'outil exporte un tableau _variants contenant des ClineToolSpec écrits pour différentes ModelFamily (allToolVariants:34). Une fois enregistrés, chaque variante déclare elle-même les ids d'outils qu'elle veut utiliser :

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

La variante generic liste 18 ids d'outils (generic tools:58). getEnabledTools prend cette liste, la résout via getToolByNameWithFallback pour chaque entrée, puis applique le filtre contextRequirements. Si l'on doit utiliser l'appel d'outil natif, un convertisseur traduit la ClineToolSpec dans le schéma attendu par le provider :

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
}

À noter : la fonction replacer substitue les variables de template / / (replacer:404), ce qui permet d'écrire ces placeholders dans la description des outils, remplis au runtime à partir du context.

Limites et échecs

  • enregistrement en doublon d'outil : une même combinaison famille + id n'est pas ajoutée deux fois dans le Set, avec une vérification explicite some(t => t.config.id === config.id) (dedup:25).
  • name d'outil MCP trop long : si mcpToolName.length > 64, l'outil est ignoré (length check:243), absent de la toolset, et donc inaccessible au modèle.
  • variante sans outils déclarés : getEnabledTools renvoie un tableau vide quand variant.tools est vide (empty tools:89), signifiant que la variante converse en texte pur, sans aucun outil.
  • imbrication de subagents : getDynamicSubagentToolSpecs renvoie directement un tableau vide quand context.isSubagentRun est true (subagent guard:109), empêchant un subagent de générer à son tour des outils subagent et évitant toute récursion infinie.
  • label use_native_tools non défini : par défaut, si variant.labels.use_native_tools !== 1, getNativeTools renvoie undefined (label gate:175), même si context.enableNativeToolCalls est vrai — il faut que la variante l'active explicitement.
  • convertisseur qui lève : les convertisseurs comme toolSpecInputSchema lèvent "Tool X does not meet context requirements" si tool.contextRequirements n'est pas satisfait (contextRequirements throw:55). En théorie getEnabledTools a déjà filtré ; c'est un filet de sécurité.
  • MCP server.disabled : à la récupération des outils MCP, on filtre d'abord par filter(s => s.disabled !== true) (disabled filter:183). Les outils d'un serveur désactivé n'entrent jamais dans la toolset.

Résumé

ClineToolSet est à la fois « registre central + sélecteur + traducteur de schéma » pour les outils. Le point clé du design est qu'une même ClineToolSpec peut emprunter deux sorties à la fois : « section XML texte » et « schéma d'outil natif », ce qui permet à Cline de servir aussi bien les modèles qui supportent le function calling natif que ceux qui ne savent produire que du texte. Les outils MCP sont convertis au même format que les outils internes et gérés par le même mécanisme ; les outils subagent sont générés dynamiquement selon la configuration runtime. La toolset n'est donc pas une table statique mais une construction dynamique.

  • Comment les prompts d'outils deviennent une section XML : /prompts/prompt-builder
  • Différences d'outils entre modes plan / act : /prompts/mode
  • Comment le handler consomme le paramètre tools : /providers/anthropic

Voir la documentation officielle : documentation Cline · README