Tool definitions
Your host-app definition carries a tool catalog: the operations inside your product that an agent may perform. Each tool is a declarative record. Tanzanite turns it into a function the model can call, invokes your HTTP endpoint when the model calls it, and maps the response back into something the agent can read.
You write no code for this. A tool is data, which is what makes it reviewable — a reviewer can see exactly what an app can do before any user is asked to consent to it.
One tool, in full
Section titled “One tool, in full”{ "name": "place_object", "description": "Place an object in the current scene at the given coordinates.", "category": "write", "dangerLevel": "moderate", "requiresApproval": false, "resultRenderer": "image", "parameters": { "type": "object", "properties": { "sceneId": { "type": "string", "description": "Scene to place into." }, "assetId": { "type": "string", "description": "Asset to place." }, "x": { "type": "number" }, "y": { "type": "number" }, "rotation": { "type": "number", "description": "Degrees, 0–360." } }, "required": ["sceneId", "assetId", "x", "y"] }, "endpoint": { "method": "POST", "path": "/scenes/{{args.sceneId}}/objects", "headers": { "X-Acme-Client": "tanzanite" }, "queryParams": { "dryRun": "{{args.dryRun}}" }, "bodyTemplate": { "asset": "{{args.assetId}}", "position": { "x": "{{args.x}}", "y": "{{args.y}}" }, "rotation": "{{args.rotation}}" } }, "responseMapping": { "objectId": "id", "previewUrl": "preview.url" }}Fields
Section titled “Fields”The tool’s identifier. The agent sees it as integration_<name> — so
place_object above is offered to the model as integration_place_object. The
prefix is not decoration: it is what keeps a host app’s tool from colliding with,
or being mistaken for, a platform tool.
Names are also how you refer to the tool in context pins, approvals and logs.
Pick stable ones; renaming a tool is, to every agent that learned it, a deletion
plus a creation.
description
Section titled “description”What the model reads to decide whether to call this tool. It is prefixed with
[<App name>] when presented, so the agent always knows whose tool it is.
This field does more work than any other. Write what the tool does, when to use it, when not to use it, and what the arguments mean in your domain. A tool with a vague description is called at the wrong moments, and no amount of prompt context elsewhere fixes it.
category
Section titled “category”read | write | action
read— fetches information and changes nothing.write— creates or modifies data in your product.action— triggers something with an effect beyond stored data: a build, a send, a deploy.
The category is part of what your app is approved for, and it is re-checked on every catalog refresh. See the ceiling.
dangerLevel
Section titled “dangerLevel”safe | moderate | destructive
Independent of category, because they answer different questions. category
says what kind of operation it is; dangerLevel says how bad it is if the agent
is wrong. A write that appends a comment is safe; a write that overwrites a
scene is destructive.
Be honest here. Understating danger to avoid an approval prompt is the change most likely to be caught in review, and the one most likely to burn a user’s trust in your integration if it is not.
requiresApproval
Section titled “requiresApproval”When true, the agent must get a human approval before the call executes. The
session moves to waiting_human and emits a human_input_needed event
carrying the tool name and the arguments it intends to use, which you answer with
POST /v1/sessions/:id/human-input/:inputId.
Set it on anything destructive, anything that spends the user’s money outside Tanzanite, and anything a user would be startled to discover after the fact.
resultRenderer
Section titled “resultRenderer”image | diff | link | table | json
A hint for how the first-party transcript should present this tool’s result. Pick
the one that matches your responseMapping output: image for a URL to a
picture, diff for a before/after, link for a deep link back into your app,
table for rows, json for anything else.
This is a fixed enum, and it is the entire vocabulary. A host app may never hand the platform markup — not HTML, not a template, not a component. A render hint cannot become a script injected into another user’s transcript; a markup field always eventually can.
parameters
Section titled “parameters”A JSON Schema object describing the tool’s arguments. type must be "object",
with properties and a required array. Per-property you can use type,
description, enum, items, nested properties and nested required.
Constrain aggressively. An enum of the four asset kinds you actually support is
worth more than a string and a sentence asking the model nicely. Describe every
property — the description is read at call time, and an undescribed parameter is
a guessed parameter.
endpoint
Section titled “endpoint”Where the call goes. path is appended to your definition’s baseUrl.
| Field | Purpose |
|---|---|
method |
GET, POST, PUT, DELETE or PATCH. |
path |
Appended to baseUrl. May interpolate arguments. |
headers |
Extra headers for this tool. Auth is added by the platform. |
queryParams |
Query string, values may interpolate arguments. |
bodyTemplate |
Request body for POST, PUT and PATCH. |
Argument interpolation uses {{args.<name>}} in path, queryParams and
bodyTemplate. Anything pinned by the session’s context is merged into args
and wins over what the model supplied, which is how you keep an id the model
must not choose out of the model’s hands — see
context is a pin.
Authentication is added by the platform, in the shape your definition declares,
carrying the per-user credential from Connect.
Do not put credentials in headers.
responseMapping
Section titled “responseMapping”Maps fields out of your HTTP response into the result the agent sees. Keys are the names the agent gets; values are dotted paths into your response body.
{ "objectId": "id", "previewUrl": "preview.url", "warnings": "meta.warnings" }Map only what the agent needs. Returning your whole response object costs tokens on every call and buries the two fields that matter — and a field you did not mean to expose to a model is a field you have exposed to a model.
polling (optional)
Section titled “polling (optional)”For an endpoint that returns a job rather than a result. When present, Tanzanite calls the tool, then polls until the job reports done.
| Field | Purpose |
|---|---|
statusUrl |
Where to poll. |
statusField |
Dotted path to the status value in the poll response. |
completeValue |
The value of statusField that means success. |
failedValue |
The value that means failure. |
resultMapping |
Like responseMapping, but applied to the final poll response. |
pollIntervalMs |
Delay between polls. |
maxPolls |
Give up after this many. |
Pick pollIntervalMs and maxPolls so their product comfortably exceeds your
job’s realistic worst case. A tool that gives up while the job is still running
reports a failure for work that then completes, and the agent acts on the wrong
belief.
The toolsUrl catalog contract
Section titled “The toolsUrl catalog contract”A definition can carry a static catalog, a toolsUrl, or both. When toolsUrl
is set, the platform re-fetches your catalog at the start of every session, so
you can ship a new tool without touching your definition.
Three rules govern it:
It must be on the same origin as baseUrl. A catalog endpoint elsewhere
would let the record that says what your app can do live somewhere other than
the app itself.
A refreshed catalog is filtered against the ceiling you were approved for. See below — this is the important one.
A stale snapshot is used when the catalog is unreachable. Your endpoint being down degrades your integration to the last known-good catalog rather than breaking every session. This is a safety net, not a strategy: a catalog that stays unreachable stays frozen at whatever it was.
The approved ceiling
Section titled “The approved ceiling”Your definition is approved for a maximum category and a maximum
dangerLevel. When a refreshed catalog arrives, every tool in it is checked
against that ceiling, and a tool that exceeds it is dropped, not accepted.
This is why a catalog refresh cannot be used to widen reach. An app reviewed and
approved for read tools cannot become an app with destructive action tools
by editing the JSON its own server returns — the new tools simply do not appear.
Raising the ceiling is a change to the definition, which bumps its version and
sends existing users through
re-consent.
Plan for it: if a dropped tool is missing from your sessions, the ceiling is the first thing to check, and the fix is a definition change rather than a catalog change.
Prompt context
Section titled “Prompt context”Your definition also carries a block of prompt context describing your product to the agent: what it is, what the tools operate on, the conventions the agent should follow. It is capped in length and is presented to the model as clearly-marked third-party content, so instructions in it cannot override the platform’s own rules or the user’s.
Write it as domain briefing, not as commands. “Scenes are addressed by id; assets
are immutable once placed” is useful. “Always call place_object first” is not,
and will not be obeyed the way you expect.
A checklist before you publish
Section titled “A checklist before you publish”- Every tool has a description that says when not to use it.
- Every parameter has a description, and every closed set is an
enum. dangerLevelis honest, andrequiresApprovalis set on everything destructive.responseMappingreturns the few fields the agent needs and nothing else.pollingbudgets exceed your realistic worst case.- Your
toolsUrlis on the same origin asbaseUrland answers quickly. - Nothing in the catalog exceeds the ceiling you are asking to be approved for.