xero-oauth-mcp-server
A custom Model Context Protocol server for Xero, using the standard OAuth2 authorization-code + refresh-token flow (no paid Custom Connection required). Supports multiple connected organisations (tenants) — every tool takes an optional org argument.
Capabilities
- Contacts:
list_contacts,get_contact,create_contact,update_contact - Invoices:
list_invoices,get_invoice,create_invoice,update_invoice,
list_overdue_invoices, list_recurring_invoices
- Utility:
list_organisations,list_accounts
How org selection works
Each tool accepts an optional org. It is resolved (case-insensitive) against:
- an alias from
XERO_ORG_ALIASES(e.g.main→ a tenantId), - an exact
tenantId, then - a substring of the org's name in Xero.
If org is omitted, the server uses XERO_DEFAULT_ORG (which may itself be an alias), or the first connected org when that is unset.
One-time setup
1. Create a Xero app
developer.xero.com → New app → Web app.
- Add redirect URI:
http://localhost:49831/callback - Copy the Client ID and generate a Client Secret.
Newer Xero apps use granular scopes. This server requests
accounting.contacts,accounting.invoices, andaccounting.settings.read(plusopenid profile email offline_access). Make sure those are enabled on the app.
2. Build
npm install
npm run build
3. Authorize (interactive, once)
XERO_CLIENT_ID=xxx XERO_CLIENT_SECRET=yyy npm run auth
A browser opens — log in and authorize each organisation you want to use (re-run to add more later). The refresh token and the connected tenant list are saved to ~/.xero-mcp-id/tokens.json (chmod 600). The refresh token rotates automatically on every use.
4. Register with an MCP client
Example (Claude Code): ``bash claude mcp add xero -s user \ -e XERO_CLIENT_ID=xxx \ -e XERO_CLIENT_SECRET=yyy \ -e XERO_DEFAULT_ORG=main \ -e XERO_ORG_ALIASES='{"main":"<tenant-guid>","secondary":"<tenant-guid>"}' \ -- node /absolute/path/to/dist/index.js ``
Environment variables
| Var | Default | Purpose | |-----|---------|---------| | XERO_CLIENT_ID / XERO_CLIENT_SECRET | — | App credentials (required) | | XERO_DEFAULT_ORG | first org | Org used when a tool omits org (alias/tenantId/name) | | XERO_ORG_ALIASES | {} | JSON map of friendly alias → tenantId | | XERO_REDIRECT_PORT | 49831 | Local callback port (must match the app's redirect URI) | | XERO_READONLY | unset | When true, all write tools refuse | | XERO_TOKEN_DIR | ~/.xero-mcp-id | Where the token store lives |
Security
- No credentials or tokens are stored in this repository.
- Client ID/secret are passed via environment; the refresh token + tenant list
live only in XERO_TOKEN_DIR (default ~/.xero-mcp-id/, chmod 600), which is git-ignored.
- Write tools (
create_/update_) can be globally disabled with
XERO_READONLY=true.
License
MIT











