Simpler.Asia

asia.simpler/simpler-asia · v1.0.0 · finance-and-payments

Simpler.Asia is healthy: it completed an MCP handshake in 3020 ms and exposes 318 tools.

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

Endpoint: https://simpler.asia/mcp (streamable-http)

No repository listed · Website · JSON · Add to my inventory

Facts

Users (last 30 days)0
HandshakeHealthy
Auth neededNone
Tool count318
Latency3020 ms
Last checked (UTC)2026-10-23T05:00:15.388Z

Health history

Checked (UTC)StatusLatencyError
2026-10-23T05:00:15.388ZHealthy3020

Tools (100)

simpler.directoryDISCOVERY 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.
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 auth required. NEXT: simpler.invoke {service, operation, args} — or call the named tool directly.
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). Requires auth. args = the underlying tool's arguments object.
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). START HERE for a business task. No auth required. NEXT: call the named tool directly, or simpler.directory for the full catalog.
simpler.approval_statusCheck 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 kee
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. Both tenants must be KYB-approved and the recipient must hold an active grant (claims can narrow it further); every recipient-side failure refuses
contact_upsertCreate-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
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). 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-refu
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. Nobody is notified automatically — approvers see it appear in the Team Claims view. NEXT: expense_approve or expense_reject
expense_approveApproves 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: exp
interaction_createLog 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 his
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). client_name finds a client by name or creates one. Birth status 'paid' needs items in the same call. tax_scheme sg_gst
invoice_adjustment_record[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 record
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 multiply or divide yourself. A draft invoice auto-advances to 'sent'; a cancelled invoice refuses; an amount above the rem
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 object. Refuses until myinvois_settings_save is done; invoice_type 02/03/04 also needs original_invoice_id naming a type
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). 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: c
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 line, totals), coded values with their meaning. Simpler never submits to LHDN; portal_field_sheet.portal_routes names the t
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 CENTS) together. client_name resolves an existing client by name or creates one. tax_scheme sg_gst or my_sst taxes each li
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 records converted_invoice_id — nothing else to do afterward. Refuses if the quote is already converted (the error carri
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 settlement-rails door executes it. amount_cents is a nonzero integer CENTS (positive = you owe them, negative = a refund/re
sticky_note_createCreate a sticky_note.
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 moved. An unmatched name CREATES the item at quantity 0 first so the delivery still lands as an audited ledger row; a name
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 validation instead of being silently overridden. Accepts natural language for the common fields: task_name/task for title, d
workflow_createCreate 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.
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. Pending and rejected drafts never appear here — approval happens only in the inbox, never through this call. Optional limit 1-100 (default 50),
ai.budgetYour 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.
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. Anonymous — open catalog includes open pricing. Returns integer micro-USD plus a USD total. NEXT: run the action, then ai.budget to
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 days (the sales_settings time zone), so an invoice due today is still current. Only issued invoices with a balance left co
sales_document_showShow 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 meth
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); left out, the one that fits. language 'en', 'ms' or 'zh'; left out, the client's (invoice_client_update message_language),
invoice_reminders_dueToday'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_upd
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 received, credit and write-off in it with a running balance, the balance at its end (closing), and what was still owed at i
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 (invoice_message_prepare's words, with the link). ready lists the parts made; missing gives each other part's reason_code, r
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 probabilities. The paths are clustered into 3-7 outcome bands for the next 6 months, with per-month revenue and cost percentiles and
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) and expenses by expense date, monthly in the tenant's base currency, up to the last complete month (the current month is
forecast.readRead 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), t
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 requests only ($0.0406/image), then @cf/black-forest-labs/flux-2-dev via Cloudflare Workers AI ($0.00/image), then google/nano
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), and return extracted text capped at 100K chars. Free. STREAMING SUPPORTED: send Accept: text/event-stream for started/fe
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. 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 (
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. Idempotent: re-confirming an already-read note is a no-op, and the subject/body stay fixed forever so a recipient can never rewrite w
agent_note_listList your agent_note rows (newest first).
agent_note_readRead one agent_note by id.
brand_assets_readRead one brand_assets by id.
brand_assets_listList your brand_assets rows (newest first).
consent_grant_listList your consent_grant rows (newest first).
consent_grant_readRead one consent_grant by id.
contact_createCreate 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_updateUpdate a contact you own (optionally compare-and-set via expected_updated_at).
contact_deleteDelete a contact you own.
contact_readRead one contact by id.
contact_listList your contact rows (newest first).
contact_searchSearch your contact rows by text.
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. 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
contact_note_listList your contact_note rows (newest first).
contact_note_readRead one contact_note by id.
contact_note_deleteDelete a contact_note you own.
desk_surface_createPins 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 privat
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. 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
desk_surface_readRead one desk_surface by id.
desk_surface_listList your desk_surface rows (newest first).
desk_surface_deleteDelete a desk_surface you own.
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-addressed R2 key before the row is saved, so canvas_json never carries image bytes and identical images dedupe across pr
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 the row is left alone. A canvas_json you send goes through the same image-externalization and hygiene refusal as edit_pro
edit_project_readRead one edit_project by id.
edit_project_listList your edit_project rows (newest first).
edit_project_deleteDelete a edit_project you own.
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, expense_reimburse). amount_cents, if changed, must stay integer CENTS. Only the expense's own creator may edit it, and conte
expense_deleteDeletes 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 delet
expense_rejectRejects 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 r
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). Stamps reimbursed_at, after which the row's content freezes unconditionally since the amount approved is the amount recorded as paid. Rec
expense_readRead one expense by id.
expense_listList your expense rows (newest first).
expense_searchSearch your expense rows by text.
expense_aggregateAggregate (count/sum) over your expense rows.
expense_budget_createCreate a expense_budget.
expense_budget_updateUpdate a expense_budget you own (optionally compare-and-set via expected_updated_at).
expense_budget_deleteDelete a expense_budget you own.
expense_budget_readRead one expense_budget by id.
expense_budget_listList your expense_budget rows (newest first).
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). 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 d
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. color, when given, must be a #rrggbb hex value. Create a new category instead of trying to rename an existing slug.
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 CONFLICT (default_category: true). Expenses already carrying the deleted category keep their category_id untouched and sim
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 slug that already exists. Takes no arguments and is safe to call repeatedly or concurrently — it converges rather than
expense_category_readRead one expense_category by id.
expense_category_listList your expense_category rows (newest first).
expense_recurrence_createCreate a expense_recurrence.
expense_recurrence_updateUpdate a expense_recurrence you own (optionally compare-and-set via expected_updated_at).
expense_recurrence_deleteDelete a expense_recurrence you own.
expense_recurrence_readRead one expense_recurrence by id.
expense_recurrence_listList your expense_recurrence rows (newest first).
interaction_upsertCreate-or-update a interaction keyed on source_ref.
interaction_listList your interaction rows (newest first).
interaction_readRead one interaction by id.
invoice_updateEdits 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
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. 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 fo
invoice_payer_confirmationActs 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
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. 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 curren
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 days to pay unless due_date is sent. A foreign-currency sg_gst invoice issued before today needs fx_rate_to_sgd and fx_r
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. clear true removes it. invoice_read then shows promise_status: pending until that business day ends, kept when paid in full by then, bro
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_cents (copy remaining_cents, never compute it). state: draft, sent, partially_paid, overdue, written_off, paid or cancelled