> ## Documentation Index
> Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Usar la API de mods

> Llamar a la API de mods desde un mod de Claude Code para agregar comandos y herramientas, llamar a un modelo, ejecutar trabajo en un temporizador, enviar mensajes a otras sesiones y acceder a archivos y la red.

La API de mods es el conjunto de métodos que un mod llama para actuar: agregar comandos y herramientas, llamar a un modelo, ejecutar trabajo entre eventos y acceder al sistema de archivos, procesos y la red. Cada hook la recibe como su primer argumento, `$`, con los métodos agrupados en espacios de nombres como `$.ui` y `$.fs`. [Los eventos](/docs/es/plugins/mods/events) deciden cuándo se ejecuta un hook, y la API de mods es lo que el hook llama una vez que lo hace.

Construya su [primer mod](/docs/es/plugins/mods/create) antes de comenzar aquí. Para cada método, consulte [métodos de la API de mods](/docs/es/plugins/mods/reference#mods-api-methods) o lea [los tipos para su compilación](/docs/es/plugins/mods/create#get-the-types-for-your-build).

<h2 id="add-a-command-or-a-tool">
  Agregar un comando o una herramienta
</h2>

Un mod puede agregar un comando para que el usuario ejecute y una herramienta para que Claude llame. Registre ambos en un hook [`session.start`](/docs/es/plugins/mods/reference#session). Claude Code espera ese hook antes del primer prompt, por lo que lo que registre está disponible desde el primer turno.

<h3 id="add-a-command">
  Agregar un comando
</h3>

Un comando es para el usuario. Regístrelo y luego maneje [`command.run`](/docs/es/plugins/mods/reference#commands-and-configuration) para su nombre. Este ejemplo agrega un comando `/standup` que toma un número opcional de días:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  // Add /standup to the command list, with the description the user sees there
  await $.command.register({ name: 'standup', description: 'Summarize what changed today', argumentHint: '[days]' })
  return next(e)
})

// The matcher limits the hook to /standup, so other commands don't reach it
on('command.run', { command: 'standup' }, async ($, e) => {
  // e.args is the text typed after the command name, or an empty string
  return { text: 'Summary for the last ' + (e.args || '1') + ' day(s): ...' }
})
```

Después de que la sesión comienza, `/standup` aparece con su descripción en la lista que ve cuando escribe `/`. El `argumentHint` se muestra en el prompt después de que escribe el comando y un espacio, como en `/standup [days]`. Cuando ejecuta `/standup 3`, el segundo hook devuelve `Summary for the last 3 day(s): ...`, y la transcripción muestra ese texto después del nombre del plugin. El hook nunca llama a `next`, porque el comando no tiene comportamiento que no sea el suyo.

El `text` que devuelve se imprime en la transcripción y Claude lo lee. Para no imprimir nada, como un comando que solo abre un [pane](/docs/es/plugins/mods/interface#pick-where-to-draw), devuelva `{}`. Para permitir que el comando se ejecute mientras Claude está trabajando, agregue `immediate: true` al registro.

Elija un nombre que ningún comando integrado use. Escriba `/` en una sesión para verlos. `$.command.register` lanza una excepción para un nombre tomado, con un mensaje como `"/focus" refused: it is the built-in /focus"`. Un hook que lanza una excepción se omite, por lo que el resto de su hook `session.start` tampoco se ejecuta. Registre comandos al final en ese hook, o envuelva la llamada en `try` y `catch`.

<h3 id="add-a-tool">
  Agregar una herramienta
</h3>

Una herramienta es para Claude. Regístrela con un nombre, una descripción que Claude lee y un JSON Schema para su entrada. Claude la ve bajo un nombre más largo hecho de `mcp__`, el nombre de su plugin, dos guiones bajos y el nombre que registró. Maneja sus llamadas en un hook [`tool.call`](/docs/es/plugins/mods/events#guard-or-change-a-tool-call) filtrado a ese nombre completo. Este ejemplo, de un plugin llamado `my-mod`, registra `ticket`, por lo que el nombre completo es `mcp__my-mod__ticket`. Le da a Claude una herramienta que busca un ticket en un rastreador de problemas:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  await $.tool.register({
    name: 'ticket',
    // Claude decides when to call the tool from this description
    description: 'Look up a ticket by its id and return its title and status',
    // The arguments Claude has to send: one required string named id
    inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
  })
  return next(e)
})

// The full tool name is mcp__, the plugin's name, and the registered name
on('tool.call', { tool: 'mcp__my-mod__ticket' }, async ($, e) => {
  // The tool's arguments are fields of e, so the id is e.id
  const response = await $.http.fetch('https://tickets.example.com/api/' + encodeURIComponent(e.id))
  // Return a result either way, so Claude learns when the lookup failed
  return { result: response.ok ? response.text : 'Lookup failed with status ' + response.status }
})
```

Cuando pregunta sobre un ticket, Claude puede llamar a `mcp__my-mod__ticket` con su id. El segundo hook obtiene el ticket y devuelve el cuerpo de la respuesta, que Claude lee como el resultado de la herramienta. Cuando el servidor responde con un estado de error, Claude lee `Lookup failed with status` y el número.

<h2 id="call-a-model">
  Llamar a un modelo
</h2>

Un mod puede hacer una pregunta a un modelo por su cuenta, fuera de la conversación, para un trabajo pequeño como ordenar o resumir un fragmento de texto. `$.model.complete` envía un prompt a un modelo con las credenciales de su sesión y se resuelve en la respuesta. No tiene historial de conversación.

Este hook responde a un comando `/triage`, [registrado como un comando](#add-a-command), pidiendo a un modelo pequeño que etiquete el texto escrito después de él:

```javascript theme={null}
on('command.run', { command: 'triage' }, async ($, e) => {
  const r = await $.model.complete({
    model: 'haiku',
    // The system prompt sets the job, and the prompt carries the text to label
    system: 'Reply with one word: bug, feature, or question.',
    prompt: e.args,
    // One word needs few tokens, and the call gives up after 15 seconds
    maxTokens: 20,
    timeoutMs: 15000,
  })
  // r.text exists only when the model answered, so check r.isAnswered first
  const label = r.isAnswered ? r.text.trim() : 'unknown'
  return { text: 'Label: ' + label }
})
```

Cuando ejecuta `/triage the export button does nothing`, el mod envía ese texto al modelo e imprime su respuesta, como `Label: bug`. La conversación de Claude no es parte de la solicitud. Cuando el modelo no responde, la etiqueta es `unknown`.

Una falla de la API de Claude no rechaza la llamada, por lo que verifique `r.isAnswered` y lea `r.reason` cuando es `false`. La llamada solo rechaza una solicitud que Claude Code no enviará, como un modelo que su organización bloquea. [Los tipos para su compilación](/docs/es/plugins/mods/create#get-the-types-for-your-build) enumeran las otras opciones, como `effort`, y los [límites](/docs/es/plugins/mods/reference#limits) dan el valor predeterminado de `maxTokens`.

`$.model.fork({ prompt })` hace una pregunta sobre la conversación actual en su lugar, con el mismo modelo y prompt del sistema, por lo que la API de Claude sirve la mayoría de ella desde el caché de prompts.

Estas llamadas usan el plan o la clave API del usuario.

<h2 id="run-work-in-the-background">
  Ejecutar trabajo en segundo plano
</h2>

El trabajo que sobrevive a un evento, como verificar algo una vez por minuto, se ejecuta en un temporizador que inicia desde `session.start`. Un hook en sí se ejecuta para un evento y tiene un límite de tiempo de 10 segundos de su propio tiempo de ejecución. El tiempo dedicado a esperar en `next` o en una llamada de la API de mods no cuenta, excepto un `$.clock.sleep`. `$.clock.every` y `$.clock.after` toman el lugar de `setInterval` y `setTimeout`, con el retraso en milisegundos primero: `$.clock.after(5000, fn)` llama a `fn` una vez, cinco segundos a partir de ahora. Cada uno devuelve un temporizador con un método `cancel()`, y `await $.clock.now()` da la hora en milisegundos.

Este hook busca las comprobaciones de una solicitud de extracción una vez por minuto y muestra el resultado bajo el prompt. `summarize` es una función propia que convierte la salida JSON del comando en pocas palabras:

```javascript theme={null}
on('session.start', async ($, e, next) => {
  // Call the function every 60,000 milliseconds, starting one minute from now
  $.clock.every(60_000, async () => {
    const status = await $.process.run(['gh', 'pr', 'checks', '--json', 'state'])
    // Replace the line under the prompt with the latest summary
    $.ui.status('checks: ' + summarize(status.stdout))
  })
  // Return without waiting for the timer, so the session starts right away
  return next(e)
})
```

La sesión comienza como de costumbre. Un minuto después, aparece una línea bajo el prompt con un `⚠`, el nombre del mod y luego `checks:` y su resumen. Se reemplaza una vez por minuto después de eso. La devolución de llamada del temporizador se ejecuta fuera de cualquier evento, por lo que sigue ejecutándose entre turnos y no inicia uno. Si la devolución de llamada lanza una excepción, el error va al [registro de depuración](/docs/es/plugins/mods/troubleshoot#read-the-debug-log) y el temporizador se ejecuta nuevamente en el siguiente intervalo.

<h3 id="show-something-without-starting-a-turn">
  Mostrar algo sin iniciar un turno
</h3>

Un trabajo en segundo plano puede mostrar al usuario algo sin iniciar un turno. Cada una de estas llamadas pone texto en un lugar diferente:

| Llamada | Lo que el usuario ve |
| :- | :- |
| `$.ui.status(text)` | Una línea bajo el prompt que permanece hasta que la cambie. Comienza con `⚠` y el nombre del mod, como en `⚠ my-mod: checks: 3 passing`. |
| `$.ui.toast(text)` | Una pequeña caja en la esquina superior derecha, con el nombre del mod encima del texto, que desaparece después de unos segundos |
| `$.ui.log(text)` | Una línea tenue en la transcripción que Claude no lee. Comienza con `●` y el nombre del mod, como en `● my-mod: build finished`. |

<h3 id="start-a-turn-from-a-background-job">
  Iniciar un turno desde un trabajo en segundo plano
</h3>

Cuando un trabajo en segundo plano encuentra algo que necesita la atención de Claude, puede iniciar un turno enviando un prompt con `$.prompt.submit({ text })`. Claude lee el texto después de una oración que nombra su mod como el remitente. Para enviarlo como las propias palabras del usuario, sin esa oración, agregue `asUser: true`. La llamada espera hasta que la sesión esté inactiva y luego inicia un nuevo turno. Se resuelve cuando ese turno comienza, por lo que no lo `await` en un controlador que se ejecuta mientras Claude está trabajando.

<h3 id="stop-background-work">
  Detener el trabajo en segundo plano
</h3>

El trabajo en segundo plano se detiene de dos formas. Los temporizadores se detienen cuando el módulo se recarga. Para trabajo de larga duración dentro de un hook, [`next.signal`](/docs/es/plugins/mods/reference#the-hook-function) es un `AbortSignal` que se cancela cuando se abandona el evento que maneja su hook, por ejemplo cuando el usuario interrumpe, por lo que páselo a cualquier cosa de larga duración.

<h2 id="send-and-receive-messages-between-sessions">
  Enviar y recibir mensajes entre sesiones
</h2>

Un mod puede enviar un mensaje de texto sin formato a otra de sus sesiones o a uno de los subagentes de esta sesión, y observar los mensajes que llegan y se van. `$.session.send({ to, text })` envía uno, la misma entrega que hace la herramienta SendMessage. `to` es `{ sessionId }` para una sesión, `{ agentId }` para un subagente de `$.agent.list()`, o la dirección de cadena de la que provino un mensaje recibido. La llamada se resuelve una vez que el mensaje se pone en cola, con `{ isDelivered: true }`. Cuando nada fue entregado se resuelve con `{ isDelivered: false, reason }`, y `reason` dice por qué.

Este hook responde a un comando `/ping`, [registrado como un comando](#add-a-command), pidiendo a la sesión cuyo id escribe después de él un estado:

```javascript theme={null}
on('command.run', { command: 'ping' }, async ($, e) => {
  // e.args is the session id typed after /ping
  const sent = await $.session.send({ to: { sessionId: e.args }, text: 'Status? One line.' })
  // The call resolves either way, so check isDelivered to learn what happened
  if (!sent.isDelivered) $.ui.toast('Not delivered: ' + sent.reason)
  // An empty result prints nothing in this session's transcript
  return {}
})
```

Cuando el mensaje se pone en cola, nada aparece en su sesión, y Claude de la otra sesión lee `Status? One line.` Cuando nada fue entregado, una pequeña caja en la esquina superior derecha da la razón y desaparece después de unos segundos.

Dos eventos permiten que un mod observe los mensajes. Devuelva `next(e)` de ambos para pasar cada mensaje sin cambios:

| Evento | Se dispara cuando | Campos útiles |
| :- | :- | :- |
| `session.receive` | Un mensaje llega para esta sesión, antes de que Claude lo lea | `e.text`, y `e.origin.kind`, como `peer` o `peer-send-message` para otra sesión o agente, `task-notification`, o `scheduled-trigger`. Devuelva `{ consumed: reason }` para evitar que llegue a Claude. |
| `session.send` | Un mensaje está a punto de salir, desde la herramienta SendMessage o un mod | `e.to`, `e.text`, y `e.origin.kind`, que es `model` o `plugin` |

Una sesión configurada para [rechazar mensajes entrantes](/docs/es/cross-session-messaging#control-inbound-messages) rechaza un mensaje antes de que se dispare `session.receive`, por lo que un hook nunca lo ve. Un mensaje que se retiene para su aprobación llega al hook primero, por lo que un mod puede leer un mensaje que aún no ha aprobado. El `next(e)` del hook rechaza cuando el mensaje no se entrega.

El nombre del remitente en un mensaje recibido es lo que escribió el remitente, por lo que no base una decisión en él.

<h2 id="reach-files-processes-and-the-network">
  Acceder a archivos, procesos y la red
</h2>

Un mod accede al sistema de archivos, procesos y la red a través de la API de mods, con los mismos permisos que el usuario que ejecuta Claude Code. El módulo de hooks en sí no tiene APIs de Node.js, sin globales de temporizador como `setTimeout`, y sin acceso a la red o archivos propios. Las APIs estándar de JavaScript y web como `URL`, `TextEncoder`, `AbortController` y `crypto.subtle` están disponibles. Cada espacio de nombres a continuación cubre un tipo de acceso:

| Espacio de nombres | Lo que hace |
| :- | :- |
| `$.fs` | `read(path)`, `write(path, text)`, `exists(path)`, `stat(path)` y `list(path)` funcionan en archivos y directorios |
| `$.process` | `run(['git', 'status'])` inicia un comando y se resuelve cuando sale. `spawn` transmite la salida de un comando de larga duración. |
| `$.http` | `fetch(url, init)` sobre `http` o `https`. Se resuelve a `{ status, ok, headers, text }` una vez que se lee el cuerpo. |
| `$.store` | Un almacén de clave-valor JSON propio de su plugin, mantenido entre sesiones |
| `$.env` | `get` y `set` variables de entorno. Escriba el nombre como una cadena literal. |
| `$.settings` | `read` lo que los archivos de configuración y la política administrada contienen |
| `$.session` | `messages()` devuelve la transcripción como una lista de `{ role, text, toolUses }`. También el directorio de trabajo, modelo y más. [`usage()`](/docs/es/plugins/mods/reference#mods-api-methods) devuelve el uso de la ventana de contexto y los límites del plan. |
| `$.mcp` | `call` una herramienta en un servidor MCP conectado |

Los archivos y procesos tienen algunas reglas propias:

* **Rutas**: una ruta relativa está bajo el directorio de trabajo de la sesión
* **`$.fs.list`**: devuelve las entradas de un directorio como `{ name, kind, size, isLink }` y no desciende a subdirectorios
* **`$.process.run`**: toma una lista de argumentos y no usa shell. Se resuelve a `{ exitCode, stdout, stderr }` sea cual sea el código de salida. Rechaza si el programa no puede iniciarse o sigue ejecutándose en el tiempo de espera, que es de 30 segundos por defecto, por lo que envuélvalo en `try` y `catch`.

Cada una de estas llamadas es en sí misma un evento, nombrado para su espacio de nombres y método sin el `$.`, como `fs.read` para `$.fs.read`. Un mod [anterior en la cadena](/docs/es/plugins/mods/events#the-order-mods-run-in) puede observar, reescribir o rechazar su llamada, que es cómo una organización restringe lo que los mods alcanzan.

<h2 id="next-steps">
  Próximos pasos
</h2>

* [Reaccionar a eventos](/docs/es/plugins/mods/events): hook de llamadas de herramientas, prompts y turnos
* [Dibujar en la interfaz](/docs/es/plugins/mods/interface): mostrar lo que su mod recopila en un pane o encima del prompt
* [Probar un mod](/docs/es/plugins/mods/test): stub cualquiera de estas llamadas en una prueba
* [Referencia de mods](/docs/es/plugins/mods/reference): cada evento, cada método de la API de mods y los límites
