Tools give an 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. Unlike skills, there's no separate CLI to install tools: you author each one as a TypeScript file with defineTool, and eve discovers it with no registration step.
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.
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.
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.
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.
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:
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, passz.object({}). Zod and Standard Schema infer theinputtype inexecute.execute(input, ctx): the implementation, which can be sync or async. Thectxargument carries runtime accessors likectx.session,ctx.getSandbox(), andctx.getSkill(id).
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:
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.
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 needsApproval with the helpers from eve/tools/approval.
Create agent/tools/refund_charge.ts:
The helpers cover the common cases:
| Helper | Behavior |
|---|---|
never() | Never require approval. This is the default when you omit needsApproval. |
once() | Require approval the first time the tool runs in a session, then auto-allow. |
always() | Require approval before every call. |
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:
Because an omitted needsApproval 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.requestedstream event carrying the pending approval. - The client answers with structured
inputResponseskeyed 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 of two ways a tool can pause for a person. The other is the built-in ask_question tool, which lets the agent ask the user a clarifying question using the same protocol. It's part of the default harness, so you don't define it yourself. The model calls it with:
prompt: the question to put to the user.options(optional): choices that channels render as buttons or a select menu.allowFreeform: whether the user can answer with free text.
The rule of thumb: reach for ask_question when the model is missing information it shouldn't guess at, and needsApproval when the model has decided what to do, but a person should sign off first.
Start your agent and send a request that needs the tool. The init command starts the dev server for you when you scaffold a project; otherwise, use the dev script from your project's README. 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 in the Vercel dashboard.
- Tools in eve: the complete
defineToolAPI,ctx,outputSchema, andtoModelOutput. - Human-in-the-loop: gate a tool on approval, or have the agent ask the user a question.
- Default harness: the built-in file, shell, web, and delegation tools, and how to override or disable them.
- Skills in eve: on-demand procedures the model loads when a task calls for them, for guidance rather than actions.
- Dynamic capabilities: resolve a tool set per session with
defineDynamic.