Announcing CIMD support for MCP Client registration
Learn more

Should you use Xero MCP or Xero API for building AI Agents?

TL;DR

  • Xero ships an official MCP server, but it runs locally over STDIO only (via npx @xeroapi/xero-mcp-server). There is no hosted, remote Xero MCP endpoint. That single fact shapes every deployment decision.
  • The server has two auth modes: single-organisation Custom Connections (client credentials) or a bring-your-own Bearer Token. Neither gives you a hosted, per-user OAuth flow across many customer organisations.
  • The MCP server exposes 51 commands at the time of writing. The REST API covers the full Accounting surface plus Payroll, purchase orders, batch payments, repeating invoices, invoice void and delete, and every report. If your agent needs those, MCP alone is not enough.
  • Xero access tokens expire every 30 minutes and each call needs a Xero-Tenant-Id. A multi-tenant agent is N connected accounts, each with its own refresh cycle. Neither path stores, refreshes, or revokes those credentials for you.
  • Scalekit's Xero connector runs the per-user OAuth flow, stores and refreshes tokens, and injects the tenant ID. The MCP-vs-API choice stops changing your auth infrastructure.

Your agent needs to read and write Xero: pull a profit and loss report, raise an invoice when a deal closes, reconcile a payment on behalf of an accountant. Xero now ships two ways to do that. There is an official MCP server, and there is the REST API the platform has run for years. They are not the same object; one runs locally against a single organisation, the other is a multi-tenant HTTP API you authenticate per user. Picking the wrong one shows up late, usually the first time a second customer connects. Here is how to choose.

What Xero MCP and Xero API actually are

Before comparing them, it helps to be precise about the two things on the table. One is a process you run on a machine. The other is an HTTP surface you call over the network. The difference is not cosmetic; it decides how your agent authenticates and where it can run.

The official Xero MCP server

The Xero has an official MCP Server, open-source implementation, written in TypeScript and shipped under the @xeroapi/xero-mcp-server package. It is part of Xero's Agentic Toolkit. It runs locally over STDIO; Xero's own guidance states it works with any MCP client that supports local or STDIO servers, and the documented setup launches it through npx inside a client such as Claude Desktop or Cursor. There is no remote, Xero-hosted endpoint to point an agent at.

Authentication comes in two modes. Custom Connections use a XERO_CLIENT_ID and XERO_CLIENT_SECRET scoped to one specific organisation; this is the recommended mode for desktop clients. Bearer Token mode takes a XERO_CLIENT_BEARER_TOKEN your client obtains itself, which is how you support multiple Xero accounts at runtime.

The Xero REST API

The Xero REST API is the surface every Xero integration has used for years. The Accounting API is globally available; Payroll is a separate, region-specific API for AU, UK, and NZ; and Files, Assets, Projects, and Bank Feeds are their own APIs again. Requests are JSON, and every call carries a Xero-Tenant-Id header identifying the organisation.

Authentication is OAuth 2.0. The grant types are Authorization Code Flow (with PKCE for public clients) for per-user, multi-tenant access, and Client Credentials via Custom Connections for single-organisation machine access. A single OAuth connection can be authorized for multiple tenants, listed through the GET /connections endpoint.

What your agent can actually do

Both paths reach the same underlying Xero data, but the MCP server exposes a deliberately smaller slice of it. The gap is not obscure edge cases; it is core accounting write operations your agent will reach for on day two. The table below covers the actions that matter most for agent use cases.

The capability gap at a glance

Capability
Xero MCP server
Xero REST API
List invoices, contacts, accounts, items
Yes
Yes
Create invoice or bill (ACCREC / ACCPAY)
Yes
Yes
Update a draft invoice
Yes
Yes
Authorise, void, or delete an invoice
No
Yes
Payments (single, batch)
Single only
Both
Purchase orders
No
Yes
Repeating invoices
No
Yes
Overpayments and prepayments
No
Yes
Bank transactions and transfers
Transactions only
Both
Financial reports
Partial: P&L, Balance Sheet, Trial Balance, Aged
Full, plus ad-hoc reports
Email invoice, online invoice URL, attachments
No
Yes
Payroll (employees, timesheets, leave)
Partial: AU, UK, NZ subset
Full, region-specific Payroll API

Where the MCP server stops

The official server ships create, read, and update tools, plus a set of payroll timesheet actions. What it does not ship is telling. Its update-invoice command updates a draft only; there is no authorise, void, or delete for invoices. Purchase orders, batch payments, repeating invoices, overpayments, prepayments, bank transfers, and invoice attachments are absent. Its report coverage stops at profit and loss, balance sheet, trial balance, and aged receivables and payables. For an interactive assistant answering questions about one business, that surface is enough. For an agent that manages an invoice lifecycle end to end, it is not.

What the Scalekit connector adds on the API side

The Scalekit Xero connector is an API-based connector, not a wrapper around the local MCP server. It exposes 94 tools over Xero's OAuth 2.0 surface, including the operations the MCP server omits: xero_invoice_delete to void an invoice, xero_purchase_order_create, xero_batch_payment_create, xero_repeating_invoice_create, and the full report set through tools like xero_report_profit_and_loss and xero_report_balance_sheet. It also removes a piece of manual work: on the first tool call it fetches the tenant ID from GET /connections and caches it, so you never pass xero_tenant_id by hand.

The auth path each one puts you on

Capability decides what your agent can do. Auth decides whether it can run for more than one customer. This is where the two paths diverge most sharply, and where the local-only nature of the MCP server has consequences.

Xero MCP: single-org custom connections or your own bearer token

A Custom Connection is machine-to-machine and tied to one organisation; the client credentials you configure authorise that organisation and no other. For a solo operator wiring Claude Desktop to their own books, that is the fast path. For a product serving many customers, it does not compose: one client secret per organisation is not a model you scale to hundreds of tenants.

Bearer Token mode lifts the single-organisation limit, but it moves the work to you. The server does not run the OAuth flow; your MCP client has to obtain the token, which means you build and operate the Authorization Code or PKCE flow, then feed the result in as an environment variable per session.

Xero REST API: per-user OAuth 2.0 built for multiple tenants

The REST API's Authorization Code Flow is the model designed for multi-tenant products. Each user consents through a browser redirect, your backend exchanges the code for tokens, and each connection can span multiple Xero organisations, resolved per call through the Xero-Tenant-Id header. Request offline_access and you receive a refresh token to keep acting after the user's session ends.

The multi-tenant implication

The catch is lifecycle. Xero access tokens expire after 30 minutes and refresh tokens after 60 days, so a background agent constantly meets expired tokens with no user present to re-authenticate. Uncertified apps are also capped at 25 connected organisations until they pass Xero's certification. The path gives you a token per user; it does not store, refresh, or revoke it. That part is infrastructure, and it is the same whether you chose MCP or the API. For a deeper look at how to handle token refresh for AI agents, the lifecycle problem is well worth understanding before committing to either path.

What you own in production

The comparison that matters in production is not features; it is the maintenance surface. The question is simple: what breaks, what needs your attention, and who owns fixing it.

Running and routing the local MCP server

With the local MCP server, you own the process. That means a Node runtime per environment, a way to launch and route STDIO servers, and, for Custom Connections, one client secret per organisation. There is no hosted infrastructure to lean on and no per-user OAuth flow to inherit. Tool schemas also move underneath you: they update when Xero publishes a new version of the package, without a versioning contract you control.

Owning the REST API surface

With the REST API, you own the full stack. Token storage, proactive refresh against the 30-minute expiry, Xero-Tenant-Id injection, pagination that varies by endpoint, and rate limits all become your code. Those limits are strict: 5 concurrent calls, 60 calls per minute, and 5,000 calls per day per tenant, with a 10,000-per-minute ceiling across all tenants. Exceed one and Xero returns HTTP 429 with a Retry-After header. Webhooks add HMAC-SHA256 validation on a short response window. None of it is exotic; all of it is yours to build and keep working. The hidden cost of building OAuth internally for AI agents adds up quickly once rate limits, token storage, and webhook validation are all on your plate.

Where Scalekit removes the credential work

This is the layer Scalekit's connector replaces. It runs the per-user Authorization Code Flow, stores tokens in a per-user, per-tool vault, refreshes them before they expire, and injects the tenant ID automatically. Credentials never touch the agent runtime. You still write your agent; you stop writing the token lifecycle.

When to use MCP, when to use the API

Neither path is universally correct. The decision turns on how many organisations your agent serves and whether a person is present when it runs.

Use the Xero MCP server when

  • You are building a local or interactive assistant, such as Claude Desktop or Cursor, against a single Xero organisation.
  • One Custom Connection to one set of books is acceptable, and you do not need per-user isolation across customers.
  • Your use case is covered by the roughly 50 read, create, and update commands the server exposes, including single-org payroll timesheet flows.
  • You want to prototype an accounting agent quickly without building any auth infrastructure.

Use the Xero REST API when

  • You are building a multi-tenant product where N customer organisations each need per-user OAuth and independent revocation.
  • Your agent runs headless, scheduled, or in the background, such as overnight reconciliation or invoice sync, with no browser session available.
  • You need capabilities the MCP server does not expose: purchase orders, batch payments, repeating invoices, invoice void and delete, or the full report set.
  • You run at volume and must manage rate limits, pagination, and the 30-minute token expiry deterministically.

Connecting Xero to your agent with Scalekit

If the API is your path, the work is auth and tool schemas, not accounting logic. Scalekit's connector gives your agent authenticated, per-user access to Xero and returns tool definitions in your model's native format, so you write neither. The pattern is always the same three steps: discovery, scope, then execution. The example below uses Python and the Claude SDK.

The sequence starts with discovery, and discovery is the point worth reading closely. Your agent is not loading a flat catalog of every Xero tool. It is loading only the tools the current user's connected account is authorized to call. That is the distinction between a per-user agent and a shared-credential one. First set up the connection and connect a user.

Set up the connection and connect a user

The identifier represents the current person in your own system. In production it comes from your authenticated session, never from the client.

import os import anthropic import scalekit.client from dotenv import find_dotenv, load_dotenv from google.protobuf.json_format import MessageToDict load_dotenv(find_dotenv()) scalekit_client = scalekit.client.ScalekitClient( env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), ) actions = scalekit_client.actions client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) # Ensure this user has an active Xero connected account response = actions.get_or_create_connected_account( connection_name="xero", identifier="user_123", ) if response.connected_account.status != "ACTIVE": link = actions.get_authorization_link(connection_name="xero", identifier="user_123") print("Authorize Xero:", link.link) input("Press Enter after authorizing...")

Load the tools scoped to that user

list_scoped_tools returns only the tools this user's Xero connection authorizes, already in Anthropic's native format. The connection_names filter must match the connection name you created in the Scalekit dashboard, character for character; a mismatch returns an empty list with no error.

scoped_response, _ = actions.tools.list_scoped_tools( identifier="user_123", filter={"connection_names": ["xero"]}, page_size=100, ) llm_tools = [ { "name": MessageToDict(tool.tool).get("definition", {}).get("name"), "description": MessageToDict(tool.tool).get("definition", {}).get("description", ""), "input_schema": MessageToDict(tool.tool).get("definition", {}).get("input_schema", {}), } for tool in scoped_response.tools ] print(f"Discovered {len(llm_tools)} Xero tools")

Run the agent loop

This is the standard Claude tool-use loop. Send the conversation, check stop_reason, execute each requested tool through Scalekit with the user's identifier, then append the results and continue. Scalekit looks up the stored token for that user and makes the real Xero call.

messages = [ {"role": "user", "content": "Raise a draft invoice for Acme Ltd for 5 hours of consulting at 150 each, then send it."} ] while True: response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=llm_tools, messages=messages, ) if response.stop_reason == "end_turn": print(response.content[0].text) break tool_results = [] for block in response.content: if block.type == "tool_use": print(f" -> Calling: {block.name}") try: result = actions.execute_tool( tool_name=block.name, identifier="user_123", tool_input=block.input, ) content = str(result.data) except Exception as e: content = f"Error: {str(e)}" tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": content, }) messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results})

The same connector works with LangChain, CrewAI, Google ADK, and the Vercel AI SDK; the connected-account pattern does not change. Install the SDK with pip install scalekit-sdk-python anthropic, and keep credentials in a .env file loaded via dotenv.

The credential problem that exists on both paths

The auth divergence between MCP and the API is real, but it hides a problem that sits under both choices. Whichever path you take, you end up holding credentials you have to manage.

N connected accounts, one lifecycle each

Both paths hand you a token or credential per organisation. The MCP server gives you a Custom Connection secret or a bearer token; the REST API gives you an OAuth token per user. In a multi-tenant product that is N credentials, each with a 30-minute access token, a 60-day refresh token, and its own revocation event when a customer churns. Storage, rotation, and revocation are yours in both cases. The path changes the token type. It does not change the infrastructure required. Understanding who holds the token across agent tool-calling patterns is a foundational question regardless of which Xero integration path you choose.

Where Scalekit fits

Scalekit's Xero connector handles the OAuth flow, token storage, refresh, and tenant-ID injection for the API path, so the MCP-vs-API decision no longer changes your auth infrastructure.

An audit trail for every downstream tool call

Once an agent acts on real books, "which agent did what, for which user, when" stops being a nice-to-have. It is the question a security reviewer or an auditor asks first, and neither raw path answers it well.

What the raw paths leave you without

The local MCP server runs on a machine and logs to that machine. The REST API records requests on Xero's side, tied to the token, not to the person in your system who triggered the agent. Neither gives you a queryable record that links a specific user, a specific agent run, and a specific tool call.

What Scalekit auth logs record

Because every tool call routes through Scalekit, each one produces an entry in auth logs: which user initiated it, which connected account acted, which tool ran, and when. That is an immutable trail across every downstream Xero call, queryable when a reviewer asks, without instrumenting your agent by hand. Audit trails for agent auth in B2B SaaS explains why this layer matters and what it takes to build it correctly.

Virtual MCP servers for multi-tool, multi-tenant Xero agents

There is a way to get MCP ergonomics without the local server's limits, and without handing an agent every tool a connector exposes. This is the payoff for going through Scalekit rather than pointing at a raw server.

The overreach and token-bloat problem

A full connector surface is both a security and a cost problem. An agent that only needs to read reports should not hold xero_invoice_delete; every extra tool widens the blast radius if something goes wrong. It is also tokens: 40 tools at roughly 200 tokens each is about 8,000 tokens burned before the agent does any work, on every run. The fix is not better prompting. It is surface reduction.

One definition, a per-user token each run

Virtual MCP servers solve both at once. You declare one server per agent role, listing exactly which connections and which tools it exposes, and you get a static endpoint. Scoping from 40 tools to 5–10 cuts token overhead by around 80 percent and sharpens tool selection. Per-user isolation is handled by session tokens: one definition serves all users, and each run mints a short-lived token scoped to that user's connected accounts. No credential sharing, no per-user server to host. That is the multi-tenant model the local Xero MCP server cannot provide. This is the same architectural thinking behind how tool calling auth changes when you move from single-tenant to multi-tenant.

Which one to build against

If your agent is a single-organisation, interactive assistant and one Custom Connection is enough, the official Xero MCP server is a legitimate, fast path; run it locally and accept its coverage. If your agent is multi-tenant, background, or needs the write operations the MCP server omits, build against the Xero REST API. The deciding question is whether your agent acts for more than one customer organisation with independent tokens and revocation. If yes, it is the API. Either way, the credential lifecycle is the same problem, and that is the part worth putting on production-grade infrastructure rather than rebuilding per tool.

Recommended reading: QuickBooks MCP vs QuickBooks API for AI Agents, the same decision for Xero's closest competitor.

Build your Xero agent with Scalekit

Explore the connector, then wire it into your agent. See the Scalekit Xero connector and the connector setup docs for the full tool list and quickstart. For a finance-agent pattern to start from, look at the revenue forecast commentary agent, and check pricing when you are ready to scale.

Building a Xero agent and want another set of eyes? Join the Scalekit Slack community, or use the talk to us page for immediate help.

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.