Calling tools
Every tool call goes through your organization's policy before anything reaches your system. This page explains what the Gateway offers an agent, what each call returns, and how an agent should wait for an approval.
What the agent sees
tools/list returns:
get_execution_status, always. The agent uses it to follow up on earlier calls.- The tools the policy could let this agent run. Tools that are turned off, and tools the policy always denies to this agent, aren't listed. A tool that's allowed only under some conditions, for example only below an amount, is listed.
Each tool comes with its description and its input schema, taken from your system's API description, or from the operations an admin described in the console. Tool names look like erp.purchase_orders.create or erp.create_purchase_order: the system key your admin chose, a dot, and the operation's id, in lowercase. Use the names tools/list returns rather than building them yourself.
If the Gateway is turned off, or hasn't been able to sync with the Control Plane for too long, the list holds only get_execution_status. Calling a tool the agent can't see answers unknown_tool, the same as a tool that doesn't exist.
The result of a call
Every tools/call returns one result object. It's in the result's structuredContent, and the same JSON is in its first text content block, for clients that only read text:
{
"status": "pending_approval",
"execution_id": "0199d2a4-5c1e-7b3a-9f10-2c8e4b7a1d55",
"result": null,
"code": null,
"message": null,
"approval_url": null,
"input_errors": null
}
| Field | What it holds |
|---|---|
status | Where the call stands. See the table below. |
execution_id | The call's id in Gatelion. Use it with get_execution_status, and quote it when you ask someone to look at the call in the console. |
result | When the call succeeded: what your system answered, limited to the fields the tool is set up to return. Fields that hold secrets come back as "[REDACTED]". |
code | Why the call didn't succeed, as a stable code your agent can act on. See Error codes. |
message | A short explanation for people. Don't parse it. |
approval_url | Only for outside agents, while the call waits for the customer: the page where the customer confirms it. |
input_errors | When the arguments were refused: a list of {path, code, message}, one per problem. |
Fields that don't apply are null; they are never left out. The result's isError is true when the status is denied, failed, unknown, rejected or expired.
A field holds a secret when the tool marks it as sensitive, or when its name says so, such as password, api_key or access_token. Names are read as whole words, so fields such as passenger_name or boarding_pass come back as they are.
A system's own error messages never reach the agent. When your system refuses a call, the agent gets its error code (or http_ followed by the status, such as http_409), not its free text.
Statuses
| Status | Meaning | What the agent should do |
|---|---|---|
| Succeeded | The call ran and your system answered. | Use result. |
| Waiting for approval | The policy requires a person to approve this exact request. Nothing has been sent yet. | Tell the user it's waiting, then check later with get_execution_status. |
| Approved | Approved, and the Gateway is running it or about to. | Check again in a few seconds. |
| Rejected | The approver (or the customer) said no, the request could no longer be approved, or it was cancelled because something changed while it waited (code says what, for example policy_changed). Nothing was sent. | Tell the user. Don't ask again unchanged after approval_rejected. After a cancellation, see Handling errors. |
| Expired | Nobody decided in time. Nothing was sent. | Tell the user. Ask again only if they still want it. |
| Denied | The Gateway refused the call before sending anything. code says why. | See Handling errors. |
| Failed | It was sent, or tried, and definitely didn't succeed. | Look at code. A read can be tried again; for a write, see Handling errors. |
| Unknown: reconcile | A write was sent, but no reliable answer came back. It may or may not have happened. | Don't send it again. Tell the user a person must check. See below. |
Reads and writes that don't need approval run while the agent waits, so the first answer is usually succeeded, failed, unknown or denied.
Waiting for approval
When a call needs approval, the first answer is pending_approval with an execution_id. Approvers in your organization are notified and decide in the console. When one approves, the Gateway runs the call once, with exactly the arguments the approver saw.
The agent follows up with the status tool:
{"name": "get_execution_status", "arguments": {"execution_id": "0199d2a4-5c1e-7b3a-9f10-2c8e4b7a1d55"}}
It answers with the same result object: pending_approval until someone decides, then approved while it runs, then the final status, with result when it succeeded.
- Poll gently. People take minutes or hours to decide, not seconds. Checking every 30 to 60 seconds is plenty; the Gateway itself picks up decisions about every 5 seconds. Better still, tell the user the request is waiting and check when they come back to it.
- Requests expire. If nobody decides within the environment's approval time (24 hours unless an admin changed it), the status becomes
expiredand nothing runs. - Only the agent's own calls. The status tool answers only for calls this agent made. Any other id, or an id that doesn't exist, answers
unknownwith codenot_found. An id that isn't a UUID answersunknownwith codeinvalid_arguments. - Don't make the call again while it waits. A second call is a second request, which needs its own approval and could run twice.
When the customer confirms
For an outside agent acting for one of your customers, some calls wait for the customer's confirmation instead of (or as well as) your staff's. The result then has an approval_url and this message:
Waiting for the customer: ask them to open approval_url and confirm. Then check with the status tool.
The agent should show the link to the customer and ask them to open it. The customer signs in again and sees exactly what the agent asked for. After that, the agent checks with get_execution_status as above. See What your customers see.
When the outcome is unknown
A write ends unknown when the Gateway sent it but got no reliable answer: a timeout, a dropped connection, a server error, a redirect, or an answer too large or not readable. Your system may have done it, or not.
Reads never end unknown: a read that gets no usable answer is failed, and can be tried again.
Arguments
Before the policy looks at a call, the Gateway puts its arguments into one standard form, so that the same request always gets the same decision and the approver sees exactly what will be sent:
- Unknown fields are refused. Send only the fields in the tool's input schema. An extra field gives
invalid_argumentswith anunknown_fielderror. - Text is normalized. Strings are put in Unicode normal form (NFC) and trimmed of leading and trailing spaces.
- Digits are normalized in amount and number fields: Arabic-Indic and Persian digits count the same as
0–9, and1500.00is the same as1500. - Identifiers can't mix scripts. Fields marked as identifiers (codes, references) may use Latin or Arabic letters but not both, plus ASCII digits and
-_./. Anything else givesmixed_script,unsupported_scriptordisallowed_character. - Size limits. The arguments may be at most 64 KB and nested at most 32 levels deep. A whole MCP request may be at most 256 KB.
When arguments are refused, the status is denied, the code is invalid_arguments, and input_errors lists each problem with a JSON path such as /max_total:
{
"status": "denied",
"code": "invalid_arguments",
"message": "The arguments do not match the tool's input schema.",
"input_errors": [
{"path": "/max_total", "code": "invalid_decimal", "message": "Expected a decimal number such as 1500 or 1500.25."}
]
}
The agent can fix the arguments and call again.
Related: Handling errors · Approvals · Error codes
Still stuck? Contact support from the console.