La arquitectura de herramientas de Claude Code

@spandan_madan
INGLÉShace 4 semanas · 17 jun 2026
552K
416
115
86
479

TL;DR

Este análisis técnico explora la arquitectura de herramientas de Claude Code, detallando su pipeline de despacho de siete fases, su programador de concurrencia y su sistema de permisos de cierre por fallo (fail-closed) que permite acciones complejas de agentes de IA.

Un análisis profundo de cómo Claude Code descubre, despacha y ejecuta herramientas

Introducción

En un artículo anterior, realizamos ingeniería inversa del sistema de memoria del Anthropic Harness a partir de su código fuente filtrado. La memoria resultó ser sorprendentemente simple: archivos markdown, metadatos frontmatter e ingeniería de prompts. El sistema de herramientas es todo lo contrario. Es el subsistema más elaborado de toda la base de código: más de 43 herramientas, un pipeline de ejecución en streaming, un sistema de permisos en capas, un framework de hooks y un planificador de concurrencia, todo interconectado para convertir un modelo de lenguaje sin estado en algo capaz de leer archivos, ejecutar comandos de shell, buscar en la web y generar subagentes.

Este artículo recorre el ciclo de vida de una herramienta: desde cómo se define, hasta cómo se despachan las llamadas a herramientas del modelo y cómo los resultados regresan a la conversación. En términos generales, el sistema se compone de cuatro capas: una interfaz de herramienta que implementa cada herramienta, un registro que arma el conjunto de herramientas, un pipeline de despacho que valida, verifica permisos y ejecuta cada llamada, y un planificador de concurrencia que decide qué se ejecuta en paralelo.

La Arquitectura de un Vistazo

Spandan Madan - inline image

La Interfaz de la Herramienta

Cada herramienta en Claude Code implementa la misma interfaz, definida en Tool.ts. El tipo es genérico sobre tres parámetros: Input (un esquema de Zod), Output (el tipo de resultado) y P (datos de progreso). En la práctica, una herramienta es un objeto con aproximadamente 30 métodos, pero solo unos pocos importan para entender el sistema.

La forma central:

text
1type Tool<Input, Output, P> = {
2 name: string
3 inputSchema: ZodType // Esquema Zod para validación de entrada
4 call(input, context, canUseTool,
5 parentMessage, onProgress): Promise<ToolResult>
6
7 // Declaraciones de comportamiento
8 isConcurrencySafe(input): boolean // ¿Puede ejecutarse en paralelo?
9 isReadOnly(input): boolean // ¿Operación de solo lectura?
10 isDestructive(input): boolean // ¿Acción destructiva?
11
12 // Permisos y validación
13 checkPermissions(input, context): Promise<PermissionResult>
14 validateInput(input, context): Promise<ValidationResult>
15
16 // Integración con API
17 description(input, options): Promise<string>
18 prompt(options): Promise<string> // Texto del prompt del sistema para esta herramienta
19 mapToolResultToToolResultBlockParam(result, toolUseId): ToolResultBlockParam
20
21 // Renderizado de UI (React)
22 renderToolUseMessage(input, options): ReactNode
23 renderToolResultMessage(content, ...): ReactNode
24}

Ninguna herramienta implementa esto desde cero. Una función de fábrica llamada buildTool() completa con valores predeterminados seguros:

Spandan Madan - inline image

Los valores predeterminados son deliberadamente conservadores. Un autor de herramienta que olvide declarar seguridad de concurrencia obtendrá ejecución en serie. Un autor que olvide implementar verificaciones de permisos obtendrá el flujo de permisos predeterminado. El sistema falla de forma segura.

El tipo ToolResult es digno de mención:

text
1type ToolResult<T> = {
2 data: T // La salida real
3 newMessages?: Message[] // Mensajes de seguimiento opcionales
4 contextModifier?: (ctx) => ToolUseContext // Modifica el contexto para la siguiente herramienta
5 mcpMeta?: { ... } // Metadatos del protocolo MCP
6}

El contextModifier es importante: permite que una herramienta cambie el contexto de ejecución para las herramientas siguientes en el mismo turno. Así es como herramientas como EnterWorktree cambian el directorio de trabajo para todo lo que sigue. Críticamente, los modificadores de contexto solo están permitidos para herramientas que no son seguras para concurrencia. Si una herramienta se ejecuta en paralelo, no puede modificar el estado compartido.

El Registro de Herramientas

Todas las herramientas se registran en una sola función: getAllBaseTools() en tools.ts. Devuelve un arreglo plano. Algunas herramientas están siempre presentes; otras están controladas por banderas de funcionalidad, variables de entorno o verificaciones de plataforma.

Siempre disponibles (16 herramientas)

Spandan Madan - inline image

Herramientas con Banderas de Funcionalidad (~27 herramientas)

Las herramientas restantes se incluyen condicionalmente. Algunas están controladas por variables de entorno (USER_TYPE=ant para herramientas internas de Anthropic como config y tungsten). Algunas están controladas por banderas de funcionalidad a través de Statsig (web_browser, sleep, monitor). Algunas son específicas de plataforma (powershell en Windows). Otras están controladas por condiciones compuestas: la herramienta repl requiere tanto USER_TYPE=ant como una bandera de funcionalidad REPL.

Lista completa de herramientas con banderas de funcionalidad

Solo Ant: config, tungsten, suggest_background_pr, repl (también necesita bandera REPL)

Banderas de funcionalidad: web_browser, web_search, sleep, monitor, overflow_test, ctx_inspect, terminal_capture, list_peers, workflow, snip

Disparadores de agente: cron_create, cron_delete, cron_list, remote_trigger

Kairos (agente proactivo): sleep, send_user_file, push_notification, subscribe_pr

Enjambres multiagente: team_create, team_delete, send_message

Todo v2: task_create, task_get, task_update, task_list

Entorno: lsp (ENABLE_LSP_TOOL), enter_worktree / exit_worktree (modo worktree), powershell (Windows)

Descubrimiento de herramientas: tool_search (cuando el conjunto de herramientas es grande)

Solo prueba: testing_permission (NODE_ENV=test)

Herramientas MCP

Más allá de las herramientas integradas, Claude Code admite servidores Model Context Protocol (MCP) — procesos externos que exponen sus propias herramientas a través de un protocolo estandarizado. Las herramientas MCP se registran dinámicamente en tiempo de ejecución desde los servidores conectados y se envuelven en la misma interfaz Tool. Desde la perspectiva del pipeline de despacho, una herramienta MCP es indistinguible de una herramienta integrada.

Cada herramienta MCP lleva metadatos sobre su servidor de origen (mcpInfo: { serverName, toolName }), que se utilizan para reglas de permisos, manejo de errores y autenticación. Cuando una herramienta MCP falla con un error de autenticación, el sistema actualiza automáticamente el estado del servidor a needs-auth y muestra el problema al usuario.

Ensamblaje del Conjunto de Herramientas

Tres funciones ensamblan el conjunto final de herramientas:

  1. getAllBaseTools() — devuelve la lista sin procesar de más de 43 herramientas integradas con las banderas de funcionalidad aplicadas
  2. getTools(permissionContext) — filtra por reglas de denegación y isEnabled()
  3. assembleToolPool(permissionContext, mcpTools) — fusiona herramientas integradas y MCP

La estrategia de fusión en assembleToolPool() es deliberada:

Las herramientas integradas van primero, por lo que en caso de colisión de nombres, gana la integrada. La ordenación alfabética dentro de cada partición mantiene el orden estable entre sesiones, lo que importa para el almacenamiento en caché de prompts — el arreglo de herramientas es parte de la solicitud a la API, y reordenarlo invalidaría el caché.

La estrategia de fusión en assembleToolPool() es deliberada:

text
1// Ordenar cada partición alfabéticamente, concatenar, deduplicar
2const byName = (a, b) => a.name.localeCompare(b.name)
3return uniqBy(
4 [...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)),
5 'name',
6)

Serialización de la API

Antes de que las herramientas lleguen a la API de Claude, toolToAPISchema() convierte el esquema Zod de cada herramienta al formato JSON Schema de la API de Anthropic.

El Pipeline de Despacho

Cuando Claude responde, su mensaje puede contener bloques tool_use — solicitudes estructuradas para invocar herramientas. El pipeline de despacho procesa estos bloques a través de siete fases. Cada llamada a herramienta pasa por todas las fases, en orden.

Fase 1: Extracción

En el bucle principal de consulta (query.ts), los bloques tool_use se filtran del mensaje del asistente:

text
1const msgToolUseBlocks = message.message.content.filter(
2 content => content.type === 'tool_use',
3) as ToolUseBlock[]

Cada bloque tiene un name, un objeto input y un id único. El id es fundamental — el resultado de la herramienta debe hacer referencia al mismo id cuando se envía de vuelta a la API, o la conversación se rompe.

Fase 2: Validación de Entrada

El esquema Zod de la herramienta valida la entrada sin procesar usando safeParse() — una variante que no lanza excepciones y devuelve datos válidos o un error estructurado. Si la validación falla, el modelo recibe un mensaje de error formateado con pistas del esquema y la ejecución se detiene para esa herramienta. No se ejecuta código con entrada no válida.

text
1const parsedInput = tool.inputSchema.safeParse(input)
2if (!parsedInput.success) {
3 let errorContent = formatZodValidationError(tool.name, parsedInput.error)
4 // Devolver error al modelo, omitir ejecución
5}

Después de la validación de Zod, algunas herramientas ejecutan una segunda verificación validateInput() para validación semántica que no se puede expresar en un esquema — por ejemplo, verificar que una ruta de archivo sea absoluta, no relativa.

Fase 3: Hooks Previos a la Herramienta

Antes de las verificaciones de permisos, se ejecutan los hooks configurados por el usuario. Estos son comandos o scripts de shell externos que se activan en las invocaciones de herramientas. Un hook previo puede:

  • Permitir la llamada a la herramienta, omitiendo el aviso de permiso interactivo
  • Denegar la llamada a la herramienta directamente
  • Modificar la entrada antes de la ejecución
  • Bloquear la ejecución con un mensaje de error
  • Proporcionar contexto adicional al usuario

Una invariante crítica: la permisión de un hook no omite las reglas de denegación de la configuración. El código fuente tiene un comentario explícito al respecto: "Hook 'allow' does NOT bypass settings.json deny/ask rules". La intención es que los hooks pueden abrir puertas, pero no anular candados.

Fase 4: Verificación de Permisos

El sistema de permisos es la parte más intrincada del pipeline. Se resuelve a través de múltiples capas, en orden:

  1. Reglas de denegación — se verifican primero. Si alguna regla de denegación coincide, la ejecución se detiene inmediatamente. Las reglas de denegación son definitivas y no pueden ser anuladas por ninguna otra capa.
  2. Reglas de consulta — si coinciden, se solicita la aprobación del usuario (a menos que se aplique la aprobación automática de sandbox para Bash).
  3. Permisos específicos de la herramienta — se ejecuta el método checkPermissions() de la propia herramienta. BashTool, por ejemplo, analiza el comando para verificar reglas a nivel de subcomando.
  4. Verificaciones de seguridad — protecciones codificadas para rutas sensibles (.git/, .claude/, configuraciones de shell). Estas son inmunes a la omisión — incluso en modo de omisión total, requieren aprobación interactiva.
  5. Modo de permiso — el modo configurado del usuario determina el comportamiento predeterminado.
  6. Reglas de permisión — se verifican al final. Si una regla de permisión coincide y no se activó ninguna regla de denegación/consulta, la herramienta procede.

Modos de permiso

default — Siempre solicitar al usuario en decisiones de "consulta".

acceptEdits — Permitir automáticamente operaciones de archivo seguras (lecturas, ediciones), solicitar para todo lo demás.

bypassPermissions — Permitir automáticamente todo excepto reglas de denegación y verificaciones de seguridad.

plan — Aprobar un plan primero, luego seguir el modo anterior para la ejecución.

auto — Usar un clasificador de IA para decidir si permitir o solicitar.

dontAsk — Convertir todas las decisiones de "consulta" en "denegación" — nunca solicitar, simplemente rechazar.

Las reglas de permisos provienen de múltiples fuentes, resueltas en orden de prioridad: policySettings, localSettings, projectSettings, userSettings, flagSettings, cliArg, command, session. Esto permite que las políticas de la organización anulen las preferencias del usuario, y que los argumentos de la CLI anulen ambas.

Fase 5: Ejecución

Si se concede el permiso, se invoca el método call() de la herramienta:

text
1const result = await tool.call(
2 callInput,
3 { ...toolUseContext, toolUseId: toolUseID },
4 canUseTool,
5 assistantMessage,
6 progress => onToolProgress({ toolUseID: progress.toolUseID, data: progress.data })
7)

Cinco argumentos: la entrada validada, un contexto de ejecución (directorio de trabajo, controlador de cancelación, estado de la aplicación), un callback de permiso (para herramientas que necesitan solicitar permisos adicionales durante la ejecución), el mensaje del asistente padre y un callback de progreso para actualizaciones en tiempo real. La duración se rastrea globalmente.

Un detalle sutil: la entrada que se pasa a call() es la entrada original del modelo, no la versión completada que vieron los hooks y los permisos. Esto preserva la consistencia de la transcripción — la llamada a la herramienta registrada en la conversación coincide exactamente con lo que generó el modelo.

Fase 6: Hooks Posteriores a la Herramienta

Después de la ejecución, se activan los hooks posteriores a la herramienta. Estos pueden modificar la salida de la herramienta MCP, proporcionar contexto adicional o bloquear la continuación de la conversación. También hay un hook PostToolUseFailure separado que se activa solo en errores, dando a los sistemas externos la oportunidad de registrar fallos o sugerir soluciones.

Fase 7: Mapeo de Resultados

Cada herramienta implementa mapToolResultToToolResultBlockParam() para convertir su salida al formato ToolResultBlockParam de la API de Anthropic — un bloque tool_result con una referencia tool_use_id y contenido estructurado o de cadena.

Si el resultado supera un umbral de tamaño, se persiste en disco en sessionDir/tool-results/{toolUseId}.txt y se envía a la API una vista previa con una referencia al archivo. Esto evita que las salidas grandes (un archivo de 10,000 líneas leído, la salida de un comando verboso) inflen el contexto de la conversación.

El Planificador de Concurrencia

Cuando el modelo emite múltiples llamadas a herramientas en un solo mensaje, no todas se ejecutan a la vez. Un planificador las particiona en lotes basándose en la seguridad de concurrencia.

El algoritmo es simple. Recorre las llamadas a herramientas en orden. Para cada una, verifica isConcurrencySafe(input). Si es segura y el lote anterior también era seguro, la agrega al lote. De lo contrario, inicia un nuevo lote.

text
1// Simplificado de toolOrchestration.ts
2for (const toolUse of toolUseMessages) {
3 const isSafe = tool.isConcurrencySafe(parsedInput)
4 if (isSafe && lastBatch.isConcurrencySafe) {
5 lastBatch.blocks.push(toolUse) // Fusionar en lote concurrente
6 } else {
7 batches.push({ isConcurrencySafe: isSafe, blocks: [toolUse] })
8 }
9}

Los lotes seguros se ejecutan concurrentemente (hasta un límite de 10, configurable mediante CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY). Los lotes no seguros se ejecutan en serie, una herramienta a la vez. Los modificadores de contexto solo se aplican entre lotes, no dentro de ellos.

En la práctica, esto significa que un mensaje como "lee estos 5 archivos" produce un lote concurrente, mientras que "lee este archivo, luego edítalo" produce dos lotes en serie. El modelo puede incluso activar ambos patrones en un solo turno — las llamadas de solo lectura consecutivas se agrupan, y la primera escritura rompe el lote.

El Ejecutor en Streaming

Hay una segunda ruta de ejecución: el StreamingToolExecutor. Cuando el streaming está habilitado, las herramientas comienzan a ejecutarse mientras el modelo todavía está generando su respuesta. A medida que cada bloque tool_use se completa en el flujo, se encola inmediatamente para su ejecución en lugar de esperar la respuesta completa.

El ejecutor en streaming usa las mismas reglas de concurrencia pero agrega un comportamiento: cascada de errores de Bash. Si un comando de Bash falla mientras las herramientas hermanas se ejecutan en paralelo, el ejecutor cancela todas las herramientas hermanas. La lógica es que un comando de Bash fallido probablemente invalida el contexto en el que están operando otras herramientas — continuar con ellas pierde tiempo y puede causar errores confusos.

text
1if (isErrorResult && tool.block.name === BASH_TOOL_NAME) {
2 this.hasErrored = true
3 this.siblingAbortController.abort('sibling_error')
4}

Un Ejemplo Práctico

Para hacerlo concreto, rastreemos lo que sucede cuando el modelo decide leer un archivo. El modelo emite:

text
1{
2 "type": "tool_use",
3 "id": "toolu_01XYZ",
4 "name": "read",
5 "input": { "file_path": "/src/index.ts" }
6}

Extracción: query.ts filtra esto del contenido del mensaje del asistente.

  1. Búsqueda de herramienta: findToolByName(tools, "read") encuentra FileReadTool.
  2. Validación de entrada: Zod analiza { file_path: "/src/index.ts" } contra z.object({ file_path: z.string(), offset: z.number().optional(), limit: z.number().optional(), pages: z.string().optional() }). Pasa la validación.
  3. Hooks previos: Cualquier hook configurado por el usuario se activa. Ninguno modifica la entrada.
  4. Verificación de permisos: checkPermissions() de FileReadTool llama a checkReadPermissionForTool(). Las herramientas de lectura generalmente están permitidas en la mayoría de los modos de permiso.
  5. Ejecución: FileReadTool.call() lee el archivo, aplica numeración de líneas (formato cat -n), maneja PDFs/imágenes/cuadernos como casos especiales.
  6. Mapeo de resultados: El contenido del archivo se convierte en un bloque tool_result que referencia a "toolu_01XYZ".
  7. Retorno: El resultado se añade a la conversación como un mensaje de usuario y se envía en la siguiente llamada a la API.

Debido a que FileReadTool declara isConcurrencySafe: () => true e isReadOnly: () => true, si el modelo hubiera emitido cinco llamadas de lectura en el mismo mensaje, las cinco se ejecutarían en paralelo.

Resumen

El sistema de herramientas es la columna vertebral de ejecución de Claude Code. Toma la intención del modelo — expresada como bloques tool_use estructurados — y la convierte en acciones reales en tu máquina, con validación, permisos y control de concurrencia en cada paso.

El diseño está en capas: una fábrica buildTool() conservadora garantiza valores predeterminados seguros, un registro con banderas de funcionalidad controla lo que está disponible, un pipeline de despacho de siete fases valida y verifica permisos de cada llamada, y un planificador de concurrencia maximiza el paralelismo mientras preserva la corrección. El ejecutor en streaming añade una optimización de rendimiento adicional: las herramientas comienzan a ejecutarse antes de que el modelo termine de pensar.

En comparación con el sistema de memoria (5 rutas, un directorio de archivos markdown e ingeniería de prompts), el sistema de herramientas es un runtime propiamente dicho. Es la diferencia entre un archivador y un sistema operativo.

Lo Interesante

El Modelo como Planificador

El planificador de concurrencia es reactivo — agrupa en lotes lo que sea que el modelo emita. Pero el modelo mismo es el verdadero planificador. El prompt del sistema le dice que "haga todas las llamadas a herramientas independientes en paralelo" y que "use una sola llamada a Bash con && para encadenar comandos dependientes." El runtime confía en esto. Si el modelo emite cinco lecturas seguidas de una escritura, el planificador paralelizará las lecturas y serializará la escritura. Pero el modelo decidió ese orden. El planificador está ejecutando el plan del modelo, no creando el suyo propio.

Fallar de Forma Segura por Defecto

El principio de diseño más consistente: todo falla de forma segura. ¿Herramienta desconocida? Error. ¿Entrada no válida? Error. ¿Sin declaración de concurrencia? Ejecución en serie. ¿Sin declaración de permisos? Preguntar al usuario. ¿Sin bandera de funcionalidad? La herramienta no existe. Esto es inusual para un sistema donde el usuario principal es un modelo de IA que podría alucinar nombres de herramientas o malformar entradas. El sistema está diseñado para contener los errores del modelo, no para acomodarlos.

Los Hooks como Punto de Extensión

El sistema de hooks — previo a la herramienta, posterior a la herramienta y posterior a la falla — es el principal punto de extensión. Así es como las organizaciones aplican políticas (reglas de denegación en hooks previos), cómo los sistemas de registro capturan el uso de herramientas (hooks posteriores) y cómo se integran los pipelines de CI/CD (hooks de falla). Es importante destacar que los hooks solo pueden endurecer las restricciones, no flexibilizarlas. Un hook puede denegar una herramienta que la configuración permite, pero no puede permitir una herramienta que la configuración deniega.

43 Herramientas, 1 Interfaz

Quizás lo más llamativo es la uniformidad. Un comando bash, un web_fetch, la generación de un subagente, la creación de un trabajo cron y una notificación push implementan todos la misma interfaz de 30 métodos, pasan por el mismo pipeline de siete fases y respetan el mismo sistema de permisos. No hay casos especiales en el despachador. La complejidad está en las implementaciones individuales de las herramientas y en las reglas de permisos, no en el enrutamiento.

Guardar con un clic

Lee artículos virales en profundidad con IA en YouMind

Guarda la fuente, haz preguntas concretas, resume el argumento y convierte un artículo viral en notas reutilizables en un único espacio de trabajo con IA.

Explora YouMind
Para creadores

Convierte tu Markdown en un artículo de 𝕏 impecable

Cuando publicas tus propios textos largos, dar formato en 𝕏 a imágenes, tablas y bloques de código es un fastidio. YouMind convierte un borrador completo en Markdown en un artículo de 𝕏 impecable y listo para publicar.

Prueba Markdown a 𝕏

Más patrones por descifrar

Artículos virales recientes

Explorar más artículos virales