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

# Strumenti

> Come un'app dichiara gli strumenti e come i loro handler restituiscono i risultati.

# Strumenti

Uno **strumento** è un'azione denominata che l'AI può invocare, con input tipizzati — ad es. `send_email(to, subject, body)`. Gli strumenti sono dichiarati nel file `tools.ts` di una skill e, per gli strumenti che la tua app fornisce, supportati da una rotta HTTP in uno dei tuoi componenti. Questa pagina è la fonte di verità per il contratto dello strumento; [Skills](/apps/skills) spiega dove si trova `tools.ts`.

## Dichiarare uno strumento

Ogni voce in `tools.ts` ha un `name` sicuro per il provider, un `displayName` descrittivo, una `description`, uno schema Zod `input` e un `target` che specifica cosa Kazzle invoca.

```ts theme={"theme":"material-theme-darker"}
import { z } from 'zod';
import type { KazzleTool } from '@kazzle/app/tools';

export const SaveBookmarkInput = z.object({
  url: z.string().url().describe('URL to save.'),
  title: z.string().optional().describe('Optional human-readable title.'),
}).strict();

const saveBookmarkTarget = {
  type: 'app',
  component: 'api',
  path: '/tools/save-bookmark',
  method: 'POST',
  body: '${input}',
} as const;

export const tools = [
  {
    name: 'save_bookmark',
    displayName: 'Save bookmark',
    description: 'Save a bookmark URL and return a confirmation.',
    input: SaveBookmarkInput,
    target: saveBookmarkTarget,
  },
] as const satisfies readonly KazzleTool[];
```

## Target

`target` è l'indirizzo riutilizzabile per uno strumento o un pulsante di azione richiesta. Una chiamata diretta dello strumento AI e un clic del pulsante possono usare lo stesso oggetto target, quindi entrambi eseguono lo stesso codice.

| Target   | Campi                                                               | Eseguito dove                                                                                                     |
| -------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `app`    | `component`, `path`, `method`, `query` opzionale, `headers`, `body` | In uno dei tuoi componenti. Kazzle risolve l'URL del componente e aggiunge un token di identità dell'app firmato. |
| `url`    | `url`, `method`, `query` opzionale, `headers`, `body`               | Un endpoint esterno. L'origine dell'URL e il metodo sono valori letterali dell'autore.                            |
| `kazzle` | `name`                                                              | Un handler Kazzle integrato lato client.                                                                          |

I target HTTP usano gli stessi campi di richiesta:

```ts theme={"theme":"material-theme-darker"}
type RequestSpec = {
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
  query?: Record<string, string>;
  headers?: Record<string, string>;
  body?: unknown;
};
```

Non ci sono impostazioni predefinite implicite per la richiesta. Se il tuo handler deve ricevere l'input dello strumento grezzo come JSON, scrivi `body: '${input}'`.

## Riferimenti

Kazzle risolve i riferimenti nei campi `query`, `headers`, `body` e `url` del target HTTP prima dell'invio:

* `${input}` — l'intero input dello strumento, o l'intero input del pulsante.
* `${input.path}` — un valore annidato dall'input.
* `${env.NAME}` — una variabile d'ambiente denominata dalla `env.collection` dichiarata del componente app proprietario + `env.environment`.

La risoluzione è a passaggio singolo. Se un riferimento è mancante o sconosciuto, lo strumento fallisce invece di sostituire un valore vuoto. Usa `$${input.name}` quando hai bisogno del testo letterale `${input.name}`.

## Richiesta del handler

Per un target `app`, aggiungi la rotta corrispondente nel componente target. Con `body: '${input}'`, Kazzle invia l'input tipizzato come corpo JSON:

```json theme={"theme":"material-theme-darker"}
{ "...": "the typed input" }
```

I target `app` ricevono anche:

| Header                | Valore                                                            |
| --------------------- | ----------------------------------------------------------------- |
| `Authorization`       | `Bearer <identity token>` che identifica l'utente + installazione |
| `Kazzle-Tool-Context` | `{"source":"thread","threadId":"..."}` o `{"source":"api"}`       |

I target app non possono impostare `Authorization` tramite `headers`; Kazzle possiede quell'header. Leggi il contesto con `toolContext(req)` da `@kazzle/app/tools` quando l'handler deve distinguere tra thread e Tools API.

Uno strumento dichiarato senza una rotta corrispondente non è utile — aggiungili entrambi. `tools.json` non è supportato; il compilatore dell'app fallisce se ne trova uno.

## Risposta del handler

Restituisci testo semplice, o JSON con fino a tre canali:

```json theme={"theme":"material-theme-darker"}
{ "content": "...", "markdown": "...", "embedUrl": "https://..." }
```

* **`content`** — il risultato semplice che l'AI legge (fornito al modello). Obbligatorio.
* **`markdown`** — opzionale. Un breve riepilogo in testo ricco renderizzato nella scheda dello strumento (react-markdown; l'HTML grezzo è sfuggito). Adatto per una frase, un piccolo elenco, un link inline. Un percorso relativo nudo viene renderizzato come testo preformattato morto — i link devono essere **assoluti**.
* **`embedUrl`** — opzionale. Un URL **assoluto** che la tua app fornisce; la scheda lo renderizza in un iframe sandbox. Questo è il modo per mostrare un'interfaccia utente reale e a larghezza intera — una schermata di connessione, una dashboard, un grafico. La tua app ospita e possiede la pagina, quindi può essere completamente interattiva con il tuo backend, i cookie e OAuth. Nulla viene scritto nel drive.

`embedUrl` ha la precedenza su `markdown` quando entrambi sono impostati. Mantieni sempre un `content` significativo — è quello che l'AI legge.

```ts theme={"theme":"material-theme-darker"}
if (req.method === 'POST' && new URL(req.url).pathname === '/tools/save-bookmark') {
  const input = await req.json();
  // ...do the work...
  return Response.json({ content: `Saved ${input.title}` });
}
```

## Azione richiesta

Uno strumento app può mettere in pausa il thread quando ha bisogno di un'azione dell'utente (o del dispositivo). Restituisci `type: 'action_required'` con un titolo della scheda e pulsanti. Valido solo quando `toolContext(req).source === 'thread'` — tramite Tools API (`source: 'api'`), restituisci invece un errore di dominio normale.

```ts theme={"theme":"material-theme-darker"}
import { toolContext, isThreadToolInvocation } from '@kazzle/app/tools';

const context = toolContext(req);
if (!isThreadToolInvocation(context)) {
  return Response.json(
    { code: 'connect_required', error: 'Gmail must be connected before this tool can run.' },
    { status: 409 },
  );
}

return Response.json({
  type: 'action_required',
  title: 'Connect Gmail',
  description: 'Connect Gmail before Kazzle can search messages.',
  // Optional: only this device may run the gated buttons
  // assignee: { computerId: '<from computer { list: {} }>' },
  elements: [
    {
      type: 'button',
      id: 'connect',
      label: 'Connect Gmail',
      variant: 'primary',
      input: { scope: 'gmail.readonly' },
      target: {
        type: 'app',
        component: 'api',
        path: '/oauth/start',
        method: 'POST',
        body: '${input}',
      },
    },
  ],
});
```

Kazzle salva la scheda sulla chiamata dello strumento e riprende tramite `/chat/resume` dopo che un pulsante è stato premuto. I client corrispondenti vedono i pulsanti dell'autore; i client non corrispondenti vedono ancora Skip/Cancel più "Continue on {device}". Un pulsante senza `target` invia il suo `input` come risultato dello strumento. Un pulsante con un target esegue prima quel target, quindi usa il risultato del target come risultato dello strumento.

### Interfaccia utente ricca — incorpora una pagina ospitata dall'app

Quando il risultato di uno strumento è visivo o interattivo (una schermata di connessione, un grafico, una dashboard di riepilogo), fornisci la pagina da uno dei tuoi componenti e restituisci il suo URL **assoluto** come `embedUrl`. Costruisci l'URL dall'URL del componente iniettato — mai un percorso relativo.

```ts theme={"theme":"material-theme-darker"}
// A tool that needs the user to connect their account:
return Response.json({
  content: "Gmail isn't connected yet — the user needs to connect it.",
  embedUrl: `${process.env.KAZZLE_APP_COMPONENT_URL}/connect?token=${identity}`,
});
```

La pagina viene eseguita in un iframe cross-origin (la sua origine, i cookie e gli script). Per dimensionare la scheda, invia la sua altezza al genitore — la scheda ascolta questo e si ridimensiona (limitato al 70% del viewport):

```ts theme={"theme":"material-theme-darker"}
// inside the embedded page
parent.postMessage({ __kazzle_height: document.body.scrollHeight }, '*');
```

Preferisci `embedUrl` rispetto all'emissione di stringhe HTML: la tua app fornisce già pagine, l'interfaccia utente rimane interattiva e versionata con il tuo codice, e nessun artefatto per chiamata viene scritto da nessuna parte.
