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

# Ferramentas

> Como um app declara ferramentas e como seus handlers retornam resultados.

# Ferramentas

Uma **ferramenta** é uma ação nomeada que a IA pode chamar, com entradas tipadas — por exemplo, `send_email(to, subject, body)`. As ferramentas são declaradas no `tools.ts` de uma skill e, para ferramentas que seu app oferece, são respaldadas por uma rota HTTP em um de seus componentes. Esta página é a fonte de verdade para o contrato de ferramentas; [Skills](/apps/skills) cobre onde `tools.ts` fica.

## Declarando uma ferramenta

Cada entrada em `tools.ts` tem um `name` seguro para o provedor, um `displayName` amigável, uma `description`, um schema Zod `input` e um `target` que diz o que 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[];
```

## Targets

`target` é o endereço reutilizável para uma ferramenta ou botão de ação obrigatória. Uma chamada de ferramenta de IA direta e um clique de botão podem usar o mesmo objeto target, então ambos executam o mesmo código.

| Target   | Campos                                                             | Executa onde                                                                                                              |
| -------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `app`    | `component`, `path`, `method`, `query` opcional, `headers`, `body` | Em um de seus próprios componentes. Kazzle resolve a URL do componente e adiciona um token de identidade de app assinado. |
| `url`    | `url`, `method`, `query` opcional, `headers`, `body`               | Um endpoint externo. A origem da URL e o método são valores literais do autor.                                            |
| `kazzle` | `name`                                                             | Um handler Kazzle integrado do lado do cliente.                                                                           |

Os targets HTTP usam os mesmos campos de requisição:

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

Não há padrões de requisição implícitos. Se seu handler deve receber a entrada bruta da ferramenta como JSON, escreva `body: '${input}'`.

## Referências

Kazzle resolve referências nos campos `query`, `headers`, `body` e `url` do target HTTP antes do envio:

* `${input}` — toda a entrada da ferramenta, ou toda a entrada do botão.
* `${input.path}` — um valor aninhado da entrada.
* `${env.NAME}` — uma variável de ambiente nomeada da `env.collection` declarada do componente do app proprietário + `env.environment`.

A resolução é de uma única passagem. Se uma referência está faltando ou desconhecida, a ferramenta falha em vez de substituir um valor vazio. Use `$${input.name}` quando você precisar do texto literal `${input.name}`.

## Requisição do handler

Para um target `app`, adicione a rota correspondente no componente target. Com `body: '${input}'`, Kazzle envia a entrada tipada como o corpo JSON:

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

Os targets `app` também recebem:

| Header                | Valor                                                          |
| --------------------- | -------------------------------------------------------------- |
| `Authorization`       | `Bearer <identity token>` identificando o usuário + instalação |
| `Kazzle-Tool-Context` | `{"source":"thread","threadId":"..."}` ou `{"source":"api"}`   |

Os targets de app não podem definir `Authorization` através de `headers`; Kazzle é o proprietário desse header. Leia o contexto com `toolContext(req)` de `@kazzle/app/tools` quando o handler deve ramificar thread vs Tools API.

Uma ferramenta declarada sem uma rota correspondente não faz nada útil — adicione ambas juntas. `tools.json` não é suportado; o compilador do app falha se encontrar um.

## Resposta do handler

Retorne texto simples ou JSON com até três canais:

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

* **`content`** — o resultado simples que a IA lê (alimentado ao modelo). Obrigatório.
* **`markdown`** — opcional. Um resumo de texto rico curto renderizado no cartão da ferramenta (react-markdown; HTML bruto é escapado). Bom para uma frase, uma pequena lista, um link inline. Um caminho relativo simples renderiza como texto pré-formatado morto — links devem ser **absolutos**.
* **`embedUrl`** — opcional. Uma URL **absoluta** que seu app oferece; o cartão a renderiza em um iframe em sandbox. É assim que você mostra uma UI real e de largura total — uma tela de conexão, um painel, um gráfico. Seu app hospeda e é proprietário da página, então pode ser totalmente interativa contra seu próprio backend, cookies e OAuth. Nada é escrito no drive.

`embedUrl` vence sobre `markdown` quando ambos estão definidos. Sempre mantenha um `content` significativo — é isso que a IA lê.

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

## Ação obrigatória

Uma ferramenta de app pode pausar a thread quando precisa de uma ação do usuário (ou dispositivo). Retorne `type: 'action_required'` com um título de cartão e botões. Válido apenas quando `toolContext(req).source === 'thread'` — sobre a Tools API (`source: 'api'`), retorne um erro de domínio normal em vez disso.

```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 o cartão na chamada da ferramenta e retoma através de `/chat/resume` após um botão ser pressionado. Os clientes correspondentes veem os botões do autor; clientes não correspondentes ainda veem Pular/Cancelar mais "Continuar em {dispositivo}". Um botão sem `target` envia sua `input` como o resultado da ferramenta. Um botão com um target executa esse target primeiro, depois usa o resultado do target como o resultado da ferramenta.

### UI Rica — incorporar uma página hospedada pelo app

Quando o resultado de uma ferramenta é visual ou interativo (uma tela de conexão, um gráfico, um painel de resumo), ofereça a página de um de seus componentes e retorne sua URL **absoluta** como `embedUrl`. Construa a URL a partir da URL do componente injetada — nunca um caminho 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}`,
});
```

A página é executada em um iframe de origem cruzada (sua própria origem, cookies e scripts). Para dimensionar o cartão, poste sua altura para o pai — o cartão escuta isso e redimensiona (limitado a 70% da viewport):

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

Prefira `embedUrl` em vez de emitir strings HTML: seu app já oferece páginas, a UI permanece interativa e versionada com seu código, e nenhum artefato por chamada é escrito em lugar algum.
