# Claude Code mods: qué son, cómo usarlos y a qué pueden acceder

> Un mod de Claude Code es código JS/TS dentro de un plugin que corre con tus permisos y sin sandbox. Requiere la 2.1.287+; revisa sus calls con validate.

- URL: https://blog.laozhang.ai/es/posts/claude-code-mods
- Published: 2026-10-06
- Updated: 2026-10-06
- Author: LaoZhang AI Team (https://blog.laozhang.ai/es/about)
- Topic: Claude Code
- Tags: Claude Code, Mods, Plugins, TypeScript, Anthropic, CLI

---
Un mod de Claude Code es un plugin con funciones en JavaScript o TypeScript que Claude Code ejecuta dentro de su propio proceso cuando ocurre algo: una llamada a herramienta, un prompt que envías o una parte de la interfaz que se dibuja. Por eso un mod puede hacer lo que no logran los hooks de configuración, las skills ni los servidores MCP: abrir un panel junto a la transcripción, retener un comando peligroso hasta que pulses un botón o añadir un `/comando` que responde sin gastar un turno de Claude. Anthropic los anunció el 1 de octubre de 2026 en su artículo [«Customize Claude Code with mods in TypeScript»](https://claude.com/blog/claude-code-mods). Necesitas Claude Code 2.1.287 o posterior en la terminal (la app de escritorio los admite desde la 2.1.286) y vienen activados por defecto. Lo que más conviene saber antes de instalar uno es que se ejecuta con tus permisos y sin sandbox.

Las salidas de comandos que aparecen en esta página se obtuvieron el 6 de octubre de 2026 en macOS con Claude Code 2.1.288, con la CLI sin sesión iniciada: `claude plugin validate`, `claude plugin test`, `claude -p "/tally"` y el ciclo añadir marketplace → instalar → desactivar → desinstalar. No se abrió ninguna sesión interactiva, así que nada de lo que un mod dibuja (paneles, la franja encima del prompt, el indicador de actividad modificado, la línea `mods active` de `/plugin`) ni la recarga en caliente se vio en pantalla; esas partes se describen según la [documentación oficial de mods en español](https://code.claude.com/docs/es/plugins/mods/overview). Tampoco se probaron la pestaña Code de la app de escritorio, la extensión de VS Code, Windows, la protección de los planes Team/Enterprise ni ningún mod de la comunidad.

Un apunte de nombre: estos mods no tienen nada que ver con los mods de videojuegos. Claude Code puede ayudarte a programar un mod de Minecraft, pero eso es otra cosa.

## ¿Necesitas un mod o te basta un hook de configuración, una skill o un MCP?

Necesitas un mod solo si quieres dibujar algo en la interfaz, cambiar un evento por dentro (reescribir un prompt, responder una llamada a herramienta sin ejecutarla, mandar una petición a otro modelo) o tener un comando que ejecute tu código al instante. Para bloquear o registrar algo con un script que ya tienes, sigue bastando un hook de configuración, y la documentación aclara que esos hooks no están obsoletos.

La propia documentación llama «hook de configuración» al que defines en `settings.json` y «hook» a secas a cada función de un mod. Esta tabla resume la comparación oficial:

| | Mod | Hook de configuración | Skill | Servidor MCP |
| --- | --- | --- | --- | --- |
| Qué es | Funciones dentro de un plugin que Claude Code ejecuta en su proceso | Comando de shell, petición HTTP o prompt que se lanza en un evento | Un `SKILL.md` con instrucciones | Proceso externo que da herramientas a Claude |
| Qué puede cambiar | Llamadas a herramientas, prompts, comandos, turnos y lo que se dibuja | Si una llamada o un prompt sigue adelante, sus argumentos y resultado, el contexto añadido | Lo que Claude sabe y hace | Qué herramientas tiene Claude |
| ¿Dibuja en la interfaz? | Sí | No | No | No |
| Lo escribes en | JavaScript o TypeScript | Un script y una entrada en `settings.json` | Markdown | Cualquier lenguaje |
| Elígelo cuando | Quieres un panel, una franja sobre el prompt, un comando propio o reescribir un evento | Quieres bloquear, permitir o registrar con un script | Pegas siempre las mismas instrucciones | Claude tiene que llegar a un sistema externo |

Un mismo plugin puede llevar las cuatro piezas. Si dudas entre hook de configuración, skill y comando de barra, tienes la comparación en [Claude Code: hooks, comandos de barra y skills, ¿cuál usar?](https://blog.laozhang.ai/es/posts/claude-code-hooks-slash-commands-skills). Si solo quieres una línea de estado con la rama o el contexto, un script de statusline es más simple que un mod: [Claude Code Statusline: rutas, campos, scripts y solucion de fallos](https://blog.laozhang.ai/es/posts/claude-code-statusline). Y para elegir qué instalar primero en las otras categorías, están [Las mejores skills de Claude Code para instalar primero en 2026](https://blog.laozhang.ai/es/posts/claude-code-best-skills) y [Los mejores MCP para Claude Code que conviene instalar primero en 2026](https://blog.laozhang.ai/es/posts/claude-code-best-mcp-servers).

![Guía para elegir entre mod, hook de configuración, skill o servidor MCP según lo que quieres conseguir, y dónde se ve lo que dibuja un mod: sí en la terminal y la app de escritorio, no en el chat de VS Code ni en claude -p](https://blog.laozhang.ai/posts/es/claude-code-mods/img/elegir-mod-hook-skill-mcp.webp)

## Dónde funcionan los mods: en VS Code y `claude -p` no dibujan

Los hooks de un mod se ejecutan en casi cualquier sesión que cargue el plugin, pero lo que dibuja solo aparece en la terminal y en la app de escritorio. Según la tabla oficial:

| Dónde usas Claude Code | ¿Se ejecutan los hooks? | ¿Se ve lo que dibuja? |
| --- | --- | --- |
| `claude` en una terminal (también la terminal integrada de un editor y el plugin de JetBrains) | Sí | Sí |
| Pestaña Code de la app de escritorio | Sí | Sí, salvo los elementos marcados como solo de terminal |
| Sesión WSL en la app de escritorio | No (los plugins no están disponibles en WSL) | No |
| Panel de chat de la extensión de VS Code | Sí | No |
| `claude -p` y Agent SDK | Sí | No |
| Remote Control desde claude.ai o la app móvil | Sí, en la sesión de tu máquina | En la terminal de tu máquina |
| Sesión en la nube | Sí, si el plugin llega a esa sesión | No |

Si un mod que viste en un vídeo «no hace nada» en VS Code, puede ser por eso: el código corre, pero el panel no se pinta. Ábrelo desde la terminal integrada con `claude`.

### Comprobar tu versión y si los mods pueden cargarse

En la terminal, `claude --version` debe dar 2.1.287 o más; si no, actualiza (los pasos están en [Cómo instalar Claude Code: guía completa de configuración para todas las plataformas (2026)](https://blog.laozhang.ai/es/posts/claude-code-install)). En la app de escritorio, abre una sesión local en la pestaña Code, escribe `/status` y mira la fila **Claude Code**.

Para saber si tu configuración deja cargar mods, sin instalar ninguno, ejecuta `claude plugin test` en una carpeta vacía. En 2.1.288 la salida fue esta:

```text
claude plugin test: .../empty: no hooks module to load; there is no hooks/hooks.json naming one in "modules"
```

El mensaje significa una cosa distinta según el texto que contenga:

| El mensaje incluye | Qué significa |
| --- | --- |
| `no hooks module to load` | Los mods pueden cargarse; simplemente no hay ninguno en esa carpeta |
| `hooks modules are turned off here` | Algo los bloquea: `disableAllHooks` en tu configuración o una política de tu organización |
| `hooks modules are turned off in this process` | Anthropic ha desactivado los mods instalados de forma remota; ningún ajuste local lo cambia |

Solo el primer mensaje se ha visto en la práctica; los otros dos son los que documenta Anthropic. Ojo: si tu organización usa `allowManagedModsOnly` (solo sus propios mods), esta prueba no lo detecta. Si en algún momento usaste `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` durante el acceso anticipado, bórrala: desde la 2.1.287 se ignora, y ponerla a `0` no apaga los mods.

## Revisar un mod antes de instalarlo con `claude plugin validate`

Antes de instalar un mod, descarga sus archivos y ejecuta `claude plugin validate` sobre la carpeta. El comando hace el mismo análisis estático que Claude Code al cargar el mod, sin ejecutar su código, y te devuelve dos líneas clave: `hooks:` (a qué eventos reacciona) y `calls:` (qué métodos del API de mods usa para salir de su propio código). Claude Code se niega a cargar un mod que use ese API de una forma que validate no pueda leer, así que esa lista es completa en cuanto a capacidades.

Estas son las salidas reales de los tres mods de ejemplo de Anthropic (repositorio `anthropics/claude-code-playground`, carpeta `claude-code/mods`, commit `569c5283` del 1 de octubre de 2026). Los tres pasaron con `✔ Validation passed`:

```text
$ claude plugin validate claude-code/mods/token-weather
  ❯ ./token-weather.mjs hooks: session.start, turn.complete, ui.render{component=AbovePrompt}
  ❯ ./token-weather.mjs calls: $.session.usage (via takeReading), $.ui.invalidate (via takeReading), $.ui.resolve

$ claude plugin validate claude-code/mods/blast-radius
  ❯ ./blast-radius.mjs hooks: tool.call{tool=Bash}, ui.render{component=Pane}, ui.render{component=AbovePrompt}
  ❯ ./blast-radius.mjs calls: $.clock.now, $.process.run, $.session.cwd, $.ui.close, $.ui.invalidate, $.ui.open, $.ui.resolve, $.ui.toast

$ claude plugin validate claude-code/mods/replay-theater
  ❯ ./replay-theater.mjs hooks: session.start, command.run{command=replay}, tool.call, turn.start, turn.complete, ui.render{component=AbovePrompt}, ui.render{component=Pane}, ui.close
  ❯ ./replay-theater.mjs calls: $.clock.sleep (via openReplay), $.command.register, $.fs.exists (via stepsFor), $.fs.read (via stepsFor), $.session.cwd (via stepsFor), $.ui.close (via replayView), $.ui.invalidate, $.ui.open (via openReplay), $.ui.resolve
```

`token-weather` solo lee el uso de la sesión y dibuja encima del prompt. `replay-theater` lee archivos. `blast-radius` lanza programas. Ninguno de los tres llama a `$.http.fetch`, `$.env.get` ni `$.model.complete`.

### Qué entradas de `calls:` y `hooks:` te obligan a abrir el código

Validate te dice qué capacidad usa un mod, pero no qué hace con ella. Con `blast-radius` se ve claro: la línea `calls:` solo dice `$.process.run`, es decir, «lanza programas». Hay que abrir `blast-radius.mjs` para descubrir cuáles: `sleep` en un bucle de espera mientras aguarda a que pulses Proceed o Cancel, y dos scripts `bash -c`, uno que resuelve el destino de un `cd` y otro que cuenta los archivos y bytes que borraría un `rm`. Los comandos que retiene son `rm` con `-r` o `-f`, `git reset --hard`, `git push --force` (también `-f`, `--force-with-lease` y `+ref`) y las migraciones de alembic, `rails db:migrate`, prisma y `manage.py migrate`. Todo eso es razonable para lo que promete el mod, pero solo lo sabes leyendo el código.

![Tres pasos para revisar un mod antes de instalarlo: ejecutar claude plugin validate, comprobar si aparecen $.process, $.fs, $.http.fetch, $.env.get o tool.check, y leer el código, con blast-radius como ejemplo](https://blog.laozhang.ai/posts/es/claude-code-mods/img/revisar-mod-con-validate.webp)

Usa esta tabla como criterio. Si aparece cualquiera de estas entradas, busca cada llamada en el código y comprueba a qué archivo, programa, URL o variable apunta:

| Si ves… | Significa | Qué buscar en el código |
| --- | --- | --- |
| `$.fs.read`, `$.fs.write` | Lee o escribe archivos en cualquier sitio donde puedas tú | Qué rutas; si toca `~/.ssh`, `.env` o configuraciones |
| `$.process.run`, `$.process.spawn` | Lanza programas como tú | Qué programa y con qué argumentos |
| `$.http.fetch` | Hace peticiones de red | A qué dominio y qué datos envía |
| `$.env.get`, `$.settings.read` (línea `env reads:`) | Lee variables y ajustes que pueden contener claves de API | Qué variables nombra `env reads:` y adónde van |
| `$.env.set` (línea `env writes:`) | Cambia variables para Claude Code y todo lo que lance después | Qué valores escribe |
| `$.model.complete` | Llama a un modelo con tu plan o tu clave de API | Cuándo y cuántas veces |
| `$.prompt.submit`, `$.session.send` | Envía un prompt como si fuera tuyo o un mensaje a otra sesión | Qué texto y cuándo |
| `$.mcp.call` | Llama a una herramienta de un servidor MCP conectado | Qué herramienta |
| Hook `tool.check` | Aprueba o deniega llamadas antes de que te pregunten | En qué casos devuelve allow |
| Hooks `tool.call`, `prompt.submit` | Ve y puede cambiar cada llamada a herramienta o cada prompt | Si reescribe algo o solo observa |
| Hook `session.append` | Puede reescribir las filas de la conversación antes de guardarlas | Qué cambia |
| `ui.render{component=AskUserQuestion}` | Puede redibujar el diálogo en el que Claude te pregunta | Que no altere las opciones |

Si `calls:` solo contiene métodos de interfaz (`$.ui.*`), de comandos o de lectura de la sesión y `hooks:` no incluye ninguno de los de la tabla, como en `token-weather`, validate ya te ha dado casi toda la información.

### Dónde encontrar mods y qué revisar en cada uno

Los mods se instalan desde un marketplace de plugins: un repositorio de GitHub, cualquier URL de git, una carpeta local o un `marketplace.json` alojado. Los tres ejemplos de Anthropic se publican tal cual, sin soporte. Para buscar mods de la comunidad, el repositorio [karanb192/awesome-claude-code-mods](https://github.com/karanb192/awesome-claude-code-mods) recopilaba a 6 de octubre de 2026 unos 2.685 mods públicos de GitHub junto con la salida de validate de cada uno. Es un escaneo independiente, no un directorio oficial, y el propio repositorio advierte que pasar la validación no garantiza que el mod se comporte bien. Úsalo para encontrar candidatos y haz la revisión anterior con cada uno.

## Lo que un mod puede aprobar por encima de tus reglas `ask` y `deny`

Un mod que aprueba llamadas a herramientas (hook `tool.check`) puede dar por buena una llamada por la que tu regla `ask` te habría preguntado, o una que bloqueó tu propio hook `PreToolUse` fuera de la configuración administrada. En [modo auto](https://blog.laozhang.ai/es/posts/claude-code-auto-mode), una llamada que el mod aprueba se ejecuta sin pasar por el clasificador. Ese es el punto que conviene tener claro antes de instalar cualquier mod con `tool.check`.

Las reglas `deny` solo quedan protegidas si se carga la protección integrada `sec-default`, y eso ocurre únicamente cuando se cumple una de estas dos condiciones:

- el equipo tiene configuración administrada (managed settings), o
- has iniciado sesión en Claude Code con un plan Team o Enterprise.

Quien usa una clave de API, Amazon Bedrock, Google Cloud Agent Platform o Microsoft Foundry solo la tiene con configuración administrada. Si usas un plan personal Pro o Max en un equipo sin configuración administrada, no se cumple ninguna de las dos condiciones, así que según esa regla la protección no se carga y un mod podría aprobar una llamada que tu `deny` rechaza.

Incluso con la protección activa quedan tres huecos:

- Las reglas `deny` cubren las llamadas a herramientas de Claude, no las del propio mod. Con `Read(.env)` denegado, un mod puede leer `.env` con `$.fs.read` o lanzar un programa que lo haga.
- El sandbox aísla los comandos Bash que ejecuta Claude, pero un proceso que lanza un mod corre fuera de él.
- La política de red de una organización frena `$.http.fetch`, pero no a un programa lanzado con `$.process.run`.

Lo que un mod no puede hacer es cambiar el diálogo de permiso: puede reestilizar casi toda la interfaz, pero lo que te muestra una solicitud de permiso es siempre de Claude Code. Si lo que buscas es saltarte confirmaciones, compara antes con [Claude Code --dangerously-skip-permissions: qué hace, cuándo evitarlo y alternativas](https://blog.laozhang.ai/es/posts/claude-code-dangerously-skip-permissions).

## Probar un mod en una sesión con `--plugin-dir` y luego instalarlo

La forma más prudente de probar un mod es cargarlo para una sola sesión, sin instalarlo. Con los ejemplos de Anthropic:

```bash
git clone --depth 1 https://github.com/anthropics/claude-code-playground
cd claude-code-playground/claude-code/mods
claude plugin validate ./token-weather
claude --plugin-dir ./token-weather
```

El clon completo ocupó 1,7 MB. `--plugin-dir` carga la carpeta solo en esa sesión y recarga el código cuando guardas cambios; puedes repetir la opción para cargar varias. Ten en cuenta que, mientras está cargado, el mod tiene el mismo acceso que si estuviera instalado: no hay un modo de prueba restringido. Para comprobar qué mods cargó una sesión de terminal, la documentación indica ejecutar `/plugin`: bajo las pestañas aparece una línea tenue como `1 mod active · first-mod` (no incluye los mods integrados).

Para conservarlo, añade la carpeta como marketplace e instala desde ella. Estas son las salidas reales en 2.1.288, ejecutadas desde `claude-code-playground/claude-code/mods`:

```text
$ claude plugin marketplace add ./
✔ Successfully added marketplace: claude-code-playground-mods (declared in user settings)

$ claude plugin install token-weather@claude-code-playground-mods --scope user
✔ Successfully installed plugin: token-weather@claude-code-playground-mods (scope: user)

$ claude plugin list
Installed plugins:
  ❯ token-weather@claude-code-playground-mods
    Version: 0.1.0
    Scope: user
    Status: ✔ enabled
```

Algunos detalles que cambian el resultado:

- El formato siempre es `<plugin>@<marketplace>`. Dentro de una sesión, el equivalente es `/plugin install token-weather@claude-code-playground-mods`, que abre primero el panel del plugin para que elijas el alcance.
- El alcance por defecto es `user` (todos tus proyectos). `--scope project` lo activa para quien trabaje en el repositorio y `--scope local`, solo para ti en ese repositorio.
- Si instalas desde la shell con una sesión abierta, ejecuta `/reload-plugins` en esa sesión; si no, se carga la próxima vez que arranques Claude Code.
- Un marketplace añadido desde un clon local apunta a esa carpeta: si la mueves o la borras, el mod deja de cargarse.

## Desactivar o desinstalar un mod: `/plugin`, `--safe-mode` y `disableAllHooks`

Elige según cuántos mods quieras parar y durante cuánto tiempo:

| Quieres parar | Cómo | Qué más se para |
| --- | --- | --- |
| Un mod | `/plugin` → pestaña **Installed** (tecla Tab) → desactivar o desinstalar; o `claude plugin disable` / `claude plugin uninstall` | Nada más |
| Todos los mods instalados, en una sesión | Arrancar con `claude --safe-mode` | Tus otras personalizaciones |
| Todos tus mods, en todas las sesiones | `"disableAllHooks": true` en `~/.claude/settings.json` | Tus hooks de configuración y tu statusline personalizada; lo que gestione tu organización sigue funcionando |

Desde la shell, desactivar y desinstalar dieron esto en 2.1.288:

```text
$ claude plugin disable token-weather@claude-code-playground-mods
✔ Successfully disabled plugin: token-weather (scope: user)

$ claude plugin uninstall token-weather@claude-code-playground-mods
✔ Successfully uninstalled plugin: token-weather (scope: user)
```

`disableAllHooks` para el código del mod, pero el resto del plugin sigue cargándose: sus skills, comandos, agentes y servidores MCP. Si quieres quitarlo todo, desinstala el plugin.

Claude Code también trae mods integrados, que aparecen en la pestaña **Installed** bajo **Built-in**: `cc-plugin-diff` (el panel de `/diff`), `cc-plugin-agents-md` (carga `AGENTS.md`), `cc-plugin-plugin-authoring` (la skill para escribir mods), `cc-plugin-sec-default` (la protección), `cc-plugin-telemetry` y `cc-plugin-you-should-know`, que viene desactivado. Ni `disableAllHooks`, ni `--bare`, ni `--safe-mode` los paran; se desactivan uno a uno en `/plugin`, salvo `sec-default`, que no puedes quitar tú.

## Crear tu primer mod: pídeselo a Claude o escribe tres archivos

Hay dos caminos. El rápido es pedírselo a Claude en una sesión interactiva; el que te enseña cómo funciona es escribirlo tú.

### Pedirle un mod a Claude

Describe lo que quieres con tus palabras, por ejemplo `make a mod that shows the current git branch above the prompt`. Claude usa la skill integrada `plugin-authoring` (también puedes cargarla con `/plugin-authoring`) y escribe el mod en `~/.claude/dev-mods/<ID de sesión>/<nombre-del-mod>/`. Según la documentación, en los modos `default` y `acceptEdits` tendrás que aprobar cada archivo, porque `~/.claude` es una ruta protegida, y con el primero Claude Code te pregunta si activar la recarga en caliente para esa sesión («Enable for this session» o «Not now»).

Ese mod solo se carga en la sesión que lo creó, y la carpeta se borra cuando supera `cleanupPeriodDays`. Si te gusta, cópialo fuera, por ejemplo a `~/mods/git-branch`, y cárgalo con `claude --plugin-dir ~/mods/git-branch`. Un mod escrito por Claude no se carga en `claude -p`, en modo `dontAsk`, en un directorio en el que no hayas aceptado el aviso de confianza ni con los mods desactivados. Este camino no se ha probado aquí porque requiere una sesión interactiva con cuenta.

### Escribirlo tú: `plugin.json`, `hooks.json` y `register.js`

No necesitas Node.js, ni empaquetador, ni compilar: Claude Code carga los `.js` y `.ts` directamente. Este es el mod del tutorial oficial, que cuenta las llamadas a herramientas, añade el recuento al indicador de actividad y crea un comando `/tally`:

```text
first-mod/
├── .claude-plugin/
│   └── plugin.json
└── hooks/
    ├── hooks.json
    └── register.js
```

`first-mod/.claude-plugin/plugin.json` es el manifiesto de cualquier plugin:

```json
{
  "name": "first-mod",
  "version": "0.1.0",
  "description": "Counts Claude's tool calls, shows the count beside the spinner, and adds a /tally command",
  "author": { "name": "Your Name" }
}
```

`first-mod/hooks/hooks.json` apunta al código. La clave `modules` es lo que convierte el plugin en un mod:

```json
{
  "description": "The first-mod hooks module",
  "modules": ["./register.js"]
}
```

`first-mod/hooks/register.js` exporta `register(on)`. Cada hook recibe tres argumentos: `$` (el API de mods), `e` (el evento) y `next` (pasa el evento al siguiente manejador y a Claude Code):

```javascript
// Recuento compartido por los hooks
let calls = 0

export function register(on) {
  // Al empezar la sesión: registra el comando /tally
  on('session.start', async ($, e, next) => {
    await $.command.register({
      name: 'tally',
      description: 'Show how many tool calls Claude has made',
    })
    return next(e)
  })

  // Antes de cada llamada a herramienta: cuenta y pide redibujar
  on('tool.call', async ($, e, next) => {
    calls += 1
    $.ui.invalidate('ui.render')
    return next(e)
  })

  // Solo para /tally: responde sin turno de Claude
  on('command.run', { command: 'tally' }, async () => {
    return { text: 'Claude has made ' + calls + ' tool calls since this mod loaded' }
  })

  // Al dibujar el indicador de actividad: añade el recuento
  on('ui.render', { component: 'Spinner' }, async ($, e, next) => {
    return next({ ...e, props: { ...e.props, suffix: ' · tool calls: ' + calls + '…' } })
  })
}
```

Los cuatro hooks muestran las tres cosas que puede hacer un hook con un evento: `session.start` y `tool.call` observan y dejan seguir (`next(e)`), `command.run` responde por su cuenta sin llamar a `next`, y `ui.render` reescribe el evento antes de pasarlo.

### Comprobarlo con `validate`, `test` y `claude -p`

Desde la carpeta que contiene `first-mod`, validate devolvió esto en 2.1.288:

```text
$ claude plugin validate ./first-mod
  ❯ ./register.js hooks: session.start, tool.call, command.run{command=tally}, ui.render{component=Spinner}
  ❯ ./register.js calls: $.command.register, $.ui.invalidate
✔ Validation passed
```

Si un evento que querías manejar no aparece en la línea `hooks:`, Claude Code tampoco llamará a ese hook. Para tener una prueba automática, guarda el test del tutorial como `first-mod/tests/first-mod.test.ts`:

```typescript
import { expect, test } from 'claude-code/testing'

test('/tally reports the tool calls the mod has seen', async ($, on) => {
  on('tool.call', () => ({ result: 'ok' }))
  await $.tool.call({ tool: 'Bash', command: 'ls' })
  await $.tool.call({ tool: 'Read', file_path: 'README.md' })
  const answer = await $.command.run({ command: 'tally', args: '' })
  expect(answer.text).toBe('Claude has made 2 tool calls since this mod loaded')
})
```

`claude plugin test`, ejecutado dentro de `first-mod`, funciona sin sesión, sin cuenta y sin red:

```text
tests/first-mod.test.ts:
(pass) /tally reports the tool calls the mod has seen [64.48ms]
 1 pass
 0 fail
Ran 1 test across 1 file. [0.34s]
```

Los tiempos varían en cada ejecución. Por último, el comando se puede lanzar sin modo interactivo; respondió aun con la CLI sin sesión iniciada, porque `command.run` no llama al modelo:

```text
$ claude -p "/tally" --plugin-dir ./first-mod
first-mod: Claude has made 0 tool calls since this mod loaded
```

En una sesión interactiva con `claude --plugin-dir ./first-mod`, la documentación describe el indicador como `Thinking · tool calls: 2…` y una recarga en caliente al guardar `register.js`. Cada recarga vuelve a ejecutar `register`, así que `calls` vuelve a 0; para conservar valores entre recargas está `$.state`.

### Errores reales de `validate` y reglas del análisis estático

Dos errores provocados a propósito en 2.1.288. Un nombre de evento mal escrito (`'tool.calls'`):

```text
✘ Found 1 error:
  ❯ modules../register.js: bad-mod: .../hooks/register.js:7: "tool.calls" is not an event; $ is always spelled $.noun.event(...) at the call site, on is always on("<event>", hook), and next.to always next.to(e, "<tier>")
✘ Validation failed
```

Y un `hooks.json` con la clave escrita `"module"` y sin clave `hooks`:

```text
✘ Found 1 error:
  ❯ root: hooks.json must have `hooks` (the hook matchers) or `modules` (hooks modules), or both
✘ Validation failed
```

La página oficial de solución de problemas describe otro caso: si falta `modules` o está mal escrita, validate pasa pero no muestra ninguna línea `hooks:`. En 2.1.288, con un `hooks.json` que no tenía ninguna de las dos claves, el resultado fue el fallo de arriba; ambos síntomas apuntan a revisar la clave `modules`.

Para que el análisis estático encuentre todo:

- Escribe cada llamada completa, `$.espacio.metodo(...)`. No asignes `$` ni sus espacios a variables ni los desestructures: `const ui = $.ui` falla.
- El nombre del evento en `on(...)` debe ser una cadena literal, nada de variables ni bucles.
- Importa solo archivos de dentro del plugin con `import` estático (el único import «desnudo» permitido es `claude-code`); nada de `require` ni `import()` dinámico.
- Un nombre de plugin que parezca de Anthropic, como uno que empiece por `claude-`, no pasa validate.
- Al cargar con `--plugin-dir`, Claude Code escribe las declaraciones de tipos de tu versión en `.claude-plugin/types/`; si no coinciden con la documentación, mandan los tipos.

Hay límites que conviene conocer: cada hook tiene 10 segundos de ejecución propia por evento (50 ms en `prompt.edit`), `$.process.run` corta a los 30 segundos por defecto (máximo 10 minutos) y `$.fs.read`/`$.fs.write` admiten hasta 4 MiB por archivo. Pueden cambiar entre versiones.

Para compartirlo, envía la carpeta o un `.zip`, publícalo en un marketplace propio (un repositorio privado vale) o en uno público, o envíalo al directorio de Anthropic. Indica en el README con qué versión de Claude Code lo probaste, y sigue desarrollando con `--plugin-dir`: la copia instalada se guarda en caché por versión y tus cambios no le llegan hasta que subas la versión y reinstales.

## Si el mod no hace nada: versión, aviso de confianza y `claude --debug`

Empieza siempre por `claude plugin validate` sobre la carpeta del mod: detecta eventos mal escritos, manifiestos rotos y código que Claude Code no puede leer. Si pasa, repasa estas causas:

- **Versión antigua**: menos de 2.1.287 en terminal o de 2.1.286 en la app de escritorio.
- **Directorio nuevo**: ningún mod se carga hasta que aceptas el aviso de confianza de esa carpeta en una sesión interactiva.
- **`--safe-mode` o `--bare`**: no se carga ningún mod instalado.
- **Dónde lo usas**: en VS Code o `claude -p` el mod corre pero no dibuja.
- **Política de la organización**: `allowManagedModsOnly` o `disableAllHooks` en la configuración administrada.

Cuando Claude Code rechaza un mod, escribe una línea con su nombre, del tipo `hooks module first-mod@inline not loaded: disableAllHooks in managed settings`. En una sesión con `--plugin-dir` la ves en la transcripción; en una sesión con un mod instalado desde marketplace, solo en el registro de depuración (arranca con `claude --debug`); en `claude -p`, en stderr. Si la protección integrada actúa, verás textos como `tried to lift a deny rule in your settings`, que indica que tu mod intentó aprobar una llamada que una regla `deny` rechaza y la llamada sigue denegada. El resto de mensajes están en la [guía oficial de solución de problemas de mods](https://code.claude.com/docs/es/plugins/mods/troubleshoot).

## Preguntas frecuentes sobre los mods de Claude Code

### ¿Los mods de Claude Code son gratis?

La documentación no menciona ningún precio por instalar un mod; es código dentro de un plugin que instalas desde un marketplace. Lo que sí puede consumir uso de tu plan o tu clave de API es un mod que llame a `$.model.complete`, y lo verás en la línea `calls:` de `claude plugin validate`.

### ¿Los mods sustituyen a los hooks de settings.json?

No. Los hooks de configuración siguen funcionando junto a los mods y no están obsoletos. Si ya tienes un script que bloquea o registra eventos, no hace falta convertirlo en mod; el mod tiene sentido cuando quieres dibujar, reescribir un evento por dentro o añadir un comando con código propio.

### ¿Funcionan los mods de Claude Code en VS Code?

En el panel de chat de la extensión de VS Code los hooks del mod se ejecutan, pero no se dibuja nada. Para ver paneles y franjas, usa `claude` en la terminal integrada de VS Code o la pestaña Code de la app de escritorio (excepto en sesiones WSL, donde los plugins no están disponibles).

### ¿Puede un mod leer mi archivo .env aunque lo tenga denegado?

Sí. Las reglas `deny` como `Read(.env)` limitan las llamadas a herramientas de Claude, no las llamadas `$.fs.read` del propio mod ni los programas que lance con `$.process.run`. Por eso conviene abrir el código de cualquier mod cuya línea `calls:` incluya `$.fs.*`, `$.process.*` o `$.env.get`.

### ¿Cómo quito un mod que me da problemas?

Para uno concreto, `/plugin` → **Installed** → desactivar o desinstalar, o `claude plugin uninstall <plugin>@<marketplace>` desde la shell. Para comprobar si el problema viene de algún mod, arranca una sesión con `claude --safe-mode`.

## Fuentes

Páginas externas que cita esta guía, en el orden en que aparecen. Última actualización: 2026-10-06.

- [«Customize Claude Code with mods in TypeScript»](https://claude.com/blog/claude-code-mods) (claude.com)
- [documentación oficial de mods en español](https://code.claude.com/docs/es/plugins/mods/overview) (code.claude.com)
- [karanb192/awesome-claude-code-mods](https://github.com/karanb192/awesome-claude-code-mods) (github.com)
- [guía oficial de solución de problemas de mods](https://code.claude.com/docs/es/plugins/mods/troubleshoot) (code.claude.com)
