docs · tools
How tools work
You know what a tool is and how it reaches your agent. This page is the operating manual: what a run actually does, what it assumes about your session, and what the four ways it can go wrong mean.
Calling one
A tool is invoked by name, with a params object. What comes back is JSON shaped like the fields that tool declares — the agent reasons over those, not over a screenshot:
run("linkedin-get-profile", { slug: "gastonfrancois" })In practice you never type that. The model picks the tool and fills the params from what you asked for in plain language; the shape above is just what it ends up sending.
A run is deterministic
This is the property everything else hangs off, so it is worth stating plainly: there is no model inside a run. The tool already contains the requests to make and the code to read the answer. Calling it twice with the same params does the same thing twice.
That is what you get from it:
- It is fast, because nothing is being decided — no screenshot to look at, no element to identify, no round trip to a model in the middle.
- It is cheap, because the page never enters the context window. Only the params go in and only the fields come out.
- It fails honestly. A deterministic thing either works or breaks in a way you can name — which is what makes the typed errors below possible. An agent improvising against a page can always half-succeed and tell you it went fine.
A composite keeps the property: it runs its steps in order and the glue between them is stable data — an id, a URL, a resolved region — never live browser state. Each step would work just as well on its own.
Side effects
Every tool declares what running it does to the world. This is the one field to read before calling something you have not called before:
| Level | Meaning | Examples |
|---|---|---|
| read | Changes nothing — safe to run and retry freely | search people, read a product, list orders |
| write · reversible | Changes state, and the change can be undone | add to cart, react to a post, repost |
| write · irreversible | Sends or publishes for real. There is no undo | send a DM, post a comment, submit a connection request |
A run of a write-irreversible tool executes immediately — nothing pauses to ask. The message is sent, the comment is public. Treat the level as the gate: build the confirmation into your own flow before calling one.
Sessions, not API keys
Most tools assume you are already logged in on the site. There are no tokens to configure and no credentials in a tool definition by design: the session lives in the browser profile the agent drives, exactly as it would if you were clicking around yourself.
- Already signed in → just run the tool. It rides the cookies the profile already has.
- Not signed in → open the site and log in by hand once, then retry. Nothing automates the login.
- Session expired mid-run → the run comes back
re-authand leaves the login tab in front of you. Log in, run it again.
Each tool declares what it assumes about the environment, and for the vast majority that is a single line: a live session on its site. Which browser holds that session, and why it is a separate one, is on the MCP server page.
Three ways a tool executes
Each tool records how it does its work. You rarely have to act on it, but it explains why some tools are quicker and sturdier than others:
| Kind | How it runs | Trade-off |
|---|---|---|
| fetch-replay | A request issued inside the page, from the site’s own origin, so the browser attaches the session itself | The common case on signed-in sites, and the cheapest — the tab is usually already there |
| playwright | Real UI steps — navigate, click, type, wait for an element | Works where nothing else does; slower, and more sensitive to a redesign |
| http | A direct HTTP call, made outside the browser | Carries no cookies, so it only fits public endpoints that need no session |
When a tool fails
Failures are typed, so an agent can recover by rule instead of guessing. Three cases cover almost everything:
| Error | What happened | What to do |
|---|---|---|
re-auth | The session expired or was never there | Log in manually in the tab that was left open, then retry. Retryable |
not-applicable | The action does not apply — no permission, the thing does not exist, the state is wrong | Stop. Retrying changes nothing |
tool-broken | The site moved: a selector or an endpoint changed | The tool has to be re-learned. This is what the request → save loop is for |
Beyond those, a tool can return an error field of its own for conditions it knows about — a bad input, a rate limit, a product that is out of stock. Those are part of its declared result, not failures of the tool.
The distinction that earns its keep is re-auth versus tool-broken. One is your environment’s fault and is fixed by logging in; the other is the tool’s fault and is fixed by re-learning it. Collapsing them into “it didn’t work” makes every stale login look like breakage.
When a site changes underneath
It will. Sites ship redesigns, rename endpoints and rotate internal ids — a tool frozen against yesterday’s version eventually stops matching reality, and says so with tool-broken.
The fix is not a patch you write. It is the same loop that created the tool: the agent falls back to the browser, does the action the slow way, and request → save replaces the recipe with one that matches the site as it is now. The tool keeps its name and its params, so nothing that calls it has to change — it just gets a new version underneath.
Each tool carries that version and a small health record — when it last succeeded, how many times it has failed — which is what makes a genuine breakage visible instead of showing up as an agent that quietly got worse.