# Cómo crear una nueva herramienta (tool) Las herramientas viven en `tools/` y siguen el patrón **spec puro + función impura**. ## Pasos ### 1. Crear el archivo `tools/.go` ```go package tools import ( "context" "fmt" ) // NewMiTool creates a mi_tool tool that does X. // Accepts dependencies needed for execution (configs, clients, etc). func NewMiTool(/* deps */) Tool { return Tool{ Def: Def{ Name: "mi_tool", Description: "Description clara de qué hace la herramienta para el LLM.", Parameters: []Param{ {Name: "param1", Type: "string", Description: "What this param is", Required: true}, {Name: "param2", Type: "number", Description: "Optional param", Required: false}, }, }, Exec: func(ctx context.Context, args map[string]any) Result { p1 := getString(args, "param1") if p1 == "" { return Result{Err: fmt.Errorf("mi_tool: param1 is required")} } // Execute the actual work here (impure) output := doSomething(p1) return Result{Output: output} }, } } ``` ### 2. Registrar en `devagents/registry_build.go` → `buildToolRegistry()` ```go if /* condición basada en config */ { reg.Register(tools.NewMiTool(/* deps */)) logger.Debug("registered mi_tool") } ``` ### 3. Habilitar en el config del agente (`agents//config.yaml`) Asegurarse de que `llm.tool_use.enabled: true` y la sección relevante de `tools:` esté habilitada. ## Reglas - **Def es PURO**: solo datos (nombre, descripción, parámetros). Sin side effects. - **Exec es IMPURO**: hace I/O real. Recibe `context.Context` y `map[string]any`. - **Validar inputs**: siempre validar parámetros requeridos al inicio del Exec. - **Validar permisos**: usar los campos del config (AllowedDomains, AllowedPaths, etc.) para restringir acceso. - **Limitar output**: truncar a 64 KB máximo para no saturar el contexto del LLM. - **Usar `getString()`**: helper del package para extraer strings de args de forma segura. - **Param types válidos**: "string", "number", "integer", "boolean", "object", "array" (JSON Schema types). - **Descripción clara**: el LLM decide cuándo usar la tool basándose en el Description del Def. ## Seguridad — requisitos obligatorios Toda tool que haga I/O externo debe implementar protecciones: - **Deny-by-default**: si la tool tiene una allowlist (AllowedPaths, AllowedDomains, AllowedCommands, etc.), un allowlist vacio debe denegar todo, no permitir todo. - **Path traversal**: para tools que aceptan rutas de archivo, resolver symlinks con `filepath.EvalSymlinks` y validar que el path resuelto este dentro de los paths permitidos. Proteger contra `../` y prefix confusion. - **SSRF protection**: para tools que hacen HTTP, resolver la IP del dominio antes de conectar y bloquear IPs privadas (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16). - **Command injection**: para tools que ejecutan comandos, validar sintaxis shell (no permitir pipes `|`, subshells `$()`, redirects `>`, chains `&&`/`||`/`;`) a menos que esten explicitamente permitidos. - **Rate limiting**: las tools estan sujetas a rate limiting por room via `security.tool_rate_limit` en el config. No se necesita implementar nada en la tool — el registry lo maneja automaticamente. Referencia de implementaciones: `tools/file/`, `tools/ssh/`, `tools/http/`.