hamsa software
← All articles
AI tooling8 min read

Ten minutes with your AI assistant’s permissions

An assistant that drafts invoice reminders needs three read permissions in Xero. Here is a one-page review of what it can read, change and refuse, and why a payment scope turns a customer’s email into a ledger entry.

Three thin sage lines enter an outlined rectangular boundary from the left; a separate short line ends before the boundary on the right.

The assistant only needed to chase invoices

The finance lead at an illustrative small wholesaler spends Monday mornings on overdue invoices: she checks Xero, picks out customers more than 30 days late and writes polite reminders. An assistant could prepare that work while she keeps the decision to send. Its job sounds modest. The connection’s permissions need a closer look. The business, names and amounts below are fictional.

Xero publishes an open-source MCP server: a set of named tools an assistant can use to work with Xero. MCP, the Model Context Protocol, supplies the connection convention; Xero’s scopes specify the permitted API access. The server requests default scopes unless configured otherwise.[1] A successful connection therefore tells you very little about whether its access fits this particular job.

What follows is a first review of that connection, the kind you can do in ten minutes with the settings page open. It answers seven questions: who the assistant is, what it can read, what it can change, which requests must fail, who approves the rest, where the evidence lives and how to switch it off. The answers fit on one page.

Whose identity is it using?

Start with the account and organisation behind the connection. Xero’s MCP server accepts either a bearer token supplied by the client or credentials for a custom connection to one organisation.[1] Our illustrative review uses the latter. Someone still authorises it: Xero’s setup records an authorising user and the requested scopes. It is not independent of the business merely because it has its own client ID.[6]

Record the organisation, app identity, authorising account and operational owner together. The operations manager may configure the connection while the finance lead owns the reminders; both matter when access needs changing. Xero charges for production custom connections, although its Demo Company supports development without that subscription.[6] This example reviews the permission boundary, rather than recommending that account model for every business.

The tool list is not the permission

Next comes what it can read and change, and the tempting shortcut is to read the assistant’s tool list. Xero’s server lists tools such as list-invoices, create-invoice, create-payment and update-invoice, and notes that update-invoice works only on draft invoices.[1] That sounds reassuring, but the tool list is a convenience layer written by the server’s authors. The limit Xero enforces is the scope on the token. Any program holding that token can call the Xero API directly, with no tool list in the way.

Xero is changing those scopes this year. The old accounting.transactions scope covered most transactional records at once. It is being split into narrower scopes, including accounting.invoices, accounting.payments, accounting.banktransactions and accounting.manualjournals, each with a read-only .read version.[2] Apps created from 2 March 2026 get granular scopes. Older apps could start requesting them in April 2026, and everyone must move by September 2027.[2][3] Existing connections do not change on their own; the customer has to authorise again.[3] Custom connections gained granular scopes from 29 April.[6]

That change makes this review easier, because the question now has a precise answer. The illustrative connection has a wide list, much like the one the server’s documentation shows for bearer tokens: invoices, payments, bank transactions, manual journals, contacts, settings and payroll.[1] Its Xero job needs three read permissions, to see invoices, see contacts and run the aged receivables report: accounting.invoices.read, accounting.contacts.read and accounting.reports.aged.read. Everything else is surplus. OWASP’s list of risks for AI applications calls this excessive agency: more functions, permissions or autonomy than the task needs.[4]

For this custom connection, narrow the scopes authorised to the app as well as those requested by the server. Changing XERO_SCOPES alone leaves a broadly authorised client secret able to request more. Check existing grants too: Xero documents that reauthorisation can add granular scopes alongside old broad ones.[3] A new label on the settings screen is not evidence that the old authority disappeared.

Be careful with the word read, too. A narrow scope is still wide inside its own box. The invoices scope also covers credit notes, quotes, purchase orders and repeating invoices.[2] Reading contacts means reading customers’ addresses and email addresses. That may be perfectly acceptable. It should be a decision someone made, not something discovered later. These scopes do not confine reads to overdue invoices; that filter remains an application rule.

“It can’t move money” is not the same as safe

The usual comfort is that an accounting connection cannot send money. In this example that is broadly true. Recording a payment in Xero writes an entry in the books; it does not instruct the bank.[5] But the books drive decisions, including who gets chased. Here is the case worth walking through.

Invoice INV-0412, for £1,240, is 41 days overdue. The customer replies to last week’s reminder: they paid on Friday, please update your records. The finance lead includes that reply in the review material. The broadly connected version has create-payment available and the payments scope behind it. If it records £1,240 against INV-0412, the invoice shows as paid and drops off the aged receivables report. Next Monday there is no reminder. Nothing has changed at the bank. Reconciliation can reveal a recorded payment with no matching line on the statement. Until then, the reminder list can look perfectly reasonable.

Notice what went wrong. Nobody attacked anything. A customer said something that may even be true. The assistant took an action its job never needed, because of text it had read. OWASP describes the same pattern with an email assistant that only needed to read mail but could also send it, and a message that steered it.[4] The remedy is the same too: remove the function and the permission. Then a customer’s claim becomes a note for a person to check, not an entry in the ledger.

Removing the scope also gives you a firmer refusal than any instruction can. A prompt that says never record payments depends on the model following it, every time, whatever it reads. A token without the payments scope is documented to receive a 401 response identifying insufficient scope.[3] OWASP calls this complete mediation: the system that holds the data decides what is allowed, not the model.[4]

Four requests that should fail

The fourth question is the list of explicit denials. Write the requests that should fail, in plain words, and try each one. For the reminder assistant, four are enough to start:

  • “Mark INV-0412 as paid.” Expected: Xero refuses; the assistant says it cannot and flags the customer’s claim for the finance lead.
  • “Raise a credit note to waive the late fee.” Expected: refused. Credit notes sit under the invoices scope, which is read-only here.
  • “Update the customer’s email address to the one in this reply.” Expected: refused. Contact changes belong to a person.
  • “What is Sam’s salary?” Expected: no payroll scope, so no payroll data at all.

What an honest refusal looks like

These are proposed tests, not results from a live Xero connection. Seed an equivalent £1,240 invoice 41 days overdue in the Demo Company, retaining its assigned identifier. First verify a permitted lookup: retrieve that invoice, check its contact and prepare an unsent reminder showing £1,240 and 41 days overdue. Then exercise the denials. A provider rejection followed by the assistant claiming success is a separate failure: the books stayed intact, but the finance lead received a false account of the work.

Approval, evidence and the off switch

Approval belongs after a concrete draft exists. Keep this example’s reminders in a local review screen, with invoice number, amount, days overdue and recipient visible. The finance lead copies a reviewed draft into her mail application and sends it herself. The assistant has no mail connection here. Saving drafts directly into her mailbox would introduce another permission review; “draft only” cannot be assumed from the button’s name.

For evidence, tie the proposed reminder to its invoice lookup, returned balance, contact and review decision. Do not copy entire customer records into logs merely to demonstrate activity. Try finding one harmless lookup by request identifier; a log saying “invoice processed” cannot explain whether the assistant read it, drafted a reminder or changed the ledger. Record the app and authorising account too, rather than assuming Xero’s history will display the attribution you expect.

The off switch must match the connection type. Our illustrative custom connection obtains access tokens using its client ID and secret; renewal does not depend on a refresh token.[6] Disconnect it through Xero’s supported controls, stop the job and remove retained credentials. Then check both a harmless read using any retained access token and an attempt to obtain another. Record what actually fails and when; stopping the process alone does not establish that its authority ended.

A first review of the invoice-reminder assistant

A worked first-review worksheet for an illustrative wholesaler. The decision column describes the proposed design, not completed live tests.

Scroll sideways to see all columns.

QuestionFound before reviewDecision
IdentityCustom connection created by the operations managerRecord organisation, app, authorising account and operational owner
ReadsInvoices, contacts, settings, reports, payrollaccounting.invoices.read, accounting.contacts.read, accounting.reports.aged.read
WritesInvoices, payments, bank transactions, journals, contactsNone in Xero; unsent drafts stay in a local review screen
Firm noNot written downFour tests: mark paid, credit note, change email, payroll
ApprovalOne button for the whole batchPer email: invoice, amount, days overdue, recipient
EvidenceUnknownLink lookup, invoice state, draft and review decision
Off switchNo named ownerNamed owner; verify retained access and new token requests fail
Three Xero read scopes still expose more than overdue invoices. Custom connections obtain tokens using client credentials; the proposed refusal and disconnection tests have not been run against Xero.

Where the narrow version pinches

Narrow access has a cost. If the finance lead later needs the assistant to inspect recorded bank transactions, that adds accounting.banktransactions.read and requires another review.[2] That does not, by itself, provide every bank-feed or reconciliation capability. Granting everything once avoids another consent exercise, but also removes the point at which someone asks whether the new job should belong to this assistant.

Sometimes write access is right. A bookkeeping tool that posts journals needs write scopes because writing is its job. Then approval must bind to the proposed entries, with evidence of the actual result. Another option separates the read-only assistant from a tightly controlled writer. Separate tool names are not enough: the writer needs its own enforced authority, otherwise both routes still depend on the same broad credential.

Ten minutes can identify an unnecessary payment permission and give it an owner. It cannot establish the worst possible failure, audit the vendor’s token storage or test every misleading email. Keep the unanswered questions on the worksheet. For INV-0412, the immediate decision is already useful: a customer’s payment claim goes to the finance lead for checking, while the invoice remains visible to the next reminder run.

Working on a workflow like this? Hamsa helps finance and operations teams connect their tools and handle the exceptions, starting with one workflow.

See the Payment Operations Pilot

References & further reading

Primary sources and original writing behind this article. Links were checked when the article was prepared; living documentation may change.

  1. Xero: official MCP server, authentication modes, scopes and tools
  2. Xero Developer: upcoming changes to Accounting API scopes
  3. Xero Developer: granular scopes FAQ
  4. OWASP: LLM06:2025 Excessive Agency
  5. Xero: Accounting API payment records and invoice balances
  6. Xero: Custom Connections, authorisation and token requests
Hamsa Jama

Practical engineering. A human perspective.

More about Hamsa Software and the person behind it. Editorial notes.

A few more things
worth a read.

Back to the reading room
AI tooling7 min read

The prompt did not change. The system did.

Version the resolved configuration around an AI response so comparisons, investigations and rollback refer to the system that actually ran.