Sets up or reconnects the Xero OAuth 2.0 MCP integration. Use when connecting Xero for the first time, re-authenticating after token expiry, troubleshooting Xero MCP errors, or adding Xero to a new project. Handles app creation, credentials, MCP config, auth script, and verification.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add MattVOLTA/outworkos_diy --skill xero-connect --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Xero Connect?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/mattvolta-xero-connect)More formats (shields.io, HTML) on the badges page.
---
name: xero-connect
description: "Sets up or reconnects the Xero OAuth 2.0 MCP integration. Use when connecting Xero for the first time, re-authenticating after token expiry, troubleshooting Xero MCP errors, or adding Xero to a new project. Handles app creation, credentials, MCP config, auth script, and verification."
---
# /xero-connect
Connect or reconnect the Xero accounting API via OAuth 2.0 MCP server.
## When to Use
- Setting up Xero on a new project
- Re-authenticating after token expiry or "Failed to get Xero token" errors
- Troubleshooting Xero MCP connection issues
- Migrating from Custom Connections to OAuth 2.0
## Key Constraints
- **Xero is READ ONLY** — never write to Xero under any circumstances
- Custom Connections are NOT available in Canada — must use OAuth 2.0 auth code flow
- Uses the `wspringer/xero-mcp-server` fork which supports browser-based OAuth with auto token refresh
- Tokens are stored at `~/.xero-mcp-tokens.json` (shared across all projects)
- Access tokens expire after 30 minutes; refresh tokens are single-use (each refresh returns a new one)
## Workflow
### Step 1: Detect Current State
Check what already exists:
1. Look for `xero` entry in the project's `.mcp.json`
2. Check if `xero_client_id` and `xero_client_secret` exist in Vault (via `$OUTWORKOS_ROOT/scripts/get-secret.sh`)
3. Check if `~/.xero-mcp-tokens.json` exists and contains valid tokens
4. Check if `scripts/xero-auth.sh` exists
Report findings and determine which steps to skip.
### Step 2: Xero Developer App Setup
If no credentials exist, guide the user through app creation:
1. Direct user to: https://developer.xero.com/app/manage
2. Click "New app"
3. Settings:
- **App name**: Any name (e.g., "OutworkOS")
- **Integration type**: Web app
- **Company or application URL**: Any URL
- **Redirect URI**: `http://localhost:8749/callback`
4. After creation, ask user for the **Client ID** and **Client Secret**
If credentials already exist, ask if they want to use the existing ones or enter new ones.
### Step 3: Store Credentials in Vault
Store the Xero credentials in Vault:
```bash
"$OUTWORKOS_ROOT/scripts/set-secret.sh" xero_client_id "<client-id>" "Xero OAuth client ID (READ ONLY)"
"$OUTWORKOS_ROOT/scripts/set-secret.sh" xero_client_secret "<client-secret>" "Xero OAuth client secret (READ ONLY)"
```
### Step 4: Configure .mcp.json
Add or update the `xero` entry in `.mcp.json`:
```json
"xero": {
"command": "npx",
"args": ["-y", "github:wspringer/xero-mcp-server"],
"env": {
"XERO_CLIENT_ID": "<client-id>",
"XERO_CLIENT_SECRET": "<client-secret>",
"XERO_USE_BROWSER_AUTH": "true"
}
}
```
**Critical**: Must use `github:wspringer/xero-mcp-server` (NOT `@xeroapi/xero-mcp-server@latest`). The official package does not support OAuth 2.0 browser auth. The `XERO_USE_BROWSER_AUTH` env var activates the auth code flow in the fork.
### Step 5: Ensure .gitignore Protection
Before committing anything, verify `.gitignore` includes:
```
.mcp.json
```
If no `.gitignore` exists, create one. If it exists but is missing these entries, add them.
### Step 6: Deploy Auth Script
Check if `scripts/xero-auth.sh` exists in the project. If not, copy it from the template at `${CLAUDE_SKILL_DIR}/scripts/xero-auth.sh`.
The auth script:
- Reads credentials from Vault via `get-secret.sh`
- Starts a local callback server on port 8749
- Opens browser for Xero login with PKCE
- Exchanges auth code for tokens
- Saves tokens to `~/.xero-mcp-tokens.json` (mode 0600)
- Supports `--force` (re-auth) and `--check` (test only) flags
Make executable: `chmod +x scripts/xero-auth.sh`
### Step 7: Authenticate
Run the auth script:
```bash
./scripts/xero-auth.sh
```
This opens a browser. The user logs into Xero and grants access. The script captures the callback, exchanges the code for tokens, and saves them.
If re-authenticating: `./scripts/xero-auth.sh --force`
**Important**: The MCP server subprocess cannot reliably open a browser on its own. The standalone auth script handles initial authentication. After that, the MCP server uses saved tokens and refreshes them automatically.
### Step 8: Restart and Verify
1. Tell the user to restart Claude Code (so the MCP server reloads with new config)
2. After restart, use `ToolSearch` to find `xero organisation`
3. Call `mcp__xero__list-organisation-details`
4. If successful, report the org name, currency, and country
## Troubleshooting
| Symptom | Cause | Fix |
|---------|-------|-----|
| "Failed to get Xero token: [object Object]" | MCP server using Custom Connections flow (not browser auth) | Verify `.mcp.json` has `XERO_USE_BROWSER_AUTH: "true"` and uses `github:wspringer/xero-mcp-server`. Restart Claude Code. |
| "Using custom connections authentication" in logs | `XERO_USE_BROWSER_AUTH` env var not reaching the server | Check `.mcp.json` env block. The var must be inside the `env` object. |
| Token file exists but API returns 401 | Refresh token expired (unused for 60 days) | Run `./scripts/xero-auth.sh --force` |
| "No such tool: mcp__xero__*" | Xero MCP server failed to start | Check `npx -y github:wspringer/xero-mcp-server` runs manually. May need `npm cache clean --force`. |
| Port 8749 in use | Another process on the callback port | `lsof -i :8749` to identify and kill it |
| "Custom Connections not available" | Xero org is in Canada (or other unsupported region) | This is why we use OAuth 2.0 — Custom Connections only work in AU/NZ/UK/US |
## Architecture Notes
- **Token storage**: `~/.xero-mcp-tokens.json` — shared across all projects, contains access_token, refresh_token, expires_at
- **Token refresh**: The wspringer fork auto-refreshes tokens when they're within 5 minutes of expiry
- **Callback port**: 8749 (hardcoded in the fork) — must match the redirect URI in the Xero app
- **Scopes requested**: `openid profile email offline_access accounting.transactions.read accounting.contacts.read accounting.reports.read accounting.journals.read accounting.settings.read payroll.employees.read payroll.timesheets.read`
- **PKCE**: The auth flow uses S256 code challenge for security
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!