Skip to content

Simpler.Asia

asia.simpler/simpler-asiasimpler.asiastreamable-httpv1.0.0Scanned

Free back office for small businesses in Singapore and Southeast Asia: invoices, expenses, stock.

318 tools: 38 can delete or cancel things, 12 do something that can't be undone, 177 change things and 91 only read.

Tools: 38 delete, 12 irreversible, 177 write, 91 read.
  • Reads private data · 28
  • Reads text others write · 7
  • Can send data out · 11
  • 24 findings

Anlyon alternative

227 of these tools change something. Run those as governed actions.

Declare what an action changes, and Anlyon puts it under a shared limit, binds every approval to the exact request, and grades the outcome. It works on any HTTPS API you can describe as an action.

Anlyon does not run, proxy or govern this MCP server. A tool your own code calls directly is not routed through Anlyon and is not governed by it.

Import an OpenAPI spec →

Tools

Grouped by what each tool does, most consequential first. Open one for its reasons and full description; descriptions are the publisher's own words.

318 tools

Deletes or cancels 38Claude Code: deny
  • contact_deleteDelete a contact you own.No findings

    Why it is in this group

    • The name "contact_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a contact you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • contact_note_deleteDelete a contact_note you own.No findings

    Why it is in this group

    • The name "contact_note_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a contact_note you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • desk_surface_deleteDelete a desk_surface you own.No findings

    Why it is in this group

    • The name "desk_surface_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a desk_surface you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • edit_project_deleteDelete a edit_project you own.No findings

    Why it is in this group

    • The name "edit_project_delete" says "edit": a word for a change.
    • The description's first verb is "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a edit_project you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • expense_budget_deleteDelete a expense_budget you own.No findings

    Why it is in this group

    • The name "expense_budget_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a expense_budget you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • expense_category_deleteDeletes a custom category — categories are tenant-shared, so any tenant member can remove one regardless of who created it; the 9 seeded defaults are undeletable and refuse with CO…No findings

    Why it is in this group

    • The name "expense_category_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a custom category — categories are tenant-shared, so any tenant member can remove one regardless of who created it; the 9 seeded defaults are undeletable and refuse with CONFLICT (default_category: true). Expenses already carrying the deleted category keep their category_id untouched and simply render as Uncategorized; nothing is reassigned or blocked.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • expense_deleteDeletes an expense you created — draft and rejected rows only.No findings

    Why it is in this group

    • The name "expense_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes an expense you created — draft and rejected rows only. Pending, approved, and reimbursed rows are approval-trail records and refuse with CONFLICT (approval_locked: true), and the lock is pinned into the delete itself so a status change racing your read still refuses instead of silently deleting. NEXT: expense_reject it first (admin+) if it must leave the books.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • expense_recurrence_deleteDelete a expense_recurrence you own.No findings

    Why it is in this group

    • The name "expense_recurrence_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a expense_recurrence you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • invoice_adjustment_delete[REQUIRES APPROVAL] Retires a dilution fact row — the only correction lane (adjustments are append-only; amounts are never edited).No findings

    Why it is in this group

    • The name "invoice_adjustment_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    [REQUIRES APPROVAL] Retires a dilution fact row — the only correction lane (adjustments are append-only; amounts are never edited). If the retired row was a credit that settled the invoice, the status rolls from 'paid' back to 'sent' and paid_at clears; the remaining balance and the derived 'written_off' state re-derive from the live rows immediately. An agent's call parks here for human approval. NEXT: invoice_adjustment_record to re-record the correct amount.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • invoice_client_deleteDelete a invoice_client you own.No findings

    Why it is in this group

    • The name "invoice_client_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a invoice_client you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • invoice_item_deleteSoft-deletes a line item — it leaves the invoice and its derived total immediately, but the row is retired for audit history, never removed.No findings

    Why it is in this group

    • The name "invoice_item_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Soft-deletes a line item — it leaves the invoice and its derived total immediately, but the row is retired for audit history, never removed. expected_updated_at is required: the line's updated_at from your read (invoice_item_read); without it the removal is refused and the refusal names the current value. Refuses when the parent invoice is 'paid' (ledger-locked: retire the payment first) or 'cancelled' (terminal: its lines stay as issued), and with CONFLICT when the line changed since that read (read it again). If retiring the line settles the remaining balance on a partially-paid invoice, the parent re-derives to 'paid' by itself. NEXT: invoice_read to see the updated total.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • invoice_payment_delete[REQUIRES APPROVAL] Retires a payment fact row — the only correction lane (payments are append-only; amounts are never edited).No findings

    Why it is in this group

    • The name "invoice_payment_delete" says "payment": a word that usually means money moves, which cannot be taken back.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    [REQUIRES APPROVAL] Retires a payment fact row — the only correction lane (payments are append-only; amounts are never edited). If retiring it un-settles the invoice, the reversal itself rolls the status from 'paid' back to 'sent' and clears paid_at. An agent's call parks here for a human to approve in the Simpler UI; a human's call is its own approval. NEXT: invoice_payment_record to re-record the correct amount.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • invoice_recurring_deleteDelete a invoice_recurring you own.No findings

    Why it is in this group

    • The name "invoice_recurring_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a invoice_recurring you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • invoice_reissueVoids a 'sent' invoice with no money on it and reissues it under the next number: 'sent', dated today, same client, currency, notes, terms and lines, tax on today's rules, the old …1 finding

    Why it is in this group

    • The description's first verb is "void": a word for removing or ending something.
    • The server marks it not destructive (destructiveHint: false), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "invoice_reissue" not destructive (destructiveHint: false), and its own name, description or input give the class "destructive".

      Matched text

      annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}; The description's first verb is "void": a word for removing or ending something.

      What this check looks for
    Description, as the server wrote it
    Voids a 'sent' invoice with no money on it and reissues it under the next number: 'sent', dated today, same client, currency, notes, terms and lines, tax on today's rules, the old days to pay unless due_date is sent. A foreign-currency sg_gst invoice issued before today needs fx_rate_to_sgd and fx_rate_date for today unless its rate is dated today. The old one is cancelled, its client link stops working, both trails link them. Paid, part-paid, credited or written-off invoices refuse: correct them with a credit note (invoice_adjustment_record). When present, the second content block is the new invoice as copy-ready text. Nothing is sent. NEXT: invoice_share only if the user asks for a link.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • myinvois_customer_deleteDelete a registered buyer.No findings

    Why it is in this group

    • The name "myinvois_customer_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a registered buyer. E-invoices already generated for this buyer keep their sealed copy of the buyer party and are never touched. Refuses an id outside your tenant with NOT_FOUND: call myinvois_customer_list for valid ids.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_template_deleteDelete a saved line-item preset.No findings

    Why it is in this group

    • The name "myinvois_template_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a saved line-item preset. E-invoices generated from it are never touched: each one sealed its own lines. Refuses an id outside your tenant with NOT_FOUND: call myinvois_template_list for valid ids.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • quote_item_deleteSoft-deletes a quote line — it leaves the quote and its totals immediately, but the row is retired for audit history, never removed.No findings

    Why it is in this group

    • The name "quote_item_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Soft-deletes a quote line — it leaves the quote and its totals immediately, but the row is retired for audit history, never removed. expected_updated_at is required: the line's updated_at from your read (quote_item_read); without it the removal is refused and the refusal names the current value. Refuses with CONFLICT when the line changed since that read (read it again and decide on the current line) and on a converted quote (its lines are frozen: change its invoice instead). NEXT: quote_read to see the quote.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • spreadsheet_file_deleteSoft-delete a spreadsheet file: stamps deleted_at on the row so it drops out of the library and folder listings, while the row and its R2 object both survive untouched — there is n…No findings

    Why it is in this group

    • The name "spreadsheet_file_delete" says "delete": a word for removing or ending something.
    • The description says "cannot be reversed".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Soft-delete a spreadsheet file: stamps deleted_at on the row so it drops out of the library and folder listings, while the row and its R2 object both survive untouched — there is no purge or restore lane yet, so this cannot be reversed through any current door. Requires the file id; an empty id is refused before anything is touched. NEXT: spreadsheet_file_read to confirm deleted_at is now set — the generic list tool still includes soft-deleted rows.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • spreadsheet_folder_deleteDelete a spreadsheet_folder you own.No findings

    Why it is in this group

    • The name "spreadsheet_folder_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a spreadsheet_folder you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • sticky_note_deleteDelete a sticky_note you own.No findings

    Why it is in this group

    • The name "sticky_note_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a sticky_note you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • stock_item_deleteSoft-delete a stock item by id: sets is_active to 0 while its movements-ledger history stays intact.No findings

    Why it is in this group

    • The name "stock_item_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Soft-delete a stock item by id: sets is_active to 0 while its movements-ledger history stays intact. stock_item_adjust then refuses the id as NOT_FOUND and stops resolving it by name, so a later delivery under the same name creates a fresh item instead of reviving this one. stock_item_list and stock_item_read still return it with is_active: 0 — only the dashboard's stats header excludes it. Restore by running a normal update that sets is_active back to 1; there is no separate restore door.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • studio_character_deleteDelete a studio_character you own.No findings

    Why it is in this group

    • The name "studio_character_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a studio_character you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • studio_environment_deleteDelete a studio_environment you own.No findings

    Why it is in this group

    • The name "studio_environment_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a studio_environment you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • studio_project_deleteDelete a studio_project you own.No findings

    Why it is in this group

    • The name "studio_project_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a studio_project you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • studio.delete_elementDelete a scene, clip, or sticker by id (captions soft-delete reversibly).No findings

    Why it is in this group

    • The name "studio.delete_element" says "delete": a word for removing or ending something.
    Description, as the server wrote it
    Delete a scene, clip, or sticker by id (captions soft-delete reversibly). Deleting a scene with ripple_secondary "shift" (the default) removes its span of time from every lane, so clips, captions and stickers over the scene go with it and everything after it moves earlier in sync; "none" closes the main track alone. A deleted clip still leaves its gap open whatever ripple_secondary says; ripple_delete closes it. Refuses a clip on a locked track, and a shift that moves a locked track (unlock it with lock_track, or pass ripple_secondary "none").

    Annotations the server declares

    • readOnlyHint: false
  • studio.delete_elementsDelete several scenes, timeline clips, stickers and captions in one edit: pass 1 to 200 ids in element_ids.No findings

    Why it is in this group

    • The name "studio.delete_elements" says "delete": a word for removing or ending something.
    Description, as the server wrote it
    Delete several scenes, timeline clips, stickers and captions in one edit: pass 1 to 200 ids in element_ids. All or nothing: an unknown id, a clip on a locked track, or a locked track a scene's cut would move refuses the whole call, names the first offender, and deletes nothing (unlock it with lock_track, or pass ripple_secondary "none"). Scenes follow ripple_secondary as in delete_element: "shift" (default) removes each deleted scene's time from every lane, keeping later clips in sync; "none" closes the main track alone. The receipt's clips_removed and clips_trimmed count each unlisted clip once, however many deleted scenes it spanned. Listed clips leave gaps (ripple_delete closes one); captions hide reversibly (restore_fillers brings them back).

    Annotations the server declares

    • readOnlyHint: false
  • studio.remove_dead_airCut every silent pause the transcript found OUT OF THE FOOTAGE, keeping a little breathing room at each one.No findings

    Why it is in this group

    • The name "studio.remove_dead_air" says "remove": a word for removing or ending something.
    Description, as the server wrote it
    Cut every silent pause the transcript found OUT OF THE FOOTAGE, keeping a little breathing room at each one. This is the real edit, not a transcript cleanup: the video gets shorter. Needs a transcript first (transcribe). Not undoable.

    Annotations the server declares

    • readOnlyHint: false
  • studio.remove_fillersHide filler words (um, uh, you know, kind of) from the transcript.No findings

    Why it is in this group

    • The name "studio.remove_fillers" says "remove": a word for removing or ending something.
    Description, as the server wrote it
    Hide filler words (um, uh, you know, kind of) from the transcript. REVERSIBLE: rows are soft-deleted, excluded from burn-in and SRT, and restore_fillers brings them back. Read the composition afterwards if you need to see which rows went.

    Annotations the server declares

    • readOnlyHint: false
  • studio.remove_silenceDelete every '[silence]' row from the TRANSCRIPT.No findings

    Why it is in this group

    • The name "studio.remove_silence" says "remove": a word for removing or ending something.
    Description, as the server wrote it
    Delete every '[silence]' row from the TRANSCRIPT. This cleans up the caption text only - the footage keeps its full length and the pauses still play. To cut the pauses out of the video for real, use remove_dead_air. Silence rows are derived markers, not content, so this is a HARD delete and is not undoable - re-run transcription to get them back.

    Annotations the server declares

    • readOnlyHint: false
  • studio.remove_snapshotRemove the name from a studio project's named version, freeing one of its 20 names.No findings

    Why it is in this group

    • The name "studio.remove_snapshot" says "remove": a word for removing or ending something.
    Description, as the server wrote it
    Remove the name from a studio project's named version, freeing one of its 20 names. The version stays stored; from the next save it follows the normal retention and may be deleted. A version without a name answers removed: false. Refuses: a project_id outside your scope (NOT_FOUND): studio_project_list lists your projects; pass one of their ids; a version the project no longer stores (SIMPLER_STUDIO_VERSION_NOT_STORED): call studio.list_versions and pick a listed one; an environment where names are not switched on yet (SIMPLER_STUDIO_SNAPSHOTS_UNAVAILABLE): saving and restoring still work; ask the owner to apply DB_MEDIA migration 0005. Free.

    Annotations the server declares

    • readOnlyHint: false
  • studio.remove_trackRemove one track by track_id.No findings

    Why it is in this group

    • The name "studio.remove_track" says "remove": a word for removing or ending something.
    Description, as the server wrote it
    Remove one track by track_id. A track that still holds clips is refused unless delete_clips is true, which removes its clips with it. Refuses, naming the fix: an unknown track_id, a locked track, a track with clips and no delete_clips, or the last track of its role (every project keeps one of each).

    Annotations the server declares

    • readOnlyHint: false
  • studio.ripple_deleteDelete a scene or timeline clip AND close the time it held.No findings

    Why it is in this group

    • The name "studio.ripple_delete" says "delete": a word for removing or ending something.
    Description, as the server wrote it
    Delete a scene or timeline clip AND close the time it held. scope "all_tracks" (the default) removes that span of time from every lane, so everything after it moves earlier together and stays in sync; clips and captions over the span go with it, and one that crosses its edge is split or shortened. scope "own_track" closes the gap on the element's own lane only (for a scene, the other tracks then drift by its length). delete_element and delete_elements leave a clip's gap open instead. Refuses, naming the fix: an unknown id, a sticker or caption (no lane time to close), a locked track the edit moves, or with all_tracks a clip that runs past the main track's end.

    Annotations the server declares

    • readOnlyHint: false
  • team_assignment_deleteDeletes an assignment outright — only the assigner may call this.No findings

    Why it is in this group

    • The name "team_assignment_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes an assignment outright — only the assigner may call this. The row is removed first, then its attachment and any job photos are best-effort deleted from R2 afterward, so a storage hiccup never leaves the assignment stranded. NEXT: team_assignment_list to confirm it is gone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • user_interest_deleteDelete a user_interest you own.No findings

    Why it is in this group

    • The name "user_interest_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a user_interest you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • user_setting_deleteDelete a user_setting you own.No findings

    Why it is in this group

    • The name "user_setting_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a user_setting you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
  • video.export_cancelStop a video export that is queued or rendering.No findings

    Why it is in this group

    • The name "video.export_cancel" says "cancel": a word for removing or ending something.
    Description, as the server wrote it
    Stop a video export that is queued or rendering. The job ends as failed with "Cancelled by user.", anything it charged is refunded, the render stops, and any partial file is deleted. Cancelling a job that already finished or failed changes nothing (cancelled: false), so a repeat is safe. Refuses an unknown job (SIMPLER_STUDIO_EXPORT_JOB_NOT_FOUND): video.export_create returns job ids. Refuses a read-only connection (SIMPLER_MCP_SCOPE_READONLY): connect again with the 'mcp' scope. Free.

    Annotations the server declares

    • readOnlyHint: false
  • video.export_deleteDelete a finished video export's files (the mp4 and any SRT) now, before the automatic deletion 30 days after the export finished.No findings

    Why it is in this group

    • The name "video.export_delete" says "delete": a word for removing or ending something.
    Description, as the server wrote it
    Delete a finished video export's files (the mp4 and any SRT) now, before the automatic deletion 30 days after the export finished. Every download link for that job stops working at once; the job record stays, so the daily export limit still counts it. Refuses a job that is still rendering (SIMPLER_STUDIO_EXPORT_DELETE_ACTIVE): stop it with video.export_cancel, or follow it to the end with video.export, then delete it. Refuses an unknown job (SIMPLER_STUDIO_EXPORT_JOB_NOT_FOUND): video.export_create returns job ids. Free.

    Annotations the server declares

    • readOnlyHint: false
  • workflow_deleteDelete a workflow you own.No findings

    Why it is in this group

    • The name "workflow_delete" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Delete a workflow you own.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: false
Can't be undone 12Claude Code: ask
  • agent_note_send_cross_tenantSend a cross-tenant AI-to-AI message into another tenant's agent_note inbox — the door that turns a cross_tenant_comm consent grant into an actual reach.Can send data outNo findings

    Why it is in this group

    • The name "agent_note_send_cross_tenant" says "send": a word for sending, and what is sent cannot be taken back.

    What it can reach

    • outboundThe name "agent_note_send_cross_tenant" says "send": it sends or publishes something to other people.
    Description, as the server wrote it
    Send a cross-tenant AI-to-AI message into another tenant's agent_note inbox — the door that turns a cross_tenant_comm consent grant into an actual reach. Both tenants must be KYB-approved and the recipient must hold an active grant (claims can narrow it further); every recipient-side failure refuses identically so the network can't be probed for who exists — only your OWN unapproved KYB answers distinctly, since that discloses nothing about the target. Your sender identity is injected server-side into from_tenant_id and cannot be spoofed. NEXT: agent_note_list on the recipient side confirms delivery.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_createLogs a new expense — amount_cents must be integer CENTS (a decimal string via `amount` also works, and `category` resolves to a category_id by name).No findings

    Why it is in this group

    • The name "expense_create" says "create": a word for a change.
    • The input carries an amount (input.amount) and a currency, so it moves money.
    Description, as the server wrote it
    Logs a new expense — amount_cents must be integer CENTS (a decimal string via `amount` also works, and `category` resolves to a category_id by name). A same-amount, same-UTC-day duplicate for the same user is refused as CONFLICT unless you resend identically with force: true. Status is honor-or-refuse: admin/owner creators land 'approved' by default (or may request it explicitly), everyone else always starts 'draft'. NEXT: expense_submit sends a draft into the approval queue.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_submitSends your own draft expense into the approval queue — draft only; an already-submitted or resolved expense refuses CONFLICT, and only the expense's own creator may submit it.No findings

    Why it is in this group

    • The name "expense_submit" says "submit": a word for a change.
    • The description's first verb is "send": a word for sending, and what is sent cannot be taken back.
    Description, as the server wrote it
    Sends your own draft expense into the approval queue — draft only; an already-submitted or resolved expense refuses CONFLICT, and only the expense's own creator may submit it. Nobody is notified automatically — approvers see it appear in the Team Claims view. NEXT: expense_approve or expense_reject (admin/owner).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • image.style_transfer[paid: $0.04] Apply a style to a photo while preserving the content (FLUX img2img, strength 0.55).No findings

    Why it is in this group

    • The name "image.style_transfer" says "transfer": a word for a change that another call does not undo.
    Description, as the server wrote it
    [paid: $0.04] Apply a style to a photo while preserving the content (FLUX img2img, strength 0.55). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), VALIDATION_ERROR on an empty prompt (name the look), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • invoice_adjustment_record[REQUIRES APPROVAL] Records a credit note, write-off or post-issue discount on an issued invoice.No findings

    Why it is in this group

    • The input carries an amount (input.amount) and a currency, so it moves money.
    Description, as the server wrote it
    [REQUIRES APPROVAL] Records a credit note, write-off or post-issue discount on an issued invoice. A credit_note is numbered from the credit note series, holds its tax at the invoice's rate and takes reason_code and credit_outcome: reduce_balance (default) lowers what is owed; refunded_outside records a refund the business made outside Simpler, moves no money and is how a 'paid' invoice gets one. write_off and discount_post_issue need a 'sent' invoice. Money is integer CENTS (amount_cents) or a decimal STRING (amount); caps: see preconditions. Refuses a draft, a cancelled invoice, an amount over its cap and dispute_settlement; each refusal names the fix. An agent's call parks for approval. NEXT: invoice_read.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_debit_note[REQUIRES APPROVAL] Issues a numbered debit note that raises what an issued ('sent' or 'paid') invoice is owed: an extra charge, or a price or tax correction.No findings

    Why it is in this group

    • The input carries an amount (input.amount) and a currency, so it moves money.
    Description, as the server wrote it
    [REQUIRES APPROVAL] Issues a numbered debit note that raises what an issued ('sent' or 'paid') invoice is owed: an extra charge, or a price or tax correction. It is numbered from the debit note series and adds amount_cents (or a decimal STRING amount), tax at the invoice's rate inside, in its currency; a 'paid' invoice goes back to 'sent' until the rest is paid. Refuses a draft (change its lines instead), a cancelled invoice, an invoice with no lines, another currency, and a paid invoice with no payment recorded (record it first with invoice_payment_record); every refusal names the call to send. Never deleted: undo one with a credit_note. An agent's call parks for approval. Nothing is sent. NEXT: invoice_read.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_payment_listList your invoice_payment rows (newest first).1 finding

    Why it is in this group

    • The name "invoice_payment_list" says "payment": a word that usually means money moves, which cannot be taken back.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "invoice_payment_list" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "invoice_payment_list" says "payment": a word that usually means money moves, which cannot be taken back.

      What this check looks for
    Description, as the server wrote it
    List your invoice_payment rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_payment_readRead one invoice_payment by id.1 finding

    Why it is in this group

    • The name "invoice_payment_read" says "payment": a word that usually means money moves, which cannot be taken back.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "invoice_payment_read" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "invoice_payment_read" says "payment": a word that usually means money moves, which cannot be taken back.

      What this check looks for
    Description, as the server wrote it
    Read one invoice_payment by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_payment_recordRecords a money-in fact row against an invoice (by invoice_id or its invoice_number) in ONE call: money is integer CENTS via amount_cents, or a decimal STRING via amount — never mu…No findings

    Why it is in this group

    • The name "invoice_payment_record" says "payment": a word that usually means money moves, which cannot be taken back.
    • The input carries an amount (input.amount) and a currency, so it moves money.
    Description, as the server wrote it
    Records a money-in fact row against an invoice (by invoice_id or its invoice_number) in ONE call: money is integer CENTS via amount_cents, or a decimal STRING via amount — never multiply or divide yourself. A draft invoice auto-advances to 'sent'; a cancelled invoice refuses; an amount above the remaining balance refuses unless allow_overpayment is true. status 'paid' derives from the ledger this writes, so this is how an invoice becomes paid. A same invoice/amount/day repeat refuses as a likely duplicate unless resent with force:true. NEXT: invoice_read to read it back.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • statement_pdfMake the PDF of a client's statement of account (the same page statement_prepare's document_path shows), printed on A4 with the business's fonts, brand colours and logo, and kept, …Can send data outNo findings

    Why it is in this group

    • The input addresses people (input.to): what it sends cannot be taken back.

    What it can reach

    • outboundThe input takes a destination (input.to): what the tool writes goes there.
    Description, as the server wrote it
    Make the PDF of a client's statement of account (the same page statement_prepare's document_path shows), printed on A4 with the business's fonts, brand colours and logo, and kept, so asking again for an unchanged statement returns the stored file (cached: true) without printing it again. It prepares a file and sends nothing. Returns version, pdf_path (downloads while signed in) and closing_balances, and download_url when signed links are on; while they are off, download_url_unavailable says why and pdf_path still works. Refusals: a client_id that is not one of your clients is NOT_FOUND (find it with invoice_client_search or invoice_client_list); a date not written YYYY-MM-DD, a to after today, a from after to, or a period longer than 366 days is VALIDATION_ERROR naming the field; a client with more than 2000 issued invoices is PAYLOAD_TOO_LARGE SIMPLER_SALES_STATEMENT_TOO_LARGE (sales_receivables still ages them). More than 50 new PDFs (invoices and statements together) in 24 hours is refused RATE_LIMITED SIMPLER_SALES_PDF_DAILY_CAP (wait retry_after seconds; stored PDFs still download); when PDF printing is unavailable (no browser here, storage not set up, printer busy or failing) it answers SERVICE_UNAVAILABLE with a reason_code and retry_after, and the print-ready page stays the fallback. A read-only token or a viewer is refused FORBIDDEN before anything is made. Free.

    Annotations the server declares

    • readOnlyHint: false
  • statement_prepareA client's statement of account over a period of the business's own days (the sales_settings time zone): the balance they owed before it (opening), each invoice issued, payment rec…Can send data out1 finding

    Why it is in this group

    • The input addresses people (input.to): what it sends cannot be taken back.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    What it can reach

    • outboundThe input takes a destination (input.to): what the tool writes goes there.

    Findings

    • Warn

      The server marks "statement_prepare" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

      Matched text

      annotations: {"readOnlyHint":true}; The input addresses people (input.to): what it sends cannot be taken back.

      What this check looks for
    Description, as the server wrote it
    A client's statement of account over a period of the business's own days (the sales_settings time zone): the balance they owed before it (opening), each invoice issued, payment received, credit and write-off in it with a running balance, the balance at its end (closing), and what was still owed at its end aged current / 1-30 / 31-60 / 61-90 / over 90 days past due, counted as sales_receivables counts. Only issued invoices count: drafts and cancelled invoices never do. Money is integer CENTS in each invoice's own currency, one section per currency, never converted or added across currencies; read the figures as given. It prepares nothing and sends nothing: returns the figures, document_path (the print-ready page, while signed in) and pdf_next_call (the statement_pdf call that makes the PDF). A statement with more than 150 rows lists its first rows, whole business days only, and lines_cut says which and gives the next_call that lists the rest; the balances, totals and ageing always cover the whole period. When the result has a second content block, it is the statement as copy-ready text (a very long one shortened), and hosts that draw MCP Apps show the statement with a Copy button, as plain text when it is too long to draw as a document. Refusals: a client_id that is not one of your clients is NOT_FOUND (find it with invoice_client_search or invoice_client_list); a date not written YYYY-MM-DD, a to after today, a from after to, or a period longer than 366 days is VALIDATION_ERROR naming the field; a client with more than 2000 issued invoices is PAYLOAD_TOO_LARGE SIMPLER_SALES_STATEMENT_TOO_LARGE (sales_receivables still ages them). Free.

    Annotations the server declares

    • readOnlyHint: true
  • studio.style_transfer[$0.04/sec at provider cost] Restyle a scene's footage, keeping its motion.No findings

    Why it is in this group

    • The name "studio.style_transfer" says "transfer": a word for a change that another call does not undo.
    Description, as the server wrote it
    [$0.04/sec at provider cost] Restyle a scene's footage, keeping its motion. style must be one of the presets (anime, comic, watercolor, oil_painting, sketch, cinematic, vintage, neon). A free-text style is refused, because free text over the user's footage could change who appears in it; pick the closest preset instead. Mints a NEW asset - the original is never destroyed.

    Annotations the server declares

    • readOnlyHint: false
Changes things 177Claude Code: ask
  • agent_note_mark_readConfirm receipt of a note in your cross-tenant agent_note inbox — the ONLY edit a recipient holds, moving status unread to read under the registry's transition guard.No findings

    Why it is in this group

    • The name "agent_note_mark_read" says "mark": a word for a change.
    Description, as the server wrote it
    Confirm receipt of a note in your cross-tenant agent_note inbox — the ONLY edit a recipient holds, moving status unread to read under the registry's transition guard. Idempotent: re-confirming an already-read note is a no-op, and the subject/body stay fixed forever so a recipient can never rewrite what the sender said. NEXT: agent_note_list to see the rest of the inbox.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • ai.budgetYour prepaid wallet, period spend, caps, and runway as a compact line DSL (~8 lines).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Your prepaid wallet, period spend, caps, and runway as a compact line DSL (~8 lines). Requires auth (your balance is closed data). Read this BEFORE committing paid actions; pair with ai.estimate_cost to price a specific call first.

    Annotations the server declares

    • readOnlyHint: true
  • ai.estimate_costPrice an AI task BEFORE running it: provider passthrough at wholesale cost, no markup — the per-token platform fee is retired, so text/reasoning passes through at cost.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Price an AI task BEFORE running it: provider passthrough at wholesale cost, no markup — the per-token platform fee is retired, so text/reasoning passes through at cost. Anonymous — open catalog includes open pricing. Returns integer micro-USD plus a USD total. NEXT: run the action, then ai.budget to watch the runway.

    Annotations the server declares

    • readOnlyHint: true
  • banking.import_statementImport one bank account's statement as CSV text (CSV today, PDF later) so the forecast starts from the bank's own balance.1 finding

    Why it is in this group

    • The name "banking.import_statement" says "import": a word for a change.

    Findings

    • Info

      The description is 2,501 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...SERVICE_UNAVAILABLE SIMPLER_BANKING_STATEMENT_WRITE_FAIL (retry the same upload)."

      What this check looks for
    Description, as the server wrote it
    Import one bank account's statement as CSV text (CSV today, PDF later) so the forecast starts from the bank's own balance. Owner or admin only. Stores each line and each day's closing balance for the account named by bank_label, account_last4 and currency; uploading the same statement again adds nothing. Name an account the same way every time (the label is matched ignoring case). Before anything is stored, only the last 4 digits of the account number are kept, and runs of 6 or more digits, email addresses and phone numbers are cut from descriptions and counterparties; payee and payer names stay as the statement gives them until the workspace is closed, when they are emptied and the lines are kept as figures and dates. The columns are recognised from the header (date, balance, and one signed amount or separate withdrawal and deposit columns); no bank layout is verified yet, so check the mapping in the reply. The latest balance, if under 31 days old, becomes the forecast's starting cash, graded 'statement', in place of a typed balance for the same account (a typed account whose label ends in other last 4 digits and dated on or after the statement is added to it, and the sum is graded 'declared'; an older one is left out until it is saved again with today's date), and the stored forecast is rerun. Returns counts, statement_balance, the mapping used, balance_checks, and forecast.status with next_step; verify with forecast.read. Refuses with FORBIDDEN SIMPLER_BANKING_ROLE_REFUSED for any other role (ask an owner or admin); VALIDATION_ERROR SIMPLER_BANKING_ACCOUNT_LAST4_INVALID when account_last4 has fewer than 4 digits (send the last 4 as text); VALIDATION_ERROR SIMPLER_BANKING_STATEMENT_FIELDS_INVALID naming each bad field; CONFLICT SIMPLER_BANKING_ACCOUNT_LABEL_CONFLICT with saved_accounts when an account with the same account_last4 and currency is saved under another bank_label, before anything is stored, because one account under two names would count its balance twice (resend with the saved bank_label for that account, or with new_account: true for a different account); VALIDATION_ERROR SIMPLER_BANKING_STATEMENT_UNPARSEABLE with reason and the file's columns when the columns or dates cannot be read (send mapping with 1-based column numbers, or date_order); PAYLOAD_TOO_LARGE SIMPLER_BANKING_STATEMENT_TOO_LARGE over 2,000,000 bytes or 20,000 lines (export a shorter date range); SERVICE_UNAVAILABLE SIMPLER_BANKING_STATEMENT_WRITE_FAIL (retry the same upload).

    Annotations the server declares

    • readOnlyHint: false
  • contact_createCreate a contact and, in the same call, log its first note and/or first interaction — the composite first-touch door.Can send data outNo findings

    Why it is in this group

    • The name "contact_create" says "create": a word for a change.

    What it can reach

    • outboundThe input takes a destination (input.phone): what the tool writes goes there.
    Description, as the server wrote it
    Create a contact and, in the same call, log its first note and/or first interaction — the composite first-touch door. Children validate before the contact write; if the contact commits but a child then fails, the error names the contact's id plus the exact call to finish it, never a silent orphan. A contact with the same email or phone already in this tenant refuses with CONFLICT. NEXT: contact_upsert merges into an existing contact instead of creating a duplicate.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • contact_note_createAdd a note to a contact — the same door as the granular tool, extended only to accept contact_name, contact_email or contact_phone in place of contact_id.No findings

    Why it is in this group

    • The name "contact_note_create" says "create": a word for a change.
    Description, as the server wrote it
    Add a note to a contact — the same door as the granular tool, extended only to accept contact_name, contact_email or contact_phone in place of contact_id. A name match must be unique: more than one hit refuses, listing the candidate ids; zero matches on every alias refuses with the runnable two-step call to create the contact first, then retry. NEXT: contact_upsert when no contact exists yet.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • contact_updateUpdate a contact you own (optionally compare-and-set via expected_updated_at).Can send data outNo findings

    Why it is in this group

    • The name "contact_update" says "update": a word for a change.

    What it can reach

    • outboundThe input takes a destination (input.phone): what the tool writes goes there.
    Description, as the server wrote it
    Update a contact you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • contact_upsertCreate-or-merge a contact keyed on email-then-phone identity — the sync door the future desktop harness and ingest lanes write through.Can send data outNo findings

    Why it is in this group

    • The name "contact_upsert" says "upsert": a word for a change.

    What it can reach

    • outboundThe input takes a destination (input.phone): what the tool writes goes there.
    Description, as the server wrote it
    Create-or-merge a contact keyed on email-then-phone identity — the sync door the future desktop harness and ingest lanes write through. Matching probes email first, then phone; a hit MERGES only the fields you send (omitted fields keep their stored values, so a partial sync payload can never wipe a curated field), a miss creates. Status moves still obey the transition machine — an upsert cannot jump straight to vip. Accepts the same first-note/first-interaction composite children as contact_create.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • desk_surface_createPins one widget or a saved artifact onto the operator's home canvas.No findings

    Why it is in this group

    • The name "desk_surface_create" says "create": a word for a change.
    Description, as the server wrote it
    Pins one widget or a saved artifact onto the operator's home canvas. kind='widget' requires widget_key from the shipped enum; kind='artifact' requires content_ref (an assistant artifact id) — the door enforces whichever pair the kind demands, since the schema alone cannot express it. Rows are private to the creating user, even though teammates share the same tenant. NEXT: desk_surface_list reads the current layout.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • desk_surface_updateMoves, resizes, or restacks a surface already on the desk canvas — x/y/w/h/z_index are real column writes, not a layout cache.No findings

    Why it is in this group

    • The name "desk_surface_update" says "update": a word for a change.
    Description, as the server wrote it
    Moves, resizes, or restacks a surface already on the desk canvas — x/y/w/h/z_index are real column writes, not a layout cache. The surface's identity is frozen here: kind, widget_key, and content_ref never change through this door, only via delete then re-create. NEXT: desk_surface_list to find the id to move.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • edit_project_createCreate a canvas project from a Fabric.js toJSON serialization (canvas_json, required): every embedded data:image src is pulled out, hashed, and written to a tenant-scoped, content-…No findings

    Why it is in this group

    • The name "edit_project_create" says "edit": a word for a change.
    Description, as the server wrote it
    Create a canvas project from a Fabric.js toJSON serialization (canvas_json, required): every embedded data:image src is pulled out, hashed, and written to a tenant-scoped, content-addressed R2 key before the row is saved, so canvas_json never carries image bytes and identical images dedupe across projects. SVG, an unrecognized image type, an oversized embedded image, or a canvas still too large after externalization refuse the whole create — nothing is silently dropped. An optional thumbnail_data_url is externalized the same way into thumbnail_key; thumbnail_key and version are server-set and refuse if you pass them. NEXT: edit_project_update to autosave the same project as it changes.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • edit_project_listList your edit_project rows (newest first).1 finding

    Why it is in this group

    • The name "edit_project_list" says "edit": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "edit_project_list" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "edit_project_list" says "edit": a word for a change.

      What this check looks for
    Description, as the server wrote it
    List your edit_project rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • edit_project_readRead one edit_project by id.1 finding

    Why it is in this group

    • The name "edit_project_read" says "edit": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "edit_project_read" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "edit_project_read" says "edit": a word for a change.

      What this check looks for
    Description, as the server wrote it
    Read one edit_project by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • edit_project_updateSave changes to an existing canvas project by id — the autosave door: send only the fields that changed (name, canvas_json, width, height, thumbnail_data_url), everything else on t…No findings

    Why it is in this group

    • The name "edit_project_update" says "edit": a word for a change.
    Description, as the server wrote it
    Save changes to an existing canvas project by id — the autosave door: send only the fields that changed (name, canvas_json, width, height, thumbnail_data_url), everything else on the row is left alone. A canvas_json you send goes through the same image-externalization and hygiene refusal as edit_project_create (embedded images become R2 keys; SVG, unrecognized types, oversized images, or a still-too-large canvas refuse the whole save). version is bumped by the door itself on every save and thumbnail_key is server-minted from thumbnail_data_url — both refuse if you pass them directly. NEXT: edit_project_read to confirm the saved version.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_aggregateAggregate (count/sum) over your expense rows.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Aggregate (count/sum) over your expense rows.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_approveApproves a submitted expense claim — requires the admin or owner role, and only a 'pending' expense qualifies (CONFLICT otherwise).No findings

    Why it is in this group

    • The name "expense_approve" says "approve": a word for a change.
    Description, as the server wrote it
    Approves a submitted expense claim — requires the admin or owner role, and only a 'pending' expense qualifies (CONFLICT otherwise). Stamps approved_by and approved_at with the caller and the current time; once approved, the row's content freezes for everyone except the approver themselves. NEXT: expense_reimburse (owner only) to pay it out.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_budget_createCreate a expense_budget.No findings

    Why it is in this group

    • The name "expense_budget_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a expense_budget.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_budget_updateUpdate a expense_budget you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "expense_budget_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a expense_budget you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_category_createAdds a custom category to your tenant's category table — slug must be 1-50 chars of lowercase a-z, 0-9, '-' or '_' and unique per tenant (CONFLICT if taken).No findings

    Why it is in this group

    • The name "expense_category_create" says "create": a word for a change.
    Description, as the server wrote it
    Adds a custom category to your tenant's category table — slug must be 1-50 chars of lowercase a-z, 0-9, '-' or '_' and unique per tenant (CONFLICT if taken). is_default is server-owned: supplying it at all refuses with VALIDATION_ERROR, and this door always creates with is_default 0 — the 9 seeded defaults exist only via expense_category_ensure_defaults. sort_order defaults to the next open slot when omitted.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_category_ensure_defaultsSeeds the 9 canonical default categories (Food & Dining, Transport, Utilities, Shopping, Entertainment, Healthcare, Travel, Office & Supplies, Other) into your tenant, skipping any…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Seeds the 9 canonical default categories (Food & Dining, Transport, Utilities, Shopping, Entertainment, Healthcare, Travel, Office & Supplies, Other) into your tenant, skipping any slug that already exists. Takes no arguments and is safe to call repeatedly or concurrently — it converges rather than duplicating. NEXT: expense_category_list to see the result.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_category_updateEdits a category's name, icon, color, or sort_order — slug and is_default are the category's fixed identity, and any attempt to change either refuses with VALIDATION_ERROR.No findings

    Why it is in this group

    • The name "expense_category_update" says "update": a word for a change.
    Description, as the server wrote it
    Edits a category's name, icon, color, or sort_order — slug and is_default are the category's fixed identity, and any attempt to change either refuses with VALIDATION_ERROR. color, when given, must be a #rrggbb hex value. Create a new category instead of trying to rename an existing slug.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_recurrence_createCreate a expense_recurrence.No findings

    Why it is in this group

    • The name "expense_recurrence_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a expense_recurrence.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_recurrence_updateUpdate a expense_recurrence you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "expense_recurrence_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a expense_recurrence you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_reimburseMarks an approved expense as paid out — restricted to the tenant owner because it moves real money, and only an 'approved' expense qualifies (CONFLICT otherwise).No findings

    Why it is in this group

    • The description's first verb is "mark": a word for a change.
    Description, as the server wrote it
    Marks an approved expense as paid out — restricted to the tenant owner because it moves real money, and only an 'approved' expense qualifies (CONFLICT otherwise). Stamps reimbursed_at, after which the row's content freezes unconditionally since the amount approved is the amount recorded as paid. Records the payout only — no funds move automatically.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_rejectRejects a submitted expense claim — requires the admin or owner role, and only a 'pending' expense qualifies (CONFLICT otherwise).No findings

    Why it is in this group

    • The name "expense_reject" says "reject": a word for a change.
    Description, as the server wrote it
    Rejects a submitted expense claim — requires the admin or owner role, and only a 'pending' expense qualifies (CONFLICT otherwise). rejection_reason (up to 500 chars) is optional but records why; rejected is terminal and never counts as spend, so a corrected claim is a fresh expense_create, never a resubmit.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • expense_updateEdits an expense's own fields only — status and approval stamps refuse with VALIDATION_ERROR here since those are transitions (expense_submit, expense_approve, expense_reject, expe…No findings

    Why it is in this group

    • The name "expense_update" says "update": a word for a change.
    Description, as the server wrote it
    Edits an expense's own fields only — status and approval stamps refuse with VALIDATION_ERROR here since those are transitions (expense_submit, expense_approve, expense_reject, expense_reimburse). amount_cents, if changed, must stay integer CENTS. Only the expense's own creator may edit it, and content freezes once the row is 'pending', reimbursed, or approved by someone else — draft, rejected, and your own self-approved rows stay editable.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • forecast.runWHAT-IF / scenario lane: runs monthly data YOU supply through a seeded 5,000-path simulation of monthly revenue and costs; bands are simulation outputs, not calibrated probabilitie…2 findings

    Why it is in this group

    • The name "forecast.run" says "run": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Info

      The description is 8,895 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...xt/event-stream for phase progress (started/fitted/simulated/clustered/complete)."

      What this check looks for
    • Warn

      The server marks "forecast.run" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true}; The name "forecast.run" says "run": a word for a change.

      What this check looks for
    Description, as the server wrote it
    WHAT-IF / scenario lane: runs monthly data YOU supply through a seeded 5,000-path simulation of monthly revenue and costs; bands are simulation outputs, not calibrated probabilities. The paths are clustered into 3-7 outcome bands for the next 6 months, with per-month revenue and cost percentiles and deathRisk, the share of paths the engine marks closed (a distress signal, not a cash-out date). Amounts are in currency (major units, default USD): 1500 or "1500.00" - never cents. currency is one of 16 codes (USD, EUR, GBP, JPY, CNY, AUD, HKD, INR, SGD, MYR, IDR, THB, PHP, VND, BND, MMK); leave it out and USD is assumed, and the output says currency_assumed: true. The engine runs in USD; every output figure converts back into currency at fx_rate_to_usd (USD per 1 unit), named in the top-level currency field. Send cash_balance (the bank balance, negative for an overdraft) with cash_as_of (its date, YYYY-MM-DD, within 45 days) and the cash block adds runway, cash-out share, lowest balance and health from that balance plus each path's revenue minus costs (balance_source 'request'); without them it carries net-flow figures only and its next_step says what to send. Send declared_totals {revenue, costs}, the totals your own source shows for the months sent, and the sums are checked on the server; totals_check echoes them. history_months, shallow (true under 5 months) and blend {yours_pct, peers_pct} (null when shallow) say how much of the result is your own history. Earlier releases removed the per-band cash range and the per-month cash percentiles (they started from an invented balance) and interpreted_money.unit (the top-level currency field replaces it). Refuses with VALIDATION_ERROR and a fix per field: fewer than 2 or more than 120 rows, a date that is not YYYY-MM, revenue or costs sent in cents or as text that is not a number (an exponent such as 1e5, letters other than one currency code or symbol, or a comma that does not group thousands), an amount whose code or symbol names another currency than the one it is read in (currency; cash_balance is read in cash_currency when sent: send that currency, or the amount in it; never converted), no month with revenue, or a currency outside the list; SIMPLER_FORECAST_CASH_WITHOUT_DATE when cash_balance comes without cash_as_of (send both); SIMPLER_FORECAST_CASH_AS_OF_INVALID when cash_as_of is not a real YYYY-MM-DD date or comes without cash_balance; SIMPLER_FORECAST_GRID_TOTALS_MISMATCH when a declared total and the months' sum are more than 0.5% apart (correct the months or the total and resend). Refuses with SERVICE_UNAVAILABLE SIMPLER_FORECAST_FX_UNAVAILABLE when no rate for a non-USD currency is available: retry shortly, or pass USD amounts. Send drivers (at most 20) to ask what if: each is {kind, start, end?, amount?, pct?}, kind one of revenue_pct, cost_pct, revenue_step, cost_step, hire, one_off_in, one_off_out, lose_customer, loan_drawdown, loan_repayment, festive_month. start and end are YYYY-MM (the whole month) or YYYY-MM-DD (that day); revenue_pct, cost_pct, lose_customer and festive_month take months only; start must fall inside the forecast months, and end may be left out to run to the last one. amount is in currency (major units): per month for revenue_step and cost_step (negative for a cut), hire (the full monthly cost), loan_repayment and lose_customer (that customer's average monthly receipts; forecast.run reads no records, so customer_ref is refused here), and the whole sum for one_off_in, one_off_out and loan_drawdown, which hit one month or one day and take no end. In text, commas only group thousands ("4,000.00"); a currency code or symbol may lead or follow ("SGD 4,000") only when it is the currency the amount is read in, and one naming another currency is refused, never converted; no exponent or other letters. The same applies to every amount this tool takes. pct is -100 to 500, not zero, for revenue_pct, cost_pct and festive_month (a revenue_pct on one month you pick: your assumption, not learned seasonality). loan_drawdown and loan_repayment move the balance only, never revenue, costs or net. With drivers the answer adds base and scenario, each with monthly revenue, costs, net, financing and balance percentiles and a cash block, on the same simulated paths; drivers change money on those paths, not growth or closures (no knock-on effects). Refuses with VALIDATION_ERROR and simpler_code SIMPLER_FORECAST_DRIVER_INVALID (fix the field as it says; a start outside the 6 months after the last month sent refuses before anything runs or is counted), SIMPLER_FORECAST_DRIVER_LIMIT (more than 20 drivers: send fewer) or SIMPLER_FORECAST_DRIVER_UNKNOWN_CUSTOMER (customer_ref sent: send amount instead, or use forecast.read view scenarios). No forecast and nothing you send is stored, and your business records are NOT read (the one setting read is the workspace's time zone, to know today; like every tool call, the call itself is counted in usage): for YOUR stored forecast call forecast.run_stored; read the last stored result with forecast.read. cash_currency (optional, one of the same codes) names the currency of cash_balance when it is not currency; the balance converts into currency at the cached rate, and interpreted_money.cash_balance echoes both. Your own accounting system's rows (for example from a Xero, QuickBooks or Zoho connection) add the next 13 weeks: send any of open_receivables (at most 2,000: {client_ref?, issued_at?, due_date, amount, currency?}), paid_history (at most 5,000: {client_ref?, due_date, paid_at, amount?}), open_payables (at most 2,000: {vendor_ref?, due_date, amount, currency?}) and recurring (at most 100: {direction in or out, amount, currency?, cadence weekly, monthly or quarterly, next_date}); an empty list counts as sent. Dates are YYYY-MM-DD; amounts are major units of the row's currency (default currency), converted into currency at the cached rates; client_ref and vendor_ref are your own ids, at most 60 ch… [2895 more characters not shown]

    Annotations the server declares

    • readOnlyHint: true
  • forecast.run_storedRun the tenant's REAL forecast from the books — cash receipts (recorded invoice payments by payment date, plus paid invoices with no recorded payment at their paid date, flagged) a…1 finding

    Why it is in this group

    • The name "forecast.run_stored" says "run": a word for a change.

    Findings

    • Info

      The description is 3,893 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...its summary and its no-forecast error carry readiness with the same refusal_code."

      What this check looks for
    Description, as the server wrote it
    Run the tenant's REAL forecast from the books — cash receipts (recorded invoice payments by payment date, plus paid invoices with no recorded payment at their paid date, flagged) and expenses by expense date, monthly in the tenant's base currency, up to the last complete month (the current month is left out; months are the business's calendar months in its own time zone, set in sales settings, Asia/Singapore when unset, and the books block names it in bucket_timezone) and at most 36 months — through the one import pipeline. The result is STORED, replacing the forecast every member reads, so it is owner or admin only: any other role is refused with FORBIDDEN SIMPLER_FORECAST_IMPORT_ROLE_REFUSED before anything is read, saved, run or charged (ask an owner or admin to run it; forecast.read reads the forecast already stored). No arguments are needed. Optional cash_balance (the bank balance in cash_currency, negative for an overdraft), cash_currency (one of 16 codes) and cash_as_of (its date, YYYY-MM-DD) go together: they are saved as the workspace's declared bank balance, replacing the balance entered before, and then the forecast runs and starts its cash block from that balance. When more than one bank account is saved on the Forecast page, it refuses with CONFLICT SIMPLER_FORECAST_CASH_MULTI_ACCOUNT_REFUSED before anything is saved or run, because one balance would replace them all: ask the owner or an admin to update the balances on the Forecast page, or call without the three cash fields to run from the saved balances. It refuses with SERVICE_UNAVAILABLE SIMPLER_FORECAST_SETTINGS_READ_FAIL when the saved balances cannot be read (retry in a minute). The cash fields refuse like forecast.run's (SIMPLER_FORECAST_CASH_WITHOUT_DATE, SIMPLER_FORECAST_CASH_AS_OF_INVALID, VALIDATION_ERROR naming cash_currency when it is missing or outside the list, or cash_balance when its code or symbol names another currency than cash_currency, before anything is saved) and with SERVICE_UNAVAILABLE SIMPLER_FORECAST_FX_UNAVAILABLE when no rate for cash_currency is available (retry shortly, or send the balance in USD). A refusal after the balance was saved carries cash_balance_saved: true. Cost: no metered model cost. Returns the first-3-months outlook and the cash block (as forecast.read describes it) with money formatted in the business's currency, named in the top-level currency field: the engine runs in USD and figures convert back at the rate pinned on the stored result (fx_rate_to_usd); before this release they were USD. Verify with forecast.read before reporting done. Outlook rows carry distress_risk_pct, the share of paths the engine marks closed by that month (renamed this release from the old death-risk column); they no longer carry a cash median, which started from an invented balance. Runway and cash-out come only from forecast.read's cash block. Refuses with RATE_LIMITED after 5 runs an hour and with 402 when the workspace credit cannot cover the cost: when a forecast is already stored the error names forecast.read, so read that instead. The books block in the result names the base currency, gap months, converted amounts and any paid invoices counted without a recorded payment. Refuses with BAD_REQUEST: SIMPLER_FORECAST_LEDGER_TOO_SHORT when the books have fewer than 2 complete months of receipts or spend, SIMPLER_FORECAST_LEDGER_GAPS when 2 or more of the last 6 complete months have no receipts and no spend, and SIMPLER_FORECAST_LEDGER_NO_RECEIPTS when the books carry no receipts or the last complete month has none. Fix: Record payments against your invoices (invoice_payment_record) and log expenses so each month has activity, or upload a spreadsheet on the Forecast page instead. Then run again. To check first without spending a run, call forecast.read: its summary and its no-forecast error carry readiness with the same refusal_code.

    Annotations the server declares

    • readOnlyHint: false
  • image.add_shadowAdd a product shadow to a cut-out (subject on transparency), cast from its alpha: kind drop, contact or reflection.1 finding

    Why it is in this group

    • The name "image.add_shadow" says "add": a word for a change.

    Findings

    • Info

      The description is 2,545 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...t_key to the next image.* tool, or open download_url when the result carries one."

      What this check looks for
    Description, as the server wrote it
    Add a product shadow to a cut-out (subject on transparency), cast from its alpha: kind drop, contact or reflection. angle 0..359 is the light direction counterclockwise from 3 o'clock (default 120; the shadow falls the other way); distance 0..200 px (default 12; the gap under the base for a reflection); blur 0..100 px (default 16); opacity 0..100 (default 50); transparent true (default) keeps the backdrop see-through, false fills background #RRGGBB (default #ffffff) behind the shadow. contact ignores angle and distance, reflection ignores angle. The result grows past the input by the shadow's reach (distance plus blur) so no visible shadow pixel is cut: output pad {left, top, right, bottom} gives the growth in input pixels and subject_box the subject in the result; clipped true means the growth was trimmed to stay within 4,194,304 pixels. Free: runs on the platform's own deterministic kernel, so the same input and arguments always return the same pixels. Takes PNG, JPEG, WebP, GIF or HEIC. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_EDIT_SHADOW_NEEDS_CUTOUT (no transparent pixels: run image.cut_background and pass its asset_key), VALIDATION_ERROR with reason SIMPLER_EDIT_TOOL_ARGS_INVALID for a value out of range, SIMPLER_EDIT_SERVER_PIXELS_NEEDS_PNG (the file is not an image: send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_EDIT_SERVER_PIXELS_TOO_LARGE (at most 4,194,304 pixels: run image.convert with width and height 2048 first), SIMPLER_IMAGES_BINDING_ABSENT (this deployment has no Images binding yet, owner step O-2: send a standard PNG), SIMPLER_IMAGES_QUOTA_EXCEEDED or SIMPLER_IMAGES_LEGACY_SUBSCRIPTION (an owner account step: send a standard PNG meanwhile), SIMPLER_IMAGES_INPUT_UNREADABLE (send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_IMAGES_INPUT_TOO_LARGE (under 20,000,000 bytes and 100 megapixels), SIMPLER_IMAGES_TRANSFORM_FAILED (retry, or send a standard PNG); nothing is charged on any of them, and INSUFFICIENT_CREDITS or PAYMENT_REQUIRED only when the workspace is already overdrawn or past its spend cap (add credits or raise the cap). NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • image.adjustApply 1..10 adjustments to a photo in order, with the editor's own kernels: a one-op stack returns the same pixels as that tool in the editor.1 finding

    Why it is in this group

    • The description's first verb is "apply": a word for a change.

    Findings

    • Info

      The description is 2,738 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...t_key to the next image.* tool, or open download_url when the result carries one."

      What this check looks for
    Description, as the server wrote it
    Apply 1..10 adjustments to a photo in order, with the editor's own kernels: a one-op stack returns the same pixels as that tool in the editor. ops is a list of {op, params}; each op takes only its own params keys, and a key left out takes that op's default: brightness_contrast (brightness, contrast), exposure (stops), temperature_tint (temperature, tint), vibrance_saturation (vibrance, saturation), curves (rgb, red, green, blue), levels (in_black, in_white, levels_gamma, out_black, out_white), hsl_per_range (range, hue, saturation, luminance), sharpen (amount, radius, shadow_protect, highlight_protect), blur (radius), denoise (luminance_strength, chrominance_strength, radius), vignette (amount, midpoint, feather), grain (amount), black_and_white (red_weight, green_weight, blue_weight, tint_color), auto_tone (aggressive). output_format is png (the default, free), jpeg or webp ($0.0005 for the one Images encode, charged only when the result is delivered). Free for png output, $0.0005 for jpeg or webp: runs on the platform's own deterministic kernel, so the same input and arguments always return the same pixels. Takes PNG, JPEG, WebP, GIF or HEIC. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), VALIDATION_ERROR with reason SIMPLER_EDIT_TOOL_ARGS_INVALID for an unknown op, a key the op does not take, a value outside the op's range, levels with in_white not above in_black, or an output_format outside png, jpeg and webp, SIMPLER_EDIT_SERVER_PIXELS_NEEDS_PNG (the file is not an image: send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_EDIT_SERVER_PIXELS_TOO_LARGE (at most 4,194,304 pixels: run image.convert with width and height 2048 first), SIMPLER_IMAGES_BINDING_ABSENT (this deployment has no Images binding yet, owner step O-2: send a standard PNG), SIMPLER_IMAGES_QUOTA_EXCEEDED or SIMPLER_IMAGES_LEGACY_SUBSCRIPTION (an owner account step: send a standard PNG meanwhile), SIMPLER_IMAGES_INPUT_UNREADABLE (send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_IMAGES_INPUT_TOO_LARGE (under 20,000,000 bytes and 100 megapixels), SIMPLER_IMAGES_TRANSFORM_FAILED (retry, or send a standard PNG); nothing is charged on any of them, and INSUFFICIENT_CREDITS or PAYMENT_REQUIRED only when the workspace is already overdrawn or past its spend cap (add credits or raise the cap). NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • image.convert[paid: $0.0005] Convert an image to PNG, JPEG or WebP, and optionally bound its size, with the Cloudflare Images service: a PNG, JPEG, WebP, GIF or HEIC goes in and the result is s…1 finding

    Why it is in this group

    • The name "image.convert" says "convert": a word for a change.

    Findings

    • Info

      The description is 2,373 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...download_url, when the result carries one, works for 24 hours without a session."

      What this check looks for
    Description, as the server wrote it
    [paid: $0.0005] Convert an image to PNG, JPEG or WebP, and optionally bound its size, with the Cloudflare Images service: a PNG, JPEG, WebP, GIF or HEIC goes in and the result is stored in this workspace. format is png, jpeg, webp (AVIF is not offered). quality (1-100, default 85) applies to jpeg and webp. width and height (16-4096 px) bound the result and fit picks how: scale-down (the default) shrinks to fit and never enlarges, contain fits inside and may enlarge, cover and crop fill the box and cut the overflow, pad fits inside and fills the rest with background. background (#RRGGBB) fills transparent pixels and padding; a JPEG, which has no transparency, is laid on white unless you name a colour. One call is one Images transformation at $0.0005, charged only when the result is stored; the preview is never charged. Returns JSON {images:[{url, asset_key, download_url?, width, height, content_type}], price, output: {format, bytes, quality?, input_format, input_bytes}, next} plus a preview; a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), VALIDATION_ERROR naming the field for a format outside the list, a quality with png, a size outside the range, a fit without width or height, or a background that is not #RRGGBB; SIMPLER_IMAGES_BINDING_ABSENT (this deployment has no Images binding yet, owner step O-2: send a standard PNG), SIMPLER_IMAGES_QUOTA_EXCEEDED or SIMPLER_IMAGES_LEGACY_SUBSCRIPTION (an owner account step: send a standard PNG meanwhile), SIMPLER_IMAGES_INPUT_UNREADABLE (send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_IMAGES_INPUT_TOO_LARGE (under 20,000,000 bytes and 100 megapixels), SIMPLER_IMAGES_TRANSFORM_FAILED (retry, or send a standard PNG); nothing is charged on any of them; SIMPLER_MEDIA_PIN_TOO_LARGE when the result is over 20MB (pass width and height, or pick jpeg or webp); INSUFFICIENT_CREDITS or PAYMENT_REQUIRED when the workspace cannot cover $0.0005 or is past its spend cap (add credits or raise the cap). NEXT: Pass images[0].asset_key as image_asset_key to image.adjust, image.marketplace_check or another image.* tool; a download_url, when the result carries one, works for 24 hours without a session.

    Annotations the server declares

    • readOnlyHint: false
  • image.cut_background[paid: $0.02] Remove the background from a photo, returning the subject on transparency (BiRefNet).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    [paid: $0.02] Remove the background from a photo, returning the subject on transparency (BiRefNet). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • image.describeAnalyze a photo with vision (subjects, defects, OCR text, composition) and return a description.1 finding

    Why it is in this group

    • The name "image.describe" says "describe": a word for reading.
    • The server marks it as not read-only (readOnlyHint: false).

    Findings

    • Warn

      The server marks "image.describe" as not read-only (readOnlyHint: false), and its words say it reads.

      Matched text

      annotations: {"readOnlyHint":false}; The name "image.describe" says "describe": a word for reading.

      What this check looks for
    Description, as the server wrote it
    Analyze a photo with vision (subjects, defects, OCR text, composition) and return a description. Token-billed at the platform's vision rates. Returns JSON {text, price, next}. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_MEDIA_UNKNOWN_TYPE (send PNG, JPEG or WebP), INSUFFICIENT_CREDITS (add credits). NEXT: act on the findings with image.inpaint, image.cut_background or image.upscale.

    Annotations the server declares

    • readOnlyHint: false
  • image.generate[paid: $0.042/image max] Generate 1-3 images from a text prompt on the first lane that delivers, tried in order: gemini-2.5-flash-image via the Cloudflare AI Gateway, square reques…No findings

    Why it is in this group

    • The name "image.generate" says "generate": a word for a change.
    Description, as the server wrote it
    [paid: $0.042/image max] Generate 1-3 images from a text prompt on the first lane that delivers, tried in order: gemini-2.5-flash-image via the Cloudflare AI Gateway, square requests only ($0.0406/image), then @cf/black-forest-labs/flux-2-dev via Cloudflare Workers AI ($0.00/image), then google/nano-banana-2-lite via fal ($0.042/image). Each lane settles its own price per delivered image, so an image never costs more than the fal price, the credit a call must have available before it runs; price.charged_micro_usd in the result is the actual charge. Each image is stored in this workspace's image library and returned as JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; never a provider link. A result with no download_url says why in download_url_unavailable. Refuses: VALIDATION_ERROR on an empty prompt (describe the image), INSUFFICIENT_CREDITS (add credits), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later). A partial result lists refunded outputs in output.withheld; SIMPLER_IMAGE_RESULT_UNDELIVERED means nothing was charged: retry. For canvas-sized generation inside an edit session use image.generate_for_edit. NEXT: pass images[0].asset_key as image_asset_key to image.variation or image.upscale.

    Annotations the server declares

    • readOnlyHint: false
  • image.generate_for_edit[paid: $0.04] Generate an image from a text prompt (google/nano-banana-2-lite) sized for editing.No findings

    Why it is in this group

    • The name "image.generate_for_edit" says "generate": a word for a change.
    Description, as the server wrote it
    [paid: $0.04] Generate an image from a text prompt (google/nano-banana-2-lite) sized for editing. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: VALIDATION_ERROR on an empty prompt (describe the image), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to image.inpaint, image.upscale or image.style_transfer.

    Annotations the server declares

    • readOnlyHint: false
  • image.harmonizeMatch a pasted subject's light and colour to its scene: a Reinhard colour transfer in CIELAB moves the subject's mean and spread toward the scene's, weighted by the subject mask, a…1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.

    Findings

    • Info

      The description is 2,041 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...t_key to the next image.* tool, or open download_url when the result carries one."

      What this check looks for
    Description, as the server wrote it
    Match a pasted subject's light and colour to its scene: a Reinhard colour transfer in CIELAB moves the subject's mean and spread toward the scene's, weighted by the subject mask, at strength 0..100 (default 60). Pixels outside the mask never change. Pass the composite plus a subject mask (white = subject, black = scene). Free: runs on the platform's own deterministic kernel, so the same input and arguments always return the same pixels. Takes PNG, JPEG, WebP, GIF or HEIC. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_EDIT_HARMONIZE_MASK_EMPTY (the mask selects no visible pixel: paint the subject white), SIMPLER_EDIT_HARMONIZE_NO_BACKGROUND (the mask covers everything: pass the whole composite with only the subject white), SIMPLER_EDIT_SERVER_PIXELS_NEEDS_PNG (the file is not an image: send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_EDIT_SERVER_PIXELS_TOO_LARGE (at most 4,194,304 pixels: run image.convert with width and height 2048 first), SIMPLER_IMAGES_BINDING_ABSENT (this deployment has no Images binding yet, owner step O-2: send a standard PNG), SIMPLER_IMAGES_QUOTA_EXCEEDED or SIMPLER_IMAGES_LEGACY_SUBSCRIPTION (an owner account step: send a standard PNG meanwhile), SIMPLER_IMAGES_INPUT_UNREADABLE (send a PNG, JPEG, WebP, GIF or HEIC), SIMPLER_IMAGES_INPUT_TOO_LARGE (under 20,000,000 bytes and 100 megapixels), SIMPLER_IMAGES_TRANSFORM_FAILED (retry, or send a standard PNG); nothing is charged on any of them, and INSUFFICIENT_CREDITS or PAYMENT_REQUIRED only when the workspace is already overdrawn or past its spend cap (add credits or raise the cap). NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • image.inpaint[paid: $0.07] Remove or replace a masked region of a photo (FLUX Fill).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    [paid: $0.07] Remove or replace a masked region of a photo (FLUX Fill). White mask pixels = edit region. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • image.marketplace_checkCheck a product photo against a marketplace's listing-image rules before the seller uploads it: shopee, lazada or tiktok_shop.1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Info

      The description is 2,908 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...ght for a size rule) for a file type, size or file-size failure, and check again."

      What this check looks for
    Description, as the server wrote it
    Check a product photo against a marketplace's listing-image rules before the seller uploads it: shopee, lazada or tiktok_shop. Free and read-only: the platform's own deterministic check, which stores nothing, calls no marketplace and uploads nothing. Returns JSON {images: [], price ($0.00), output: {verdict (pass, fail or partial), failed, advice, skipped, file: {width, height, format, bytes}, rules: [{rule, level (required or recommended), status (pass, fail or skipped), expected, measured, fix, reason, source, checked, note}]}, next}. It checks the minimum and maximum size, the square shape, the file type, the file size and a white background; each rule names its source page and the day it was read, and a figure read through search results says UNVERIFIED in its note. The background is read from the pixels, up to 4,194,304 pixels (for example 2048x2048); a JPEG, WebP, GIF, HEIC or interlaced PNG is read through the Images binding at no charge. That one rule is skipped, with every other verdict kept, for a larger image (reason SIMPLER_EDIT_SERVER_PIXELS_TOO_LARGE: run image.convert with width and height 2048, then check the copy), or with the binding's reason when it cannot read the image: SIMPLER_IMAGES_BINDING_ABSENT when this deployment has no Images binding yet (owner step O-2: send a standard PNG copy to check it), or SIMPLER_IMAGES_QUOTA_EXCEEDED, SIMPLER_IMAGES_LEGACY_SUBSCRIPTION, SIMPLER_IMAGES_INPUT_UNREADABLE, SIMPLER_IMAGES_INPUT_TOO_LARGE or SIMPLER_IMAGES_TRANSFORM_FAILED, each with its fix in the rule. Added text and watermarks are skipped with reason SIMPLER_EDIT_MARKETPLACE_TEXT_CHECK_UNAVAILABLE or SIMPLER_EDIT_MARKETPLACE_WATERMARK_CHECK_UNAVAILABLE until the vision check lands (look at the image yourself). Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB); VALIDATION_ERROR for a platform outside the list; VALIDATION_ERROR whose reason is SIMPLER_MEDIA_EMPTY, SIMPLER_MEDIA_UNKNOWN_TYPE, SIMPLER_MEDIA_KIND_NOT_ALLOWED, SIMPLER_MEDIA_SVG_REFUSED or SIMPLER_MEDIA_DIMENSIONS_UNREADABLE (send a JPEG, PNG, WebP, GIF or HEIC) or SIMPLER_MEDIA_PNG_CORRUPT (save it again as a standard PNG); for a HEIC only, the binding's reason when it cannot read the size (SIMPLER_IMAGES_BINDING_ABSENT while this deployment has no Images binding: send a JPEG or PNG copy); and INSUFFICIENT_CREDITS or PAYMENT_REQUIRED only when the workspace is already overdrawn or past its spend cap (add credits or raise the cap). NEXT: apply each failed rule's fix, for example image.add_shadow with transparent false and background #ffffff for a white background, or image.convert with format jpeg (and width and height for a size rule) for a file type, size or file-size failure, and check again.

    Annotations the server declares

    • readOnlyHint: true
  • image.replace_background[paid: $0.07] Replace a photo's background with a generated scene (FLUX Fill).No findings

    Why it is in this group

    • The name "image.replace_background" says "replace": a word for a change.
    Description, as the server wrote it
    [paid: $0.07] Replace a photo's background with a generated scene (FLUX Fill). Pass a mask covering the background (white = background). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), VALIDATION_ERROR on an empty prompt (describe the scene), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • image.replace_sky[paid: $0.07] Replace a photo's sky (FLUX Fill).No findings

    Why it is in this group

    • The name "image.replace_sky" says "replace": a word for a change.
    Description, as the server wrote it
    [paid: $0.07] Replace a photo's sky (FLUX Fill). Pass a mask covering the sky (white = sky). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • image.segment_at[paid: $0.01] Segment the object at pixel (x, y) via SAM 2 and return its mask image.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    [paid: $0.01] Segment the object at pixel (x, y) via SAM 2 and return its mask image. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), VALIDATION_ERROR with reason SIMPLER_EDIT_TOOL_ARGS_INVALID when x or y is missing or outside 0..16384 (take the point from the user, in the image's pixels), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as mask_asset_key to image.inpaint, image.replace_sky or image.replace_background.

    Annotations the server declares

    • readOnlyHint: false
  • image.upscale[paid: $0.06] Upscale a photo 4x via aura-sr super-resolution.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    [paid: $0.06] Upscale a photo 4x via aura-sr super-resolution. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), INSUFFICIENT_CREDITS (add credits). SIMPLER_EDIT_RESULT_UNDELIVERED means the charge was refunded: retry. NEXT: pass images[0].asset_key as image_asset_key to the next image.* tool, or open download_url when the result carries one.

    Annotations the server declares

    • readOnlyHint: false
  • image.variation[paid: $0.04/image] Generate 1-3 variations of a reference image guided by a prompt (FLUX Pro Kontext, context-aware).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    [paid: $0.04/image] Generate 1-3 variations of a reference image guided by a prompt (FLUX Pro Kontext, context-aware). Pass image_asset_key (an earlier result) or image_url (public https or data:image) plus a prompt describing the change. Each image is stored in this workspace's image library and returned as JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; never a provider link. A result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass one image input), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), VALIDATION_ERROR on an empty prompt, INSUFFICIENT_CREDITS (add credits), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the prompt without it, for example no real person's name or likeness; SIMPLER_IMAGE_PROMPT_CHECK_UNAVAILABLE (the safety check is down, nothing charged: retry later); SIMPLER_IMAGE_OUTPUT_FLAGGED (the output failed the content check and was refunded: change the prompt); SIMPLER_IMAGE_OUTPUT_SCAN_UNAVAILABLE (outputs cannot be checked right now, nothing charged: retry later). A partial result lists refunded outputs in output.withheld. To edit a SPECIFIC region use image.inpaint; for whole-image restyle use image.style_transfer. NEXT: pass images[0].asset_key to image.upscale or image.cut_background.

    Annotations the server declares

    • readOnlyHint: false
  • interaction_createLog an interaction — call, meeting, email, message, visit, payment, or other — against a contact.No findings

    Why it is in this group

    • The name "interaction_create" says "create": a word for a change.
    Description, as the server wrote it
    Log an interaction — call, meeting, email, message, visit, payment, or other — against a contact. The ONE activity-logging door every lane funnels through, so the contact's last_contact_at freshness bumps no matter which surface logged the touch; the bump never moves backward, so importing older history can never regress a fresher touch. Accepts contact_name, contact_email or contact_phone in place of contact_id. NEXT: contact_read to see the bumped last_contact_at.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • interaction_upsertCreate-or-update a interaction keyed on source_ref.No findings

    Why it is in this group

    • The name "interaction_upsert" says "upsert": a word for a change.
    Description, as the server wrote it
    Create-or-update a interaction keyed on source_ref.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_client_createCreate a invoice_client.Can send data outNo findings

    Why it is in this group

    • The name "invoice_client_create" says "create": a word for a change.

    What it can reach

    • outboundThe input takes a destination (input.phone): what the tool writes goes there.
    Description, as the server wrote it
    Create a invoice_client.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_client_updateUpdate a invoice_client you own (optionally compare-and-set via expected_updated_at).Can send data outNo findings

    Why it is in this group

    • The name "invoice_client_update" says "update": a word for a change.

    What it can reach

    • outboundThe input takes a destination (input.phone): what the tool writes goes there.
    Description, as the server wrote it
    Update a invoice_client you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_client_upsertCreate-or-update a invoice_client keyed on email.Can send data outNo findings

    Why it is in this group

    • The name "invoice_client_upsert" says "upsert": a word for a change.

    What it can reach

    • outboundThe input takes a destination (input.phone): what the tool writes goes there.
    Description, as the server wrote it
    Create-or-update a invoice_client keyed on email.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_createONE call creates the whole document: claims the next invoice number, then writes the header plus every line in `items` ([{description, quantity, unit_price_cents}], integer CENTS).No findings

    Why it is in this group

    • The name "invoice_create" says "create": a word for a change.
    Description, as the server wrote it
    ONE call creates the whole document: claims the next invoice number, then writes the header plus every line in `items` ([{description, quantity, unit_price_cents}], integer CENTS). client_name finds a client by name or creates one. Birth status 'paid' needs items in the same call. tax_scheme sg_gst or my_sst taxes each line by its tax_code (refusals: see tax_scheme). When present, the second content block is the invoice as copy-ready text, shown with a Copy button where the host draws MCP Apps; if the user says they cannot see the invoice, give them that block word for word; share:true adds a client link, only when the user asks for one. NEXT: invoice_payment_record to record money-in.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_handoffGet one issued invoice ready to give the client, in one call: the document, the client link (made if it has none), the PDF (printed or the stored one) and the prepared message (inv…Reads private dataReads text others write1 finding

    Why it is in this group

    • The description's first verb is "get": a word for reading.
    • The server marks it as not read-only (readOnlyHint: false).

    What it can reach

    • private-dataThe description's first sentence says "get" and "document": it reads documents.
    • untrusted-inputThe description's first sentence says "get" and "message": it reads messages, which other people can write.

    Findings

    • Warn

      The server marks "invoice_handoff" as not read-only (readOnlyHint: false), and its words say it reads.

      Matched text

      annotations: {"readOnlyHint":false}; The description's first verb is "get": a word for reading.

      What this check looks for
    Description, as the server wrote it
    Get one issued invoice ready to give the client, in one call: the document, the client link (made if it has none), the PDF (printed or the stored one) and the prepared message (invoice_message_prepare's words, with the link). ready lists the parts made; missing gives each other part's reason_code, reason and next_call. A turned-off link is never re-issued: invoice_share action 'rotate' waits for the owner's approval. NOTHING IS SENT. Refusals (CONFLICT): draft SIMPLER_SALES_MESSAGE_NOT_ISSUED (issue it with invoice_update status 'sent'); cancelled; written off; SIMPLER_SALES_MESSAGE_PURPOSE_UNFIT (send its next_call). Read-only token or viewer: FORBIDDEN. Free.

    Annotations the server declares

    • readOnlyHint: false
  • invoice_item_createAdds one line item to an invoice.No findings

    Why it is in this group

    • The name "invoice_item_create" says "create": a word for a change.
    Description, as the server wrote it
    Adds one line item to an invoice. unit_price_cents is integer CENTS, 0 or more ($15.00 → 1500); quantity is 0.001 to 1,000,000 with up to 3 decimals (1.5 hours → 1.5); unit is an optional label (hr, kg); discount_bps is an optional line discount (10% → 1000). quantity × unit_price_cents is at most 10,000,000,000. Refuses a negative price (send discount_bps instead), a 'paid' parent (retire the payment first) and a 'cancelled' parent (issue a new invoice). Re-derives the invoice's paid/sent status. tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: invoice_read to see the updated total.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_item_updateEdits a line item's description, quantity, unit, price or discount, or re-parents it to another invoice.No findings

    Why it is in this group

    • The name "invoice_item_update" says "update": a word for a change.
    Description, as the server wrote it
    Edits a line item's description, quantity, unit, price or discount, or re-parents it to another invoice. expected_updated_at is required: the line's updated_at from your read; without it nothing is written and the refusal names the current value; a line changed since then answers CONFLICT. Bounds: unit_price_cents is integer CENTS, 0 or more; quantity 0.001 to 1,000,000 (3 decimals); discount_bps 0 to 10000; quantity × unit_price_cents at most 10,000,000,000. unit "" clears the unit. Refuses when the current or target invoice is paid or cancelled. tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: invoice_read to see the updated total.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_message_preparePrepare what a business sends a client about one issued invoice: purpose 'invoice', 'reminder' (overdue), 'receipt' (paid in full) or 'promise_follow_up' (promised date passed); le…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Prepare what a business sends a client about one issued invoice: purpose 'invoice', 'reminder' (overdue), 'receipt' (paid in full) or 'promise_follow_up' (promised date passed); left out, the one that fits. language 'en', 'ms' or 'zh'; left out, the client's (invoice_client_update message_language), else English. Returns to, subject, body, share_text, a mailto and link_on; share_url only for a member or above with a live link (never made here). NOTHING IS SENT. Refusals (CONFLICT): draft SIMPLER_SALES_MESSAGE_NOT_ISSUED (issue it with invoice_update status 'sent'); cancelled; written off; SIMPLER_SALES_MESSAGE_PURPOSE_UNFIT (send its next_call). Other arguments: VALIDATION_ERROR. Free.

    Annotations the server declares

    • readOnlyHint: true
  • invoice_payer_confirmationActs on a payer's report, sent from the invoice's client page, that they have paid.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Acts on a payer's report, sent from the invoice's client page, that they have paid. action 'accept' records the payment with the same writer as invoice_payment_record, graded counterparty_confirmed: the amount is amount_cents (or amount) when given, else the balance still owed; occurred_at defaults to when the payer reported. An agent's accept waits for the owner's approval. action 'dismiss' closes a report whose money did not arrive. Find reports in invoice_read's payer_confirmations. Refuses a report already accepted or dismissed with CONFLICT (invoice_read shows it), and an invoice with nothing owed unless amount_cents comes with allow_overpayment. Nothing is sent to the payer.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_pdfMake the PDF of an issued invoice (or of its receipt once paid): the same document as its print-ready page, printed on A4 with the business's fonts, brand colours and logo, and kep…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Make the PDF of an issued invoice (or of its receipt once paid): the same document as its print-ready page, printed on A4 with the business's fonts, brand colours and logo, and kept, so asking again for an unchanged invoice returns the stored file (cached: true) without printing it again. Totals are the stored figures (the same numbers invoice_read gives). It prepares a file and sends nothing. Returns version, pdf_path (downloads while signed in), and download_url when signed links are on; while they are off, download_url_unavailable says why and pdf_path still works. Refusals: a draft is refused CONFLICT SIMPLER_SALES_PDF_NOT_ISSUED (issue it first with invoice_update status 'sent'); a cancelled invoice is refused CONFLICT SIMPLER_SALES_PDF_CANCELLED; an unknown id is NOT_FOUND (find it with invoice_list); more than 50 new PDFs in 24 hours is refused RATE_LIMITED SIMPLER_SALES_PDF_DAILY_CAP (wait retry_after seconds; stored PDFs still download); when PDF printing is unavailable (no browser here, printer busy or failing) it answers SERVICE_UNAVAILABLE with a reason_code and retry_after, and the print-ready page stays the fallback. A read-only token or a viewer is refused FORBIDDEN before anything is made. Free.

    Annotations the server declares

    • readOnlyHint: false
  • invoice_promise_setRecords the date a client promised to pay an issued invoice that still has money owed: promised_payment_date 'YYYY-MM-DD', the business's own date, today or later.No findings

    Why it is in this group

    • The name "invoice_promise_set" says "set": a word for a change.
    Description, as the server wrote it
    Records the date a client promised to pay an issued invoice that still has money owed: promised_payment_date 'YYYY-MM-DD', the business's own date, today or later. clear true removes it. invoice_read then shows promise_status: pending until that business day ends, kept when paid in full by then, broken after it. A broken promise makes promise_follow_up the default purpose of invoice_message_prepare. Each change is kept in the invoice's activity as promise_set. Refuses a draft, a cancelled invoice and one with nothing owed (CONFLICT), and a past or malformed date (VALIDATION_ERROR). Nothing is sent. NEXT: invoice_read.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_recurring_createCreate a invoice_recurring.No findings

    Why it is in this group

    • The name "invoice_recurring_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a invoice_recurring.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_recurring_skipSkips a recurring template's next invoice: next_due moves one period on (the template's own weekly, monthly or yearly day) and no invoice is made.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Skips a recurring template's next invoice: next_due moves one period on (the template's own weekly, monthly or yearly day) and no invoice is made. A skip does not count toward max_invoices. Refuses NOT_FOUND for an id outside your tenant (call invoice_recurring_list) and CONFLICT when the template has ended (raise ends_on or max_invoices with invoice_recurring_update) or changed since expected_updated_at (read it again). Each call skips one run: to skip the run after, send expected_updated_at from this answer. Nothing is sent. NEXT: invoice_recurring_read.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_recurring_updateUpdate a invoice_recurring you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "invoice_recurring_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a invoice_recurring you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_reminders_dueToday's collection list, in the business's days (its sales_settings time zone).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Today's collection list, in the business's days (its sales_settings time zone). reminders: each issued invoice still owed that is 3 days before its due date, on it, or 7 or 14 days after, as its client's reminder_cadence allows (standard, from_due, overdue_only or off; set it with invoice_client_update), with the amount owed in CENTS, the step, the language and next_call (the invoice_message_prepare call). A step shows only on its own day. issues: drafts the recurring run made, each with the invoice_update call that issues it; recurring_held: due templates it could not make, and why. NOTHING IS PREPARED OR SENT. Any argument is refused VALIDATION_ERROR (it takes none). Free; changes nothing.

    Annotations the server declares

    • readOnlyHint: true
  • invoice_shareReturns share_url, the client's view link for this invoice, and the full document (parties, lines, totals in integer CENTS) in one call.Reads private dataReads text others write1 finding

    Why it is in this group

    • The description's first verb is "return": a word for reading.
    • The server marks it as not read-only (readOnlyHint: false).

    What it can reach

    • private-dataThe description's first sentence says "return" and "document": it reads documents.
    • untrusted-inputThe description's first sentence says "return" and "url": it reads web pages, which anyone can write.

    Findings

    • Warn

      The server marks "invoice_share" as not read-only (readOnlyHint: false), and its words say it reads.

      Matched text

      annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}; The description's first verb is "return": a word for reading.

      What this check looks for
    Description, as the server wrote it
    Returns share_url, the client's view link for this invoice, and the full document (parties, lines, totals in integer CENTS) in one call. action: 'get' (default) returns the live link; 'rotate' replaces it and the old link stops working; 'revoke' turns it off. A link the owner turned off stays off for agents: 'get' never re-issues it, and an agent's 'rotate' waits for the owner's approval. Anyone with the link can open the page until the invoice is cancelled or 90 days after payment (see link_serving). There is no PDF file yet; the link opens a print-ready page the client can print or save as PDF. Nothing is sent: give the link to the owner, who decides whether it goes to the client.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • invoice_updateEdits an invoice's header fields and status.No findings

    Why it is in this group

    • The name "invoice_update" says "update": a word for a change.
    Description, as the server wrote it
    Edits an invoice's header fields and status. status 'paid' cannot be set here — it derives from the payment ledger, so call invoice_payment_record instead. Status moves are machine-checked (draft to sent/cancelled, sent to cancelled); cancelled is terminal and a paid invoice never reopens by hand. A move to 'sent' stamps sent_at automatically when omitted, and currency is ledger-locked once any live payment exists. An agent's change to notes or payment_terms on an issued invoice waits for the owner's approval; on a draft it does not. tax_scheme and prices_include_tax are fixed at creation, as is the tax rate. NEXT: invoice_payment_record to settle money, invoice_read to verify.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • market_signal_createPost to one of the tenant's 8 market-signal wish-bar slots — a buy, sell, swap, capacity-offer or capacity-request visible ONLY to platform-matched partners, never the wider networ…No findings

    Why it is in this group

    • The name "market_signal_create" says "create": a word for a change.
    Description, as the server wrote it
    Post to one of the tenant's 8 market-signal wish-bar slots — a buy, sell, swap, capacity-offer or capacity-request visible ONLY to platform-matched partners, never the wider network. The door counts your OPEN signals server-side before writing: the 9th is refused with slots_used/slots_max in the error rather than silently queued. NEXT: market_signal_update to close or fulfil a signal and free a slot.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • market_signal_updateUpdate a market_signal you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "market_signal_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a market_signal you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_customer_createRegister a buyer in the LHDN buyer registry so it attaches to future e-invoices with one pick (customer_id) instead of typing the party every time.No findings

    Why it is in this group

    • The name "myinvois_customer_create" says "create": a word for a change.
    Description, as the server wrote it
    Register a buyer in the LHDN buyer registry so it attaches to future e-invoices with one pick (customer_id) instead of typing the party every time. Validates the TIN format, and validates state_code against the LHDN state table when you send one — a buyer with no real TIN uses the LHDN generic TIN (EI00000000010 Malaysian public, EI00000000020 foreign) instead. NEXT: myinvois_invoice_generate with customer_id.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • myinvois_customer_updateEdit a registered buyer's LHDN details — send only the fields you are changing.No findings

    Why it is in this group

    • The name "myinvois_customer_update" says "update": a word for a change.
    Description, as the server wrote it
    Edit a registered buyer's LHDN details — send only the fields you are changing. When tin is included it is re-checked against the same format gate create runs, and when state_code is included it is re-checked against the LHDN state table, so an edited buyer stays valid to generate against. Never touches e-invoices already generated for this buyer; each one sealed its own copy of the buyer party. NEXT: myinvois_customer_list to find the id.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • myinvois_invoice_generateMint a sealed LHDN e-invoice in ONE call: invoice_type, line_items (unit_price_cents in integer CENTS, tax_rate_bps in basis points), and a buyer as customer_id or an inline buyer …No findings

    Why it is in this group

    • The name "myinvois_invoice_generate" says "generate": a word for a change.
    Description, as the server wrote it
    Mint a sealed LHDN e-invoice in ONE call: invoice_type, line_items (unit_price_cents in integer CENTS, tax_rate_bps in basis points), and a buyer as customer_id or an inline buyer object. Refuses until myinvois_settings_save is done; invoice_type 02/03/04 also needs original_invoice_id naming a type-01 invoice with a recorded LHDN UUID, checked BEFORE the number is claimed, so a rejected note never burns a number. The result is immutable and carries portal_field_sheet; Simpler never submits it to LHDN. NEXT: the owner keys the field sheet into the MyInvois portal form, then myinvois_invoice_record_submission with the UUID the portal assigns.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • myinvois_invoice_record_submissionRecord the UUID the LHDN MyInvois portal assigned after the owner keyed this e-invoice into the portal (its form or LHDN's Excel batch template).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Record the UUID the LHDN MyInvois portal assigned after the owner keyed this e-invoice into the portal (its form or LHDN's Excel batch template). Simpler never submits to LHDN and never calls the MyInvois API; this only stores the UUID the owner copies back. Only a type-01 original accepts a UUID: credit, debit and refund notes are refused (they link through BillingReference), so record the UUID on the original instead. Moves status generated to submitted; calling it again on a submitted invoice corrects a mistyped UUID. NEXT: myinvois_invoice_read to see the UUID attached.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • myinvois_settings_save[REQUIRES APPROVAL] Save the one-time LHDN supplier profile — the door checks for an existing row on your tenant and creates or updates in place, so this is the only write you ever…No findings

    Why it is in this group

    • The name "myinvois_settings_save" says "save": a word for a change.
    Description, as the server wrote it
    [REQUIRES APPROVAL] Save the one-time LHDN supplier profile — the door checks for an existing row on your tenant and creates or updates in place, so this is the only write you ever call. Validates the TIN format, checks msic_code is a 5-digit MSIC code, and checks state_code against the LHDN state table before the row moves. NEXT: myinvois_invoice_generate reads this profile as the supplier party of every e-invoice.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • myinvois_template_createSave a reusable line-item preset so a repeat invoice's items do not get re-typed.No findings

    Why it is in this group

    • The name "myinvois_template_create" says "create": a word for a change.
    Description, as the server wrote it
    Save a reusable line-item preset so a repeat invoice's items do not get re-typed. line_items_json is parsed and each item validated by the SAME checker myinvois_invoice_generate uses (description, quantity, unit_price_cents in integer CENTS, tax_rate_bps in basis points, tax_type/classification_code/unit_code against the LHDN code tables) and re-serialized canonically, so a saved template's items are always generate-ready. NEXT: myinvois_template_list to pull the items back out for a generate call.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • myinvois_template_updateEdit a saved line-item preset.No findings

    Why it is in this group

    • The name "myinvois_template_update" says "update": a word for a change.
    Description, as the server wrote it
    Edit a saved line-item preset. When line_items_json is included it is re-parsed and every item re-validated exactly as create does, so an edited template cannot drift into a shape myinvois_invoice_generate would reject. NEXT: myinvois_template_list to find the id.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • platform.request_capabilityFile a missing-capability report when no available tool can accomplish your task.No findings

    Why it is in this group

    • The name "platform.request_capability" says "request": a word for a change.
    Description, as the server wrote it
    File a missing-capability report when no available tool can accomplish your task. The platform operator reviews these to decide what gets built next. Free; capped at 10 reports per tenant per day. Refuses: VALIDATION_ERROR when intent is under 10 characters (describe the task in a sentence), RATE_LIMITED after 10 reports in 24 hours (wait the retry_after seconds it carries).

    Annotations the server declares

    • readOnlyHint: false
  • quote_convertConverts a quote into its invoice in ONE call: claims the quote's 'converted' status first (so a lost race never creates an orphan invoice), copies every live line item across, and…No findings

    Why it is in this group

    • The name "quote_convert" says "convert": a word for a change.
    Description, as the server wrote it
    Converts a quote into its invoice in ONE call: claims the quote's 'converted' status first (so a lost race never creates an orphan invoice), copies every live line item across, and records converted_invoice_id — nothing else to do afterward. Refuses if the quote is already converted (the error carries converted_invoice_id) or declined (revive it with quote_update first), and refuses with nothing converted when the quote or its lines changed while it was converting (for example a quote_item call running at the same time): read the quote again with quote_read, then convert. NEXT: invoice_read on the new invoice.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • quote_createONE call creates the whole document: claims the next atomic quote number, then writes the header plus every line in `items` ([{description, quantity, unit_price_cents}], integer CE…No findings

    Why it is in this group

    • The name "quote_create" says "create": a word for a change.
    Description, as the server wrote it
    ONE call creates the whole document: claims the next atomic quote number, then writes the header plus every line in `items` ([{description, quantity, unit_price_cents}], integer CENTS) together. client_name resolves an existing client by name or creates one. tax_scheme sg_gst or my_sst taxes each line by its tax_code (refusals: see tax_scheme). When present, the second content block is the quote as copy-ready text, shown with a Copy button where the host draws MCP Apps; if the user says they cannot see the quote, give them that block word for word; make a client link with quote_share only when the user asks for one. NEXT: quote_convert to turn the quote into its invoice.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • quote_item_createAdds one line item to a quote.No findings

    Why it is in this group

    • The name "quote_item_create" says "create": a word for a change.
    Description, as the server wrote it
    Adds one line item to a quote. unit_price_cents is integer CENTS, 0 or more ($15.00 → 1500); quantity is 0.001 to 1,000,000 with up to 3 decimals (1.5 hours → 1.5); unit is an optional label (hr, kg); discount_bps is an optional line discount (10% → 1000). quantity × unit_price_cents is at most 10,000,000,000. Refuses a negative price (send discount_bps instead), a line above the ceiling (split it) and any line on a converted quote (CONFLICT: change its invoice instead). tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: quote_read to see the quote.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • quote_item_updateEdits a quote line's description, quantity, unit, price or discount, or moves it to another quote.No findings

    Why it is in this group

    • The name "quote_item_update" says "update": a word for a change.
    Description, as the server wrote it
    Edits a quote line's description, quantity, unit, price or discount, or moves it to another quote. expected_updated_at is required: the line's updated_at from your read; without it nothing is written and the refusal names the current value; a line changed since then answers CONFLICT. Refuses a line of a converted quote or a move onto one (CONFLICT: change its invoice instead). Bounds: unit_price_cents is integer CENTS, 0 or more; quantity 0.001 to 1,000,000 (3 decimals); discount_bps 0 to 10000; quantity × unit_price_cents at most 10,000,000,000. unit "" clears the unit. tax_code is the line's code under the document's tax_scheme, refused on a document without a scheme. NEXT: quote_read.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • quote_shareReturns share_url, the client's view link for this quote, and the full document (parties, lines, totals in integer CENTS) in one call.Reads private dataReads text others write1 finding

    Why it is in this group

    • The description's first verb is "return": a word for reading.
    • The server marks it as not read-only (readOnlyHint: false).

    What it can reach

    • private-dataThe description's first sentence says "return" and "document": it reads documents.
    • untrusted-inputThe description's first sentence says "return" and "url": it reads web pages, which anyone can write.

    Findings

    • Warn

      The server marks "quote_share" as not read-only (readOnlyHint: false), and its words say it reads.

      Matched text

      annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}; The description's first verb is "return": a word for reading.

      What this check looks for
    Description, as the server wrote it
    Returns share_url, the client's view link for this quote, and the full document (parties, lines, totals in integer CENTS) in one call. action: 'get' (default) returns the live link; 'rotate' replaces it and the old link stops working; 'revoke' turns it off. A link the owner turned off stays off for agents: 'get' never re-issues it, and an agent's 'rotate' waits for the owner's approval. Anyone with the link can open the page until the quote expires, is declined or converted (see link_serving). There is no PDF file yet; the link opens a print-ready page the client can print or save as PDF. Nothing is sent: give the link to the owner, who decides whether it goes to the client.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • quote_updateEdits a quote's header fields and bookkeeping status (draft, sent, accepted, declined move freely).No findings

    Why it is in this group

    • The name "quote_update" says "update": a word for a change.
    Description, as the server wrote it
    Edits a quote's header fields and bookkeeping status (draft, sent, accepted, declined move freely). status 'converted' and converted_invoice_id are convert-flow-owned — hand-setting either refuses, and a converted quote is frozen: no status move and no header edit (it could convert again and double-bill; its invoice is the live document, so edit that). tax_scheme and prices_include_tax are fixed at creation, as is the tax rate. NEXT: quote_convert to create the invoice and record the link atomically.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • sales_receivablesWho owes you, by client: each client's unpaid balance aged by days past its due date — current (not yet due), 1-30, 31-60, 61-90 and over 90 days — counted in the business's own da…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Who owes you, by client: each client's unpaid balance aged by days past its due date — current (not yet due), 1-30, 31-60, 61-90 and over 90 days — counted in the business's own days (the sales_settings time zone), so an invoice due today is still current. Only issued invoices with a balance left count: drafts, cancelled, paid, written-off and fully credited invoices never do. Money is integer CENTS in each invoice's own currency: one row per client per currency, and totals has one row per currency, never converted or added across currencies; read the figures as given, never multiply or divide them yourself. Clients come oldest past-due first. Optional client_id ages one client; an id that is not one of your clients is refused NOT_FOUND (find it with invoice_client_search or invoice_client_list). A limit outside 1-200 is refused VALIDATION_ERROR (leave it out for 50; totals always cover every client). NEXT: invoice_list with state 'overdue' lists the past-due invoices themselves.

    Annotations the server declares

    • readOnlyHint: true
  • sales_settings_numbering_set[REQUIRES APPROVAL] Sets where a document type's numbers continue, for example after moving from another invoicing tool.No findings

    Why it is in this group

    • The name "sales_settings_numbering_set" says "set": a word for a change.
    Description, as the server wrote it
    [REQUIRES APPROVAL] Sets where a document type's numbers continue, for example after moving from another invoicing tool. doc_type: invoice, quote, credit_note, debit_note or receipt. next_number is the sequence number the next document gets (42 prints as INV-2026-042). Numbering only moves forward: a number at or below the last one used or skipped is refused with VALIDATION_ERROR naming that number, and nothing changes. prefix sets the credit_note, debit_note or receipt prefix; invoice and quote prefixes are invoice_prefix and quote_prefix on sales_settings_upsert. An agent call waits for the owner's approval. Read the current numbers with sales_settings_numbering_read.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • sales_settings_upsert[REQUIRES APPROVAL] Saves your business identity and document defaults (one row per business; scope is 'default').No findings

    Why it is in this group

    • The name "sales_settings_upsert" says "upsert": a word for a change.
    Description, as the server wrote it
    [REQUIRES APPROVAL] Saves your business identity and document defaults (one row per business; scope is 'default'). timezone is the business clock: an invoice turns overdue at midnight in that zone after its due day, and days_overdue counts days there; unset means Asia/Singapore. Left out, timezone and the text fields keep their saved value, but defaulted fields (default_currency, default_tax_rate_bps, payment_terms_days, invoice_prefix, quote_prefix, template_style) reset, so resend them from sales_settings_read. An agent call waits for the owner's approval. Refuses an unlisted timezone or currency, or a tax rate outside 0-10000, with VALIDATION_ERROR before it waits for approval: send a listed value.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • settlement_proposal_acceptAccept a settlement obligation recorded against your tenant as payee — moves status proposed to accepted and stamps accepted_at under the registry's transition guard.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Accept a settlement obligation recorded against your tenant as payee — moves status proposed to accepted and stamps accepted_at under the registry's transition guard. Calling it again on an already-accepted row is idempotent (accepted_at simply re-stamps); once a proposal reaches settled the guard refuses any further move, since settled is terminal. Recording agreement only: no money moves here, settlement execution is a later rail's door. NEXT: settlement_proposal_read to verify the accepted row.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • settlement_proposal_propose_cross_tenantRecord a payment obligation against a KYB-verified partner tenant's settlement_proposals — the record-don't-move on-ramp: this writes the row only, no money moves until a later set…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Record a payment obligation against a KYB-verified partner tenant's settlement_proposals — the record-don't-move on-ramp: this writes the row only, no money moves until a later settlement-rails door executes it. amount_cents is a nonzero integer CENTS (positive = you owe them, negative = a refund/reversal record) and currency must be a 3-letter code. Requires the payee's cross_tenant_settlement consent grant and both sides KYB-approved; payee-side failures refuse identically to block probing, but your OWN unapproved KYB answers distinctly. Your tenant is injected as payer_tenant_id and cannot be spoofed. NEXT: settlement_proposal_accept moves it to accepted on the payee side.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • simpler.directoryDISCOVERY TIER 1 of 3 — the service directory: one token-dense line per service (s:name st:status cost:class ops:N — purpose).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    DISCOVERY TIER 1 of 3 — the service directory: one token-dense line per service (s:name st:status cost:class ops:N — purpose). START HERE instead of reading every tool. No auth required. NEXT: simpler.describe_service with a service name from the listing.

    Annotations the server declares

    • readOnlyHint: true
  • simpler.invokeDISCOVERY TIER 3 of 3 — execute one operation: dispatches to the underlying generated tool (same gates, same envelope, same idempotency as calling it directly).No findings

    Why it is in this group

    • The name "simpler.invoke" says "invoke": a word for a change.
    Description, as the server wrote it
    DISCOVERY TIER 3 of 3 — execute one operation: dispatches to the underlying generated tool (same gates, same envelope, same idempotency as calling it directly). Requires auth. args = the underlying tool's arguments object.

    Annotations the server declares

    • readOnlyHint: false
  • simpler.outcomesDISCOVERY TIER 0 — the OUTCOME MENU: one line per common business outcome, each mapped to ONE tool with one-call notes (composites: ONE call creates the whole document).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    DISCOVERY TIER 0 — the OUTCOME MENU: one line per common business outcome, each mapped to ONE tool with one-call notes (composites: ONE call creates the whole document). START HERE for a business task. No auth required. NEXT: call the named tool directly, or simpler.directory for the full catalog.

    Annotations the server declares

    • readOnlyHint: true
  • spreadsheet_file_createRegister a spreadsheet file row in the library for bytes that already sit in R2: r2_key must be a spreadsheets/<your tenant>/ key minted by the upload route or spreadsheet.create_w…No findings

    Why it is in this group

    • The name "spreadsheet_file_create" says "create": a word for a change.
    Description, as the server wrote it
    Register a spreadsheet file row in the library for bytes that already sit in R2: r2_key must be a spreadsheets/<your tenant>/ key minted by the upload route or spreadsheet.create_with_content — a caller-invented, foreign-tenant, or wrong-domain key is refused. sheet_count, row_count, and column_count are unverified client-reported hints, since nothing server-side parses the binary. NEXT: spreadsheet.create_with_content stores the bytes and registers the row together.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • spreadsheet_file_updateEdit a spreadsheet file's metadata — title, tags, folder_id, or the AI-analysis fields — without touching the underlying R2 object.No findings

    Why it is in this group

    • The name "spreadsheet_file_update" says "update": a word for a change.
    Description, as the server wrote it
    Edit a spreadsheet file's metadata — title, tags, folder_id, or the AI-analysis fields — without touching the underlying R2 object. r2_key and source are birth facts: including either field in the call at all, even set to its current value, is refused — re-upload as a new file to change custody or provenance. Moving folder_id re-verifies the target folder belongs to your tenant. NEXT: spreadsheet_file_read to confirm the change landed.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • spreadsheet_folder_createCreate a spreadsheet_folder.No findings

    Why it is in this group

    • The name "spreadsheet_folder_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a spreadsheet_folder.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • spreadsheet_folder_updateUpdate a spreadsheet_folder you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "spreadsheet_folder_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a spreadsheet_folder you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • spreadsheet.create_with_contentCreate a spreadsheet in the user's library from CSV content in ONE call: the platform stores the bytes (honest text/csv) and registers the file row.No findings

    Why it is in this group

    • The name "spreadsheet.create_with_content" says "create": a word for a change.
    Description, as the server wrote it
    Create a spreadsheet in the user's library from CSV content in ONE call: the platform stores the bytes (honest text/csv) and registers the file row. Free — storage costs nothing; only AI lanes bill. Returns the file row including its id. Refuses: VALIDATION_ERROR when title or csv_content is empty (send both), VALIDATION_ERROR when csv_content is over 10 MB (split it into smaller files), NOT_FOUND when folder_id names no folder in this workspace (call spreadsheet_folder_list, or leave folder_id out), FORBIDDEN when your role is viewer or your token is read-only (ask the owner for the member role, or connect with the 'mcp' scope). Nothing is stored when it refuses. NEXT: spreadsheet_file_list to see the library.

    Annotations the server declares

    • readOnlyHint: false
  • sticky_note_createCreate a sticky_note.No findings

    Why it is in this group

    • The name "sticky_note_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a sticky_note.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • sticky_note_dismissClear a sticky note's reminder (triggers_at back to NULL) so it drops out of the sticky_note_due poll — call this after the host fires the local notification for it.No findings

    Why it is in this group

    • The name "sticky_note_dismiss" says "dismiss": a word for a change.
    Description, as the server wrote it
    Clear a sticky note's reminder (triggers_at back to NULL) so it drops out of the sticky_note_due poll — call this after the host fires the local notification for it. Runs through the same tenant-scoped write path as every entity update, but via a clear-properties op: ordinary updates ignore null values, so this is the only way to null the column. Idempotent — dismissing an already-cleared note just re-nulls a NULL and succeeds. NEXT: sticky_note_due to see what's still outstanding.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • sticky_note_updateUpdate a sticky_note you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "sticky_note_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a sticky_note you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • stock_item_adjustMove stock in or out by id or item name, in one atomic write with its movements-ledger row: stock-out clamps at zero (never negative) and applied_quantity reports what actually mov…No findings

    Why it is in this group

    • The description's first verb is "move": a word for a change.
    Description, as the server wrote it
    Move stock in or out by id or item name, in one atomic write with its movements-ledger row: stock-out clamps at zero (never negative) and applied_quantity reports what actually moved. An unmatched name CREATES the item at quantity 0 first so the delivery still lands as an audited ledger row; a name matching multiple active items refuses with the candidate ids. Concurrent adjusts on the same item retry instead of losing a write. NEXT: stock_item_list to see the item's updated balance in the full inventory.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • stock_item_createCreate a stock_item.No findings

    Why it is in this group

    • The name "stock_item_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a stock_item.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • stock_item_updateUpdate a stock_item you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "stock_item_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a stock_item you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_character_createCreate a studio_character.No findings

    Why it is in this group

    • The name "studio_character_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a studio_character.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • studio_character_updateUpdate a studio_character you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "studio_character_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a studio_character you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_environment_createCreate a studio_environment.No findings

    Why it is in this group

    • The name "studio_environment_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a studio_environment.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • studio_environment_updateUpdate a studio_environment you own (optionally compare-and-set via expected_updated_at).No findings

    Why it is in this group

    • The name "studio_environment_update" says "update": a word for a change.
    Description, as the server wrote it
    Update a studio_environment you own (optionally compare-and-set via expected_updated_at).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_project_createCreate a video studio project — the composition document (scenes, tracks, clips, captions) starts empty and is built up afterward through the studio.* director tools.No findings

    Why it is in this group

    • The name "studio_project_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a video studio project — the composition document (scenes, tracks, clips, captions) starts empty and is built up afterward through the studio.* director tools. thumbnail_key is server-owned: send thumbnail_data_url instead, and this door externalizes it into a content-addressed R2 key through the media hygiene kit (magic bytes decide the type; SVG refuses). Free. NEXT: studio_project_read to verify.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • studio_project_updateRename a project, change its status, or replace its thumbnail on a project you own — send thumbnail_data_url, never thumbnail_key, which is server-owned and computed by this door t…No findings

    Why it is in this group

    • The name "studio_project_update" says "update": a word for a change.
    Description, as the server wrote it
    Rename a project, change its status, or replace its thumbnail on a project you own — send thumbnail_data_url, never thumbnail_key, which is server-owned and computed by this door the same way studio_project_create does. status here is UI-summary-only by design: it never gates render or generation, which are tracked separately by the export job row and the composition document's own scene status. Free. NOT_FOUND when the id is outside your scope — list first with studio_project_list.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • studio.add_audio_to_timelinePlace an audio asset on a role track: 'audio-music' (background, default volume 0.3 — voice ducks music by role) or 'audio-voice' (narration, volume 1.0).No findings

    Why it is in this group

    • The name "studio.add_audio_to_timeline" says "add": a word for a change.
    Description, as the server wrote it
    Place an audio asset on a role track: 'audio-music' (background, default volume 0.3 — voice ducks music by role) or 'audio-voice' (narration, volume 1.0). It lands on the role's first track, or on the one track_id names (a second music bed, a second voice).

    Annotations the server declares

    • readOnlyHint: false
  • studio.add_clip_to_timelinePlace an existing video/image asset on a b-roll track at a position: the first b-roll track, or the one track_id names (a later b-roll track draws over an earlier one, for picture-…No findings

    Why it is in this group

    • The name "studio.add_clip_to_timeline" says "add": a word for a change.
    Description, as the server wrote it
    Place an existing video/image asset on a b-roll track at a position: the first b-roll track, or the one track_id names (a later b-roll track draws over an earlier one, for picture-in-picture). Overlap on that track is refused with the conflicting range so you can re-place or stack it on another track.

    Annotations the server declares

    • readOnlyHint: false
  • studio.add_sceneAdd a PLANNED scene (storyboard placeholder) to the primary lane — free; no pixels are generated.No findings

    Why it is in this group

    • The name "studio.add_scene" says "add": a word for a change.
    Description, as the server wrote it
    Add a PLANNED scene (storyboard placeholder) to the primary lane — free; no pixels are generated. The prompt is the scene's recipe. NEVER mention camera equipment (crane, dolly, rig) — describe what to SHOW. Say 'camera rises' NOT 'crane shot'. Include: subject, action, lighting, color palette, mood.

    Annotations the server declares

    • readOnlyHint: false
  • studio.add_scene_from_assetAppend an uploaded video or image asset to the main track as a finished scene, or insert it at at_index.No findings

    Why it is in this group

    • The name "studio.add_scene_from_asset" says "add": a word for a change.
    Description, as the server wrote it
    Append an uploaded video or image asset to the main track as a finished scene, or insert it at at_index. Assets longer than 10 minutes become consecutive scenes. A video with no measured length lands as one untrimmed 5s scene marked length_estimated: true, here and in get_composition, until the file is measured in the studio editor. Audio assets are refused; use add_audio_to_timeline.

    Annotations the server declares

    • readOnlyHint: false
  • studio.add_sound[$0.001/sec at provider cost] Generate sound effects and ambience FOR a scene from its own footage (foley).No findings

    Why it is in this group

    • The name "studio.add_sound" says "add": a word for a change.
    Description, as the server wrote it
    [$0.001/sec at provider cost] Generate sound effects and ambience FOR a scene from its own footage (foley). Different from generate_music: this is the sound the shot itself would make.

    Annotations the server declares

    • readOnlyHint: false
  • studio.add_stickerAdd a sticker/element overlay (creator CTAs, emojis, shapes) at a normalized position with its own time range and animation.No findings

    Why it is in this group

    • The name "studio.add_sticker" says "add": a word for a change.
    Description, as the server wrote it
    Add a sticker/element overlay (creator CTAs, emojis, shapes) at a normalized position with its own time range and animation.

    Annotations the server declares

    • readOnlyHint: false
  • studio.add_text_overlayAdd a text clip to the first text track, or to the one track_id names (a later text track draws over an earlier one).No findings

    Why it is in this group

    • The name "studio.add_text_overlay" says "add": a word for a change.
    Description, as the server wrote it
    Add a text clip to the first text track, or to the one track_id names (a later text track draws over an earlier one). Pick a semantic preset (title/subtitle/lower_third/callout/price_tag) — it carries complete opinionated styling; override only what matters. Keep text SHORT (max 5-7 words).

    Annotations the server declares

    • readOnlyHint: false
  • studio.add_trackAdd an empty track of a role (video-broll, text, audio-music or audio-voice), up to 16 tracks in the project.No findings

    Why it is in this group

    • The name "studio.add_track" says "add": a word for a change.
    Description, as the server wrote it
    Add an empty track of a role (video-broll, text, audio-music or audio-voice), up to 16 tracks in the project. The new track lands on top of its role: a video-broll or text track draws over the ones before it (picture-in-picture, a second title layer), an audio track mixes in beside the others. Place clips on it by passing the returned track_id to add_clip_to_timeline, add_audio_to_timeline or add_text_overlay. Refuses at the track cap, naming the fix.

    Annotations the server declares

    • readOnlyHint: false
  • studio.adjust_audio_levelsAdjust an audio role's mix: set every clip's volume on every track of that role (or only on the track track_id names) and/or toggle ducking (voice ducks music BY ROLE — no track id…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Adjust an audio role's mix: set every clip's volume on every track of that role (or only on the track track_id names) and/or toggle ducking (voice ducks music BY ROLE — no track ids needed).

    Annotations the server declares

    • readOnlyHint: false
  • studio.apply_opsApply several edits atomically against base_version; any refusal applies nothing and names the failing op.No findings

    Why it is in this group

    • The name "studio.apply_ops" says "apply": a word for a change.
    Description, as the server wrote it
    Apply several edits atomically against base_version; any refusal applies nothing and names the failing op. Small models: prefer the single-purpose verbs. Each op is {op, args}; the ops and their args are: add_scene_from_asset {asset_id, at_index?}; delete_elements {element_ids, ripple_secondary?}; shift_clips {clip_ids, delta_ms}; duplicate_clips {clip_ids, at_ms}; insert_time {at_ms, length_ms}; ripple_delete {element_id, scope?}; roll_edit {left_id, right_id, delta_ms}; slip_edit {clip_id, delta_ms}; slide_edit {clip_id, delta_ms}; move_caption {caption_id?, at_ms?, delta_ms}; cut_transcript_span {from_caption_id, to_caption_id, from_word_index?, to_word_index?}; add_track {role, name?}; remove_track {track_id, delete_clips?}; move_track {track_id, position}; rename_track {track_id, name}; set_track_muted {track_id, muted}; move_clips_to_track {clip_ids, track_id, delta_ms?}; place_product_card {stock_item_id, product_name, sell_price_cents, currency, start_ms?, duration_ms?, style?, track_id?}. base_version is the composition_version you last read; a stale one is refused with a 409 that carries head_version, so re-read with studio.get_composition and resend. Send 1 to 50 ops per call.

    Annotations the server declares

    • readOnlyHint: false
  • studio.apply_templateAppend a stored choreography - its scenes (as PENDING prompts, nothing generated yet) plus its text timing - onto this composition.No findings

    Why it is in this group

    • The name "studio.apply_template" says "apply": a word for a change.
    Description, as the server wrote it
    Append a stored choreography - its scenes (as PENDING prompts, nothing generated yet) plus its text timing - onto this composition. FREE. Templates: tpl_product_showcase (Perfect for product demos and commercials); tpl_social_clip (Vertical format perfect for TikTok, Reels, and Shorts); tpl_explainer (Classic explainer video structure); tpl_testimonial (Square format ideal for customer testimonials); tpl_ad_bumper (Short 6-second ad bumper); tpl_announcement (Perfect for news, launches, and important updates); tpl_story_highlight (Vertical story format for Instagram Stories and similar platforms); tpl_tutorial_intro (Professional intro for tutorials and how-to videos).

    Annotations the server declares

    • readOnlyHint: false
  • studio.begin_asset_uploadStart uploading a video, audio or image file (up to 2 GB) into a studio project.No findings

    Why it is in this group

    • The name "studio.begin_asset_upload" says "upload": a word for a change.
    Description, as the server wrote it
    Start uploading a video, audio or image file (up to 2 GB) into a studio project. Returns upload_id and the plan: one PUT for a file up to 64 MB, else fixed 64 MB parts, each a PUT to this server with the same Bearer token this MCP connection uses and the part's exact Content-Length. The file's bytes are reserved against the workspace's 20 GB studio upload quota until the upload finishes or is cancelled (after 24 hours without a part). Then call studio.finish_asset_upload. Refuses: a size over 2 GB (SIMPLER_STUDIO_UPLOAD_TOO_LARGE): compress or split the file; a mime outside the list (SIMPLER_STUDIO_UPLOAD_TYPE_UNSUPPORTED): convert the file; the quota (SIMPLER_STUDIO_UPLOAD_QUOTA): GET /api/studio/uploads lists your stored files, then delete unused ones; too many open uploads (SIMPLER_STUDIO_UPLOAD_OPEN_LIMIT): finish or cancel one; an environment without the upload tables (SIMPLER_STUDIO_UPLOAD_TABLES_MISSING): upload in one request with POST /api/studio/projects/{id}/assets. Free. Nothing is sent anywhere; the file only comes in.

    Annotations the server declares

    • readOnlyHint: false
  • studio.color_gradeApply color grading to a scene (each channel -100..100; temperature negative = cool, positive = warm).No findings

    Why it is in this group

    • The description's first verb is "apply": a word for a change.
    Description, as the server wrote it
    Apply color grading to a scene (each channel -100..100; temperature negative = cool, positive = warm).

    Annotations the server declares

    • readOnlyHint: false
  • studio.create_social_clipMake one short as a NEW studio project from a range of an existing project: the finished scenes in the range are trimmed copies (no footage is generated or billed again), the capti…No findings

    Why it is in this group

    • The name "studio.create_social_clip" says "create": a word for a change.
    Description, as the server wrote it
    Make one short as a NEW studio project from a range of an existing project: the finished scenes in the range are trimmed copies (no footage is generated or billed again), the captions move with them and the new project takes the target preset's shape. The source project is not changed. Free. Use the start_ms, end_ms and composition_version from studio.suggest_social_clips. Calling again within 24 hours with the same range, preset and version returns the same project (replayed: true) instead of a second copy; after 24 hours the same call makes a new project. Refuses, creating nothing: a composition_version that is no longer current (409 SIMPLER_STUDIO_CLIPS_STALE_VERSION names the current version; suggest again or resend with it), the same short made within 24 hours whose project was since deleted (409 SIMPLER_STUDIO_CLIPS_EARLIER_SHORT_GONE; move start_ms or end_ms by a few milliseconds to make it again), a range outside one stretch of finished footage or outside 5 to 60 s (shorter still when the preset's own limit is lower), read-only tokens and viewer roles. Next: video.export_create with the returned project_id and the same preset.

    Annotations the server declares

    • readOnlyHint: false
  • studio.cut_rangeCut a span of TIME out of the whole video.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Cut a span of TIME out of the whole video. Everything after it moves earlier by the same amount, on every lane at once - scenes, clips, captions and stickers stay in sync. A scene or voice clip the cut lands inside is SPLIT, so the material after the cut still plays. A scene piece the cut would leave under 100ms merges into the scene beside it; when no scene beside it has the footage, the cut widens to that scene's edge on every lane, and cut_start_ms / cut_end_ms report the range applied. Refuses when splitting a scene would pass the 100-scene cap (delete a scene first) and when a locked track holds clips after the cut (unlock it with lock_track). Not undoable.

    Annotations the server declares

    • readOnlyHint: false
  • studio.cut_transcript_spanCut the words of a transcript span out of the whole video, the way deleting text in a transcript editor does.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Cut the words of a transcript span out of the whole video, the way deleting text in a transcript editor does. The span runs from from_caption_id to to_caption_id (row ids from get_transcript) and takes both rows whole; from_word_index and to_word_index narrow it to a word inside the from row and the to row (0-based, in the order get_transcript lists a row's words). The span's time comes out of every lane through the same cut as cut_range, so the scenes, clips, captions and stickers after it move earlier together, and a transcribed row that loses words keeps the words that are left. cut_start_ms and cut_end_ms report the range applied, which is wider than the span when a scene edge needs it. Not undoable. Refuses, naming the fix: an unknown caption id, a span that runs backwards, a word index on a manual caption or a silence row (neither has word timings, so it cuts only as a whole row), a word index past the row's last word, a span that covers no time, a locked track after the span, and whatever else cut_range refuses there (a span past the timeline end, a scene split with no room).

    Annotations the server declares

    • readOnlyHint: false
  • studio.duplicate_clipCopy a timeline clip and place the copy immediately after the original on the same track.No findings

    Why it is in this group

    • The description's first verb is "copy": a word for a change.
    Description, as the server wrote it
    Copy a timeline clip and place the copy immediately after the original on the same track. Refuses if there is no room after it. Returns the new clip id.

    Annotations the server declares

    • readOnlyHint: false
  • studio.duplicate_clipsCopy 1 to 200 timeline clips so the earliest copy starts at at_ms and the others keep their offsets from it, each copy on its original's track.No findings

    Why it is in this group

    • The description's first verb is "copy": a word for a change.
    Description, as the server wrote it
    Copy 1 to 200 timeline clips so the earliest copy starts at at_ms and the others keep their offsets from it, each copy on its original's track. All or nothing: an unknown clip id, a clip on a locked track, copies that would run past the timeline cap, a track that would pass its clip cap, or a copy that overlaps any existing clip (the originals included) refuses the whole call, names the first offender, and copies nothing; an overlap refusal gives the nearest at_ms that fits. Returns new_clip_ids in the order the clip_ids were given.

    Annotations the server declares

    • readOnlyHint: false
  • studio.edit_captionEdit ONE transcript row: its text, its start, its end, or any combination.No findings

    Why it is in this group

    • The name "studio.edit_caption" says "edit": a word for a change.
    Description, as the server wrote it
    Edit ONE transcript row: its text, its start, its end, or any combination. Times are timeline milliseconds. Editing the TEXT marks the row hand-edited, so a later replace_transcribed will not overwrite it - which is exactly how you correct a name the recognizer got wrong.

    Annotations the server declares

    • readOnlyHint: false
  • studio.enhance_audioClean up an audio clip on the timeline: noise removal, voice shaping, loudness normalization.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Clean up an audio clip on the timeline: noise removal, voice shaping, loudness normalization. FREE (platform compute, no provider). Mints an enhanced asset beside the original - the original is never destroyed.

    Annotations the server declares

    • readOnlyHint: false
  • studio.finish_asset_uploadFinish an upload started with studio.begin_asset_upload once every part is PUT: the stored bytes are checked (the moov box is read wherever it sits), counted against the 20 GB quot…No findings

    Why it is in this group

    • The name "studio.finish_asset_upload" says "upload": a word for a change.
    Description, as the server wrote it
    Finish an upload started with studio.begin_asset_upload once every part is PUT: the stored bytes are checked (the moov box is read wherever it sits), counted against the 20 GB quota, and added to the project's media. Returns {asset_id, media_type, probe}. Calling it again returns the same asset. Refuses: parts not yet sent (SIMPLER_STUDIO_UPLOAD_PARTS_MISSING lists them): PUT them, then finish again; bytes that are not the declared type (SIMPLER_STUDIO_UPLOAD_CONTENT_MISMATCH; the file is deleted): begin again with the real mime; a cancelled or expired upload (SIMPLER_STUDIO_UPLOAD_NOT_OPEN): begin again. Free.

    Annotations the server declares

    • readOnlyHint: false
  • studio.generate_music[$0.05/track at provider cost] Generate a background music track from a mood/style description.No findings

    Why it is in this group

    • The name "studio.generate_music" says "generate": a word for a change.
    Description, as the server wrote it
    [$0.05/track at provider cost] Generate a background music track from a mood/style description. Returns an audio asset id — place it with add_audio_to_timeline.

    Annotations the server declares

    • readOnlyHint: false
  • studio.generate_scene[$0.10/sec at provider cost] Generate video for a scene from its prompt (text-to-video).No findings

    Why it is in this group

    • The name "studio.generate_scene" says "generate": a word for a change.
    Description, as the server wrote it
    [$0.10/sec at provider cost] Generate video for a scene from its prompt (text-to-video). Pass scene_id to fill a planned scene, or prompt to create-and-generate in one step. NEVER mention camera equipment (crane, dolly, rig) — describe what to SHOW. Say 'camera rises' NOT 'crane shot'. Include: subject, action, lighting, color palette, mood.

    Annotations the server declares

    • readOnlyHint: false
  • studio.generate_voiceoverGenerate narration audio from a script.No findings

    Why it is in this group

    • The name "studio.generate_voiceover" says "generate": a word for a change.
    • The input takes a statement or command to run (input.script), which can change anything it can reach.
    Description, as the server wrote it
    Generate narration audio from a script. voice_tier is the REAL choice the user makes: 'standard' ($0.015/1K chars at provider cost) vs 'premium' ($0.15/1K chars at provider cost) — quote both prices when proposing; never pick silently. Returns an audio asset id.

    Annotations the server declares

    • readOnlyHint: false
  • studio.import_asset_from_urlCopy a video, audio or image file from a public http(s) link into a studio project's media (up to 2 GB, within the 20 GB quota).Can send data outNo findings

    Why it is in this group

    • The name "studio.import_asset_from_url" says "import": a word for a change.

    What it can reach

    • outboundThe input takes a URL (input.url): a request to a URL can carry data to whoever runs it.
    Description, as the server wrote it
    Copy a video, audio or image file from a public http(s) link into a studio project's media (up to 2 GB, within the 20 GB quota). Returns {asset_id, media_type, probe}, the same as studio.finish_asset_upload. The link is only downloaded; nothing is posted to it. Refuses: localhost, private, link-local or cloud-metadata addresses, or a redirect to one (SIMPLER_STUDIO_IMPORT_URL_REFUSED): give a public link; a link that fails or answers an error (SIMPLER_STUDIO_IMPORT_FETCH_FAILED): check it downloads without signing in; a file over the limit or the quota (SIMPLER_STUDIO_UPLOAD_TOO_LARGE / SIMPLER_STUDIO_UPLOAD_QUOTA): GET /api/studio/uploads lists your stored files, then delete unused ones; a page or unsupported format (SIMPLER_STUDIO_UPLOAD_TYPE_UNSUPPORTED): link to the media file itself; a source that stalls (SIMPLER_STUDIO_IMPORT_TIMEOUT): try again or upload with studio.begin_asset_upload. Free.

    Annotations the server declares

    • readOnlyHint: false
  • studio.insert_product_cardPut a stock item's name and selling price on the video as a product card: a price_tag text clip on the first text track (or the one track_id names), from start_ms for duration_ms.No findings

    Why it is in this group

    • The name "studio.insert_product_card" says "insert": a word for a change.
    Description, as the server wrote it
    Put a stock item's name and selling price on the video as a product card: a price_tag text clip on the first text track (or the one track_id names), from start_ms for duration_ms. It reads the stock record as you, so the card shows what stock_item_read shows; the values are copied into the project, and later stock edits do not change a card already placed. The price prints with the currency code (SGD 12.90), taken from the business's sales_settings default_currency unless you pass currency. style: tag (name over price, lower left), banner (name and price on one line, bottom centre), price_only (the price alone, large, top right). Free. The result names the clip, its track and the text shown. Refuses, placing nothing: an unknown stock_item_id (stock_item_list lists the ids), a retired item (restore it with stock_item_update, is_active 1), an item with no sell_price_cents (set it with stock_item_update), no currency (pass currency, or save one with sales_settings_upsert), a token without the stock namespace (reconnect with stock in scope, or pass the values yourself to studio.place_product_card), a locked, unknown or non-text track, and an overlap with another clip (the refusal gives the nearest start_ms that fits).

    Annotations the server declares

    • readOnlyHint: false
  • studio.insert_timeOpen length_ms of empty time at at_ms on every lane, so everything at or after at_ms moves later together.No findings

    Why it is in this group

    • The name "studio.insert_time" says "insert": a word for a change.
    Description, as the server wrote it
    Open length_ms of empty time at at_ms on every lane, so everything at or after at_ms moves later together. The main track receives a placeholder scene there (a pending scene with the prompt "Gap"; fill it with update_scene and generate_scene, or add footage). A scene or voice clip at_ms lands inside is split around the new time; text, stills, captions and stickers that span it stretch. Refuses, naming the fix: at_ms past the end of the main track, a locked track with clips at or after at_ms, at_ms within 100ms of a scene's edge, no free scene slot, the timeline or a stretched clip passing its cap, or a split passing a track's clip cap.

    Annotations the server declares

    • readOnlyHint: false
  • studio.lock_trackLock or unlock a track.No findings

    Why it is in this group

    • The name "studio.lock_track" says "lock": a word for a change.
    Description, as the server wrote it
    Lock or unlock a track. A locked track refuses every edit - move, trim, split, duplicate, delete - until it is unlocked. Lock a finished voiceover before restructuring the picture so the timing cannot drift underneath it. track names the role and locks every track of it; add track_id to lock only that one.

    Annotations the server declares

    • readOnlyHint: false
  • studio.move_captionMove ONE whole caption row along the timeline by delta_ms (negative moves it earlier): its start, its end and every word timing move together, so the words keep their spacing.No findings

    Why it is in this group

    • The name "studio.move_caption" says "move": a word for a change.
    Description, as the server wrote it
    Move ONE whole caption row along the timeline by delta_ms (negative moves it earlier): its start, its end and every word timing move together, so the words keep their spacing. Pick the row by caption_id, or by at_ms: the visible caption showing at that timeline millisecond (hidden rows are skipped). The row stops at 0, at the timeline cap and at the visible caption on either side, so a move never runs into its neighbours. To change only a start or an end, use edit_caption. Refuses, naming the fix: neither caption_id nor at_ms given, an unknown caption_id, no visible caption at at_ms, or a delta_ms outside the range that fits (the refusal gives that range).

    Annotations the server declares

    • readOnlyHint: false
  • studio.move_clips_to_trackMove 1 to 200 timeline clips onto another track of the same kind (a b-roll clip to a video-broll track, a title to a text track, music to an audio-music track, a voice clip to an a…No findings

    Why it is in this group

    • The name "studio.move_clips_to_track" says "move": a word for a change.
    Description, as the server wrote it
    Move 1 to 200 timeline clips onto another track of the same kind (a b-roll clip to a video-broll track, a title to a text track, music to an audio-music track, a voice clip to an audio-voice track), keeping their spacing; delta_ms also moves them in time (default 0). Stacking follows the track: get_composition lists tracks in stacking order, and a clip on a later video-broll or text track draws over the earlier ones. All or nothing. Refuses, naming the first offender and moving nothing: an unknown clip id or track_id (get_composition lists the ids), a locked source or target track (unlock it with lock_track), a target of another kind (pick a track of the clip's kind, or add one with add_track), a target that would pass its clip cap (move fewer clips, or add a track with add_track), listed clips that overlap each other (move them one at a time), a clip pushed before 0 or past the timeline cap (keep delta_ms within the range the refusal gives), or a landing that overlaps a clip already on the target (use another delta_ms; the refusal gives the nearest one that fits).

    Annotations the server declares

    • readOnlyHint: false
  • studio.move_trackMove a track to position within its own role.No findings

    Why it is in this group

    • The name "studio.move_track" says "move": a word for a change.
    Description, as the server wrote it
    Move a track to position within its own role. Position 0 is the lowest: its pictures draw first, so every later video-broll or text track draws over it. Tracks of other roles keep their order; text always draws over video. Locked tracks may be reordered. Refuses an unknown track_id or a position past the role's last track, naming the fix.

    Annotations the server declares

    • readOnlyHint: false
  • studio.place_product_cardPlace a product card: a price_tag text clip showing a stock item's name and price, built from values you already hold.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Place a product card: a price_tag text clip showing a stock item's name and price, built from values you already hold. studio.insert_product_card reads them from the stock record for you; use this op inside studio.apply_ops or when the values come from elsewhere. The price prints as the currency code and the amount with two decimals (SGD 12.90), never a symbol. style: tag (name over price, lower left), banner (name and price on one line, bottom centre), price_only (the price alone, large, top right); default tag. A name longer than 32 characters is shortened on screen and kept whole in the card's record. Lands on the first text track, or the one track_id names, at start_ms (default 0) for duration_ms (default 4000). Refuses, naming the fix: a blank product_name, an unknown or non-text track_id, a locked text track, a card past the timeline cap, a full track, or an overlap with another clip (the refusal gives the nearest start_ms that fits).

    Annotations the server declares

    • readOnlyHint: false
  • studio.reframe_sceneRe-frame one scene for a different aspect ratio.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Re-frame one scene for a different aspect ratio. method 'analytic' re-centers and scales the EXISTING footage on a focus point (cheap, no new pixels, needs the source dimensions); method 'generative' outpaints new edges [$0.10/sec at provider cost].

    Annotations the server declares

    • readOnlyHint: false
  • studio.rename_trackGive a track a display name up to 40 characters, or pass an empty name to go back to the role's label.No findings

    Why it is in this group

    • The name "studio.rename_track" says "rename": a word for a change.
    Description, as the server wrote it
    Give a track a display name up to 40 characters, or pass an empty name to go back to the role's label. Names are for people and never address a track: tools take track_id. Locked tracks may be renamed. Refuses an unknown track_id, naming the fix.

    Annotations the server declares

    • readOnlyHint: false
  • studio.reorder_scenesMove a scene to a different position in the primary lane.No findings

    Why it is in this group

    • The description's first verb is "move": a word for a change.
    Description, as the server wrote it
    Move a scene to a different position in the primary lane. Positions are 0-based, so to_index 0 makes it the opening scene. Scene start times are DERIVED from order, so every scene after the move shifts in time. With ripple_secondary "shift" (the default) every clip, caption and sticker that sits wholly inside a scene moves with that scene; one that spans a scene edge stays put and is listed in straddling_ids. "none" moves the scenes alone. Refuses a shift that moves a clip on a locked track (unlock it with lock_track, or pass ripple_secondary "none") or lands a moved clip over one that stays put (pass ripple_secondary "none", or move or split that clip first).

    Annotations the server declares

    • readOnlyHint: false
  • studio.replace_transcribedReplace the transcript with fresh rows from your own transcription.No findings

    Why it is in this group

    • The name "studio.replace_transcribed" says "replace": a word for a change.
    Description, as the server wrote it
    Replace the transcript with fresh rows from your own transcription. Rows the operator hand-edited (source 'manual') SURVIVE - only machine-transcribed rows are replaced, so a re-transcribe never silently discards someone's correction. Rows are re-sorted by start time.

    Annotations the server declares

    • readOnlyHint: false
  • studio.request_regenerationFlag a generated scene that does not match intent: record WHY and an improved prompt.No findings

    Why it is in this group

    • The name "studio.request_regeneration" says "request": a word for a change.
    Description, as the server wrote it
    Flag a generated scene that does not match intent: record WHY and an improved prompt. This stages the improved prompt on the scene (regeneration is a separate paid, user-approved step — it never silently destroys footage or spends).

    Annotations the server declares

    • readOnlyHint: false
  • studio.restore_fillersUn-hide transcript rows that were soft-deleted.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Un-hide transcript rows that were soft-deleted. Scope is EVERY hidden row, not only filler words - a caption hidden by delete_element comes back too. There is no per-row restore.

    Annotations the server declares

    • readOnlyHint: false
  • studio.restore_versionBring back an earlier version of a studio project: it saves that version as a new version; history is never rewritten, so the version it replaces stays listed and restoring that on…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Bring back an earlier version of a studio project: it saves that version as a new version; history is never rewritten, so the version it replaces stays listed and restoring that one undoes this. Returns {version, restored_from, previous_version}. A person editing the project in the browser is told at their next save that a newer version is on the server and reloads. Refuses: a project_id outside your scope (NOT_FOUND): studio_project_list lists your projects; pass one of their ids; a version the project no longer stores (SIMPLER_STUDIO_VERSION_NOT_STORED): call studio.list_versions and pick a listed one; a stored version that no longer passes the composition schema (SIMPLER_STUDIO_VERSION_UNREADABLE): restore a different version; another save landing at the same moment (SIMPLER_STUDIO_RESTORE_CONFLICT): call again. Free.

    Annotations the server declares

    • readOnlyHint: false
  • studio.roll_editMove the cut between two neighbours by delta_ms (positive moves it later): left_id's end and right_id's start move together, and their footage in/out points follow.No findings

    Why it is in this group

    • The name "studio.roll_edit" says "edit": a word for a change.
    Description, as the server wrote it
    Move the cut between two neighbours by delta_ms (positive moves it later): left_id's end and right_id's start move together, and their footage in/out points follow. The pair's total length and every other element stay where they are. The neighbours are a scene and the next scene, or two touching clips on one track, left one first. Refuses, naming the fix: an unknown id, ids that do not share a cut, a locked track, or a delta that would leave either side under 100ms, past its length cap or past its footage (the refusal gives the delta_ms range that fits).

    Annotations the server declares

    • readOnlyHint: false
  • studio.set_configSet project output settings: aspect ratio, resolution, frame rate, background colour, caption style.No findings

    Why it is in this group

    • The name "studio.set_config" says "set": a word for a change.
    Description, as the server wrote it
    Set project output settings: aspect ratio, resolution, frame rate, background colour, caption style. Every field is optional - only what you pass changes. Changing aspect does NOT re-crop existing footage (it letterboxes); reframe the scenes if you need the framing to follow.

    Annotations the server declares

    • readOnlyHint: false
  • studio.set_duckingConfigure the voice-over-music ducking mix intent (by role; the renderer honors it).No findings

    Why it is in this group

    • The name "studio.set_ducking" says "set": a word for a change.
    Description, as the server wrote it
    Configure the voice-over-music ducking mix intent (by role; the renderer honors it).

    Annotations the server declares

    • readOnlyHint: false
  • studio.set_track_mutedMute or unmute a whole track.No findings

    Why it is in this group

    • The name "studio.set_track_muted" says "set": a word for a change.
    Description, as the server wrote it
    Mute or unmute a whole track. A muted audio track plays nothing and a muted voice track stops ducking the music; a muted video-broll track keeps its pictures and drops its clips' own sound. The preview and the export both honor it, and each clip keeps its own muted and volume. Locked tracks may be muted. Refuses an unknown track_id or a text track, naming the fix.

    Annotations the server declares

    • readOnlyHint: false
  • studio.set_transitionSet a scene's transition (in = how it enters, out = how it leaves).No findings

    Why it is in this group

    • The name "studio.set_transition" says "set": a word for a change.
    Description, as the server wrote it
    Set a scene's transition (in = how it enters, out = how it leaves). Dissolve creates connection (500-800ms); fade for scene breaks; cut clears the transition.

    Annotations the server declares

    • readOnlyHint: false
  • studio.shift_clipsMove 1 to 200 timeline clips by the same delta_ms (negative moves earlier), keeping their spacing and their tracks.No findings

    Why it is in this group

    • The description's first verb is "move": a word for a change.
    Description, as the server wrote it
    Move 1 to 200 timeline clips by the same delta_ms (negative moves earlier), keeping their spacing and their tracks. All or nothing: an unknown clip id, a clip on a locked track, a clip pushed before 0 or past the timeline cap, or a landing that overlaps a clip you did not list refuses the whole call, names the first offending clip, and moves nothing; an overlap refusal gives the nearest delta_ms that fits. Scenes on the main track keep their order and are not clips; use reorder_scenes for them.

    Annotations the server declares

    • readOnlyHint: false
  • studio.slide_editMove a video or audio clip by delta_ms between its neighbours on its track: the clip touching its start gets longer or shorter at its end, the clip touching its end at its start, s…No findings

    Why it is in this group

    • The name "studio.slide_edit" says "edit": a word for a change.
    Description, as the server wrote it
    Move a video or audio clip by delta_ms between its neighbours on its track: the clip touching its start gets longer or shorter at its end, the clip touching its end at its start, so the three-clip span and every other track stay put. A side with a gap instead lets the clip move into the gap and no further. Refuses, naming the fix: an unknown id, a scene (scenes reorder with reorder_scenes), text or a still image (move it with shift_clips), a locked track, or a delta that would leave a neighbour under 100ms or past its footage (the refusal gives the delta_ms range that fits).

    Annotations the server declares

    • readOnlyHint: false
  • studio.slip_editShow a different part of a clip's or scene's footage without moving it: its in and out points move by delta_ms of timeline (positive shows later footage), while its position and le…No findings

    Why it is in this group

    • The name "studio.slip_edit" says "edit": a word for a change.
    Description, as the server wrote it
    Show a different part of a clip's or scene's footage without moving it: its in and out points move by delta_ms of timeline (positive shows later footage), while its position and length stay. Works on video and audio clips and on scenes with finished video. Refuses, naming the fix: an unknown id, text or a still image or a scene with no footage yet (only video and audio can slip), a locked track, or a delta that runs past the footage (the refusal gives the delta_ms range that fits).

    Annotations the server declares

    • readOnlyHint: false
  • studio.snapshot_versionName a studio project's version (the current one unless version is given) so it is never deleted by retention; naming a named version renames it.1 finding

    Why it is in this group

    • The name "studio.snapshot_version" says "snapshot": a word for reading.
    • The server marks it as not read-only (readOnlyHint: false).

    Findings

    • Warn

      The server marks "studio.snapshot_version" as not read-only (readOnlyHint: false), and its words say it reads.

      Matched text

      annotations: {"readOnlyHint":false}; The name "studio.snapshot_version" says "snapshot": a word for reading.

      What this check looks for
    Description, as the server wrote it
    Name a studio project's version (the current one unless version is given) so it is never deleted by retention; naming a named version renames it. At most 20 named versions per project. Adds no new version, so an editor open on the project keeps saving. Refuses: a project_id outside your scope (NOT_FOUND): studio_project_list lists your projects; pass one of their ids; a label that is empty, over 80 characters or more than one line (SIMPLER_STUDIO_SNAPSHOT_LABEL_INVALID): send one line of 1 to 80 characters; 20 names already used (SIMPLER_STUDIO_SNAPSHOTS_FULL): remove one with studio.remove_snapshot; a project with no saved version (SIMPLER_STUDIO_SNAPSHOT_NOTHING_SAVED): save it first; a version the project no longer stores (SIMPLER_STUDIO_VERSION_NOT_STORED): call studio.list_versions and pick a listed one; an environment where names are not switched on yet (SIMPLER_STUDIO_SNAPSHOTS_UNAVAILABLE): saving and restoring still work; ask the owner to apply DB_MEDIA migration 0005. Free.

    Annotations the server declares

    • readOnlyHint: false
  • studio.splitCut a scene or a timeline clip in two at a timeline millisecond.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Cut a scene or a timeline clip in two at a timeline millisecond. The left half keeps the id; the right half is new and is returned. Both halves must keep at least 100ms of footage or the split refuses. Transitions stay on the outer edges - the new seam is a hard cut.

    Annotations the server declares

    • readOnlyHint: false
  • studio.suggest_social_clipsFind the best short-form moments in a long studio video and return them as ranges with a title, a hook line, a one-sentence rationale and measured evidence (captioned words, words …1 finding

    Why it is in this group

    • The description's first verb is "find": a word for reading.
    • The server marks it as not read-only (readOnlyHint: false).

    Findings

    • Warn

      The server marks "studio.suggest_social_clips" as not read-only (readOnlyHint: false), and its words say it reads.

      Matched text

      annotations: {"readOnlyHint":false}; The description's first verb is "find": a word for reading.

      What this check looks for
    Description, as the server wrote it
    Find the best short-form moments in a long studio video and return them as ranges with a title, a hook line, a one-sentence rationale and measured evidence (captioned words, words per second, whether it starts on a scene cut). It creates and changes nothing. To make a suggestion into a project, call studio.create_social_clip with its start_ms and end_ms; then render that project with video.export_create and the same preset. Pass count (1 to 10), target_preset (default shorts_9x16; it sets the shape and the longest length, never over 60 s) and optionally asset_id to use only the scenes cut from one uploaded video. Costs up to about $0.0012 per call for the model; the wallet is checked first and an unfunded wallet gets PAYMENT_REQUIRED with nothing charged. Refuses, with nothing charged: a project with no finished footage (COLD_START: add footage with studio.add_scene_from_asset or studio.generate_scene), an asset_id that is not a video or not on the timeline yet (place it with studio.add_scene_from_asset), footage shorter than 5 s, read-only tokens and viewer roles. A pick outside the real footage is dropped with its reason in 'dropped', never stretched to fit.

    Annotations the server declares

    • readOnlyHint: false
  • studio.transcribe[$0.0043/min at provider cost] Transcribe an audio or video asset into word-timed captions.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    [$0.0043/min at provider cost] Transcribe an audio or video asset into word-timed captions. REPLACES every previously transcribed caption on this project; captions you wrote by hand survive. Speaker labels are asked for: a row carries speaker (Speaker 1, Speaker 2, ...) only when the recognizer labelled its words, and the reply's speaker_count is 0 when it labelled none. Read the rows with get_transcript; follow with remove_fillers / remove_silence to clean the transcript.

    Annotations the server declares

    • readOnlyHint: false
  • studio.trim_clipTrim a timeline clip's start or end edge to an absolute TIMELINE millisecond.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Trim a timeline clip's start or end edge to an absolute TIMELINE millisecond. The clip keeps its id and its source in/out points move with the edge, so the footage stays in sync. Refuses if the trim would leave under 100ms, cross a neighbouring clip, or run past the source media.

    Annotations the server declares

    • readOnlyHint: false
  • studio.trim_sceneTrim a generated scene's footage: cut milliseconds off its start and/or end.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Trim a generated scene's footage: cut milliseconds off its start and/or end. With ripple_secondary "shift" (the default) the other lanes follow the footage: time the scene loses at its start or end is cut from every lane at that edge, and time it gains is inserted there, so clips, captions and stickers stay over the frames they were placed on (one that spans that edge is split or stretched). "none" changes the main track alone. Refuses a shift that moves a locked track (unlock it with lock_track, or pass ripple_secondary "none").

    Annotations the server declares

    • readOnlyHint: false
  • studio.update_clipMove a clip or adjust its volume/mute on any secondary track.No findings

    Why it is in this group

    • The name "studio.update_clip" says "update": a word for a change.
    Description, as the server wrote it
    Move a clip or adjust its volume/mute on any secondary track.

    Annotations the server declares

    • readOnlyHint: false
  • studio.update_sceneUpdate a scene's prompt or duration.No findings

    Why it is in this group

    • The name "studio.update_scene" says "update": a word for a change.
    Description, as the server wrote it
    Update a scene's prompt or duration. EDIT-INVALIDATES-ASSET: changing the prompt of a generated scene resets it to pending and drops its footage (a stale render cannot survive a prompt edit) — say so before doing it. With ripple_secondary "shift" (the default) the other lanes follow the footage: time the scene loses at its start or end is cut from every lane at that edge, and time it gains is inserted there, so clips, captions and stickers stay over the frames they were placed on (one that spans that edge is split or stretched). "none" changes the main track alone. Refuses a shift that moves a locked track (unlock it with lock_track, or pass ripple_secondary "none").

    Annotations the server declares

    • readOnlyHint: false
  • studio.update_stickerMove, resize, rotate, fade or re-time a sticker overlay.No findings

    Why it is in this group

    • The name "studio.update_sticker" says "update": a word for a change.
    Description, as the server wrote it
    Move, resize, rotate, fade or re-time a sticker overlay. Only the fields you pass change. Positions are normalized 0-1 of the frame.

    Annotations the server declares

    • readOnlyHint: false
  • studio.update_text_overlayUpdate an existing text clip: content, position, timing.No findings

    Why it is in this group

    • The name "studio.update_text_overlay" says "update": a word for a change.
    Description, as the server wrote it
    Update an existing text clip: content, position, timing.

    Annotations the server declares

    • readOnlyHint: false
  • team_assignment_acceptAccepts a pending job — only the assignee may call this, and only while the assignment is still 'pending'; it stamps accepted_at and moves status to 'accepted'.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Accepts a pending job — only the assignee may call this, and only while the assignment is still 'pending'; it stamps accepted_at and moves status to 'accepted'. NEXT: team_assignment_start once you begin the work.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • team_assignment_add_photoAttaches a job photo to an assignment — only the assignee, and only while the job is 'accepted', 'in_progress', or 'completed'.No findings

    Why it is in this group

    • The name "team_assignment_add_photo" says "add": a word for a change.
    Description, as the server wrote it
    Attaches a job photo to an assignment — only the assignee, and only while the job is 'accepted', 'in_progress', or 'completed'. photo_key must be one minted by POST /api/desk/assignments/upload for this tenant; re-attaching the same key is a no-op, and the row holds at most 20 photos. NEXT: team_assignment_complete once proof is attached.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • team_assignment_completeFinishes a job — only the assignee, and only from status 'in_progress'; it stamps completed_at, moves status to 'completed', and stores completion_notes when you supply them.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Finishes a job — only the assignee, and only from status 'in_progress'; it stamps completed_at, moves status to 'completed', and stores completion_notes when you supply them. Completed is terminal: the row can no longer be edited or reassigned (deletion by the assigner is still possible).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • team_assignment_create[REQUIRES APPROVAL] Delegates a task to a teammate (or yourself) as a new team_assignment; status is workflow-owned and always starts 'pending' — passing status yourself fails vali…No findings

    Why it is in this group

    • The name "team_assignment_create" says "create": a word for a change.
    Description, as the server wrote it
    [REQUIRES APPROVAL] Delegates a task to a teammate (or yourself) as a new team_assignment; status is workflow-owned and always starts 'pending' — passing status yourself fails validation instead of being silently overridden. Accepts natural language for the common fields: task_name/task for title, due_date/due for due_at ('YYYY-MM-DD', epoch millis, 'today', 'tomorrow', or 'next <weekday>'), and 'self'/'me'/'yourself' for assigned_to. assigned_to must already be a member of your tenant. NEXT: team_assignment_accept, then _start and _complete, drive the lifecycle from there.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • team_assignment_declineDeclines a job — only the assignee, and only while it is 'pending' or 'accepted'; decline_reason is required and is stamped alongside declined_at.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Declines a job — only the assignee, and only while it is 'pending' or 'accepted'; decline_reason is required and is stamped alongside declined_at. Declined is terminal, same as completed: the assignment cannot be edited or reassigned afterward.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • team_assignment_reassignHands an assignment to a different tenant member — only the original assigner or an admin+, never the current assignee reassigning themself, and never onto a terminal (completed/de…No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Hands an assignment to a different tenant member — only the original assigner or an admin+, never the current assignee reassigning themself, and never onto a terminal (completed/declined) row. It resets status to 'pending' for the new assignee and records the prior assignee/status so the move can be undone. NEXT: team_assignment_undo_reassign within 30 seconds reverts it.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • team_assignment_startMarks a job as started — only the assignee, and only from status 'accepted'; it stamps started_at and moves status to 'in_progress'.No findings

    Why it is in this group

    • The name "team_assignment_start" says "start": a word for a change.
    Description, as the server wrote it
    Marks a job as started — only the assignee, and only from status 'accepted'; it stamps started_at and moves status to 'in_progress'. NEXT: team_assignment_add_photo for proof, then team_assignment_complete when done.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • team_assignment_undo_reassignReverts an assignment's most recent reassignment — restores the previous assignee and status.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Reverts an assignment's most recent reassignment — restores the previous assignee and status. Only the person who performed that reassignment may call it, and only within the 30-second window after it happened; past that, CONFLICT says to reassign again instead.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • team_assignment_updateEdits an assignment's content (title/description/priority/due_at/attachment) — only the assigner may call this, and only before the job reaches a terminal state.No findings

    Why it is in this group

    • The name "team_assignment_update" says "update": a word for a change.
    Description, as the server wrote it
    Edits an assignment's content (title/description/priority/due_at/attachment) — only the assigner may call this, and only before the job reaches a terminal state. assigned_to cannot move here even though it is a writable column: reassigning is a separate transition that also opens a 30-second undo window. NEXT: team_assignment_reassign to hand the job to someone else.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • user_interest_upsertCreate-or-update a user_interest keyed on interest.No findings

    Why it is in this group

    • The name "user_interest_upsert" says "upsert": a word for a change.
    Description, as the server wrote it
    Create-or-update a user_interest keyed on interest.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • user_setting_upsertCreate-or-update a user_setting keyed on key.No findings

    Why it is in this group

    • The name "user_setting_upsert" says "upsert": a word for a change.
    Description, as the server wrote it
    Create-or-update a user_setting keyed on key.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • video.exportFollow one video export to completion and return its download links.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Follow one video export to completion and return its download links. Start the export with video.export_create, then pass the job_id it returned (without job_id this follows the project's newest export). Polls the durable job row every 3s through queued -> processing -> uploading -> completed. On completion it returns download_path (for a session signed in to this workspace) and, when signed links are enabled, download_url (plus captions_url when there is an SRT): a link valid for 24 hours that needs no session; call again with the job_id for a fresh one. While signed links are held off, download_url_unavailable says why. Pricing: free (rendering runs on platform compute, no provider cost), charged by video.export_create, never here. STREAMING SUPPORTED: send Accept: text/event-stream for per-phase progress events. A job waiting for a free renderer reports "Queued (position N)". The files are kept 30 days after the export finishes (files_kept_until), then deleted automatically. Refuses: no such job (SIMPLER_STUDIO_EXPORT_NO_JOB): start one with video.export_create {project_id}; a failed job (SIMPLER_STUDIO_EXPORT_FAILED names the reason): fix it and create a new export; a cancelled job (SIMPLER_STUDIO_EXPORT_CANCELLED): create a new export; deleted files (SIMPLER_STUDIO_EXPORT_OUTPUT_GONE) or files past the 30 days (SIMPLER_STUDIO_EXPORT_EXPIRED, "expired; export again"): render again with video.export_create.

    Annotations the server declares

    • readOnlyHint: true
  • video.export_createStart rendering a studio project to a file: a video, a GIF, the sound mix alone or one poster still (format).1 finding

    Why it is in this group

    • The name "video.export_create" says "create": a word for a change.

    Findings

    • Info

      The description is 2,141 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...thing is posted, emailed or uploaded anywhere; the result is a file you download."

      What this check looks for
    Description, as the server wrote it
    Start rendering a studio project to a file: a video, a GIF, the sound mix alone or one poster still (format). Returns job_id immediately; call video.export with job_id to follow progress and receive a download link valid for 24 hours. Refuses, naming the fix, when the project is not exportable, the preset's aspect differs from the project, or the tenant's active-job or daily limit is reached. format picks the file (mp4_h264_aac, webm_vp9_opus, gif, m4a_aac, wav_pcm, poster_jpg; default mp4_h264_aac). Format refusals: a preset other than original with any format but mp4_h264_aac (SIMPLER_STUDIO_EXPORT_FORMAT_PRESET: send preset original); gif longer than 30 s (SIMPLER_STUDIO_EXPORT_GIF_TOO_LONG: trim, or pick a video format); m4a_aac or wav_pcm with nothing audible (SIMPLER_STUDIO_EXPORT_FORMAT_NO_AUDIO: add or unmute sound); poster_jpg without poster_time_ms inside the timeline, or poster_time_ms with another format (SIMPLER_STUDIO_EXPORT_POSTER_TIME). Repeating the same request within 24 hours returns the same job, except a job still waiting for a free renderer: the repeat replaces it with a fresh attempt (the old job then reads SIMPLER_STUDIO_EXPORT_SUPERSEDED). Pricing: free (rendering runs on platform compute, no provider cost). Limits per workspace: 2 renders at a time (SIMPLER_STUDIO_EXPORT_ACTIVE_CAP) and 60 output minutes per rolling 24 hours (SIMPLER_STUDIO_EXPORT_DAILY_QUOTA); wait, or export a shorter cut. A composition_version older than the saved one is refused (SIMPLER_STUDIO_EXPORT_VERSION_STALE): re-read with studio.get_composition. One export may run 15 min at 1080p and 5 min at 4K, and a sound-only export 15 min at any resolution (SIMPLER_STUDIO_EXPORT_OUTPUT_CAP): trim the timeline or pick a smaller preset. A poster renders the timeline up to its frame, so the cap counts up to its poster_time_ms: pick an earlier one. When every renderer is busy the job waits in a queue (queue_position) and starts on its own; video.export_cancel {job_id} withdraws it. The files are kept 30 days after the export finishes. Nothing is posted, emailed or uploaded anywhere; the result is a file you download.

    Annotations the server declares

    • readOnlyHint: false
  • workflow_createCreate a workflow — an AI operator's job: validates definition_json (a { nodes, edges, schedule?No findings

    Why it is in this group

    • The name "workflow_create" says "create": a word for a change.
    Description, as the server wrote it
    Create a workflow — an AI operator's job: validates definition_json (a { nodes, edges, schedule? } graph) against the platform's node vocabulary, refusing unknown node types and any comms/message-shaped step that isn't a prepare_output node, since the platform prepares outputs but never sends them. trigger_type 'schedule' requires a schedule block; next_run_at is then computed server-side from it, never from a caller value. is_active defaults to 0 (draft) — going live is a separate explicit choice. NEXT: workflow_update to fix a rejected definition or flip is_active.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • workflow_prepared_outputsList this tenant's APPROVED prepared outputs: drafts a workflow's prepare_output nodes parked for review, which a human then approved in the platform inbox.Reads private dataReads text others write1 finding

    Why it is in this group

    • The description's first verb is "list": a word for reading.
    • The server marks it as not read-only (readOnlyHint: false).

    What it can reach

    • private-dataThe description's first sentence says "list" and "inbox": it reads mail.
    • untrusted-inputThe description's first sentence says "list" and "review": it reads comments, which other people can write.

    Findings

    • Warn

      The server marks "workflow_prepared_outputs" as not read-only (readOnlyHint: false), and its words say it reads.

      Matched text

      annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}; The description's first verb is "list": a word for reading.

      What this check looks for
    Description, as the server wrote it
    List this tenant's APPROVED prepared outputs: drafts a workflow's prepare_output nodes parked for review, which a human then approved in the platform inbox. Pending and rejected drafts never appear here — approval happens only in the inbox, never through this call. Optional limit 1-100 (default 50), newest-decided first. This is the platform-prepares-never-sends handoff: the desktop harness pulls approved drafts here and sends them locally.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
  • workflow_updateEdit a workflow's fields or its definition graph: every call re-validates the definition — yours if resent, else the stored one decoded back — against the resulting trigger_type, s…No findings

    Why it is in this group

    • The name "workflow_update" says "update": a word for a change.
    Description, as the server wrote it
    Edit a workflow's fields or its definition graph: every call re-validates the definition — yours if resent, else the stored one decoded back — against the resulting trigger_type, so a plain rename can fail if that stored graph no longer validates, and flipping to 'schedule' without a schedule block fails here rather than at the scheduler. When trigger_type ends as 'schedule' and is_active as 1, next_run_at recomputes from schedule and now on every call, even one touching neither field; any value you send for it is ignored. Optional expected_updated_at compare-and-set: a stale value refuses CONFLICT — re-read, merge, retry. NEXT: workflow_read to confirm the new next_run_at.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: false
Only reads 91Show 91 read-only tools
  • agent_note_listList your agent_note rows (newest first).Reads private dataNo findings

    Why it is in this group

    • The name "agent_note_list" says "list": a word for reading.

    What it can reach

    • private-dataThe name "agent_note_list" says "list" and "note": it reads notes.
    • private-dataThe description's first sentence says "list" and "note": it reads notes.
    Description, as the server wrote it
    List your agent_note rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • agent_note_readRead one agent_note by id.Reads private dataNo findings

    Why it is in this group

    • The name "agent_note_read" says "read": a word for reading.

    What it can reach

    • private-dataThe name "agent_note_read" says "read" and "note": it reads notes.
    • private-dataThe description's first sentence says "read" and "note": it reads notes.
    Description, as the server wrote it
    Read one agent_note by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • brand_assets_listList your brand_assets rows (newest first).No findings

    Why it is in this group

    • The name "brand_assets_list" says "list": a word for reading.
    Description, as the server wrote it
    List your brand_assets rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • brand_assets_readRead one brand_assets by id.No findings

    Why it is in this group

    • The name "brand_assets_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one brand_assets by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • contact_listList your contact rows (newest first).Reads private dataNo findings

    Why it is in this group

    • The name "contact_list" says "list": a word for reading.

    What it can reach

    • private-dataThe name "contact_list" says "list" and "contact": it reads contacts.
    • private-dataThe description's first sentence says "list" and "contact": it reads contacts.
    Description, as the server wrote it
    List your contact rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • contact_note_listList your contact_note rows (newest first).Reads private dataNo findings

    Why it is in this group

    • The name "contact_note_list" says "list": a word for reading.

    What it can reach

    • private-dataThe name "contact_note_list" says "list" and "contact": it reads contacts.
    • private-dataThe description's first sentence says "list" and "contact": it reads contacts.
    Description, as the server wrote it
    List your contact_note rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • contact_note_readRead one contact_note by id.Reads private dataNo findings

    Why it is in this group

    • The name "contact_note_read" says "read": a word for reading.

    What it can reach

    • private-dataThe name "contact_note_read" says "read" and "contact": it reads contacts.
    • private-dataThe description's first sentence says "read" and "contact": it reads contacts.
    Description, as the server wrote it
    Read one contact_note by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • contact_readRead one contact by id.Reads private dataNo findings

    Why it is in this group

    • The name "contact_read" says "read": a word for reading.

    What it can reach

    • private-dataThe name "contact_read" says "read" and "contact": it reads contacts.
    • private-dataThe description's first sentence says "read" and "contact": it reads contacts.
    Description, as the server wrote it
    Read one contact by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • desk_surface_listList your desk_surface rows (newest first).No findings

    Why it is in this group

    • The name "desk_surface_list" says "list": a word for reading.
    Description, as the server wrote it
    List your desk_surface rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • desk_surface_readRead one desk_surface by id.No findings

    Why it is in this group

    • The name "desk_surface_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one desk_surface by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • document.extractFetch a document from a URL, classify its type (personal/medical/business/unknown) with keyword + PII-pattern heuristics (documents containing actual PII data are NEVER business), …Reads private dataReads text others writeNo findings

    Why it is in this group

    • The description's first verb is "fetch": a word for reading.

    What it can reach

    • private-dataThe name "document.extract" says "document": it reads documents.
    • private-dataThe description's first sentence says "fetch" and "document": it reads documents.
    • untrusted-inputThe description's first sentence says "fetch" and "url": it reads web pages, which anyone can write.
    Description, as the server wrote it
    Fetch a document from a URL, classify its type (personal/medical/business/unknown) with keyword + PII-pattern heuristics (documents containing actual PII data are NEVER business), and return extracted text capped at 100K chars. Free. STREAMING SUPPORTED: send Accept: text/event-stream for started/fetched/classifying/completed phases.

    Annotations the server declares

    • readOnlyHint: true
  • expense_budget_listList your expense_budget rows (newest first).No findings

    Why it is in this group

    • The name "expense_budget_list" says "list": a word for reading.
    Description, as the server wrote it
    List your expense_budget rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_budget_readRead one expense_budget by id.No findings

    Why it is in this group

    • The name "expense_budget_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one expense_budget by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_category_listList your expense_category rows (newest first).No findings

    Why it is in this group

    • The name "expense_category_list" says "list": a word for reading.
    Description, as the server wrote it
    List your expense_category rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_category_readRead one expense_category by id.No findings

    Why it is in this group

    • The name "expense_category_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one expense_category by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_listList your expense rows (newest first).No findings

    Why it is in this group

    • The name "expense_list" says "list": a word for reading.
    Description, as the server wrote it
    List your expense rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_readRead one expense by id.No findings

    Why it is in this group

    • The name "expense_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one expense by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_recurrence_listList your expense_recurrence rows (newest first).No findings

    Why it is in this group

    • The name "expense_recurrence_list" says "list": a word for reading.
    Description, as the server wrote it
    List your expense_recurrence rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • expense_recurrence_readRead one expense_recurrence by id.No findings

    Why it is in this group

    • The name "expense_recurrence_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one expense_recurrence by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • forecast.readRead the tenant's STORED forecast — the verify-read for this rail.1 finding

    Why it is in this group

    • The name "forecast.read" says "read": a word for reading.

    Findings

    • Info

      The description is 11,077 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...t_call is instead a sentence: ask a workspace owner or admin to run the forecast."

      What this check looks for
    Description, as the server wrote it
    Read the tenant's STORED forecast — the verify-read for this rail. view picks the answer, one of summary, monthly, variance, scenarios, weekly (default summary). summary: the outcome bands (label, growth_direction, probability_pct, final_month_revenue_low and _high, distress_risk_pct, path_count), the cash block, blend {yours_pct, peers_pct} (the share of the result from this business's own history versus businesses like it; null when shallow). monthly: per month revenue, costs and net at p10, p25, p50, p75 and p90 with distress_risk_pct, plus balance_p10 to balance_p90 and below_zero_cum_pct when a balance anchors the cash block, and the cash block. This release moved monthlyForecast out of the default answer into view 'monthly'. Every view carries view, dataMonths, history_months, shallow (true under 5 months of history) and CFO narration status, money formatted in the business's currency, named in the top-level currency field: the engine runs in USD and figures convert back at the rate pinned on the stored result (fx_rate_to_usd); before this release they were USD. Free. Refuses with VALIDATION_ERROR naming view when it is not in the list. The cash block is computed from a real bank balance plus each simulated path's revenue minus costs: with basis 'anchored' it carries provenance (the balance grade), balance_source ('statement': the latest balance on bank statements imported with banking.import_statement, used while under 31 days old in place of a typed balance for the same account; a typed account whose label ends in other last 4 digits, or the same digits in another currency, and dated on or after the statement, is added to it and provenance is then declared (an older one is left out: save it again with today's date); 'settings': entered on the Forecast page or saved by forecast.run_stored; 'upload_row': the cash row of an uploaded sheet), balance_as_of, runway_months_p10 and _p50 (null = no cash-out within the horizon), cash_out_pct, lowest_balance_p10, burn_rate_p50_per_month, cost_cut_to_survive_pct and health. With no balance dated within 45 days before the day the forecast was computed and before its first month, basis is 'unanchored': runway, cash-out and health are null, net-flow figures remain, and unavailable_reason plus next_step say how the owner adds today's balance. unavailable_reason 'forecast_out_of_date' means the forecast's months end before today or before the balance on record, so today's balance cannot start it: rebuild from recent months first (forecast.run_stored, or an upload on the Forecast page), as its next_step says. This release replaced the old cashFlow block (its runway started from an invented balance), removed the monthly cash median and renamed the monthly death-risk column to distress_risk_pct. The summary view also carries readiness, whether the books are ready for forecast.run_stored without running it: ready, months, first_period, last_period, min_months, gap_months, recent_gap_months, gap_window_months, months_with_receipts, refusal_code (null when ready, else SIMPLER_FORECAST_LEDGER_TOO_SHORT, SIMPLER_FORECAST_LEDGER_GAPS or SIMPLER_FORECAST_LEDGER_NO_RECEIPTS), message, next_step, base_currency, base_currency_source, legacy_paid_without_payments, fx_unconverted_currencies, bucket_timezone and excluded_period; when the books cannot be read just now readiness is null and readiness_unavailable {code SIMPLER_FORECAST_LEDGER_READ_FAIL, message, next_step} says to retry, and the forecast still returns. variance: the track record, earlier saved forecasts against what happened, worked out on each read and never stored. variance.rows has one row per month with both an actual and an earlier forecast: month, horizon_months (1 = the forecast whose data ended the month before), forecast_made_on, actual, p10, p50, p90, inside_80 (the actual landed between p10 and p90), error_pct ((actual - p50) / p50 x 100, null when p50 is not above zero) and fx_pinned (false = converted at today's rate because that saved forecast carries no rate of its own). variance.summary: n, inside_80_count, inside_80_pct, median_abs_pct_error, min_months and verdict, one of too_few, too_narrow, consistent, too_wide: too_few under 12 months, otherwise whether the share inside sits below, within or above what an 80% range should give over n months. Actuals are paid receipts from the books for a books forecast, or the latest upload's revenue for a sheet, in variance.currency; months with zero revenue count. When they cannot be read, variance is null and variance_unavailable {code SIMPLER_FORECAST_VARIANCE_READ_FAIL, message, next_step} says to retry. scenarios: what if, on the stored forecast. It reruns the stored months at the stored fx_rate_to_usd (never a live rate) and applies up to 3 driver sets to the same simulated paths; it stores nothing and calls no model. drivers is a flat list of {kind, start, end?, amount?, pct?, customer_ref?, set?}: set (1 to 3, default 1) says which compared set a driver belongs to, at most 20 in a set; kinds, dates and amounts as forecast.run describes, amounts in the stored forecast's currency; lose_customer takes customer_ref (an invoice client id from invoice_client_list; the books give its average monthly receipts over the 6 months before the first forecast month) or amount. With no drivers it answers the base alone. scenarios.base and each scenarios.compared entry carry set, drivers, floored_path_months (path-months a driver would have taken below zero, held at zero), months (revenue, costs and net at p10 to p90, financing, and balance_p10 to _p90 with below_zero_cum_pct when a balance anchors it) and a cash block; scenarios.lose_customer echoes each customer average used. Drivers change the money on the paths, not growth or closures (no knock-on effects). Refuses with VALIDATION_ERROR and simpler_code SIMPLER_FORECAST_DRIVER_INVALID (a field is wrong, an amount names another currency than the stored forecast's, or a start falls outside the foreca… [5077 more characters not shown]

    Annotations the server declares

    • readOnlyHint: true
  • interaction_listList your interaction rows (newest first).No findings

    Why it is in this group

    • The name "interaction_list" says "list": a word for reading.
    Description, as the server wrote it
    List your interaction rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • interaction_readRead one interaction by id.No findings

    Why it is in this group

    • The name "interaction_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one interaction by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_adjustment_listList your invoice_adjustment rows (newest first).No findings

    Why it is in this group

    • The name "invoice_adjustment_list" says "list": a word for reading.
    Description, as the server wrote it
    List your invoice_adjustment rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_adjustment_readRead one invoice_adjustment by id.No findings

    Why it is in this group

    • The name "invoice_adjustment_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one invoice_adjustment by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_client_listList your invoice_client rows (newest first).No findings

    Why it is in this group

    • The name "invoice_client_list" says "list": a word for reading.
    Description, as the server wrote it
    List your invoice_client rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_client_readRead one invoice_client by id.No findings

    Why it is in this group

    • The name "invoice_client_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one invoice_client by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_item_listList your invoice_item rows (newest first).No findings

    Why it is in this group

    • The name "invoice_item_list" says "list": a word for reading.
    Description, as the server wrote it
    List your invoice_item rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_item_readRead one invoice_item by id.No findings

    Why it is in this group

    • The name "invoice_item_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one invoice_item by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_listLists your invoices, newest first (200 at most; pass limit and cursor to page).No findings

    Why it is in this group

    • The name "invoice_list" says "list": a word for reading.
    Description, as the server wrote it
    Lists your invoices, newest first (200 at most; pass limit and cursor to page). Every row carries the same worked-out money and state as invoice_read: total_cents, remaining_cents, state, is_overdue, days_overdue (integer CENTS, business days). To find what to chase, pass state 'overdue': only invoices issued, still owed and due before today in the business time zone come back. payer_confirmations_pending counts the payer's "I've paid" reports waiting on each row. Refuses an unlisted state, a limit outside 1-200 or a malformed cursor with VALIDATION_ERROR: resend with a listed state, without limit, or with next_cursor exactly as returned.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_readReturns one invoice with its money and state worked out on read, in integer CENTS: subtotal_cents, tax_cents, total_cents, paid_cents, credited_cents, adjusted_cents, remaining_cen…No findings

    Why it is in this group

    • The name "invoice_read" says "read": a word for reading.
    Description, as the server wrote it
    Returns one invoice with its money and state worked out on read, in integer CENTS: subtotal_cents, tax_cents, total_cents, paid_cents, credited_cents, adjusted_cents, remaining_cents (copy remaining_cents, never compute it). state: draft, sent, partially_paid, overdue, written_off, paid or cancelled. Overdue means issued, still owed and due before today in the business time zone (sales_settings.timezone, unset = Asia/Singapore); days_overdue counts days there. activity lists the newest 50 events; viewed_count and last_viewed_at count every view. payer_confirmations lists the payer's "I've paid" reports. An unknown id refuses with NOT_FOUND: find it with invoice_list or invoice_search.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_recurring_listList your invoice_recurring rows (newest first).No findings

    Why it is in this group

    • The name "invoice_recurring_list" says "list": a word for reading.
    Description, as the server wrote it
    List your invoice_recurring rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • invoice_recurring_readRead one invoice_recurring by id.No findings

    Why it is in this group

    • The name "invoice_recurring_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one invoice_recurring by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • market_signal_listList your market_signal rows (newest first).No findings

    Why it is in this group

    • The name "market_signal_list" says "list": a word for reading.
    Description, as the server wrote it
    List your market_signal rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • market_signal_readRead one market_signal by id.No findings

    Why it is in this group

    • The name "market_signal_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one market_signal by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_customer_listList registered buyers, newest first, to pick a customer_id for myinvois_invoice_generate.No findings

    Why it is in this group

    • The name "myinvois_customer_list" says "list": a word for reading.
    Description, as the server wrote it
    List registered buyers, newest first, to pick a customer_id for myinvois_invoice_generate. An empty list means none are registered yet: call myinvois_customer_create, or send an inline buyer {name, tin} to myinvois_invoice_generate instead.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_customer_readRead one registered buyer by id: name, TIN, BRN, SST number, address and contact.Reads private dataNo findings

    Why it is in this group

    • The name "myinvois_customer_read" says "read": a word for reading.

    What it can reach

    • private-dataThe description's first sentence says "read" and "contact": it reads contacts.
    Description, as the server wrote it
    Read one registered buyer by id: name, TIN, BRN, SST number, address and contact. myinvois_invoice_generate seals the name, TIN, BRN, address and contact; the buyer's SST number stays on this record and is not sealed on the e-invoice. Refuses an id outside your tenant with NOT_FOUND: call myinvois_customer_list or myinvois_customer_search for valid ids. NEXT: myinvois_invoice_generate with this id as customer_id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_invoice_listList your myinvois_invoice rows (newest first).No findings

    Why it is in this group

    • The name "myinvois_invoice_list" says "list": a word for reading.
    Description, as the server wrote it
    List your myinvois_invoice rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_invoice_readRead one sealed e-invoice by id, with its portal_field_sheet: the document's values laid out to key into the LHDN MyInvois portal form (e-invoice details, supplier, buyer, each lin…Reads private dataNo findings

    Why it is in this group

    • The name "myinvois_invoice_read" says "read": a word for reading.

    What it can reach

    • private-dataThe description's first sentence says "read" and "document": it reads documents.
    Description, as the server wrote it
    Read one sealed e-invoice by id, with its portal_field_sheet: the document's values laid out to key into the LHDN MyInvois portal form (e-invoice details, supplier, buyer, each line, totals), coded values with their meaning. Simpler never submits to LHDN; portal_field_sheet.portal_routes names the two portal routes (the online form and LHDN's Excel batch template). Refuses an id outside your tenant with NOT_FOUND: call myinvois_invoice_list for valid ids. NEXT: after the portal accepts it, myinvois_invoice_record_submission with the UUID it assigns.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_settings_listList the supplier profile: at most one row per tenant.No findings

    Why it is in this group

    • The name "myinvois_settings_list" says "list": a word for reading.
    Description, as the server wrote it
    List the supplier profile: at most one row per tenant. An empty list means setup is not done, and myinvois_invoice_generate refuses until it is: call myinvois_settings_save with the company, TIN, BRN, MSIC code and address. NEXT: myinvois_invoice_generate.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_settings_readRead the supplier profile by id: the party printed on every e-invoice (company, TIN, BRN, SST and tourism tax numbers, MSIC code, address).No findings

    Why it is in this group

    • The name "myinvois_settings_read" says "read": a word for reading.
    Description, as the server wrote it
    Read the supplier profile by id: the party printed on every e-invoice (company, TIN, BRN, SST and tourism tax numbers, MSIC code, address). Refuses an id outside your tenant with NOT_FOUND: call myinvois_settings_list, which returns the one profile row. NEXT: myinvois_settings_save to change it.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_template_listList saved line-item presets, newest first.No findings

    Why it is in this group

    • The name "myinvois_template_list" says "list": a word for reading.
    Description, as the server wrote it
    List saved line-item presets, newest first. Each line_items_json is generate-ready: pass its items as line_items to myinvois_invoice_generate. An empty list means none are saved yet: call myinvois_template_create.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • myinvois_template_readRead one line-item preset by id.No findings

    Why it is in this group

    • The name "myinvois_template_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one line-item preset by id. Its line_items_json holds generate-ready items: pass them as line_items to myinvois_invoice_generate. Refuses an id outside your tenant with NOT_FOUND: call myinvois_template_list for valid ids.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • quote_item_listList your quote_item rows (newest first).No findings

    Why it is in this group

    • The name "quote_item_list" says "list": a word for reading.
    Description, as the server wrote it
    List your quote_item rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • quote_item_readRead one quote_item by id.No findings

    Why it is in this group

    • The name "quote_item_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one quote_item by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • quote_listLists your quotes, newest first (200 at most; pass limit and cursor to page).No findings

    Why it is in this group

    • The name "quote_list" says "list": a word for reading.
    Description, as the server wrote it
    Lists your quotes, newest first (200 at most; pass limit and cursor to page). Every row carries total_cents (integer CENTS) and effective_status, so an expired quote reads 'expired'. Refuses a limit outside 1-200 or a malformed cursor with VALIDATION_ERROR: resend without them, or with next_cursor exactly as returned.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • quote_readReturns one quote with subtotal_cents, tax_cents and total_cents worked out from its live lines (integer CENTS) and effective_status, which reads 'expired' once a sent quote's vali…No findings

    Why it is in this group

    • The name "quote_read" says "read": a word for reading.
    Description, as the server wrote it
    Returns one quote with subtotal_cents, tax_cents and total_cents worked out from its live lines (integer CENTS) and effective_status, which reads 'expired' once a sent quote's valid_until has passed (the stored status never says so). A quote with a tax_scheme also returns tax_summary (each line's tax code, net, tax and gross, and the per-code totals). Refuses an unknown id with NOT_FOUND: find the id with quote_list or quote_search.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • sales_document_showShow one invoice, quote or receipt as the document itself.Reads private dataNo findings

    Why it is in this group

    • The name "sales_document_show" says "show": a word for reading.

    What it can reach

    • private-dataThe name "sales_document_show" says "show" and "document": it reads documents.
    • private-dataThe description's first sentence says "show" and "document": it reads documents.
    Description, as the server wrote it
    Show one invoice, quote or receipt as the document itself. When the result has a second content block, it is the document as copy-ready plain text (title, business, billed to, dates, one line per item, the totals as printed, notes, payment terms and details, or on a receipt the day paid and the method) to paste into an email or a chat, and hosts that draw MCP Apps show the page with a Copy button. A paid invoice shows as its receipt, and each payment has its own numbered receipt (the receipt_number invoice_payment_record answers, for example RCT-2026-001), shown by that number or the payment id. Pass id or number (exactly one, from invoice_list, quote_list or invoice_payment_list); kind 'invoice', 'quote' or 'receipt' is optional (else invoice first, then quote, then receipt). It sends nothing and makes no link. Refusals: unknown id or number NOT_FOUND (a removed payment's receipt: reason_code SIMPLER_SALES_RECEIPT_VOID); a missing or doubled id/number or another argument VALIDATION_ERROR; kind 'receipt' before receipts are set up SERVICE_UNAVAILABLE SIMPLER_SALES_RECEIPT_UNMIGRATED; stylesheet unreadable SERVICE_UNAVAILABLE SIMPLER_SALES_DOCUMENT_CHAT_ASSETS_UNAVAILABLE (retry_after); not read within 3 seconds SERVICE_UNAVAILABLE SIMPLER_SALES_DOCUMENT_SHOW_SLOW (call again after retry_after). Read-only tokens and viewers may call it. Free.

    Annotations the server declares

    • readOnlyHint: true
  • sales_settings_listList your sales_settings rows (newest first).No findings

    Why it is in this group

    • The name "sales_settings_list" says "list": a word for reading.
    Description, as the server wrote it
    List your sales_settings rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • sales_settings_numbering_readHow each numbered document type counts, one row each for invoice, quote, credit_note, debit_note and receipt: prefix, last_number (the highest number already used or skipped), next…Reads private dataNo findings

    Why it is in this group

    • The name "sales_settings_numbering_read" says "read": a word for reading.

    What it can reach

    • private-dataThe description's first sentence says "document": it reads documents.
    Description, as the server wrote it
    How each numbered document type counts, one row each for invoice, quote, credit_note, debit_note and receipt: prefix, last_number (the highest number already used or skipped), next_number, and next_preview, the number the next one prints (this year's form, such as INV-2026-042). Copy next_preview, never build it. Change a next number, or a credit note, debit note or receipt prefix, with sales_settings_numbering_set; invoice and quote prefixes with sales_settings_upsert (prefix_set_with names the tool). A row with available false is not set up in this environment yet; unavailable_reason says why and what still works. Takes no arguments: any argument is refused with VALIDATION_ERROR.

    Annotations the server declares

    • readOnlyHint: true
  • sales_settings_readRead one sales_settings by id.No findings

    Why it is in this group

    • The name "sales_settings_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one sales_settings by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • settlement_proposal_listList your settlement_proposal rows (newest first).No findings

    Why it is in this group

    • The name "settlement_proposal_list" says "list": a word for reading.
    Description, as the server wrote it
    List your settlement_proposal rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • settlement_proposal_readRead one settlement_proposal by id.No findings

    Why it is in this group

    • The name "settlement_proposal_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one settlement_proposal by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • simpler.approval_statusCheck a parked approval by approval_id (returned in an APPROVAL_REQUIRED refusal).No findings

    Why it is in this group

    • The name "simpler.approval_status" says "status": a word for reading.
    Description, as the server wrote it
    Check a parked approval by approval_id (returned in an APPROVAL_REQUIRED refusal). States: pending (the account owner has not decided — tell the user, do NOT retry the original call), executed (done — the result rides this response; never re-run the call; an approved invoice_share or quote_share keeps no link or document here, so read the current link with that tool and action 'get' as result.link_not_stored.next_call says), rejected / expired / failed (terminal — re-compose only if the user asks again). Scoped to the granting user's approvals.

    Annotations the server declares

    • readOnlyHint: true
  • simpler.describe_serviceDISCOVERY TIER 2 of 3 — one service's operations and entity shapes from the live catalog, token-dense (op lines name the underlying tool).No findings

    Why it is in this group

    • The name "simpler.describe_service" says "describe": a word for reading.
    Description, as the server wrote it
    DISCOVERY TIER 2 of 3 — one service's operations and entity shapes from the live catalog, token-dense (op lines name the underlying tool). No auth required. NEXT: simpler.invoke {service, operation, args} — or call the named tool directly.

    Annotations the server declares

    • readOnlyHint: true
  • spreadsheet_file_listList your spreadsheet_file rows (newest first).Reads private dataNo findings

    Why it is in this group

    • The name "spreadsheet_file_list" says "list": a word for reading.

    What it can reach

    • private-dataThe name "spreadsheet_file_list" says "list" and "file": it reads files.
    • private-dataThe description's first sentence says "list" and "file": it reads files.
    Description, as the server wrote it
    List your spreadsheet_file rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • spreadsheet_file_readRead one spreadsheet_file by id.Reads private dataNo findings

    Why it is in this group

    • The name "spreadsheet_file_read" says "read": a word for reading.

    What it can reach

    • private-dataThe name "spreadsheet_file_read" says "read" and "file": it reads files.
    • private-dataThe description's first sentence says "read" and "file": it reads files.
    Description, as the server wrote it
    Read one spreadsheet_file by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • spreadsheet_folder_listList your spreadsheet_folder rows (newest first).Reads private dataNo findings

    Why it is in this group

    • The name "spreadsheet_folder_list" says "list": a word for reading.

    What it can reach

    • private-dataThe name "spreadsheet_folder_list" says "list" and "folder": it reads files.
    • private-dataThe description's first sentence says "list" and "folder": it reads files.
    Description, as the server wrote it
    List your spreadsheet_folder rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • spreadsheet_folder_readRead one spreadsheet_folder by id.Reads private dataNo findings

    Why it is in this group

    • The name "spreadsheet_folder_read" says "read": a word for reading.

    What it can reach

    • private-dataThe name "spreadsheet_folder_read" says "read" and "folder": it reads files.
    • private-dataThe description's first sentence says "read" and "folder": it reads files.
    Description, as the server wrote it
    Read one spreadsheet_folder by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • sticky_note_dueRead this tenant's sticky notes whose reminder time (triggers_at) has arrived — the poll the desktop host runs to know what to fire as a local notification, since the web itself ha…Reads private dataReads text others writeNo findings

    Why it is in this group

    • The description's first verb is "read": a word for reading.

    What it can reach

    • private-dataThe name "sticky_note_due" says "note": it reads notes.
    • private-dataThe description's first sentence says "read" and "note": it reads notes.
    • untrusted-inputThe description's first sentence says "read" and "web": it reads web pages, which anyone can write.
    Description, as the server wrote it
    Read this tenant's sticky notes whose reminder time (triggers_at) has arrived — the poll the desktop host runs to know what to fire as a local notification, since the web itself has no send channel. Read-only, no arguments: now is always the server's clock, never a caller-supplied value, and only notes with triggers_at set and <= now come back, soonest-due first, capped at 200. NEXT: sticky_note_dismiss clears triggers_at once you've notified, so the same note stops showing up here.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • sticky_note_listList your sticky_note rows (newest first).Reads private dataNo findings

    Why it is in this group

    • The name "sticky_note_list" says "list": a word for reading.

    What it can reach

    • private-dataThe name "sticky_note_list" says "list" and "note": it reads notes.
    • private-dataThe description's first sentence says "list" and "note": it reads notes.
    Description, as the server wrote it
    List your sticky_note rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • stock_item_listList your stock_item rows (newest first).No findings

    Why it is in this group

    • The name "stock_item_list" says "list": a word for reading.
    Description, as the server wrote it
    List your stock_item rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • stock_item_readRead one stock_item by id.No findings

    Why it is in this group

    • The name "stock_item_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one stock_item by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_character_listList your studio_character rows (newest first).No findings

    Why it is in this group

    • The name "studio_character_list" says "list": a word for reading.
    Description, as the server wrote it
    List your studio_character rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_character_readRead one studio_character by id.No findings

    Why it is in this group

    • The name "studio_character_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one studio_character by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_environment_listList your studio_environment rows (newest first).No findings

    Why it is in this group

    • The name "studio_environment_list" says "list": a word for reading.
    Description, as the server wrote it
    List your studio_environment rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_environment_readRead one studio_environment by id.No findings

    Why it is in this group

    • The name "studio_environment_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one studio_environment by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_project_listList your studio_project rows (newest first).No findings

    Why it is in this group

    • The name "studio_project_list" says "list": a word for reading.
    Description, as the server wrote it
    List your studio_project rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio_project_readRead one studio_project by id.No findings

    Why it is in this group

    • The name "studio_project_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one studio_project by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • studio.analyze_sceneLook at a generated scene's actual frames (vision) and describe subject, lighting, composition, mood.No findings

    Why it is in this group

    • The name "studio.analyze_scene" says "analyze": a word for reading.
    Description, as the server wrote it
    Look at a generated scene's actual frames (vision) and describe subject, lighting, composition, mood. Make editing decisions based on what you SEE, not assumptions. Refuses with SIMPLER_STUDIO_VISION_NONE while no model on this platform can look at video frames (nothing is sent or spent): judge the scene from its prompt and the composition instead.

    Annotations the server declares

    • readOnlyHint: true
  • studio.get_compositionRead the current composition: scenes in order (with derived start times and generation status; length_estimated: true marks a scene sized by an estimate because its video file repo…Reads private dataNo findings

    Why it is in this group

    • The name "studio.get_composition" says "get": a word for reading.

    What it can reach

    • private-dataThe description's first sentence says "read" and "file": it reads files.
    Description, as the server wrote it
    Read the current composition: scenes in order (with derived start times and generation status; length_estimated: true marks a scene sized by an estimate because its video file reported no length), tracks (id, role, name, locked, muted) and clips, overlays, caption count, total duration, and the assets (the ids every asset_id argument takes). Call when you need fresh truth after edits.

    Annotations the server declares

    • readOnlyHint: true
  • studio.get_transcriptRead the caption rows in time order with the ids edit_caption, move_caption and cut_transcript_span take.No findings

    Why it is in this group

    • The name "studio.get_transcript" says "get": a word for reading.
    Description, as the server wrote it
    Read the caption rows in time order with the ids edit_caption, move_caption and cut_transcript_span take. Each row gives id, type (speech, filler, silence or manual), source (transcription or manual), start_ms, end_ms, speaker when the transcription labelled one, hidden: true for a row remove_fillers hid, text, and words. words lists a transcribed row's words in order, so a word's index is its position there (the from_word_index and to_word_index cut_transcript_span takes); a manual row and a silence row list no words and cut only as a whole row. A long transcript comes back in pages: pass the reply's next_from_row as from_row to read on. Refuses a from_row past the last row, naming the row count.

    Annotations the server declares

    • readOnlyHint: true
  • studio.list_versionsList a studio project's stored versions, newest first: {version, created_at, label, current}, plus every named version (snapshots) and how many of the 20 names are used.No findings

    Why it is in this group

    • The name "studio.list_versions" says "list": a word for reading.
    Description, as the server wrote it
    List a studio project's stored versions, newest first: {version, created_at, label, current}, plus every named version (snapshots) and how many of the 20 names are used. A project keeps its newest 20 saved versions, one per 10 minutes for the last day, one per day for the last 30 days, and every named snapshot (up to 20); older unnamed versions are deleted. Refuses: a project_id outside your scope (NOT_FOUND): studio_project_list lists your projects; pass one of their ids; a limit outside 1 to 50 (VALIDATION_ERROR): pass a smaller limit. Free; reads only.

    Annotations the server declares

    • readOnlyHint: true
  • team_assignment_listList your team_assignment rows (newest first).No findings

    Why it is in this group

    • The name "team_assignment_list" says "list": a word for reading.
    Description, as the server wrote it
    List your team_assignment rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • team_assignment_readRead one team_assignment by id.No findings

    Why it is in this group

    • The name "team_assignment_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one team_assignment by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • user_interest_listList your user_interest rows (newest first).No findings

    Why it is in this group

    • The name "user_interest_list" says "list": a word for reading.
    Description, as the server wrote it
    List your user_interest rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • user_setting_listList your user_setting rows (newest first).No findings

    Why it is in this group

    • The name "user_setting_list" says "list": a word for reading.
    Description, as the server wrote it
    List your user_setting rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • user_setting_readRead one user_setting by id.No findings

    Why it is in this group

    • The name "user_setting_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one user_setting by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • workflow_listList your workflow rows (newest first).No findings

    Why it is in this group

    • The name "workflow_list" says "list": a word for reading.
    Description, as the server wrote it
    List your workflow rows (newest first).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false
  • workflow_readRead one workflow by id.No findings

    Why it is in this group

    • The name "workflow_read" says "read": a word for reading.
    Description, as the server wrote it
    Read one workflow by id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: false

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Exfil surface

Which tools read private data, which read text someone else can write, and which can send data out. One of each is a path a prompt injection can use.

2156 paths in this server alone from private data, through text someone else wrote, to a way out. Denying 7 untrusted-input tools breaks every one.

Reads text others write7
Brings in text someone else can write: web pages, inbound email, messages, issues and comments.
deny to break every path
{
  "permissions": {
    "deny": [
      "mcp__simpler-asia__document_extract",
      "mcp__simpler-asia__invoice_handoff",
      "mcp__simpler-asia__invoice_share",
      "mcp__simpler-asia__myinvois_customer_search",
      "mcp__simpler-asia__quote_share",
      "mcp__simpler-asia__sticky_note_due",
      "mcp__simpler-asia__workflow_prepared_outputs"
    ]
  }
}

Other servers in the same agent add their own tools to these paths. Audit your whole setup to see them together.

Findings

Matches of fixed checks, with the text they matched.

14 warn10 info
  • The server marks "workflow_prepared_outputs" as not read-only (readOnlyHint: false), and its words say it reads.

    Matched text

    annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}; The description's first verb is "list": a word for reading.

    What this check looks for
  • The server marks "statement_prepare" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

    Matched text

    annotations: {"readOnlyHint":true}; The input addresses people (input.to): what it sends cannot be taken back.

    What this check looks for
  • The server marks "invoice_handoff" as not read-only (readOnlyHint: false), and its words say it reads.

    Matched text

    annotations: {"readOnlyHint":false}; The description's first verb is "get": a word for reading.

    What this check looks for
  • The server marks "forecast.run" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true}; The name "forecast.run" says "run": a word for a change.

    What this check looks for
  • The server marks "edit_project_read" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "edit_project_read" says "edit": a word for a change.

    What this check looks for
  • The server marks "edit_project_list" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "edit_project_list" says "edit": a word for a change.

    What this check looks for
Show 18 more findings
  • The server marks "invoice_share" as not read-only (readOnlyHint: false), and its words say it reads.

    Matched text

    annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}; The description's first verb is "return": a word for reading.

    What this check looks for
  • The server marks "invoice_reissue" not destructive (destructiveHint: false), and its own name, description or input give the class "destructive".

    Matched text

    annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}; The description's first verb is "void": a word for removing or ending something.

    What this check looks for
  • The server marks "invoice_payment_read" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "invoice_payment_read" says "payment": a word that usually means money moves, which cannot be taken back.

    What this check looks for
  • The server marks "invoice_payment_list" read-only (readOnlyHint: true), and its own name, description or input give the class "irreversible".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":false}; The name "invoice_payment_list" says "payment": a word that usually means money moves, which cannot be taken back.

    What this check looks for
  • The server marks "quote_share" as not read-only (readOnlyHint: false), and its words say it reads.

    Matched text

    annotations: {"readOnlyHint":false,"destructiveHint":false,"idempotentHint":false,"openWorldHint":false}; The description's first verb is "return": a word for reading.

    What this check looks for
  • The server marks "image.describe" as not read-only (readOnlyHint: false), and its words say it reads.

    Matched text

    annotations: {"readOnlyHint":false}; The name "image.describe" says "describe": a word for reading.

    What this check looks for
  • The server marks "studio.snapshot_version" as not read-only (readOnlyHint: false), and its words say it reads.

    Matched text

    annotations: {"readOnlyHint":false}; The name "studio.snapshot_version" says "snapshot": a word for reading.

    What this check looks for
  • The server marks "studio.suggest_social_clips" as not read-only (readOnlyHint: false), and its words say it reads.

    Matched text

    annotations: {"readOnlyHint":false}; The description's first verb is "find": a word for reading.

    What this check looks for
  • The description is 8,895 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...xt/event-stream for phase progress (started/fitted/simulated/clustered/complete)."

    What this check looks for
  • The description is 3,893 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...its summary and its no-forecast error carry readiness with the same refusal_code."

    What this check looks for
  • The description is 11,077 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...t_call is instead a sentence: ask a workspace owner or admin to run the forecast."

    What this check looks for
  • The description is 2,501 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...SERVICE_UNAVAILABLE SIMPLER_BANKING_STATEMENT_WRITE_FAIL (retry the same upload)."

    What this check looks for
  • The description is 2,373 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...download_url, when the result carries one, works for 24 hours without a session."

    What this check looks for
  • The description is 2,041 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...t_key to the next image.* tool, or open download_url when the result carries one."

    What this check looks for
  • The description is 2,545 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...t_key to the next image.* tool, or open download_url when the result carries one."

    What this check looks for
  • The description is 2,738 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...t_key to the next image.* tool, or open download_url when the result carries one."

    What this check looks for
  • The description is 2,908 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...ght for a size rule) for a file type, size or file-size failure, and check again."

    What this check looks for
  • The description is 2,141 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...thing is posted, emailed or uploaded anywhere; the result is a file you download."

    What this check looks for

History

Changes to its tool definitions and to its remote endpoint, newest first.

No change seen yet. Its tools are rescanned every day, and a remote endpoint is looked at once a week.

Install

Pinned to version 1.0.0. Values in angle brackets are yours to fill in.

claude mcp add --transport http simpler-asia https://simpler.asia/mcp
claude mcp add --transport http simpler-asia https://simpler.asia/mcp

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change or a way out, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__simpler-asia__simpler_describe_service",
      "mcp__simpler-asia__simpler_approval_status",
      "mcp__simpler-asia__myinvois_invoice_read",
      "mcp__simpler-asia__sales_document_show",
      "mcp__simpler-asia__forecast_read",
      "mcp__simpler-asia__document_extract",
      "mcp__simpler-asia__agent_note_list",
      "mcp__simpler-asia__agent_note_read",
      "mcp__simpler-asia__brand_assets_read",
      "mcp__simpler-asia__brand_assets_list",
      "mcp__simpler-asia__consent_grant_list",
      "mcp__simpler-asia__consent_grant_read",
      "mcp__simpler-asia__contact_read",
      "mcp__simpler-asia__contact_list",
      "mcp__simpler-asia__contact_search",
      "mcp__simpler-asia__contact_note_list",
      "mcp__simpler-asia__contact_note_read",
      "mcp__simpler-asia__desk_surface_read",
      "mcp__simpler-asia__desk_surface_list",
      "mcp__simpler-asia__expense_read",
      "mcp__simpler-asia__expense_list",
      "mcp__simpler-asia__expense_search",
      "mcp__simpler-asia__expense_budget_read",
      "mcp__simpler-asia__expense_budget_list",
      "mcp__simpler-asia__expense_category_read",
      "mcp__simpler-asia__expense_category_list",
      "mcp__simpler-asia__expense_recurrence_read",
      "mcp__simpler-asia__expense_recurrence_list",
      "mcp__simpler-asia__interaction_list",
      "mcp__simpler-asia__interaction_read",
      "mcp__simpler-asia__invoice_read",
      "mcp__simpler-asia__invoice_list",
      "mcp__simpler-asia__invoice_search",
      "mcp__simpler-asia__invoice_adjustment_read",
      "mcp__simpler-asia__invoice_adjustment_list",
      "mcp__simpler-asia__invoice_client_read",
      "mcp__simpler-asia__invoice_client_list",
      "mcp__simpler-asia__invoice_client_search",
      "mcp__simpler-asia__invoice_item_read",
      "mcp__simpler-asia__invoice_item_list",
      "mcp__simpler-asia__invoice_recurring_read",
      "mcp__simpler-asia__invoice_recurring_list",
      "mcp__simpler-asia__market_signal_list",
      "mcp__simpler-asia__market_signal_read",
      "mcp__simpler-asia__myinvois_customer_read",
      "mcp__simpler-asia__myinvois_customer_list",
      "mcp__simpler-asia__myinvois_customer_search",
      "mcp__simpler-asia__myinvois_invoice_list",
      "mcp__simpler-asia__myinvois_invoice_search",
      "mcp__simpler-asia__myinvois_settings_read",
      "mcp__simpler-asia__myinvois_settings_list",
      "mcp__simpler-asia__myinvois_template_read",
      "mcp__simpler-asia__myinvois_template_list",
      "mcp__simpler-asia__quote_read",
      "mcp__simpler-asia__quote_list",
      "mcp__simpler-asia__quote_search",
      "mcp__simpler-asia__quote_item_read",
      "mcp__simpler-asia__quote_item_list",
      "mcp__simpler-asia__sales_settings_list",
      "mcp__simpler-asia__sales_settings_read",
      "mcp__simpler-asia__settlement_proposal_list",
      "mcp__simpler-asia__settlement_proposal_read",
      "mcp__simpler-asia__spreadsheet_file_read",
      "mcp__simpler-asia__spreadsheet_file_list",
      "mcp__simpler-asia__spreadsheet_file_search",
      "mcp__simpler-asia__spreadsheet_folder_read",
      "mcp__simpler-asia__spreadsheet_folder_list",
      "mcp__simpler-asia__sticky_note_list",
      "mcp__simpler-asia__sticky_note_due",
      "mcp__simpler-asia__stock_item_read",
      "mcp__simpler-asia__stock_item_list",
      "mcp__simpler-asia__stock_item_search",
      "mcp__simpler-asia__studio_character_read",
      "mcp__simpler-asia__studio_character_list",
      "mcp__simpler-asia__studio_environment_read",
      "mcp__simpler-asia__studio_environment_list",
      "mcp__simpler-asia__studio_project_read",
      "mcp__simpler-asia__studio_project_list",
      "mcp__simpler-asia__team_assignment_read",
      "mcp__simpler-asia__team_assignment_list",
      "mcp__simpler-asia__user_interest_list",
      "mcp__simpler-asia__user_setting_read",
      "mcp__simpler-asia__user_setting_list",
      "mcp__simpler-asia__user_setting_search",
      "mcp__simpler-asia__workflow_read",
      "mcp__simpler-asia__workflow_list",
      "mcp__simpler-asia__sales_settings_numbering_read",
      "mcp__simpler-asia__studio_list_versions",
      "mcp__simpler-asia__studio_get_composition",
      "mcp__simpler-asia__studio_get_transcript",
      "mcp__simpler-asia__studio_analyze_scene"
    ],
    "ask": [
      "mcp__simpler-asia__simpler_directory",
      "mcp__simpler-asia__simpler_invoke",
      "mcp__simpler-asia__simpler_outcomes",
      "mcp__simpler-asia__agent_note_send_cross_tenant",
      "mcp__simpler-asia__contact_upsert",
      "mcp__simpler-asia__expense_create",
      "mcp__simpler-asia__expense_submit",
      "mcp__simpler-asia__expense_approve",
      "mcp__simpler-asia__interaction_create",
      "mcp__simpler-asia__invoice_create",
      "mcp__simpler-asia__invoice_adjustment_record",
      "mcp__simpler-asia__invoice_payment_record",
      "mcp__simpler-asia__myinvois_invoice_generate",
      "mcp__simpler-asia__myinvois_invoice_record_submission",
      "mcp__simpler-asia__quote_create",
      "mcp__simpler-asia__quote_convert",
      "mcp__simpler-asia__settlement_proposal_propose_cross_tenant",
      "mcp__simpler-asia__sticky_note_create",
      "mcp__simpler-asia__stock_item_adjust",
      "mcp__simpler-asia__team_assignment_create",
      "mcp__simpler-asia__workflow_create",
      "mcp__simpler-asia__workflow_prepared_outputs",
      "mcp__simpler-asia__ai_budget",
      "mcp__simpler-asia__ai_estimate_cost",
      "mcp__simpler-asia__sales_receivables",
      "mcp__simpler-asia__invoice_message_prepare",
      "mcp__simpler-asia__invoice_reminders_due",
      "mcp__simpler-asia__statement_prepare",
      "mcp__simpler-asia__invoice_handoff",
      "mcp__simpler-asia__forecast_run",
      "mcp__simpler-asia__forecast_run_stored",
      "mcp__simpler-asia__image_generate",
      "mcp__simpler-asia__spreadsheet_create_with_content",
      "mcp__simpler-asia__agent_note_mark_read",
      "mcp__simpler-asia__contact_create",
      "mcp__simpler-asia__contact_update",
      "mcp__simpler-asia__contact_note_create",
      "mcp__simpler-asia__desk_surface_create",
      "mcp__simpler-asia__desk_surface_update",
      "mcp__simpler-asia__edit_project_create",
      "mcp__simpler-asia__edit_project_update",
      "mcp__simpler-asia__edit_project_read",
      "mcp__simpler-asia__edit_project_list",
      "mcp__simpler-asia__expense_update",
      "mcp__simpler-asia__expense_reject",
      "mcp__simpler-asia__expense_reimburse",
      "mcp__simpler-asia__expense_aggregate",
      "mcp__simpler-asia__expense_budget_create",
      "mcp__simpler-asia__expense_budget_update",
      "mcp__simpler-asia__expense_category_create",
      "mcp__simpler-asia__expense_category_update",
      "mcp__simpler-asia__expense_category_ensure_defaults",
      "mcp__simpler-asia__expense_recurrence_create",
      "mcp__simpler-asia__expense_recurrence_update",
      "mcp__simpler-asia__interaction_upsert",
      "mcp__simpler-asia__invoice_update",
      "mcp__simpler-asia__invoice_share",
      "mcp__simpler-asia__invoice_payer_confirmation",
      "mcp__simpler-asia__invoice_debit_note",
      "mcp__simpler-asia__invoice_promise_set",
      "mcp__simpler-asia__invoice_client_create",
      "mcp__simpler-asia__invoice_client_upsert",
      "mcp__simpler-asia__invoice_client_update",
      "mcp__simpler-asia__invoice_client_link_tenant",
      "mcp__simpler-asia__invoice_item_create",
      "mcp__simpler-asia__invoice_item_update",
      "mcp__simpler-asia__invoice_payment_read",
      "mcp__simpler-asia__invoice_payment_list",
      "mcp__simpler-asia__invoice_recurring_create",
      "mcp__simpler-asia__invoice_recurring_update",
      "mcp__simpler-asia__invoice_recurring_skip",
      "mcp__simpler-asia__market_signal_create",
      "mcp__simpler-asia__market_signal_update",
      "mcp__simpler-asia__myinvois_customer_create",
      "mcp__simpler-asia__myinvois_customer_update",
      "mcp__simpler-asia__myinvois_settings_save",
      "mcp__simpler-asia__myinvois_template_create",
      "mcp__simpler-asia__myinvois_template_update",
      "mcp__simpler-asia__quote_update",
      "mcp__simpler-asia__quote_share",
      "mcp__simpler-asia__quote_item_create",
      "mcp__simpler-asia__quote_item_update",
      "mcp__simpler-asia__sales_settings_upsert",
      "mcp__simpler-asia__sales_settings_numbering_set",
      "mcp__simpler-asia__settlement_proposal_accept",
      "mcp__simpler-asia__spreadsheet_file_create",
      "mcp__simpler-asia__spreadsheet_file_update",
      "mcp__simpler-asia__spreadsheet_folder_create",
      "mcp__simpler-asia__spreadsheet_folder_update",
      "mcp__simpler-asia__sticky_note_update",
      "mcp__simpler-asia__sticky_note_dismiss",
      "mcp__simpler-asia__stock_item_create",
      "mcp__simpler-asia__stock_item_update",
      "mcp__simpler-asia__studio_character_create",
      "mcp__simpler-asia__studio_character_update",
      "mcp__simpler-asia__studio_environment_create",
      "mcp__simpler-asia__studio_environment_update",
      "mcp__simpler-asia__studio_project_create",
      "mcp__simpler-asia__studio_project_update",
      "mcp__simpler-asia__team_assignment_update",
      "mcp__simpler-asia__team_assignment_accept",
      "mcp__simpler-asia__team_assignment_start",
      "mcp__simpler-asia__team_assignment_complete",
      "mcp__simpler-asia__team_assignment_decline",
      "mcp__simpler-asia__team_assignment_reassign",
      "mcp__simpler-asia__team_assignment_undo_reassign",
      "mcp__simpler-asia__team_assignment_add_photo",
      "mcp__simpler-asia__user_interest_upsert",
      "mcp__simpler-asia__user_setting_upsert",
      "mcp__simpler-asia__workflow_update",
      "mcp__simpler-asia__invoice_pdf",
      "mcp__simpler-asia__statement_pdf",
      "mcp__simpler-asia__banking_import_statement",
      "mcp__simpler-asia__image_variation",
      "mcp__simpler-asia__image_convert",
      "mcp__simpler-asia__image_cut_background",
      "mcp__simpler-asia__image_upscale",
      "mcp__simpler-asia__image_inpaint",
      "mcp__simpler-asia__image_replace_sky",
      "mcp__simpler-asia__image_replace_background",
      "mcp__simpler-asia__image_harmonize",
      "mcp__simpler-asia__image_add_shadow",
      "mcp__simpler-asia__image_adjust",
      "mcp__simpler-asia__image_style_transfer",
      "mcp__simpler-asia__image_segment_at",
      "mcp__simpler-asia__image_generate_for_edit",
      "mcp__simpler-asia__image_describe",
      "mcp__simpler-asia__image_marketplace_check",
      "mcp__simpler-asia__video_export_create",
      "mcp__simpler-asia__video_export",
      "mcp__simpler-asia__studio_begin_asset_upload",
      "mcp__simpler-asia__studio_finish_asset_upload",
      "mcp__simpler-asia__studio_import_asset_from_url",
      "mcp__simpler-asia__studio_restore_version",
      "mcp__simpler-asia__studio_snapshot_version",
      "mcp__simpler-asia__studio_add_scene",
      "mcp__simpler-asia__studio_add_scene_from_asset",
      "mcp__simpler-asia__studio_shift_clips",
      "mcp__simpler-asia__studio_duplicate_clips",
      "mcp__simpler-asia__studio_insert_time",
      "mcp__simpler-asia__studio_roll_edit",
      "mcp__simpler-asia__studio_slip_edit",
      "mcp__simpler-asia__studio_slide_edit",
      "mcp__simpler-asia__studio_move_caption",
      "mcp__simpler-asia__studio_cut_transcript_span",
      "mcp__simpler-asia__studio_add_track",
      "mcp__simpler-asia__studio_move_track",
      "mcp__simpler-asia__studio_rename_track",
      "mcp__simpler-asia__studio_set_track_muted",
      "mcp__simpler-asia__studio_move_clips_to_track",
      "mcp__simpler-asia__studio_place_product_card",
      "mcp__simpler-asia__studio_update_scene",
      "mcp__simpler-asia__studio_generate_scene",
      "mcp__simpler-asia__studio_generate_music",
      "mcp__simpler-asia__studio_generate_voiceover",
      "mcp__simpler-asia__studio_add_clip_to_timeline",
      "mcp__simpler-asia__studio_add_audio_to_timeline",
      "mcp__simpler-asia__studio_add_text_overlay",
      "mcp__simpler-asia__studio_update_text_overlay",
      "mcp__simpler-asia__studio_add_sticker",
      "mcp__simpler-asia__studio_set_transition",
      "mcp__simpler-asia__studio_trim_scene",
      "mcp__simpler-asia__studio_color_grade",
      "mcp__simpler-asia__studio_adjust_audio_levels",
      "mcp__simpler-asia__studio_update_clip",
      "mcp__simpler-asia__studio_restore_fillers",
      "mcp__simpler-asia__studio_split",
      "mcp__simpler-asia__studio_replace_transcribed",
      "mcp__simpler-asia__studio_set_config",
      "mcp__simpler-asia__studio_reorder_scenes",
      "mcp__simpler-asia__studio_trim_clip",
      "mcp__simpler-asia__studio_duplicate_clip",
      "mcp__simpler-asia__studio_lock_track",
      "mcp__simpler-asia__studio_edit_caption",
      "mcp__simpler-asia__studio_update_sticker",
      "mcp__simpler-asia__studio_cut_range",
      "mcp__simpler-asia__studio_transcribe",
      "mcp__simpler-asia__studio_reframe_scene",
      "mcp__simpler-asia__studio_style_transfer",
      "mcp__simpler-asia__studio_add_sound",
      "mcp__simpler-asia__studio_enhance_audio",
      "mcp__simpler-asia__studio_apply_template",
      "mcp__simpler-asia__studio_set_ducking",
      "mcp__simpler-asia__studio_request_regeneration",
      "mcp__simpler-asia__studio_apply_ops",
      "mcp__simpler-asia__studio_suggest_social_clips",
      "mcp__simpler-asia__studio_create_social_clip",
      "mcp__simpler-asia__studio_insert_product_card",
      "mcp__simpler-asia__platform_request_capability"
    ],
    "deny": [
      "mcp__simpler-asia__contact_delete",
      "mcp__simpler-asia__contact_note_delete",
      "mcp__simpler-asia__desk_surface_delete",
      "mcp__simpler-asia__edit_project_delete",
      "mcp__simpler-asia__expense_delete",
      "mcp__simpler-asia__expense_budget_delete",
      "mcp__simpler-asia__expense_category_delete",
      "mcp__simpler-asia__expense_recurrence_delete",
      "mcp__simpler-asia__invoice_reissue",
      "mcp__simpler-asia__invoice_adjustment_delete",
      "mcp__simpler-asia__invoice_client_delete",
      "mcp__simpler-asia__invoice_item_delete",
      "mcp__simpler-asia__invoice_payment_delete",
      "mcp__simpler-asia__invoice_recurring_delete",
      "mcp__simpler-asia__myinvois_customer_delete",
      "mcp__simpler-asia__myinvois_template_delete",
      "mcp__simpler-asia__quote_item_delete",
      "mcp__simpler-asia__spreadsheet_file_delete",
      "mcp__simpler-asia__spreadsheet_folder_delete",
      "mcp__simpler-asia__sticky_note_delete",
      "mcp__simpler-asia__stock_item_delete",
      "mcp__simpler-asia__studio_character_delete",
      "mcp__simpler-asia__studio_environment_delete",
      "mcp__simpler-asia__studio_project_delete",
      "mcp__simpler-asia__team_assignment_delete",
      "mcp__simpler-asia__user_interest_delete",
      "mcp__simpler-asia__user_setting_delete",
      "mcp__simpler-asia__workflow_delete",
      "mcp__simpler-asia__video_export_delete",
      "mcp__simpler-asia__video_export_cancel",
      "mcp__simpler-asia__studio_remove_snapshot",
      "mcp__simpler-asia__studio_delete_elements",
      "mcp__simpler-asia__studio_ripple_delete",
      "mcp__simpler-asia__studio_remove_track",
      "mcp__simpler-asia__studio_delete_element",
      "mcp__simpler-asia__studio_remove_silence",
      "mcp__simpler-asia__studio_remove_fillers",
      "mcp__simpler-asia__studio_remove_dead_air"
    ]
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. A read that can send data out (it takes a URL, say) asks first too. It assumes the server is installed as simpler-asia. Review it before you use it.

Capability tags behind 39 rules
  • mcp__simpler-asia__agent_note_send_cross_tenant outbound
  • mcp__simpler-asia__contact_upsert outbound
  • mcp__simpler-asia__myinvois_invoice_read private-data
  • mcp__simpler-asia__workflow_prepared_outputs private-data, untrusted-input
  • mcp__simpler-asia__sales_document_show private-data
  • mcp__simpler-asia__statement_prepare outbound
  • mcp__simpler-asia__invoice_handoff private-data, untrusted-input
  • mcp__simpler-asia__document_extract private-data, untrusted-input
  • mcp__simpler-asia__agent_note_list private-data
  • mcp__simpler-asia__agent_note_read private-data
  • mcp__simpler-asia__contact_create outbound
  • mcp__simpler-asia__contact_update outbound
  • mcp__simpler-asia__contact_read private-data
  • mcp__simpler-asia__contact_list private-data
  • mcp__simpler-asia__contact_search private-data
  • mcp__simpler-asia__contact_note_list private-data
  • mcp__simpler-asia__contact_note_read private-data
  • mcp__simpler-asia__invoice_share private-data, untrusted-input
  • mcp__simpler-asia__invoice_search private-data
  • mcp__simpler-asia__invoice_client_create outbound
  • mcp__simpler-asia__invoice_client_upsert outbound
  • mcp__simpler-asia__invoice_client_update outbound
  • mcp__simpler-asia__invoice_client_link_tenant outbound
  • mcp__simpler-asia__myinvois_customer_read private-data
  • mcp__simpler-asia__myinvois_customer_search private-data, untrusted-input
  • mcp__simpler-asia__myinvois_invoice_search private-data
  • mcp__simpler-asia__quote_share private-data, untrusted-input
  • mcp__simpler-asia__quote_search private-data
  • mcp__simpler-asia__spreadsheet_file_read private-data
  • mcp__simpler-asia__spreadsheet_file_list private-data
  • mcp__simpler-asia__spreadsheet_file_search private-data
  • mcp__simpler-asia__spreadsheet_folder_read private-data
  • mcp__simpler-asia__spreadsheet_folder_list private-data
  • mcp__simpler-asia__sticky_note_list private-data
  • mcp__simpler-asia__sticky_note_due private-data, untrusted-input
  • mcp__simpler-asia__sales_settings_numbering_read private-data
  • mcp__simpler-asia__statement_pdf outbound
  • mcp__simpler-asia__studio_import_asset_from_url outbound
  • mcp__simpler-asia__studio_get_composition private-data

Pin

Keep the decision: fail CI when the tools change, follow every change, or show a badge.

Pin in CI

Fail a build when the tools you reviewed are not the tools the server serves today.

curl -s "https://anlyon.dev/api/v1/servers/asia.simpler/simpler-asia/verify?hash=abc35323ea6c05937d4c3d0d1e168cd7944ef3960c4ed422b9a14928cddfb3e8" | jq -e '.changed == false'

Pinned hash abc35323ea6c, from Oct 23, 2026, 03:17 UTC.

Watch

Get told when its tools change: a new tool, a new description, a new class.

https://anlyon.dev/servers/asia.simpler/simpler-asia/changes.xml

An Atom feed. Works in any feed reader or Slack's RSS app.

Badge

anlyon.dev badge for asia.simpler/simpler-asia
[![anlyon.dev](https://anlyon.dev/badge/asia.simpler/simpler-asia.svg)](https://anlyon.dev/servers/asia.simpler/simpler-asia)

Source: MCP Registry. Synced Oct 23, 2026, 03:17 UTC.