App action tools (app__<app>__<action>)
Tool names: app__<app>__<action>
Scopes: apps:invoke
When to use
An App Action is a named business step that an app declares, for example log-hours or weekly-project-totals. It runs on the server with the same checks as a direct data call. Use an action tool when the app has one for your job: it applies the app's own rules.
The tool name is app__<app>__<action>, with each - in the app id and the action id changed to _. For example, the action log-hours of the app timesheet is app__timesheet__log_hours.
An action tool is in the tool list only when all of these are true:
- The connection holds
apps:invoke, and the person hasuseormanageaccess on the app (the same rule as theopen-appfamily). - The app's
APP.mdexposes the action to the model withexposeToModel: true. Other actions are for the app's own view only. - The action compiles on this instance.
Arguments:
input: an object that matches the input schema that the app declares for the action. The tool's input schema shows it.idempotencyKey: only on an action that changes data, and required there. Use a new key, 1 to 128 characters, for each new change. Use the same key only to retry the same change: the retry then returns the first result and does not change data twice.confirmationToken: only on an action that changes data. Send it only in the Sasha web interface, as described below.
The result is the action's declared result. It is in structuredContent, and the text is the same value as JSON.
Confirmation for a change
An action that changes data needs a person to approve it before it runs. How that happens depends on the client:
- A client that offers MCP form elicitation shows the person a question:
Confirm "<tool>": <description>. If the person accepts, the action runs. If they decline, the call is refused withAPP_CONFIRMATION_DECLINED. - A client that does not offer form elicitation cannot confirm a change. The call is refused with
APP_CONFIRMATION_REQUIRED, and a retry gets the same answer. The person can make the change in the Sasha web interface, or from a client that offers form elicitation. - In the Sasha web interface, the first call returns a result that is not an error and changes nothing.
structuredContent.confirmationhasrequired: true, aconfirmationToken,expiresAtand the action'seffects. Ask the person to approve the change. If they say yes, call the tool again with the sameinput, the sameidempotencyKeyand theconfirmationToken. A token works once, for those exact arguments, for 10 minutes. If the person says no, do not call the tool again.
A read-only action needs no confirmation and takes no idempotencyKey.
Example
The timesheet app exposes log-hours, which adds one row to its entries table. Call app__timesheet__log_hours:
{
"input": { "date": "2026-10-01", "project": "Regional health review", "hours": 6.5 },
"idempotencyKey": "log-hours-2026-10-01-1"
}
After the person confirms, the structured result (structuredContent) is the declared result of log-hours:
{ "id": 412, "date": "2026-10-01", "project": "Regional health review", "hours": 6.5 }
In the Sasha web interface, the first call returns the challenge instead:
{
"confirmation": {
"required": true,
"confirmationToken": "1b16b1df538ba12dc3f97edbb85caa7050d46c148134290feba80f8236c83db9",
"expiresAt": "2026-10-02T09:24:05.112Z",
"effects": { "readOnly": false, "destructive": false, "idempotent": false }
}
}
Then, after the person approves:
{
"input": { "date": "2026-10-01", "project": "Regional health review", "hours": 6.5 },
"idempotencyKey": "log-hours-2026-10-01-1",
"confirmationToken": "1b16b1df538ba12dc3f97edbb85caa7050d46c148134290feba80f8236c83db9"
}
Refusals and what to do
An app tool refuses with an error result. The text is the error message, and structuredContent.error holds code, message, retryable, a diagnosticId to quote, and sometimes details.field.
APP_CONFIRMATION_REQUIRED: the message says that this client cannot confirm a change, that a retry will not help, and where the change can be made instead. Tell the person that they can make it in the Sasha web interface, or from a client that offers form elicitation.APP_CONFIRMATION_DECLINED: the person was asked and said no. Ask them what they want instead. Do not send the same change again unless they say yes.APP_INPUT_INVALID(app_input_invalid):inputdoes not match the declared schema, an argument is not in the envelope, or a change has noidempotencyKey. Read the tool's input schema.APP_IDEMPOTENCY_MISMATCH(app_idempotency_mismatch): theidempotencyKeywas used before with different arguments. Use a new key for a new change.APP_BUSY(app_busy): a call with the sameidempotencyKeyis still running. Wait, then retry with the same arguments.APP_ACCESS_DENIED(app_access_denied): the person no longer has access to the app.APP_ACTION_UNAVAILABLEorAPP_REVISION_UNAVAILABLE: the action or the app changed after the tool list was built. Call again from a new tool list.- If the connection does not hold
apps:invoke, or the action is not exposed to the model, the tool is not in your tool list, and a call returnsMCP error -32602: Tool app__timesheet__log_hours not found(with the name you called). Read thepermissionssection ofgetDocs.
If an action is missing, unclear or unsafe for the job, report one concrete issue with suggestImprovement. Do not include records, source or paths.
Related
- The
open-appfamily opens the app's view. - The
app-tablefamily reads and changes the app's rows directly. listAppFilesshows the app's source, includingAPP.mdand the action files, for admin and staff users who manage the app.suggestImprovementreports a problem with an action.getDocshas thepermissionssection.