---
title: How to add tools to your eve agent
description: Add tools to an eve agent by creating a TypeScript file under agent/tools/ with defineTool, and gate sensitive ones on human approval with approval.
url: "https://vercel.com/kb/guide/how-to-add-eve-tools"
published: 2026-06-19
last_updated: 2026-09-30
authors: Ben Sabic
related_resources:
  - title: "How to classify, route, and score with Jev and AI SDK"
    url: "https://vercel.com/kb/guide/typesafe-jev-and-ai-sdk"
    description: "Use Jev from TypeSafe AI with AI SDK's experimental evaluate API to classify, route, score, and verify inside your application. Jev returns typed choices, scores, and boolean probabilities through AI Gateway."
  - title: "How to automatically approve tool calls in eve with Jev"
    url: "https://vercel.com/kb/guide/auto-approve-tool-calls-eve-jev"
    description: "Use Jev to review tool calls in eve, allow routine actions, and request human approval when needed. Configure the policy and test its failure paths."
install_vercel_plugin: npx plugins add vercel/vercel-plugin
---

Tools give an [eve](https://vercel.com/eve) agent typed actions it can call, such as hitting an API, running a query, or writing a file. With them, the agent does the real work rather than just describing it. eve exposes any tool you place under `agent/tools/`, and the filename becomes the tool name the model sees. Author custom tools as TypeScript files with `defineTool`; eve discovers them without a registration step. To add framework-provided tools, use `eve add tool/<name>`.

This guide walks you through writing a basic tool, gating a sensitive one on human approval, and then confirming that your agent calls the tool for the right requests.

## Prerequisites

Before you begin, you need an eve project and Node.js installed.

- To create a new project, run `npx eve@latest init my-agent`.
  
- To add eve to an existing app, follow the [quickstart steps](https://eve.dev/docs/getting-started#quick-start).
  

## Quick start with an AI coding agent

If you're using an AI coding agent like Claude Code or Cursor, give it this prompt, and it'll help you add a tool to your eve agent:

### Agent prompt

```txt
I want to add a custom tool to my eve agent. Use this guide as your reference: https://vercel.com/kb/guide/how-to-add-eve-tools.

Before writing any code, ask me what the tool should do. Find out:

- The action it performs (an API it calls, a query it runs, a file it writes)
- Its inputs and their types
- What it returns, and whether any of that is sensitive
- Whether the action has side effects or is hard to undo (sending email, charging a card, deleting data), so we can decide whether it needs human approval

Once you have my answers, create the tool as one TypeScript file and follow these conventions:

- Put it under `agent/tools/`. The filename is the name the model sees, so use snake_case ASCII (a file named `get_weather.ts` is exposed as `get_weather`).
- Author it with `defineTool` from `eve/tools`.
- Write a clear description aimed at the model, since that's what it reads to decide when to call the tool.
- Define an `inputSchema` with Zod. This is required. For a tool that takes no input, use `z.object({})`.
- Write the `execute(input, ctx)` function, sync or async.
- If the tool returns structured data, add an `outputSchema`. When the full return is richer than the model needs, use `toModelOutput` to shape what the model sees.
- Don't return secrets, credentials, or unneeded personal data. Filter and redact first, because the model and any connected channel can read whatever you return.

If the action is sensitive or hard to undo, gate it on human approval. Set `approval` with helpers from eve/tools/approval: never() (the default), once(), or always(). For input-dependent decisions, provide a policy that returns "user-approval" or "not-applicable" and guards against undefined toolInput. Protect non-idempotent external writes with a stable upstream idempotency key or application-level deduplication.

One distinction to keep straight: use `approval` when you've decided what to do but a person should sign off first. If instead the tool is missing information it shouldn't guess, eve's built-in `ask_question` already handles that, so you don't need to build it.

When the file is ready, tell me how to test it: start the dev server with `eve dev`, send a request that should trigger the tool, then open Agent Runs in the Vercel dashboard to confirm it ran.

If any of my requirements are unclear, ask before you generate the file rather than guessing.
```

## How eve loads tools

eve discovers the files under `agent/tools/` and advertises each tool's name, description, and input schema to the model. During this discovery step the model sees only those descriptors, never your implementation. When the model decides a tool fits the task, it calls the tool with arguments that match your `inputSchema`, and eve runs your `execute` function in your app runtime with full access to `process.env` and shared code in `lib/`, not in the [sandbox](https://eve.dev/docs/sandbox).

eve never runs a tool during discovery. Only what the model actually calls gets executed, and completed steps don't re-run, so your tools stay predictable across a session. There's no separate registry to keep in sync: add the file and eve discovers it, move or rename it and its identity moves with it.

## Add a tool by creating a file

The smallest tool is a single TypeScript file under `agent/tools/`. Its name comes from the filename, which must be snake\_case ASCII, so a file at `agent/tools/get_weather.ts` is exposed to the model as `get_weather`.

Create `agent/tools/get_weather.ts`:

```typescript
import { defineTool } from "eve/tools";
import { z } from "zod";

export default defineTool({
  description: "Get the current weather for a city.",
  inputSchema: z.object({ city: z.string().min(1) }),
  async execute({ city }, ctx) {
    return { city, condition: "Sunny", temperatureF: 72 };
  },
});
```

Every tool definition needs four things:

- **Filename slug** under `agent/tools/`: the model-facing name.
  
- `description`: what the tool does, written for the model to read.
  
- `inputSchema`: a Zod schema (or any Standard Schema, or a plain JSON Schema object). This is required. For a tool that takes no input, pass `z.object({})`. Zod and Standard Schema infer the `input` type in `execute`.
  
- `execute(input, ctx)`: the implementation, which can be synchronous, asynchronous, or an async generator. The `ctx` argument provides session metadata, the current call ID and tool name, an abort signal, and sandbox access through `ctx.getSandbox()`.
  

When a tool returns structured data, add an optional `outputSchema`, which also types the `execute` return when you use Zod or Standard Schema. If the full return is richer than the model needs, project it down with `toModelOutput`, which receives the typed `execute` return and shapes only what the model sees. Channel handlers and hooks still receive the full output, so a channel can render rich platform output that the model never reads:

```typescript
toModelOutput(output) {
  return {
    type: "text",
    value: `${output.city}: ${output.condition}, ${output.temperatureF}°F.`,
  };
},
```

Don't return secrets, credentials, unnecessary personal data, or unbounded sensitive content from a tool. Filter, minimize, and redact tool outputs before returning them, because the model and any connected channel can see what you return.

## Gate a tool on human approval

Some actions shouldn't run without a person signing off, such as issuing a refund, sending an email, or anything irreversible. A tool can require approval before it runs: set `approval` with the helpers from `eve/tools/approval`.

Create `agent/tools/refund_charge.ts`:

```typescript
import { defineTool } from "eve/tools";
import { always } from "eve/tools/approval";
import { z } from "zod";

export default defineTool({
  description: "Refund a charge.",
  inputSchema: z.object({ chargeId: z.string(), amount: z.number() }),
  approval: always(), // or once() / never() / a predicate
  async execute(input) {
    return refund(input);
  },
});
```

The helpers cover the common cases:

| Helper     | Behavior                                                                                                                                                 |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `never()`  | Never require approval. This is the default when `approval` is omitted.                                                                                  |
| `once()`   | Require approval the first time the tool runs in a session, then automatically allow later calls after all pending approval requests have been resolved. |
| `always()` | Require approval before every call.                                                                                                                      |
| `auto()`   | Ask the evaluation model whether the call can run automatically or requires human approval. The default evaluation model is Jev.                         |

When the decision depends on the input, pass your own predicate instead of a helper. It receives `{ toolName, toolInput, approvedTools }` and returns a boolean. Guard the access, since `toolInput` can be undefined. To require approval only when an amount crosses a threshold:

```typescript
approval: ({ toolInput }) => (toolInput?.amount ?? 0) > 1000,
```

Because an omitted `approval` behaves like `never()`, tool calls can run without human approval by default. Require approval or another safeguard for sensitive, irreversible, regulated, financial, healthcare, employment, housing, legal, safety-impacting, user-impacting, or external side-effecting actions. Gating a side effect this way also keeps non-idempotent work safe: a charge or email behind `always()` can't fire from a re-run step without a fresh human decision.

When a gated tool is called, the run parks durably at `session.waiting` until a person answers, then picks back up exactly where it left off. The pause survives restarts, since nothing is held in memory while it waits.

Here's how a waiting run resolves:

- eve emits an `input.requested` stream event carrying the pending approval.
  
- The client answers with structured `inputResponses` keyed by request ID, or with a normal follow-up message.
  
- Follow-up text that matches an option label automatically resolves the request.
  

Approval is one way a tool can pause for a person. Another is the opt-in `ask_question` framework tool, which lets the agent ask a clarifying question. Add it with:

`eve add tool/ask_question`

The model calls it with:

- `question`: the question and context the user needs to answer.
  
- `options` (optional): two or three mutually exclusive choices, each with a `label` and a one-sentence `description`.
  

Omit `options` for an open-ended question. The user can always type their own answer.

The rule of thumb: reach for `ask_question` when the model is missing information it shouldn't guess at, and `approval` when the model has decided what to do, but a person should sign off first.

## Verify the tool runs

Start your agent and send a request that needs the tool. If you scaffolded with `init`, the dev server is already running; otherwise start it with `eve dev`. When a request matches, the model calls the tool with arguments from your `inputSchema`, and eve runs your `execute` function and returns the result to the model.

To see which tools ran, along with timing and token usage, open [Agent Runs](https://vercel.com/d?to=%2F%5Bteam%5D%2F%5Bproject%5D%2Fobservability%2Fagent-runs) in the Vercel dashboard.

## Resources and next steps

- [Tools in eve](https://eve.dev/docs/tools): the complete `defineTool` API, `ctx`, `outputSchema`, and `toModelOutput`.
  
- [Human-in-the-loop](https://eve.dev/docs/human-in-the-loop): gate a tool on approval, or have the agent ask the user a question.
  
- [Default harness](https://eve.dev/docs/concepts/default-harness): the built-in file, shell, web, and delegation tools, and how to override or disable them.
  
- [Skills in eve](https://eve.dev/docs/skills): on-demand procedures the model loads when a task calls for them, for guidance rather than actions.
  
- [Dynamic capabilities](https://eve.dev/docs/guides/dynamic-capabilities): resolve a tool set per session with `defineDynamic`.
  

Use Jev to review tool calls in eve, allow routine actions, and request human approval when needed. Configure the policy and test its failure paths.

[How to automatically approve tool calls in eve with Jev](/kb/guide/auto-approve-tool-calls-eve-jev)

* * *

## Learn more about building with eve

[**Build subagents with eve.**Split work into focused specialists, each with their own context, tools, and isolation boundary.](https://vercel.com/kb/guide/how-to-use-eve-subagents)[**Add skills to your eve agent.**Write your own under agent/skills/, install published ones, or resolve a different one per caller.](https://vercel.com/kb/guide/how-to-add-eve-skills)

## Related Resources

- [How to classify, route, and score with Jev and AI SDK](https://vercel.com/kb/guide/typesafe-jev-and-ai-sdk): Use Jev from TypeSafe AI with AI SDK's experimental evaluate API to classify, route, score, and verify inside your application. Jev returns typed choices, scores, and boolean probabilities through AI Gateway.
- [How to automatically approve tool calls in eve with Jev](https://vercel.com/kb/guide/auto-approve-tool-calls-eve-jev): Use Jev to review tool calls in eve, allow routine actions, and request human approval when needed. Configure the policy and test its failure paths.

## More eve guides

- [How to use Vercel Drives with an eve agent](/kb/guide/vercel-drives-eve-agent): Mount Vercel Drives in an eve agent's sandbox to give each user a persistent workspace and share read-only reference data across sessions.
- [How to build a custom eve memory provider](/kb/guide/build-a-custom-eve-memory-provider): Implement the eve memory provider contract with defineMemoryProvider() to back an eve memory slot with your own database, vector index, or memory service.
- [How to choose an eve memory provider](/kb/guide/choose-an-eve-memory-provider): Compare File memory, Supermemory, Upstash AgentKit, and Kybernesis Arcana by recall, capture, and storage to choose an eve memory provider for your agent.