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

> How an app declares tools and how their handlers return results.

# Tools

एक **tool** एक नाम वाली कार्रवाई है जिसे AI कॉल कर सकता है, टाइप किए गए इनपुट के साथ — जैसे `send_email(to, subject, body)`। Tools को एक skill के `tools.ts` में घोषित किया जाता है और, जिन tools को आपका app serve करता है, उन्हें आपके किसी एक component में एक HTTP route द्वारा समर्थित किया जाता है। यह पृष्ठ tool contract के लिए सत्य का स्रोत है; [Skills](/apps/skills) में `tools.ts` कहाँ रहता है यह बताया गया है।

## Tool घोषित करना

`tools.ts` में प्रत्येक entry में एक provider-safe `name`, एक friendly `displayName`, एक `description`, एक Zod `input` schema, और एक `target` होता है जो बताता है कि Kazzle क्या invoke करता है।

```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` एक tool या required-action button के लिए पुन: प्रयोग करने योग्य पता है। एक direct AI tool call और एक button click एक ही target object का उपयोग कर सकते हैं, इसलिए दोनों एक ही code चलाते हैं।

| Target   | Fields                                                             | कहाँ चलता है                                                                                                       |
| -------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| `app`    | `component`, `path`, `method`, optional `query`, `headers`, `body` | आपके अपने components में से एक। Kazzle component URL को resolve करता है और एक signed app identity token जोड़ता है। |
| `url`    | `url`, `method`, optional `query`, `headers`, `body`               | एक external endpoint। URL origin और method literal author values हैं।                                              |
| `kazzle` | `name`                                                             | एक built-in client-side Kazzle handler।                                                                            |

HTTP targets एक ही request fields का उपयोग करते हैं:

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

कोई implicit request defaults नहीं हैं। यदि आपका handler raw tool input को JSON के रूप में प्राप्त करना चाहिए, तो `body: '${input}'` लिखें।

## References

Kazzle dispatch से पहले HTTP target `query`, `headers`, `body`, और `url` fields में references को resolve करता है:

* `${input}` — पूरा tool input, या पूरा button input।
* `${input.path}` — input से एक nested value।
* `${env.NAME}` — owning app component के declared `env.collection` + `env.environment` से एक named environment variable।

Resolution single-pass है। यदि कोई reference missing या unknown है, तो tool fail हो जाता है बजाय एक empty value को substitute करने के। जब आपको literal `${input.name}` text की आवश्यकता हो तो `$${input.name}` का उपयोग करें।

## Handler request

एक `app` target के लिए, target component में matching route जोड़ें। `body: '${input}'` के साथ, Kazzle typed input को JSON body के रूप में भेजता है:

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

`app` targets को यह भी मिलते हैं:

| Header                | Value                                                           |
| --------------------- | --------------------------------------------------------------- |
| `Authorization`       | `Bearer <identity token>` जो user + install को identify करता है |
| `Kazzle-Tool-Context` | `{"source":"thread","threadId":"..."}` या `{"source":"api"}`    |

App targets `headers` के माध्यम से `Authorization` set नहीं कर सकते; Kazzle उस header का मालिक है। जब handler को thread vs Tools API को branch करना चाहिए तो `@kazzle/app/tools` से `toolContext(req)` के साथ context को पढ़ें।

एक tool जो कोई matching route के साथ घोषित किया गया है वह कुछ भी उपयोगी नहीं करता — दोनों को एक साथ जोड़ें। `tools.json` समर्थित नहीं है; यदि app compiler को एक मिलता है तो वह fail हो जाता है।

## Handler response

Plain text, या JSON को तीन channels तक return करें:

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

* **`content`** — plain result जिसे AI पढ़ता है (model को feed किया जाता है)। Required।
* **`markdown`** — optional। एक short rich-text summary जो tool card में render होता है (react-markdown; raw HTML escaped है)। एक sentence, एक small list, एक inline link के लिए अच्छा है। एक bare relative path dead preformatted text के रूप में render होता है — links **absolute** होने चाहिए।
* **`embedUrl`** — optional। एक **absolute** URL जिसे आपका app serve करता है; card इसे एक sandboxed iframe में render करता है। यह है कि आप एक real, full-width UI कैसे दिखाते हैं — एक connect screen, एक dashboard, एक chart। आपका app page को host और own करता है, इसलिए यह आपने अपने backend, cookies, और OAuth के विरुद्ध पूरी तरह interactive हो सकता है। कुछ भी drive में नहीं लिखा जाता है।

`embedUrl` जब दोनों set हों तो `markdown` को जीतता है। हमेशा एक meaningful `content` रखें — वह है जो AI पढ़ता है।

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

## Required action

एक app tool thread को pause कर सकता है जब इसे एक user (या device) action की आवश्यकता होती है। `type: 'action_required'` को एक card title और buttons के साथ return करें। केवल तब valid है जब `toolContext(req).source === 'thread'` — Tools API (`source: 'api'`) के ऊपर, एक normal domain error return करें।

```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 tool call पर card को save करता है और एक button दबाए जाने के बाद `/chat/resume` के माध्यम से resume करता है। Matching clients को author buttons दिखाई देते हैं; non-matching clients को अभी भी Skip/Cancel plus "Continue on {device}" दिखाई देता है। `target` के बिना एक button अपने `input` को tool result के रूप में submit करता है। `target` के साथ एक button पहले उस target को चलाता है, फिर target result को tool result के रूप में उपयोग करता है।

### Rich UI — एक app-hosted page को embed करें

जब एक tool का result visual या interactive हो (एक connect screen, एक chart, एक summary dashboard), तो page को अपने किसी एक component से serve करें और इसका **absolute** URL `embedUrl` के रूप में return करें। URL को injected component URL से build करें — कभी relative path नहीं।

```ts theme={"theme":"material-theme-darker"}
// एक tool जिसे user को अपने account को connect करने की आवश्यकता है:
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}`,
});
```

Page एक cross-origin iframe में चलता है (इसका अपना origin, cookies, और scripts)। Card को size करने के लिए, इसकी height को parent को post करें — card इसे सुनता है और resize करता है (viewport के 70% पर capped):

```ts theme={"theme":"material-theme-darker"}
// embedded page के अंदर
parent.postMessage({ __kazzle_height: document.body.scrollHeight }, '*');
```

HTML strings को emit करने पर `embedUrl` को prefer करें: आपका app पहले से ही pages serve करता है, UI interactive और आपके code के साथ versioned रहता है, और कोई भी per-call artifacts कहीं भी नहीं लिखे जाते हैं।
