Skip to main content
Glama
patthewebrat

xero-oauth-mcp-server

by patthewebrat
README.md
# xero-oauth-mcp-server

A custom [Model Context Protocol](https://modelcontextprotocol.io) 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`, `get_invoice_pdf`,
  `download_invoice_pdfs`, `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:

1. an alias from `XERO_ORG_ALIASES` (e.g. `main` → a tenantId),
2. an exact `tenantId`, then
3. 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](https://developer.xero.com/app/manage) → **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`, and `accounting.settings.read`
> (plus `openid profile email offline_access`). Make sure those are enabled on
> the app.

### 2. Build
```bash
npm install
npm run build
```

### 3. Authorize (interactive, once)
```bash
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_PDF_DIR` | `~/Downloads` | Where the PDF tools save files when no path is given |
| `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

TDQS

A3.6/5.0

Scored across 12 tools

Disambiguation5/5

Each tool targets a distinct resource and action (contact, invoice, accounts, etc.). Even specialized invoice tools like list_overdue_invoices and list_recurring_invoices have clear names that prevent ambiguity.

Naming Consistency5/5

All tools follow a consistent verb_noun pattern (create_*, get_*, list_*, update_*), making it predictable and easy for an agent to infer purpose from the name.

Tool Count5/5

With 12 tools covering core CRUD for contacts and invoices plus useful listing filters, the set is well-scoped for a Xero accounting API server—neither too sparse nor too overwhelming.

Completeness4/5

Covers the main workflows for contacts and invoices (create, read, update, list) with additional filters. Missing delete operations and account creation/update, but these are often handled externally or are less critical for basic usage.

Maintenance

ActivityMaintained
ResponsivenessNo issues