Tools and their parameters
A tool is a configured, named instance of something the platform offers: a built-in, a tool imported from an MCP server, a flow, or custom code. This page is about the built-ins and the parameters they accept, because two facts about them surprise everyone who meets them through the API for the first time:
- An instance's parameters are not a copy taken when it was created. They are the deployment's current ones. A parameter the platform added last week is accepted by an instance created last year, and nothing is re-created or refreshed to get it.
fixed_parameterson aPATCHreplaces the whole set. It does not merge. Sending the one key you want to add silently unfixes the rest.
The calendar built-ins are the running example throughout.
Discovering the built-ins
Code
One entry per built-in this deployment runs. The answer describes the deployment, not your rows: it is the same for every organization, less any built-in that needs a feature your key does not carry. This is the read to make before creating anything, and the only thing that can say what a built-in accepts today.
One entry, in full:
Code
| Field | What it is |
|---|---|
name | The built-in's name, and what an instance of it carries in config.builtin when you create it. |
input_schema | Every parameter the built-in accepts today, the keys a person fixes included. This is the schema the instance has, not a template to copy and edit. |
fixed_parameters | A list of key names, not values: the parameters a person is expected to fill on the instance, because they decide what the instance is rather than what one call does. Here, which calendar and which timezone. |
provider | The connection provider an instance needs — google for the calendars — or null for a built-in that needs no authorization of yours. |
required_flag | The feature an organization must have to make an instance of it, or null. Entries naming a feature your key does not carry are absent from the answer altogether, so what you read here is what you may create. |
An entry with input_schema: null is a built-in no client creates by hand — the
three a collection brings with it (custom_data_query, custom_data_get,
custom_data_write, which appear in list_tools as query_<slug>,
get_<slug> and write_<slug> the moment the collection exists). It is listed
because the deployment runs it, and there is nothing to configure on it.
The five calendar built-ins
All five fix calendar_id and timezone, and all five point at a google
connection. Which of them that connection backs depends on what the person
authorized: see_busy backs the busy read and nothing else, see_appointments
backs the listing and no write, manage_events backs the listing and every
write. A client who wants both reads grants both — the busy read is its own
permission at Google too.
| Built-in | What it does | Parameters besides the fixed two |
|---|---|---|
calendar_read_busy | The busy ranges between two instants, and nothing computed on top: no free slots, no "next available". | time_min and time_max together, or days alone (1–31) from the moment of the call; never both forms. |
calendar_list_events | Each event between two instants as the diary holds it — start, end, title, location, its identifier, whether the platform created it — the person's own entries included. | The same interval as the busy read. |
calendar_create_event | Writes the event it was given. It does not check that the slot fits unless refuse_overlap says so. | start, end, summary (required), description, location, attendees, refuse_overlap. |
calendar_update_event | Replaces what it is given and leaves the rest as it was: a name learnt after booking, a visit moved, a second person invited. | event_id (required), then any of start, end, summary, description, location, attendees, refuse_overlap. Moving an event gives both start and end; a start alone would leave the length to a guess. |
calendar_cancel_event | Removes the event, and is its own action — so an agent can be allowed to change visits without being allowed to cancel them. | event_id. |
An event is changed or cancelled only if the platform created it in that same
calendar; one a person put there answers exactly as one that does not exist. The
identifier is the one calendar_create_event answered with.
refuse_overlap: whether a write checks the slot first
A create or an update can refuse a slot that overlaps anything already in the diary, and the refusal names the ranges it collides with, so the model can offer another time instead of booking on top of a visit.
It is off unless it is turned on, because some clients overlap on purpose.
It is a parameter of the two write tools like any other — not a setting of its
own — so a client turns it on by fixing refuse_overlap: true on the instance;
left unfixed, it is the model's to pass or not. It is a read just before the
write and not a lock, so two writes at the same instant may still both land.
location: kept exactly as it was given
Where the event is, as an address or as coordinates written lat,lng. It is
stored as given and read back the same way: nothing is geocoded, reformatted or
looked up. calendar_list_events returns it as part of each event, and an
update replaces it.
Creating an instance
Code
Three things about that body:
config.builtinis what makes it an instance of this built-in, exactly asconfig.flow_idis what makes a tool a flow. Spell the name aslist_builtinsgave it; a name this deployment does not run reaches nothing.- No
input_schema. What a built-in accepts is the platform's to decide, so there is nothing to declare: the instance has the deployment's schema whatever you send. connection_idand never a credential. The Google connection is authorized by the person whose diary it is, through Google's consent screen, andlist_connectionsgives you its id; itscapabilitiessay which of the five tools it backs. A tool's configuration that carried a key itself is refused when it is created or changed.
The answer is the instance. A tool's config is never returned — it is the
place credentials can live — which is why builtin and connection_id are
published as their own fields:
Code
The answer carries one more field, elided above for length: input_schema, the
built-in's current schema — the same object the catalogue listed. Why it is the
current one, and not the one that existed when the row was written, is the next
section.
A flow as a tool
A flow is offered to an agent as one tool by an instance with
source "builtin" and the flow's id in config.flow_id. Nothing else goes
in config: no endpoint, no connection_id, no header. The platform runs the
flow itself, so there is no address to call it at, and an HTTP tool pointed at
this platform is not a way to run one.
Code
input_schemais yours to write. It is what the agent fills in when it calls the flow, and the flow's steps read each value as$ref:trigger.<key>(here$ref:trigger.order_number). A flow that needs nothing from the conversation takes{"type": "object", "properties": {}}.- The answer's
idis what an agent carries: inglobal_tool_idson the draft (edit_draft) for every situation, or in a node'stool_ids(update_node) for one. - Testing the instance runs the flow, every step, with the parameters you
send:
POST /api/v1/tools/{id}/testwith{"parameters": {"order_number": "A-1"}}.
An instance accepts what the built-in accepts today
Code
A built-in instance's parameters are the deployment's current ones. What a
built-in accepts is the platform's to decide and it changes when the platform
does, so an instance created months ago accepts — and offers the model — what
that built-in accepts now, less what you fixed on it. Nobody re-creates or
refreshes an instance to receive a parameter the platform added. location
and refuse_overlap were both added to the calendar writes after the first
instances of those tools existed, and they work on every one of them.
Two consequences worth stating plainly, because both have been got wrong:
- An instance is not evidence about what a built-in accepts. If the
parameter you are looking for is not where you expected it on a tool row,
read
GET /api/v1/tools/builtins— the deployment's own answer — before concluding it does not exist. - A value fixed on a parameter the built-in no longer has stays on the instance and reaches nothing. It is not an error and it is not applied; it is a leftover to clean up.
fixed_parameters on the instance says which keys a person fixed, and the
model is shown the schema less those keys: the instance always says which
values are fixed on it.
Fixing a parameter
A parameter fixed on the instance by a person is absent from what the model is
shown. The model does not know it exists, cannot ask for it and cannot
overwrite it — which is the whole point for calendar_id and timezone: a
model that could name the calendar could name the wrong one, and one that could
pick the timezone would be an hour out on a schedule nobody could audit. The
record of the conversation shows what each run actually ran with, fixed values
included, so a fixed value is hidden from the model without being hidden from
you.
Any declared parameter of any instance can be fixed or left to the model, one
parameter at a time, from the instance's own schema. Turning the overlap check
on is therefore not a special call — it is fixing refuse_overlap on the
instance. Which brings us to the one trap on this page.
The trap: fixed_parameters replaces the whole set
PATCH /api/v1/tools/{tool_id} is a partial edit: only the fields you send
change. But fixed_parameters is one field whose value is an object, and the
object you send replaces the stored one whole. What your change does not name
is no longer fixed and returns to the model.
So this call, which looks like "also fix the overlap check":
Code
is accepted, and it leaves the instance fixing refuse_overlap and nothing
else. calendar_id and timezone are no longer fixed, so the model is now
shown both and asked to name them itself — and since the schema requires them,
the next call either guesses a calendar or fails outright.
The right call reads the instance, merges, and sends the whole set back:
Code
Code
Unfixing a parameter is the same call without it: there is no "remove this key"
operation, because the set you send is the set that is fixed. Every other
field of update_tool behaves as you would expect — a name alone changes the
name and touches nothing else.
Read, merge, write, every time you touch
fixed_parameters. Two people editing one instance an hour apart, each sending only their own key, leave it fixing whatever the second one sent.
If you are a model reading this
- Read the catalogue, then create.
GET /api/v1/tools/builtinsnames every built-in, its current parameters, the keys a person fixes and the connection provider it needs. Guessing a parameter name or copying one out of an older instance is how an instance gets created that the runner then refuses. - Do not send
input_schemafor a built-in. It is the platform's, not yours; sendsource: "builtin"andconfig.builtinand leave the schema alone. - Never report that a parameter does not exist because one tool row does not mention it. The catalogue is the answer; a row is one client's configuration of it.
- Treat
fixed_parametersas read-modify-write. APATCHthat names it replaces it. - A tool's
configis never returned. If you need to know which built-in an instance is, readbuiltin; for its authorization,connection_idorconnection_group.
Where next
- How it fits together — which pieces a job needs, and the words the API uses for each.
- Book an appointment without double-booking — a booking built end to end, as the sequence of operations it is, run as a test against the API before it was published.
- API Reference —
list_builtins,create_tool,get_tool,update_toolandtest_tool, with their schemas.