Tool registry
Tools are what the agent acts with. On each turn, the intent classifier picks a subset, and the tool loop lets the model call them until it can answer. This page lists the built-in registry and the tools with behaviour worth knowing. Customizing tools covers removing, replacing and adding them.
The ToolEntry shape
DEFAULT_TOOLS is a flat record of named tools. Each ToolEntry defines what a tool does and how the agent uses it:
{
description: string; // Full tool description for the model
inputSchema: ZodType; // Validated input parameters
outputSchema?: ZodType; // Structured output (improves reliability)
execute: (input, state, options?) => Promise<any>; // never throws — returns { error } instead
// Classifier and help metadata
classificationPrompt?: string; // One line: when to activate this tool — what the classifier reads
tags?: string[]; // Category labels (e.g. 'route', 'traffic')
examples?: string[]; // Usage examples, in the metadata a custom classifier receives
examplePrompts?: string[]; // Natural-language prompts shown by the help tool
relatedTools?: string[]; // Tools often used together, listed by help
dependsOn?: string[]; // Tools that must run first, shown to the classifier
// Loop control
alwaysActive?: boolean; // Offered every step, whatever the classifier picked
endsTurnOnCall?: boolean; // Calling it ends the turn
// Per-turn scope — see Scope-aware data tools
scopeSchema?: ZodType; // Shape of `toolScopes[<name>]` the classifier emits
scopePrompt?: string; // Explains the scope shape to the classifier
}
If the entry must be rebuilt per turn, a tool can also be a ToolEntryBuilder, a function from ToolBuildOptions to
ToolEntry. Add a scopable custom tool shows one.
Cancellation
execute's third argument carries the AI SDK's abortSignal for the call, which fires when the turn is cancelled:
type ToolExecuteOptions = { signal?: AbortSignal };
Forward it to every service call your tool makes, so a cancelled turn also cancels the HTTP request. An aborted call
rejects with SDKAbortError. Because execute must never throw, catch it and return an error shape:
import { SDKAbortError, discoverPlaces } from '@tomtom-org/maps-sdk/services';
execute: async ({ query }, state, options) => {
try {
const results = await discoverPlaces({ query, signal: options?.signal });
return { count: results.features.length };
} catch (error) {
if (error instanceof SDKAbortError) return { error: 'Cancelled.' };
return { error: `Search failed: ${error instanceof Error ? error.message : String(error)}` };
}
},
Do not forward it into work that outlives the turn, such as a monitor's recurring tick. That work runs after the signal has aborted. A monitor treats a rejected tick as fatal and clears its interval, so a leaked signal stops the monitor.
What's in the registry
| Category | Tools |
|---|---|
| Location | locatePlace, reverseGeocode, getCurrentLocation, getViewport, calculateBBox |
| Places & search | discoverPlaces, getPOICategoryCodes |
| Routing | setRoute, addWaypointsToRoute, removeWaypointsFromRoute, replaceWaypointInRoute, getCurrentWaypoints, startRouteMonitor, stopRouteMonitor |
| Reachable areas | findReachableAreas, for isochrones and isodistances. It needs an API key with Calculate Reachable Range access (private preview) |
| Your own data | addByodSource, setByodLayers, updateByodDisplay — see Bring your own data |
| Traffic incidents | getTrafficIncidents, setTrafficIncidentsMonitor, clusterIncidents, focusIncidents |
| Traffic area analytics | getTrafficAreaAnalytics, updateTrafficAreaAnalyticsDisplay |
| Trackers (geofence and proximity alerts) | createTracker, getTrackers, getTrackerHistory, clearTracker |
| Data tools | analyseData, processData, monitorAnalysis — see Scope-aware data tools |
| Map display | updatePlacesDisplay, updateRoutesDisplay, updateWaypointsDisplay, clearMap |
| Map control | flyTo, zoomInOrOut, setPitchBearing, setMapStandardStyle, getStandardMapStyles, setLanguage, setGeopoliticalView (whose view of disputed territories the map and later searches follow) |
| Map styling | setMapStyling — what the base map shows and how it looks, every knob by its map-wide id: Styling Foundations, whole base-map layer groups such as 3D buildings (groups.buildings3D.visible), the POI icons and their categories pois., the live traffic overlays and their filters traffic.flow. / traffic.incidents., hillshade terrain.hillshade. and 3D terrain terrain.elevation.; a map preset, a reset, or a theme from a few colours or an image whose accent also recolours routes and place pins |
| MapLibre direct | executeMaplibreCode, getMapStyleLayers, setLayoutProperties, setPaintProperties |
| State | recallState, setEntryMode, resetState |
| Utilities | clarifyIntent, help |
analyseData and processData take JavaScript as a parameter instead of a fixed argument list, as
Code generation explains. The
Agent Toolkit Plugin API Reference has every tool's input and output schema.
The help tool
When a user asks "what can you do?" or "show me routing examples", the model calls help (summary or detail, filtered
by tag or query) to surface tool descriptions, example prompts and related tools.
In your own code, getDefaultToolPrompts() returns every tool's examplePrompts as a Record<ToolName, readonly string[]>.
They make starter suggestions for your chat UI that stay in sync with the registry.
Tools that read state vs. fetch data
Some tools fetch fresh data from TomTom services, and others read state. recallState reads and never
fetches:
- No arguments - A snapshot of the session: the places, routes and ranges loaded, entry modes, map style, POIs and traffic.
{ kind }- The index of one kind:places,routes,ranges,geometries,byod,incidentsortrafficAreaAnalytics.{ kind, id }- One entry, summarised.
When a user asks "what were those places?" an hour into a session, the model calls recallState instead of guessing.
Asking the user with clarifyIntent
clarifyIntent asks the user to choose among a few options before the agent acts. It is alwaysActive, so the model can
ask mid-task. By default, the model also writes the questions as prose, for UIs without a form. If your UI renders the
tool call's questions as a form, build the tool with rendersForm: true:
import { createClarifyIntentTool, createMapAgent } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
createMapAgent(map, {
model,
tools: { clarifyIntent: createClarifyIntentTool({ rendersForm: true }) },
});
- The turn then ends on the call, with no prose. Render the call's
introandquestions(each withid,question,optionsandmultiSelect), and send the user's choices as the next message. createClarifyIntentToolalso overridesdescription,classificationPrompt,examples,examplePromptsandalwaysActive.
What getCurrentLocation sends
getCurrentLocation asks for the user's position through the browser's permission prompt, and returns it to the model.
The model provider you configured receives the position with the rest of the conversation. The prompt
names only your site, not the model, so tell users in your UI that the position goes to the assistant.
By default, the tool sends only what a "near me" query needs:
| Field | Default | With fullPrecision: true |
|---|---|---|
position |
[lng, lat] rounded to 3 decimals (about 100 m) |
the exact fix |
accuracy |
111 m, or the device's accuracy when that is coarser | the device's accuracy |
timestamp |
not sent | the fix's timestamp |
| Fix requested | enableHighAccuracy: false, a fix up to 60 s old |
enableHighAccuracy: true, a fresh fix |
To send the exact position, replace the default with createGetCurrentLocationTool:
import { createGetCurrentLocationTool, createMapAgent } from '@tomtom-org/maps-sdk-plugin-agent-toolkit';
createMapAgent(map, {
model,
tools: { getCurrentLocation: createGetCurrentLocationTool({ fullPrecision: true }) },
});
To never ask for the position, remove the tool. "Near me" queries then fall back to the map view:
createMapAgent(map, { model, tools: { getCurrentLocation: false } });
getTrafficIncidents where schema
getTrafficIncidents takes its area as where: { mode: 'within', … }, with the same multi-region fields as
discoverPlaces: viewport, boundingBox, queries, placeIds, geometries, range, route. They resolve to
bounding boxes, combined into one of at most 10,000 km². Without where, the tool uses the viewport.
// Current viewport
getTrafficIncidents({});
// Named area (geocoded → polygon bbox)
getTrafficIncidents({ where: { mode: 'within', queries: [{ query: 'Paris', queryAs: 'place' }] } });
// Buffered corridor around the latest route
getTrafficIncidents({ where: { mode: 'within', route: { widthMeters: 1000 } } });
// Multi-source union
getTrafficIncidents({
where: { mode: 'within', queries: [{ query: 'Amsterdam' }], placeIds: ['G55fc4abe-...'] },
});
- A named area wins over
viewport: truewhen the model sends both. Strict-mode providers such as Azure and OpenAI fill every field. - By default, incidents shown on the map are monitored (
monitor) every 60 s (monitorIntervalMs). Only present incidents are fetched, unlesstimeValidityFiltersays otherwise.
Related
- Customizing tools — remove, replace or add tools.
- Bring your own data — your GeoJSON as agent layers.
- Scope-aware data tools —
analyseData/processDataper-turn scope. - Code generation — JavaScript-as-parameter tools and their threat model.
- State — what each tool reads from and writes to.
- How it works — the classifier and tool loop.
- Securing your agent — the caps on tool arguments, and what bounds a turn.