
eve does something unusual with tools. It does not ask you to register them, assemble them into an array, or pass them into a constructor. It reads the filenames in agent/tools/. Add a file, the model can call it. Delete the file, it cannot.
I like this more than I expected to. It makes this the shortest integration tutorial in the series: giving your agent the ability to read and research the web is going to take five one-line files.
What you need
- An existing eve project (the package ships dual ESM and CJS builds)
- eve v0.24 or later
- A Tabstack API key from console.tabstack.ai
npm install @tabstack/eve eve zodexport TABSTACK_API_KEY="your-key-here"eve and zod are peer dependencies, so your app's single instance of each is what gets used. Zod 3 and Zod 4 both work. On Zod 4 the adapter uses the built-in z.toJSONSchema, and on Zod 3 it falls back to zod-to-json-schema. You do not have to care which, but it is worth knowing nothing will break when you upgrade.
Step 1: add a tool
Create one file under agent/tools/, named after the tool, and re-export the Tabstack tool as its default export.
// agent/tools/research_question.ts
export { researchQuestionTool as default } from "@tabstack/eve";That is the whole integration. Your agent can now answer open questions using multiple web sources, with citations.
The filename is load-bearing. eve derives tool identity from the path, so research_question.ts is what makes the tool research_question. It has to match exactly, and not only because eve requires it. That name is identical in the LangChain, Vercel AI SDK, Mastra, and Python packages, so a prompt written against one works against all of them. Rename the file to web_search.ts and you have quietly opted out of that.
Step 2: add the rest
Same pattern, one file each:
// agent/tools/extract_page_content.ts
export { extractPageContentTool as default } from "@tabstack/eve";// agent/tools/extract_structured_data.ts
export { extractStructuredDataTool as default } from "@tabstack/eve";// agent/tools/generate_structured_data.ts
export { generateStructuredDataTool as default } from "@tabstack/eve";// agent/tools/automate_browser_task.ts
export { automateBrowserTaskTool as default } from "@tabstack/eve";Here is what each one buys you:
| File name | Export | What it does |
|---|---|---|
extract_structured_data.ts | extractStructuredDataTool | Pull specific fields from a URL into a JSON shape you define |
extract_page_content.ts | extractPageContentTool | Fetch a page as clean markdown |
research_question.ts | researchQuestionTool | Synthesised answer with cited sources across multiple pages |
generate_structured_data.ts | generateStructuredDataTool | Fetch a page, then AI-transform it into derived JSON |
automate_browser_task.ts | automateBrowserTaskTool | Run a multi-step, natural language browser task |
The tools resolve TABSTACK_API_KEY lazily on first call, so importing the package never requires a key to be set. Your build steps and tests stay happy without secrets.
Step 3: choose your toolset by choosing your files
This is where eve's convention pays off. In every other framework, narrowing the toolset means editing the array or object you pass to the agent. Here you just do not create the file.
So ask yourself what your agent actually needs before you create all five. If it only reads and researches, create two files and stop. The model never sees automate_browser_task, so it can never decide to try it. Your toolset is visible in a directory listing, which is a more honest description of your agent's capabilities than most codebases manage.
That restraint is worth exercising. Every tool you expose is another option the model weighs on every step, and another way for it to choose wrong. Smaller models feel this most.
One tradeoff worth naming. Unlike the other Tabstack adapters, this package has no aggregate tabstackTools export. Given the file convention there is nowhere sensible to put one, so I think it is the right call, but if you are moving code over from the LangChain or Vercel AI SDK packages it is the difference you will hit first.
Step 4: know what the model is filling in
You never write these arguments yourself, but you will read them in traces when something goes sideways:
extract_structured_data:url,json_schema_json, a JSON encoded JSON Schema stringextract_page_content:urlresearch_question:querygenerate_structured_data:url,instructions,json_schema_jsonautomate_browser_task:task, plus optionalurl,guardrails,data,country,max_iterations,max_validation_attempts
The distinction people get wrong is extract versus generate. Extract pulls out what is already written on the page. Generate fetches the page and produces something derived from it, like a category or a summary nobody wrote down. If the field exists in the HTML, extract it. If you are asking for judgement, generate it.
There are also optional inputs the model can pass for finer control, sent to Tabstack only when present:
effort, as"min","standard", or"max", on the three fetch-based tools."max"does full server-side browser rendering.nocacheto bypass the cachecountry, an ISO 3166-1 alpha-2 code, for geotargeted fetchesmode, as"fast"or"balanced", onresearch_question
If a page comes back thin or empty, it is almost always a client-rendered app and effort: "max" is the fix. That single argument is the entire replacement for running a browser. No Playwright, no binaries in your image, nothing to keep patched.
You can steer this from your agent's instructions rather than in code:
If a page returns little or no content, retry once with effort set to max. Prefer research_question for open questions, and always cite the sources you used.
Step 5: use a custom key or client
The default tools read TABSTACK_API_KEY from the environment. When you need a key per tenant, a custom base URL, or you want to reuse an SDK client you already have, build the tools explicitly and re-export the one you want:
// agent/tools/research_question.ts
import { createTabstackEveTools } from "@tabstack/eve";
const tools = createTabstackEveTools({ apiKey: process.env.MY_KEY });
export default tools.research_question;The file convention still applies. The filename decides the tool name, whatever you did inside.
Step 6: failures
When a tool call fails it throws TabstackToolError, carrying a normalised message plus an HTTP status for API errors, and eve surfaces it in the tool result. The model sees a readable failure rather than a stack trace, which means it can retry or explain rather than falling over.
Inputs are also validated against the core Zod schema before the SDK call runs. When a model produces malformed arguments, it fails fast with a clear message instead of sending a broken request and waiting for the API to reject it.
One behaviour to know up front: automate_browser_task runs non-interactively. It never pauses for human-in-the-loop form input, so it cannot block your agent waiting for someone to fill in a field. It returns the final answer along with the data it extracted and the pages it visited. If you genuinely need a human in the middle of a browser session, that is the interactive mode flow in the API rather than this tool.
Where to take it next
Start with two files. research_question.ts and extract_page_content.ts cover most of what a general assistant needs, and you can add the structured tools when a real use case asks for them.
When you add automate_browser_task, put a guardrails instruction in your agent's system prompt. It is a plain English constraint on what a run may do, and on anything pointed at a live site it is the difference between a read-only browse and an agent cheerfully clicking a button you did not want clicked.
The package is on npm as @tabstack/eve, and the full API reference is at docs.tabstack.ai.
Ready to build? Sign up for 10,000 free credits, no card required, and hand your eve agent its first live page. Full API in the docs.