ClineToolSet: definición de herramientas y conversión de schema
Responsabilidades
ClineToolSet es el núcleo con el que Cline unifica el concepto de herramienta (tool) en tres niveles. El primero es «registro»: cada herramienta se registra como un ClineToolSpec por familia de modelo (ModelFamily), con la familia Generic como retroceso (register:19); el segundo es «selección»: getEnabledTools filtra las herramientas actualmente habilitadas a partir de la lista de ids declarada por el variant + los requisitos de contexto (contextRequirements) (getEnabledTools:87); el tercero es «conversión»: getNativeConverter elige un conversor según providerId y traduce ClineToolSpec a Anthropic Tool / OpenAI ChatCompletionTool / Google FunctionDeclaration (getNativeConverter:151).
Sus salidas siguen dos rutas: una «ruta de texto», donde el schema de la herramienta se ensambla como un segmento XML <tool_name>...<param>...</param></tool_name> que se incrusta en el system prompt, aplicable a modelos que no soportan native tool calling (PromptBuilder.tool:146); y otra «ruta nativa», donde getNativeTools devuelve el arreglo de schemas convertidos como parámetro tools de createMessage hacia ApiHandler (getNativeTools:170).
Motivación de diseño
- Doble salida de schema: un mismo
ClineToolSpecpuede convertirse tanto a texto XML como a schema de herramienta nativo, porque entre los modelos soportados por Cline algunos admiten native function calling (Anthropic / OpenAI / Gemini) y otros no (muchos modelos locales); los segundos solo pueden apoyarse en XML embebido en el system prompt para que el modelo emita en el formato esperado. - Cadena de fallback por variant:
getToolByNameWithFallbackbusca primero family exacta → luego GENERIC → luego recorre todas las variantes (fallback:48), garantizando que una herramienta registrada solo en next-gen pero no en generic también pueda usarse desde el variant generic. - Filtrado dinámico por contextRequirements: una herramienta puede declarar
contextRequirements: (ctx) => boolean; por ejemplo,browser_actionexigesupportsBrowserUse=true, y si el contexto no lo satisface se filtra (ctx filter:101), más flexible que una lista de variants codificada fija. - Herramientas dinámicas de subagente:
getDynamicSubagentToolSpecsgenera en runtime múltiples variantes de la herramientaUSE_SUBAGENTSa partir de la configuración de subagentes cargada porAgentConfigLoader(dynamic subagent:108), una por cada subagente con su propio nombre; así, el toolset no es estático. - Herramientas MCP entran al toolset de forma unificada:
mcpToolToClineToolSpectraduce las tools expuestas por un servidor MCP al formatoClineToolSpec, con iduse_mcp_tooly nameserverUid__mcp__toolName(mcp name:238); las herramientas MCP y las internas se gestionan con el mismo mecanismo. - Tope de longitud del name: si el name de una herramienta MCP supera 64 caracteres, se omite y no se registra (
length check:243), porque la API del provider rechaza nombres demasiado largos.
Archivos clave
ClineToolSet class:8— mantiene el registro estáticovariants: Map<ModelFamily, Set<ClineToolSet>>.register:19— método static; crea la instancia y la guarda en el Map por family, deduplicando por id.getToolByNameWithFallback:48— búsqueda de tres niveles: family exacta → GENERIC → escaneo completo.getToolsForVariantWithFallback:73— resolución por lotes a partir de una lista de ids, deduplicando.getEnabledTools:87— listavariant.tools+ filtro contextRequirements.getDynamicSubagentToolSpecs:108— si los subagentes están activados y no estamos en una ejecución de subagente, genera dinámicamente variantes de USE_SUBAGENTS desde AgentConfigLoader.getEnabledToolSpecs:136— fusiona herramientas estáticas con las dinámicas de subagente; cuando se generan las dinámicas, se elimina el USE_SUBAGENTS estático.getNativeConverter:151— selector por providerId: anthropic/bedrock/minimax → inputSchema, openai-compatible → functionDefinition, gemini → functionDeclarations.getNativeTools:170— solo devuelve el arreglo de schemas nativos cuandovariant.labels.use_native_tools === 1ycontext.enableNativeToolCalls.mcpToolToClineToolSpec:198— traduce el inputSchema del servidor MCP aClineToolSpec.parameters, conservando campos extra como enum / format.ClineDefaultTool enum:8— enumeración de todos los ids de herramientas internas: execute_command / read_file / write_to_file / replace_in_file / search_files, etc.ClineToolSpec:10— tipo de especificación de herramienta: id / name / description / parameters / contextRequirements.toolSpecFunctionDefinition:52— conversión a OpenAI ChatCompletionTool, constrict: falseyadditionalProperties: false.toolSpecInputSchema:145— conversión a Anthropic Tool; el campo se llamainput_schema, noparameters.toolSpecFunctionDeclarations:243— conversión a Google FunctionDeclaration; el type usa constantes en mayúsculas como STRING / NUMBER / BOOLEAN.registerClineToolSets:32— al iniciar, registra de una vez todas las variantes de herramientas en ClineToolSet.
Flujo de datos
Una herramienta recorre cuatro pasos desde el «registro» hasta que el ApiHandler la consume: registro → declaración en el variant → filtrado de habilitadas → conversión de schema. El registro se dispara al construir PromptRegistry:
// apps/vscode/src/core/prompts/system-prompt/tools/init.ts
export function registerClineToolSets(): void {
const allToolVariants = [
...access_mcp_resource_variants,
...act_mode_respond_variants,
// ...los arrays _variants de los 22 tools
...apply_patch_variants,
]
allToolVariants.forEach((v) => {
ClineToolSet.register(v)
})
}Cada archivo de herramienta exporta un arreglo _variants con ClineToolSpec escritos para distintas ModelFamily (allToolVariants:34). Tras el registro, el variant declara qué ids de herramientas va a usar:
// 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,
)El variant generic lista 18 ids de herramientas (generic tools:58); getEnabledTools toma esa lista, resuelve cada uno vía getToolByNameWithFallback y filtra por contextRequirements. Por último, si se va a usar native tool calling, el conversor traduce ClineToolSpec al schema del provider correspondiente:
// 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
}Observa que la función replacer sustituye variables de plantilla como / / (replacer:404), de modo que la descripción de la herramienta puede incluir estos placeholders y se rellenan en runtime según el context.
Límites y fallos
- Registro duplicado de herramientas: con misma family + mismo id no se añade dos veces al Set; el código verifica explícitamente con
some(t => t.config.id === config.id)(dedup:25). - Name MCP demasiado largo: si
mcpToolName.length > 64, se omite directamente (length check:243); no aparece en el toolset y el modelo no puede invocar esa herramienta MCP. - Variant sin tools declarados: si
variant.toolsestá vacío,getEnabledToolsdevuelve un arreglo vacío (empty tools:89), lo que significa que el variant hace conversación en texto puro sin ninguna herramienta. - Anidamiento de subagentes: cuando
context.isSubagentRunes true,getDynamicSubagentToolSpecsdevuelve vacío directamente (subagent guard:109), evitando que un subagente genere herramientas de subagente y previniendo recursión infinita. - Etiqueta use_native_tools no fijada: por defecto, si
variant.labels.use_native_tools !== 1,getNativeToolsdevuelve undefined (label gate:175), incluso sicontext.enableNativeToolCallsestá activo; el variant debe habilitarlo explícitamente. - Excepción en converter:
toolSpecInputSchemay similares lanzan"Tool X does not meet context requirements"cuandotool.contextRequirementsno se satisface (contextRequirements throw:55); en teoríagetEnabledToolsya lo filtró, esto es un cierre de seguridad. - MCP server.disabled: al obtener herramientas MCP, primero se filtra con
filter(s => s.disabled !== true)(disabled filter:183); las herramientas de servidores deshabilitados no entran al toolset en absoluto.
Resumen
ClineToolSet es a la vez «registro central + selector + traductor de schema» para las herramientas. Su decisión clave es que un mismo ClineToolSpec pueda salir tanto por la ruta de «segmento XML de texto» como por la de «schema de herramienta nativo», permitiendo a Cline servir tanto a modelos con native function calling como a modelos locales que solo emiten texto. Las herramientas MCP se traducen al mismo formato que las herramientas internas y entran en el mismo registro; las herramientas de subagente se generan dinámicamente según la configuración en runtime. Todo esto convierte al toolset en una construcción dinámica, no en una tabla estática.
- Cómo los prompts de herramientas se ensamblan como XML:
/prompts/prompt-builder - Diferencias de herramientas disponibles entre plan y act:
/prompts/mode - Cómo el handler consume el parámetro tools:
/providers/anthropic