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

# Tools

> Wie eine App Tools deklariert und wie ihre Handler Ergebnisse zurückgeben.

# Tools

Ein **Tool** ist eine benannte Aktion, die die KI aufrufen kann, mit typisierten Eingaben — z. B. `send_email(to, subject, body)`. Tools werden in der `tools.ts` einer Skill deklariert und, für Tools, die deine App bereitstellt, durch eine HTTP-Route in einer deiner Komponenten unterstützt. Diese Seite ist die Quelle der Wahrheit für den Tool-Vertrag; [Skills](/apps/skills) behandelt, wo `tools.ts` lebt.

## Ein Tool deklarieren

Jeder Eintrag in `tools.ts` hat einen Provider-sicheren `name`, einen benutzerfreundlichen `displayName`, eine `description`, ein Zod-`input`-Schema und ein `target`, das angibt, was Kazzle aufruft.

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

## Targets

`target` ist die wiederverwendbare Adresse für ein Tool oder eine erforderliche Aktionsschaltfläche. Ein direkter KI-Tool-Aufruf und ein Schaltflächenklick können dasselbe Target-Objekt verwenden, sodass beide denselben Code ausführen.

| Target   | Felder                                                             | Läuft wo                                                                                                                   |
| -------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| `app`    | `component`, `path`, `method`, optional `query`, `headers`, `body` | In einer deiner eigenen Komponenten. Kazzle löst die Komponenten-URL auf und fügt ein signiertes App-Identity-Token hinzu. |
| `url`    | `url`, `method`, optional `query`, `headers`, `body`               | Ein externer Endpunkt. Die URL-Origin und Methode sind Literalwerte des Autors.                                            |
| `kazzle` | `name`                                                             | Ein integrierter clientseitiger Kazzle-Handler.                                                                            |

HTTP-Targets verwenden dieselben Request-Felder:

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

Es gibt keine impliziten Request-Standardwerte. Wenn dein Handler die rohe Tool-Eingabe als JSON erhalten soll, schreibe `body: '${input}'`.

## Referenzen

Kazzle löst Referenzen in HTTP-Target-Feldern `query`, `headers`, `body` und `url` vor dem Versand auf:

* `${input}` — die gesamte Tool-Eingabe oder die gesamte Button-Eingabe.
* `${input.path}` — ein verschachtelter Wert aus der Eingabe.
* `${env.NAME}` — eine benannte Umgebungsvariable aus der deklarierten `env.collection` + `env.environment` der besitzenden App-Komponente.

Die Auflösung erfolgt in einem Durchgang. Wenn eine Referenz fehlt oder unbekannt ist, schlägt das Tool fehl, anstatt einen leeren Wert zu ersetzen. Verwende `$${input.name}`, wenn du literalen `${input.name}`-Text benötigst.

## Handler-Request

Für ein `app`-Target füge die entsprechende Route in der Target-Komponente hinzu. Mit `body: '${input}'` sendet Kazzle die typisierte Eingabe als JSON-Body:

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

`app`-Targets erhalten auch:

| Header                | Wert                                                                |
| --------------------- | ------------------------------------------------------------------- |
| `Authorization`       | `Bearer <identity token>` identifiziert den Benutzer + Installation |
| `Kazzle-Tool-Context` | `{"source":"thread","threadId":"..."}` oder `{"source":"api"}`      |

App-Targets können `Authorization` nicht über `headers` setzen; Kazzle besitzt diesen Header. Lese den Kontext mit `toolContext(req)` aus `@kazzle/app/tools`, wenn der Handler zwischen Thread und Tools API unterscheiden muss.

Ein Tool, das ohne entsprechende Route deklariert wird, ist nicht sonderlich nützlich — füge beide zusammen hinzu. `tools.json` wird nicht unterstützt; der App-Compiler schlägt fehl, wenn er eine findet.

## Handler-Response

Gib Klartext oder JSON mit bis zu drei Kanälen zurück:

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

* **`content`** — das einfache Ergebnis, das die KI liest (an das Modell weitergeleitet). Erforderlich.
* **`markdown`** — optional. Eine kurze Rich-Text-Zusammenfassung, die in der Tool-Karte gerendert wird (react-markdown; rohes HTML wird escaped). Gut für einen Satz, eine kleine Liste, einen Inline-Link. Ein bloßer relativer Pfad wird als totes vorformatiertes Text gerendert — Links müssen **absolut** sein.
* **`embedUrl`** — optional. Eine **absolute** URL, die deine App bereitstellt; die Karte rendert sie in einem Sandbox-iframe. So zeigst du eine echte, vollständige UI — einen Connect-Bildschirm, ein Dashboard, ein Diagramm. Deine App hostet und besitzt die Seite, sodass sie vollständig interaktiv gegen dein eigenes Backend, Cookies und OAuth sein kann. Nichts wird auf das Drive geschrieben.

`embedUrl` gewinnt über `markdown`, wenn beide gesetzt sind. Behalte immer einen aussagekräftigen `content` — das ist das, was die KI liest.

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

## Erforderliche Aktion

Ein App-Tool kann den Thread pausieren, wenn es eine Benutzer- (oder Geräte-)Aktion benötigt. Gib `type: 'action_required'` mit einem Kartentitel und Schaltflächen zurück. Nur gültig, wenn `toolContext(req).source === 'thread'` — über die Tools API (`source: 'api'`), gib stattdessen einen normalen Domain-Fehler zurück.

```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 speichert die Karte beim Tool-Aufruf und setzt fort über `/chat/resume`, nachdem eine Schaltfläche gedrückt wurde. Übereinstimmende Clients sehen die Autor-Schaltflächen; nicht übereinstimmende Clients sehen immer noch Skip/Cancel plus „Auf {device} fortsetzen". Eine Schaltfläche ohne `target` sendet ihre `input` als Tool-Ergebnis. Eine Schaltfläche mit einem Target führt dieses Target zuerst aus, dann verwendet das Target-Ergebnis als Tool-Ergebnis.

### Rich UI — eine App-gehostete Seite einbetten

Wenn das Ergebnis eines Tools visuell oder interaktiv ist (ein Connect-Bildschirm, ein Diagramm, ein Zusammenfassungs-Dashboard), stelle die Seite von einer deiner Komponenten bereit und gib ihre **absolute** URL als `embedUrl` zurück. Erstelle die URL aus der eingespritzt Komponenten-URL — niemals ein relativer Pfad.

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

Die Seite läuft in einem Cross-Origin-iframe (ihre eigene Origin, Cookies und Scripts). Um die Karte zu dimensionieren, poste ihre Höhe zum Parent — die Karte hört darauf und ändert die Größe (begrenzt auf 70% des Viewports):

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

Bevorzuge `embedUrl` gegenüber der Ausgabe von HTML-Strings: Deine App stellt bereits Seiten bereit, die UI bleibt interaktiv und versioniert mit deinem Code, und keine Pro-Aufruf-Artefakte werden irgendwo geschrieben.
