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

# Tools you write by hand

> One helper turns a zod schema and a function into a guarded tool.

Extraction reads your API. Some capability has no route to read — a calculation, a
call to a vendor SDK, a step that stitches three of your own services together.
Write it in TypeScript and hand it to the same slot.

## defineTool

```ts vendo.ts theme={null}
import { createVendo } from "@vendoai/vendo/server";
import { defineTool } from "@vendoai/vendo";
import { z } from "zod/v4";

const refundOrder = defineTool({
  name: "host_refundOrder",
  description: "Refund a paid order back to the original card",
  input: z.object({ orderId: z.string(), reason: z.string() }),
  risk: "destructive",
  execute: async ({ orderId, reason }, context) => {
    return await payments.refund(orderId, { reason, actor: context.principal.subject });
  },
});

export const vendo = createVendo({ tools: [refundOrder] });
```

That is the whole surface. The tool joins the one registry under the name you gave
it, guarded, audited, and projected like an extracted one.

<Warning>
  **An authored menu is an allowlist of exact names, and yours has to be in it.**
  If `.vendo/overrides.json` names a `surfaces.mcp` menu, add `host_refundOrder`
  to it — otherwise the door won't offer it, and a call to it answers the same
  not-found an unknown name gets. Vendo's own `vendo_*` tools bypass the menu and
  always ride along; yours does not.

  Forget to add it, and Vendo warns once per surface per boot, naming the tool
  you left out — but the warning doesn't add it for you.

  See [Curate the menu](/outside-agents/how-the-door-works#curate-the-menu).
</Warning>

* `input` is the single statement of the arguments. It becomes the JSON Schema the
  model is shown **and** the parse that runs before `execute`, so the two can never
  drift apart. A call that does not match is refused before your function runs.
* `risk` is required, and it is a grade: `read`, `write`, or `destructive`. You wrote
  the tool, so you know. Only extraction is allowed to answer `ungraded`.
* `context` is the run context — `context.principal` is whose authority the call
  carries.
* `execute` returns the output, or throws. The denial outcomes belong to the guard:
  nothing you write can fake an approval.

<Note>
  **zod 4 shapes only.** On zod 3.25 or later, import from `zod/v4`. On zod 4, the
  plain `zod` import is already the right shape.
</Note>

## The fields it does not ask for

The result is a plain `ToolDefinition`, so everything else on a descriptor is a
spread away.

```ts highlight={2} theme={null}
const refundOrder = {
  ...defineTool({ /* … */ }),
  confirmEach: true,
  title: "Refund an order",
};
```

`confirmEach` makes every call earn its own approval, whatever the policy says.
`title` is the human label approval cards and tool menus show.

## Where to go next

<CardGroup cols={2}>
  <Card title="API tools" href="/capabilities/api-tools">
    The other source: one command reads your API and writes the file.

    API tools →
  </Card>

  <Card title="Guard" href="/how-vendo-works">
    Risk grade, approval, and an audit line on every call.

    How Vendo works →
  </Card>
</CardGroup>
