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

# Tool Polish (Strict, Examples, toModelOutput)

> Three small but high-leverage knobs on every defineTool call

# Tool Polish

`defineTool()` exposes three additional fields in v2.0 that materially improve reliability and cost.

## 1. `strict: true` — guaranteed valid arguments

```typescript theme={null}
import { defineTool } from "@agentium/core";
import { z } from "zod";

const lookup = defineTool({
  name: "lookupOrder",
  description: "Look up an order by ID.",
  parameters: z.object({ orderId: z.string() }),
  strict: true,                              // ← new
  execute: async ({ orderId }) => { /* ... */ },
});
```

When `strict: true`:

* `additionalProperties: false` is appended to the generated JSON Schema, so the LLM cannot send junk extra keys.
* For OpenAI-family models, `strict: true` flows through to the API's structured-output mode, which guarantees the model's tool call is valid JSON matching your schema (no retries, no parse errors).

**When to use:** any tool whose schema you want to enforce hard — DB queries, payment operations, anything where "the LLM made up a field" would be embarrassing.

**Trade-offs:**

* OpenAI strict mode disables certain features (anyOf, default values on optional fields). If your schema fails to compile in strict mode, drop the flag.
* Anthropic / Google providers ignore the strict flag at the API level but the JSON Schema is still tightened, which the model usually respects.

## 2. `inputExamples` — N-shot demonstrations

```typescript theme={null}
const search = defineTool({
  name: "search",
  description: "Search the web for current information.",
  parameters: z.object({
    query: z.string(),
    timeRange: z.enum(["day", "week", "month", "year", "all"]).optional(),
  }),
  inputExamples: [
    { query: "node 22 release notes" },
    { query: "OpenAI o1 pricing", timeRange: "month" },
    { query: "rust async tutorial", timeRange: "year" },
  ],
  execute: async ({ query, timeRange }) => { /* ... */ },
});
```

The framework appends a formatted examples section to the tool description that the LLM sees:

```
Search the web for current information.

Examples:
1. {"query":"node 22 release notes"}
2. {"query":"OpenAI o1 pricing","timeRange":"month"}
3. {"query":"rust async tutorial","timeRange":"year"}
```

**Why this works:** LLMs are extraordinarily good at pattern-matching from examples. A single well-chosen example often beats two paragraphs of prose explanation.

**When to use:**

* Tools with enums or specific value formats (timezones, country codes, ISO dates)
* Tools where the LLM tends to over- or under-specify (e.g. always omits an optional field that improves results)
* New tools you don't have months of usage data on yet

**Tips:**

* 3–5 examples is the sweet spot. More doesn't help and increases prompt cost.
* Show variety, not similarity. The model already understands that strings can vary; show it the *kinds* of variation that matter.
* Don't include obvious "bad" examples — the model can latch onto them.

## 3. `toModelOutput` — async result transformer

```typescript theme={null}
const fetchOrder = defineTool({
  name: "fetchOrder",
  description: "Get order details from the warehouse system.",
  parameters: z.object({ orderId: z.string() }),
  execute: async ({ orderId }) => {
    // Real implementation returns the full order with 500 line items.
    return JSON.stringify(await db.orders.fetchFull(orderId));
  },
  toModelOutput: async (rawResult, ctx) => {
    const text = typeof rawResult === "string" ? rawResult : rawResult.content;
    const order = JSON.parse(text);
    // Shrink to just what the LLM cares about.
    return JSON.stringify({
      orderId: order.orderId,
      status: order.status,
      total: order.total,
      itemCount: order.items.length,
      firstThreeItems: order.items.slice(0, 3),
    });
  },
});
```

**Execution order:**

```
tool.execute  ──▶  rawResult
                      │
                      ▼
          toModelOutput (if set)        ──▶  transformed
                      │
                      ▼
        artifact auto-conversion          ──▶ pointer (if oversized)
                      │
                      ▼
              cache, event bus, LLM context
```

`toModelOutput` runs BEFORE the artifact auto-converter, so if your transform shrinks the output below the threshold, no pointer is created.

**Signature:**

```typescript theme={null}
toModelOutput?: (
  result: string | ToolResult,
  ctx: RunContext,
) => Promise<string | ToolResult>;
```

`ctx` gives you access to `userId`, `tenantId`, `sessionState`, `eventBus` — useful for tenant-aware transforms (redact PII per tenant policy, etc.).

**When to use:**

* Compress / summarize / redact tool output without modifying the underlying tool
* Convert verbose API responses to LLM-friendly shapes
* Strip secrets that shouldn't reach the model (API keys, internal IDs)
* A/B test response formats without touching `execute`

**`toModelOutput` vs Memory Pointers:**

| Scenario                                               | Use                                                                        |
| ------------------------------------------------------ | -------------------------------------------------------------------------- |
| You know exactly what fields the LLM needs             | `toModelOutput`                                                            |
| You want the LLM to opt-in to expanding the data later | Memory Pointers                                                            |
| Both                                                   | Stack them: `toModelOutput` summarizes, then `art:` wraps if still too big |

## Putting it together

```typescript theme={null}
const fetchOrder = defineTool({
  name: "fetchOrder",
  description: "Get order details from the warehouse system.",
  parameters: z.object({ orderId: z.string() }),
  strict: true,
  inputExamples: [{ orderId: "ord_abc123" }, { orderId: "ord_9f3-2025" }],
  toModelOutput: async (result) => {
    const data = JSON.parse(typeof result === "string" ? result : result.content);
    return JSON.stringify({ id: data.orderId, status: data.status, itemCount: data.items.length });
  },
  execute: async ({ orderId }) => JSON.stringify(await db.orders.fetchFull(orderId)),
});
```

All three flags compose cleanly: strict ensures valid input, examples teach the model what valid input looks like, `toModelOutput` keeps the response footprint small.

## See also

* [Memory Pointer Pattern](/features/memory-pointers) for outputs that genuinely need to be retrievable in full
* [Tool Loop Detection](/features/tool-loop-detection) for catching agents stuck in retry loops
