> ## 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.

# Outils

> Comment une application déclare des outils et comment leurs gestionnaires retournent les résultats.

# Outils

Un **outil** est une action nommée que l'IA peut appeler, avec des entrées typées — par exemple `send_email(to, subject, body)`. Les outils sont déclarés dans le fichier `tools.ts` d'une compétence et, pour les outils que votre application propose, soutenus par une route HTTP dans l'un de vos composants. Cette page est la source de vérité pour le contrat d'outil ; [Compétences](/apps/skills) couvre l'emplacement de `tools.ts`.

## Déclarer un outil

Chaque entrée dans `tools.ts` a un `name` sûr pour le fournisseur, un `displayName` convivial, une `description`, un schéma Zod `input`, et une `target` qui indique ce que Kazzle invoque.

```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[];
```

## Cibles

`target` est l'adresse réutilisable pour un outil ou un bouton d'action requise. Un appel d'outil IA direct et un clic de bouton peuvent utiliser le même objet cible, donc les deux exécutent le même code.

| Cible    | Champs                                                              | S'exécute où                                                                                                        |
| -------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| `app`    | `component`, `path`, `method`, `query` optionnel, `headers`, `body` | L'un de vos propres composants. Kazzle résout l'URL du composant et ajoute un jeton d'identité d'application signé. |
| `url`    | `url`, `method`, `query` optionnel, `headers`, `body`               | Un point de terminaison externe. L'origine de l'URL et la méthode sont des valeurs littérales de l'auteur.          |
| `kazzle` | `name`                                                              | Un gestionnaire Kazzle intégré côté client.                                                                         |

Les cibles HTTP utilisent les mêmes champs de requête :

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

Il n'y a pas de valeurs par défaut implicites pour les requêtes. Si votre gestionnaire doit recevoir l'entrée d'outil brute en JSON, écrivez `body: '${input}'`.

## Références

Kazzle résout les références dans les champs `query`, `headers`, `body` et `url` de la cible HTTP avant la distribution :

* `${input}` — l'entrée d'outil entière, ou l'entrée de bouton entière.
* `${input.path}` — une valeur imbriquée de l'entrée.
* `${env.NAME}` — une variable d'environnement nommée de la `env.collection` déclarée du composant d'application propriétaire + `env.environment`.

La résolution est en une seule passe. Si une référence est manquante ou inconnue, l'outil échoue au lieu de substituer une valeur vide. Utilisez `$${input.name}` quand vous avez besoin du texte littéral `${input.name}`.

## Requête du gestionnaire

Pour une cible `app`, ajoutez la route correspondante dans le composant cible. Avec `body: '${input}'`, Kazzle envoie l'entrée typée comme corps JSON :

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

Les cibles `app` reçoivent également :

| En-tête               | Valeur                                                               |
| --------------------- | -------------------------------------------------------------------- |
| `Authorization`       | `Bearer <identity token>` identifiant l'utilisateur + l'installation |
| `Kazzle-Tool-Context` | `{"source":"thread","threadId":"..."}` ou `{"source":"api"}`         |

Les cibles d'application ne peuvent pas définir `Authorization` via `headers` ; Kazzle possède cet en-tête. Lisez le contexte avec `toolContext(req)` depuis `@kazzle/app/tools` quand le gestionnaire doit différencier thread vs Tools API.

Un outil déclaré sans route correspondante ne fait rien d'utile — ajoutez les deux ensemble. `tools.json` n'est pas supporté ; le compilateur d'application échoue s'il en trouve un.

## Réponse du gestionnaire

Retournez du texte brut, ou du JSON avec jusqu'à trois canaux :

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

* **`content`** — le résultat brut que l'IA lit (fourni au modèle). Obligatoire.
* **`markdown`** — optionnel. Un court résumé en texte enrichi rendu dans la carte d'outil (react-markdown ; le HTML brut est échappé). Bon pour une phrase, une petite liste, un lien en ligne. Un chemin relatif nu s'affiche comme du texte préformaté mort — les liens doivent être **absolus**.
* **`embedUrl`** — optionnel. Une URL **absolue** que votre application propose ; la carte la rend dans une iframe en bac à sable. C'est ainsi que vous montrez une véritable interface pleine largeur — un écran de connexion, un tableau de bord, un graphique. Votre application héberge et possède la page, elle peut donc être entièrement interactive avec votre propre backend, cookies et OAuth. Rien n'est écrit sur le lecteur.

`embedUrl` prime sur `markdown` quand les deux sont définis. Gardez toujours un `content` significatif — c'est ce que l'IA lit.

```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}` });
}
```

## Action requise

Un outil d'application peut mettre en pause le thread quand il a besoin d'une action utilisateur (ou appareil). Retournez `type: 'action_required'` avec un titre de carte et des boutons. Valide uniquement quand `toolContext(req).source === 'thread'` — sur l'API Tools (`source: 'api'`), retournez une erreur de domaine normale à la place.

```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 enregistre la carte sur l'appel d'outil et reprend via `/chat/resume` après qu'un bouton soit appuyé. Les clients correspondants voient les boutons de l'auteur ; les clients non correspondants voient toujours Ignorer/Annuler plus « Continuer sur {appareil} ». Un bouton sans `target` soumet son `input` comme résultat d'outil. Un bouton avec une cible exécute d'abord cette cible, puis utilise le résultat de la cible comme résultat d'outil.

### Interface riche — intégrer une page hébergée par l'application

Quand le résultat d'un outil est visuel ou interactif (un écran de connexion, un graphique, un tableau de bord récapitulatif), proposez la page depuis l'un de vos composants et retournez son URL **absolue** comme `embedUrl`. Construisez l'URL à partir de l'URL du composant injectée — jamais un chemin relatif.

```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 page s'exécute dans une iframe cross-origin (sa propre origine, cookies et scripts). Pour dimensionner la carte, publiez sa hauteur au parent — la carte écoute cela et se redimensionne (plafonnée à 70 % de la fenêtre d'affichage) :

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

Préférez `embedUrl` à l'émission de chaînes HTML : votre application propose déjà des pages, l'interface reste interactive et versionnée avec votre code, et aucun artefact par appel n'est écrit nulle part.
