Módulo 1: Custom Subagents

3. Restricción de Herramientas para Subagents

3. Restricción de Herramientas para Subagents

Descripción

En la cápsula anterior definiste subagents con system prompts que establecen su rol y criterios de trabajo. Pero un system prompt sin restricciones de herramientas es una sugerencia, no un contrato. Puedes escribir "eres un reviewer, solo analiza código" en el prompt, y el subagent puede decidir editar un archivo "para demostrar la corrección." Un reviewer que edita no es un reviewer — es un agente genérico con un título bonito. Las restricciones de herramientas convierten sugerencias en garantías.

Claude Code ofrece dos mecanismos para controlar herramientas: tools (allowlist — solo estas) y disallowedTools (denylist — todas menos estas). Además, los permission modes definen cómo el subagent maneja solicitudes de permisos, y los hooks PreToolUse permiten validación condicional — por ejemplo, permitir Bash pero bloquear cualquier comando que no sea un SELECT.

Esta cápsula te enseña a usar cada mecanismo, cuándo elegir allowlist vs denylist, cómo combinar tools con hooks para control fino, y cómo aplicar todo al flujo reviewer → implementer → tester de la cápsula 05.


¿Por qué las restricciones definen la identidad?

Sin restricciones de herramientas, la identidad de un subagent depende de que siga instrucciones. Esto funciona la mayor parte del tiempo, pero falla en casos edge:

System prompt: "Eres un reviewer. Solo analiza código. No modifiques archivos."

Situación: El reviewer encuentra un typo obvio en un comentario.
Sin restricciones → Lo corrige "porque es trivial." Violó su rol.
Con restricciones → No puede escribir archivos. Reporta el typo. Cumple su rol.

En un flujo donde el reviewer pasa su reporte al implementer, una edición no autorizada puede:

  • 🔴 Generar conflictos con los cambios del implementer
  • 🔴 Romper la trazabilidad (¿quién cambió qué?)
  • 🔴 Producir cambios no revisados (el reviewer se revisó a sí mismo)

Piensa en las restricciones como permisos de un sistema operativo. No le pides al auditor "por favor no escribas." Le quitas el permiso de escritura.


Herramientas disponibles en Claude Code

CategoríaHerramientaQué hace
LecturaReadLee contenido de archivos
GlobBusca archivos por patrón (nombre, extensión)
GrepBusca texto dentro de archivos
EscrituraWriteCrea o sobrescribe archivos
EditModifica archivos existentes
EjecuciónBashEjecuta comandos de terminal
DelegaciónAgentDelega a otro subagent
Agent(tipo)Delega a un subagent específico
WebWebFetchObtiene contenido de una URL
WebSearchBusca en la web

Estas herramientas son las piezas que combinas para definir las capacidades de cada subagent.


El campo tools — Allowlist

El campo tools en el frontmatter YAML define una lista explícita de herramientas permitidas. Todo lo que no esté listado queda bloqueado implícitamente.

Sintaxis básica

---
name: reviewer
description: Revisa código sin modificar archivos
tools: Read, Grep, Glob
---

Este subagent puede leer archivos, buscar texto y buscar archivos por patrón. No puede escribir, editar, ejecutar comandos, ni delegar. Si intenta usar Write, Claude Code lo bloquea.

Ejemplo: Reviewer read-only

---
name: code-reviewer
description: Analiza código buscando problemas de calidad y seguridad
tools: Read, Grep, Glob
---

Eres un code reviewer especializado. Analizas código y produces un reporte estructurado.

## Qué revisas
1. Errores de lógica y edge cases no manejados
2. Vulnerabilidades de seguridad (SQL injection, XSS, secrets hardcodeados)
3. Violaciones de convenciones del proyecto
4. Code smells (funciones largas, duplicación, acoplamiento)

## Formato de reporte
Para cada hallazgo:
- **Archivo:** ruta del archivo
- **Línea:** número aproximado
- **Severidad:** CRÍTICO | WARNING | SUGERENCIA
- **Descripción:** qué encontraste y por qué es un problema

El system prompt refuerza las restricciones, pero la verdadera garantía es tools. Aunque borres esa línea del prompt, el subagent sigue sin poder escribir.

Ejemplo: Tester con solo Bash

---
name: tester
description: Ejecuta tests y reporta resultados
tools: Bash, Read
---

Eres un tester automatizado. Ejecutas la suite de tests y reportas resultados.

## Tu flujo
1. Lee la configuración de tests (pytest.ini, pyproject.toml)
2. Ejecuta: `python -m pytest --tb=short -q`
3. Si hay tests fallidos, ejecuta cada uno individualmente para más detalle

## Formato de reporte
- Tests totales: X
- Pasaron: X
- Fallaron: X (listar con nombre y causa)
- Cobertura: X% (si está configurado)

Si un test falla, reporta el fallo — no intenta arreglarlo, porque no tiene Write ni Edit.


El campo disallowedTools — Denylist

disallowedTools es el inverso de tools: lista qué herramientas están bloqueadas. El subagent puede usar todas las herramientas excepto las listadas.

---
name: research-assistant
description: Investiga código y documentación sin modificar el proyecto
disallowedTools: Write, Edit
---

Eres un investigador. Puedes leer código, buscar archivos, ejecutar comandos
de lectura, y consultar la web. No puedes modificar ningún archivo del proyecto.

Sin tools definido y con disallowedTools: Write, Edit, este subagent tiene acceso a Read, Grep, Glob, Bash, Agent, WebFetch, WebSearch — todo menos escritura.

Bloquear delegación a subagents específicos

---
name: coordinator
description: Coordina solo con reviewer e implementer
disallowedTools: Agent(tester), Agent(Explore)
---

Comparación: tools vs disallowedTools

Aspectotools (Allowlist)disallowedTools (Denylist)
Filosofía"Solo puede hacer esto""Puede hacer todo menos esto"
SeguridadMás seguro — herramientas nuevas bloqueadas por defaultMenos seguro — herramientas nuevas permitidas por default
MantenimientoRequiere actualizar si Claude Code agrega herramientas útilesSe adapta automáticamente a herramientas nuevas
Mejor paraRoles estrechos (reviewer, tester)Roles amplios (implementer, researcher)
RiesgoBloquear algo que el agente necesitaPermitir algo que no debería tener
Forward-compatibleNoSí

Regla práctica

¿El subagent necesita pocas herramientas?  → tools (allowlist)
  Ejemplos: reviewer (Read, Grep, Glob), tester (Bash, Read)

¿El subagent necesita muchas herramientas? → disallowedTools (denylist)
  Ejemplos: implementer (todo menos Agent), researcher (todo menos Write/Edit)

¿La seguridad es crítica?                  → Siempre tools (allowlist)

Puedes combinar ambos: tools define la base permitida y disallowedTools remueve de esa base:

---
name: careful-implementer
description: Implementa cambios pero no puede delegar
tools: Read, Grep, Glob, Write, Edit, Bash
disallowedTools: Agent
---

Control de delegación: Agent(agent_type)

El campo tools acepta Agent(tipo) para especificar a quién puede delegar:

---
name: dev-pipeline
description: Ejecuta el flujo completo de desarrollo
tools: Agent(reviewer), Agent(implementer), Agent(tester), Read
---

Eres un pipeline de desarrollo. Ejecuta en secuencia:

1. **Review:** Delega al reviewer → espera reporte con hallazgos
2. **Implement:** Delega al implementer con los hallazgos → espera confirmación
3. **Test:** Delega al tester → espera reporte de tests
4. **Reporte final:** Consolida resultados de los 3 agentes

Si intenta Agent(otro-agente), Claude Code lo bloquea. También puedes bloquear subagents globalmente desde .claude/settings.json:

{
  "permissions": {
    "deny": ["Agent(Explore)", "Agent(dangerous-agent)"]
  }
}

Permission Modes

Los permission modes controlan cómo el subagent maneja los prompts de permisos — una capa adicional sobre las restricciones de herramientas.

ModoComportamientoCaso de uso
defaultSolicita permisos normalmenteAgentes supervisados
acceptEditsAuto-acepta ediciones de archivosImplementers de confianza
dontAskAuto-deniega prompts de permisosAgentes read-only
bypassPermissionsSalta todas las verificacionesAutomatización CI/CD
planModo exploración read-onlyReviewers, investigadores

Tools, permission modes y hooks trabajan en capas:

Capa 1: tools/disallowedTools  → ¿Qué herramientas tiene disponibles?
Capa 2: permission modes        → ¿Cómo maneja los permisos de esas herramientas?
Capa 3: hooks PreToolUse        → ¿Qué validaciones adicionales se ejecutan?

Hooks PreToolUse — Validación condicional

El problema

tools y disallowedTools son binarios — permitido o bloqueado. A veces necesitas algo intermedio: "Bash sí, pero solo para SELECT." Los hooks PreToolUse ejecutan un script de validación antes de cada tool use.

Anatomía de un hook

---
name: db-reader
description: Ejecuta consultas SELECT únicamente
tools: Bash, Read
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/validate-readonly-query.sh"
---

Flujo de ejecución

  1. El subagent intenta usar Bash con un comando
  2. Claude Code ejecuta validate-readonly-query.sh pasando el tool input como JSON via stdin
  3. Exit code 0 → operación permitida
  4. Exit code 2 → operación bloqueada (código especial de Claude Code)
  5. Cualquier otro exit code → error del hook (operación se permite por default)

Script de validación: Solo SELECT

#!/bin/bash
# scripts/validate-readonly-query.sh

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

if echo "$COMMAND" | grep -iE '\b(INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|TRUNCATE)\b' > /dev/null; then
  echo "Bloqueado: Solo se permiten consultas SELECT" >&2
  exit 2
fi

exit 0

El JSON que recibe via stdin tiene la estructura:

{
  "tool_name": "Bash",
  "tool_input": {
    "command": "psql -d mydb -c 'SELECT * FROM users WHERE id = 1'"
  }
}

Script de validación: Solo escribir en src/

#!/bin/bash
# scripts/validate-write-path.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ -z "$FILE_PATH" ]]; then
  exit 0
fi

FILE_PATH="${FILE_PATH#./}"

if [[ "$FILE_PATH" != src/* ]]; then
  echo "Bloqueado: Solo se permite escribir en src/" >&2
  exit 2
fi

exit 0

Subagent con hook de directorio

---
name: src-implementer
description: Implementa cambios exclusivamente en src/
tools: Read, Grep, Glob, Write, Edit, Bash
hooks:
  PreToolUse:
    - matcher: "Write"
      hooks:
        - type: command
          command: "./scripts/validate-write-path.sh"
    - matcher: "Edit"
      hooks:
        - type: command
          command: "./scripts/validate-write-path.sh"
---

Eres un implementer que solo modifica archivos en src/.

Ahora la restricción de "solo src/" no depende del system prompt — está enforced por el hook.


Combinando tools + hooks — El patrón completo

La combinación más poderosa: allowlist define categorías, hooks validan operaciones específicas.

---
name: safe-implementer
description: Implementa cambios en src/ con comandos restringidos
tools: Read, Grep, Glob, Write, Edit, Bash
hooks:
  PreToolUse:
    - matcher: "Write"
      hooks:
        - type: command
          command: "./scripts/validate-write-path.sh"
    - matcher: "Edit"
      hooks:
        - type: command
          command: "./scripts/validate-write-path.sh"
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/validate-safe-commands.sh"
---

Con un script de comandos seguros basado en allowlist:

#!/bin/bash
# scripts/validate-safe-commands.sh

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Solo permitir comandos específicos
if echo "$COMMAND" | grep -qE '^(python -m pytest|ruff check|ruff format|git status|git diff)'; then
  exit 0
fi

echo "Bloqueado: Comando no autorizado" >&2
exit 2

Allowlist de comandos > denylist de comandos. Con denylist necesitas anticipar todos los comandos peligrosos (y siempre hay uno que se escapa). Con allowlist solo defines qué es legítimo.


Conexión con el proyecto

En la cápsula 05, construirás el flujo completo reviewer → implementer → tester. Las restricciones que configures aquí definen la garantía de cada agente:

reviewer    →  tools: Read, Grep, Glob
                Garantía: NO puede modificar tu código

implementer →  tools: Read, Grep, Glob, Write, Edit, Bash
                hooks: Write/Edit solo en src/
                Garantía: Solo modifica archivos de código fuente

tester      →  tools: Bash, Read
                Garantía: NO puede editar código, solo ejecutar tests

El coordinator que orquesta los tres:

---
name: dev-coordinator
description: Orquesta el flujo reviewer → implementer → tester
tools: Agent(reviewer), Agent(implementer), Agent(tester), Read
---

Solo puede delegar a los tres agentes del flujo. No puede ejecutar ni editar directamente — toda la ejecución pasa por los agentes especializados. Sin estas restricciones, el flujo no tiene garantías.


Troubleshooting

Problema 1: "Mi subagent ignora las restricciones de tools"

Causa: Los nombres de herramientas son case-sensitive. tools: read, grep no funciona — debe ser tools: Read, Grep. Verifica también que no uses nombres incorrectos como Bash (el nombre es Bash).

Solución: Usa /agents en Claude Code para inspeccionar la configuración parseada del subagent.

Problema 2: "El hook no bloquea las operaciones"

Causa: Solo exit code 2 bloquea. Exit code 1 se trata como error del hook (no como bloqueo). Exit code 0 permite.

Solución: Verifica que tu script use exit 2 para bloquear. Prueba manualmente:

echo '{"tool_input": {"command": "DROP TABLE users"}}' | ./scripts/validate-readonly-query.sh
echo $?  # Debe imprimir 2

Problema 3: "El hook no recibe el JSON correctamente"

Causa: jq no está instalado, o el script no lee stdin con INPUT=$(cat).

Solución: Verifica which jq. El script debe leer stdin completo antes de parsear:

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

Problema 4: "Mi implementer no puede escribir aunque Write está en tools"

Causa: Un hook PreToolUse para Write puede estar rechazando la ruta. Otra causa: el permission mode bloquea la escritura.

Solución: Agrega logging temporal al script de validación para ver qué ruta intenta escribir:

echo "DEBUG: Ruta solicitada: $FILE_PATH" >&2

Problema 5: "La validación por regex de Bash es frágil"

Causa: Hay infinitas formas de ejecutar comandos destructivos que un regex no captura (find -delete, python -c "import os; os.remove('...')", etc.).

Solución: Para alta seguridad, usa allowlist de comandos en vez de denylist. Si el riesgo es muy alto, elimina Bash del allowlist completamente.


Ejercicios

Ejercicio 1: Configura un reviewer read-only

Crea .claude/agents/reviewer.md con un subagent que busque funciones Python sin docstrings. Solo herramientas de lectura, formato de reporte estructurado.

Ver solución
---
name: docstring-reviewer
description: Busca funciones Python sin docstrings
tools: Read, Grep, Glob
---

Buscas funciones y clases Python sin docstrings.

## Proceso
1. Busca todos los archivos .py con Glob: `**/*.py`
2. Lee cada archivo y busca `def` y `class` sin docstring inmediato
3. Reporta cada hallazgo

## Formato
- **Archivo:** ruta
- **Línea:** número
- **Tipo:** función | clase
- **Nombre:** nombre
- **Severidad:** WARNING (públicas) | SUGERENCIA (privadas _)

## Resumen final
- Total analizadas / Total sin docstring / Porcentaje de cobertura

Verificación: Ejecuta /agents y confirma que aparece con Read, Grep, Glob. Invócalo y verifica que no intenta editar archivos.

Ejercicio 2: Hook de validación de directorio

Escribe scripts/validate-write-path.sh que solo permita escritura en src/. Exit 0 para permitir, exit 2 para bloquear.

Ver solución
#!/bin/bash
# scripts/validate-write-path.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty')

if [[ -z "$FILE_PATH" ]]; then
  exit 0
fi

FILE_PATH="${FILE_PATH#./}"

if [[ "$FILE_PATH" == src/* ]]; then
  exit 0
fi

echo "Bloqueado: Escritura en '$FILE_PATH' no permitida. Solo src/" >&2
exit 2
chmod +x scripts/validate-write-path.sh

# Debe permitir (exit 0)
echo '{"tool_input":{"file_path":"src/models/user.py"}}' | ./scripts/validate-write-path.sh
echo "Exit: $?"

# Debe bloquear (exit 2)
echo '{"tool_input":{"file_path":"tests/test_user.py"}}' | ./scripts/validate-write-path.sh
echo "Exit: $?"

Output esperado:

Exit: 0
Bloqueado: Escritura en 'tests/test_user.py' no permitida. Solo src/
Exit: 2

Ejercicio 3: Diseña restricciones para 3 subagents

Diseña la configuración de herramientas (solo frontmatter) para: api-reviewer, api-implementer, api-tester. Para cada uno decide: ¿tools o disallowedTools? ¿Hooks? Justifica.

Ver solución

api-reviewer: Allowlist puro, sin hooks necesarios.

---
name: api-reviewer
description: Revisa endpoints de API
tools: Read, Grep, Glob
---

api-implementer: Allowlist + hooks para restringir escritura a src/.

---
name: api-implementer
description: Implementa y corrige endpoints
tools: Read, Grep, Glob, Write, Edit, Bash
hooks:
  PreToolUse:
    - matcher: "Write"
      hooks:
        - type: command
          command: "./scripts/validate-write-path.sh"
    - matcher: "Edit"
      hooks:
        - type: command
          command: "./scripts/validate-write-path.sh"
---

api-tester: Allowlist + hook para restringir Bash a comandos de testing.

---
name: api-tester
description: Ejecuta tests de API
tools: Bash, Read
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/validate-test-commands.sh"
---

Los tres usan allowlist porque la seguridad importa más que la conveniencia. Ninguno tiene Agent — la delegación se controla desde el coordinator.

Ejercicio 4: Hook avanzado — Allowlist de comandos

Crea un script que solo permita: python -m pytest, ruff check, ruff format, git status, git diff. Todo lo demás bloqueado.

Ver solución
#!/bin/bash
# scripts/validate-allowed-commands.sh

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

if [[ -z "$COMMAND" ]]; then
  exit 0
fi

ALLOWED_PREFIXES=(
  "python -m pytest"
  "ruff check"
  "ruff format"
  "git status"
  "git diff"
)

for prefix in "${ALLOWED_PREFIXES[@]}"; do
  if [[ "$COMMAND" == "$prefix"* ]]; then
    exit 0
  fi
done

echo "Bloqueado: Comando no autorizado. Permitidos: pytest, ruff, git status/diff" >&2
exit 2

Prueba:

chmod +x scripts/validate-allowed-commands.sh

echo '{"tool_input":{"command":"python -m pytest tests/ -v"}}' | ./scripts/validate-allowed-commands.sh
echo "Exit: $?"  # 0

echo '{"tool_input":{"command":"rm -rf /"}}' | ./scripts/validate-allowed-commands.sh
echo "Exit: $?"  # 2

Ejercicio 5: Debugging de configuración

Este subagent tiene errores. Encuéntralos y corrígelos:

---
name: broken_reviewer
description: Revisa código
tools: read, grep, glob, bash
disallowed_tools: Write, Edit
hooks:
  preToolUse:
    - match: "Bash"
      hooks:
        - type: command
          command: "./scripts/validate.sh"
---
Ver solución

Errores:

  1. name: broken_reviewer → name: broken-reviewer (guiones, no underscores)
  2. tools: read, grep, glob, bash → tools: Read, Grep, Glob, Bash (case-sensitive)
  3. disallowed_tools → disallowedTools (camelCase)
  4. tools ya excluye Write/Edit — el disallowedTools es redundante. Eliminarlo.
  5. preToolUse → PreToolUse (PascalCase)
  6. match → matcher (nombre correcto del campo)
  7. Un reviewer no necesita Bash — eliminar del allowlist.

Corregido:

---
name: broken-reviewer
description: Revisa código
tools: Read, Grep, Glob
---

Al eliminar Bash, los hooks se vuelven innecesarios. Frecuentemente, la mejor solución a "¿cómo valido este tool use?" es "¿necesita realmente esta herramienta?"

Ejercicio 6: Sistema de 4 agentes

Diseña el frontmatter para: coordinator (orquesta), security-reviewer (busca vulnerabilidades), fix-implementer (corrige), security-tester (ejecuta bandit/safety). Incluye una tabla de capacidades.

Ver solución
# coordinator.md
---
name: security-coordinator
description: Orquesta el flujo de revisión de seguridad
tools: Agent(security-reviewer), Agent(fix-implementer), Agent(security-tester), Read
---
# security-reviewer.md
---
name: security-reviewer
description: Busca vulnerabilidades de seguridad
tools: Read, Grep, Glob
---
# fix-implementer.md
---
name: fix-implementer
description: Corrige vulnerabilidades en src/
tools: Read, Grep, Glob, Write, Edit, Bash
hooks:
  PreToolUse:
    - matcher: "Write"
      hooks:
        - type: command
          command: "./scripts/validate-write-path.sh"
    - matcher: "Edit"
      hooks:
        - type: command
          command: "./scripts/validate-write-path.sh"
---
# security-tester.md
---
name: security-tester
description: Ejecuta herramientas de análisis de seguridad
tools: Bash, Read
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/validate-security-commands.sh"
---

Mapa de capacidades:

AgenteReadGrepGlobWriteEditBashAgent
coordinator✅❌❌❌❌❌✅ (3)
security-reviewer✅✅✅❌❌❌❌
fix-implementer✅✅✅✅ (src/)✅ (src/)✅❌
security-tester✅❌❌❌❌✅ (security)❌

Cada agente tiene exactamente las herramientas que necesita. El coordinator no ejecuta directamente — toda la ejecución pasa por los especializados.


Resumen

  • Las restricciones de herramientas convierten sugerencias del system prompt en garantías ejecutables
  • tools (allowlist): define exactamente qué herramientas están disponibles — todo lo demás bloqueado. Úsalo para roles estrechos y seguridad alta
  • disallowedTools (denylist): bloquea herramientas específicas — todo lo demás permitido. Úsalo para roles amplios con pocas restricciones
  • Agent(tipo) controla delegación — un coordinator con Agent(reviewer), Agent(implementer) solo delega a esos dos
  • Los permission modes (default, acceptEdits, dontAsk, bypassPermissions, plan) controlan cómo se manejan solicitudes de permisos
  • Los hooks PreToolUse permiten validación condicional — exit code 2 bloquea la operación
  • Allowlist > denylist para seguridad — especialmente para Bash
  • La combinación tools + hooks da control granular: tools define categorías, hooks validan operaciones específicas
  • Estas restricciones son la base del flujo reviewer → implementer → tester de la cápsula 05

Recursos adicionales

  1. Create Custom Subagents (Anthropic Docs) — Documentación oficial de subagents con tool restriction y hooks
  2. Claude Code Hooks Reference — Referencia completa de hooks PreToolUse y PostToolUse
  3. Claude Code Permissions — Modelo de permisos y seguridad
  4. Claude Code CLI Reference — Flags de CLI incluyendo --permission-mode y --agent
  5. Claude Code Settings — Configuración de settings.json para permissions deny
  6. Claude Code Best Practices — Buenas prácticas que aplican a subagents
  7. jq Manual — Referencia de jq para parsear JSON en scripts de validación
  8. Principle of Least Privilege (OWASP) — El principio de seguridad que fundamenta las restricciones

Siguiente cápsula: En la cápsula 04 aprenderás cómo los subagents se comunican entre sí. El output de un subagent es texto — y necesitas parsearlo para que el siguiente agente lo entienda. Verás patrones de comunicación estructurada y cómo encadenar subagents en un flujo donde cada uno recibe exactamente la información que necesita del anterior.