Read and change the data of a Sasha App
The job
Work with the records of a Sasha App, such as a timesheet, without its view: read and total rows, add a record, and correct a row.
Prerequisites
- The connection holds
apps:invoke. Admin, staff and member connections can all hold it. - The person has
useormanageaccess on the app.apps:invokenever gives access to an app by itself. - The app stores data and its package validates. To add or change rows, the app declares its data as
read-write. - A change needs a person to approve it. That works in a client that offers MCP form elicitation, or in the Sasha web interface. Another client can read, but cannot make a change. See the app action tools.
The example app is timesheet. Its table entries has the tools app__timesheet__entries__list, app__timesheet__entries__get, app__timesheet__entries__aggregate, app__timesheet__entries__create, app__timesheet__entries__update and app__timesheet__entries__delete. It also exposes the action log-hours as app__timesheet__log_hours.
Steps
Find the app's tools in your tool list. Read each tool's description: it says whether a table is shared or owner-scoped. You can also call
open_app__timesheetwith{}to show the app's view in a client that shows MCP Apps. It changes nothing.Read recent rows with
list.{ "query": { "where": [{ "field": "date", "operator": "gte", "value": "2026-09-28" }], "orderBy": [{ "field": "date", "direction": "desc" }], "limit": 20 } }The result is in
structuredContent, withrows. When there are more rows, the result hasnextCursor. For the next page, send the samequeryagain, withnextCursorin it ascursor.Total the hours for each project with
aggregate. Do not fetch every row to add them up yourself.{ "aggregation": { "where": [ { "field": "date", "operator": "gte", "value": "2026-09-28" }, { "field": "date", "operator": "lte", "value": "2026-10-04" } ], "groupBy": ["project"], "metrics": { "totalHours": { "sum": "hours" } } } }{ "groups": [ { "project": "Client onboarding", "totalHours": 7.5 }, { "project": "Regional health review", "totalHours": 22 } ] }Add a record. The app has an action for this job, so use the action instead of
create: it applies the app's own rules. Make a newidempotencyKeyfor this change.{ "input": { "date": "2026-10-01", "project": "Regional health review", "hours": 6.5 }, "idempotencyKey": "log-hours-2026-10-01-1" }What happens next depends on the client:
- With form elicitation, the client asks the person to confirm. If they accept, the action runs and the result is the action's declared result, for example
{ "id": 412, "date": "2026-10-01", "project": "Regional health review", "hours": 6.5 }. - In the Sasha web interface, the first call changes nothing. It returns
structuredContent.confirmationwith aconfirmationToken. Ask the person to approve. If they say yes, call again with the sameinput, the sameidempotencyKeyand theconfirmationToken. If they say no, do not call again. - In any other client, the call is refused with
APP_CONFIRMATION_REQUIRED. Tell the person that they can make the change in the Sasha web interface, or from a client that offers form elicitation.
- With form elicitation, the client asks the person to confirm. If they accept, the action runs and the result is the action's declared result, for example
Correct a row. First read it with
get, to learn its_sasha_version.{ "id": 412 }{ "row": { "id": 412, "created_by": 14, "date": "2026-10-01", "project": "Regional health review", "hours": 6.5, "note": null, "_sasha_version": 1 } }Then call
update, with_sasha_versionasexpectedVersionand a newidempotencyKey. The person confirms it as in step 4.{ "id": 412, "changes": { "hours": 7 }, "expectedVersion": 1, "idempotencyKey": "fix-entry-412-hours" }The result is the changed
row, with_sasha_version2.
A delete takes id, expectedVersion and idempotencyKey. It is permanent, with no undo and no history. See the app table tools for every operation and its arguments.
What can go wrong
The full lists are in the app table tools and the app action tools. An app tool refuses with an error result, and structuredContent.error holds code, message, retryable and a diagnosticId to quote.
APP_CONFIRMATION_REQUIRED: this client cannot confirm a change, and a retry gets the same answer. Tell the person.APP_CONFIRMATION_DECLINED: the person said no. Ask them what they want instead.APP_CONFLICT: the row changed after you read it. Callgetagain, check the new values, and retry with the new_sasha_versionand a newidempotencyKey.APP_IDEMPOTENCY_MISMATCH: you used anidempotencyKeyagain with different arguments. Use a new key for a new change.APP_RECORD_NOT_FOUND: there is no row with thatidthat you can see. In an owner-scoped table, rows of other people are not visible.APP_FIELD_UNDECLARED: a column name is not declared for the table, or you cannot change that column.MCP error -32602: Tool app__timesheet__entries__update not found: the connection does not holdapps:invoke, or the person has no access to the app, or the app's data isreadonly. Read thepermissionssection of getDocs.
If an action or a table tool is missing, unclear or unsafe for the job, report one concrete issue with suggestImprovement. Do not include records, source or paths.