Production Xero MCP Server Template (TypeScript)
Overview
This template provides a complete, production-ready Model Context Protocol (MCP) server that integrates with the Xero accounting platform. Built with TypeScript and Express, it implements the full OAuth 2.0 Authorization Code flow with automatic token refresh, enabling Claude and other MCP clients to securely interact with Xero data on behalf of authenticated users.
The server exposes realistic Xero accounting tools โ creating and listing invoices, managing contacts, retrieving account balances, and tracking payments โ giving your AI assistant genuine financial superpowers. Every component follows production engineering standards: structured JSON logging with Pino, per-request correlation IDs, configurable rate limiting, a dependency-aware health endpoint, and a multi-stage Docker build that produces a lean production image.
This template targets TypeScript developers building internal finance tools, accounting automation platforms, or AI-powered bookkeeping assistants. It is designed to be cloned, configured with your Xero app credentials, and deployed to Railway, Fly.io, or any Docker-capable host within minutes.
๐ Production Repository
Want the complete production-ready starter template?
โญ GitHub Repository:
https://github.com/kamolc4/xero-mcp-server
Includes:
- Complete TypeScript source code
- OAuth 2.0 implementation
- MCP tools
- Jest tests
- GitHub Actions CI
- MIT License
- Ready for Claude Desktop, Cursor and Windsurf
What You'll Learn
- How to implement the full OAuth 2.0 Authorization Code flow (PKCE-ready) with Xero, including token storage and automatic refresh
- Registering typed MCP tools using the official
@modelcontextprotocol/sdkwith Zod input validation - Structuring a production Express + MCP HTTP server with middleware layering
- Generating and propagating per-request correlation IDs for distributed tracing
- Configuring Pino for structured JSON logging with child loggers and log-level control via environment variables
- Implementing a
/healthendpoint that reports on OAuth token validity and external API reachability - Applying express-rate-limit to protect your MCP endpoint from abuse
- Writing HTTP-level integration tests with Jest and Supertest covering auth, tools, and health
- Building a multi-stage Dockerfile that separates build and runtime layers for minimal image size
- Automating lint, type-check, test, and Docker build steps in a GitHub Actions CI pipeline
- Managing all configuration through environment variables with runtime validation
- Handling Xero API errors gracefully and surfacing them as structured MCP error responses
Architecture
Claude Desktop / Cursor
โ
โ HTTP POST /mcp (MCP JSON-RPC)
โผ
Express Server
โ
โโโ Correlation ID Middleware
โโโ Pino Request Logger
โโโ Rate Limiter (express-rate-limit)
โโโ Authentication Middleware (X-API-Key header)
โ
โผ
MCP SDK Handler
โ
โโโ Tool: xero_list_invoices โโโบ Xero Token Store โโโบ Xero API
โโโ Tool: xero_create_invoice โโโบ Xero Token Store โโโบ Xero API
โโโ Tool: xero_list_contacts โโโบ Xero Token Store โโโบ Xero API
โโโ Tool: xero_get_account โโโบ Xero Token Store โโโบ Xero API
โโโ Tool: xero_create_payment โโโบ Xero Token Store โโโบ Xero API
OAuth Flow (separate routes)
GET /auth/xero โ redirect to Xero
GET /auth/xero/callback โ exchange code, store tokens
GET /auth/status โ token validity check
GET /health โ { status, xero, uptime, version }
Project Structure
xero-mcp-server/
โโโ src/
โ โโโ __tests__/
โ โ โโโ setup.ts
โ โ โโโ config.test.ts
โ โโโ auth.ts
โ โโโ config.ts
โ โโโ health.ts
โ โโโ logger.ts
โ โโโ server.ts
โ โโโ xeroClient.ts
โโโ .dockerignore
โโโ .env.example
โโโ .gitignore
โโโ Dockerfile
โโโ docker-compose.yml
โโโ eslint.config.js
โโโ jest.config.ts
โโโ package.json
โโโ package-lock.json
โโโ tsconfig.json
Source Code
This article explains every part of the implementation.
If you want to skip the manual setup and start immediately, clone the complete repository:
https://github.com/kamolc4/xero-mcp-server
The repository includes:
- Production-ready project structure
- OAuth implementation
- Xero client
- MCP tools
- CI pipeline
- Tests
- Documentation
- MIT license
You can fork the repository or download it as a ZIP.
Prerequisites
- Node.js 20+
- npm 10+
- A Xero Developer App with OAuth 2.0 credentials
- Docker & Docker Compose (for containerised development)
- A Xero sandbox or real organisation connected to your developer app
Quick Start
# 1. Clone the template
git clone https://github.com/your-org/xero-mcp-server.git
cd xero-mcp-server
# 2. Install dependencies
npm install
# 3. Configure environment
cp .env.example .env
# Edit .env with your Xero Client ID, Client Secret, and a strong MCP_API_KEY
# 4. Start in development mode
npm run dev
# 5. Authorise with Xero (opens browser flow)
curl http://localhost:3000/auth/xero
# Follow the redirect, complete OAuth, tokens are stored automatically
# 6. Check health
curl http://localhost:3000/health
# 7. Test a tool call
curl -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-mcp-api-key' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"xero_list_invoices","arguments":{"status":"DRAFT","page":1}}}'
# 8. Run tests
npm test
Features
- Full Xero OAuth 2.0 flow โ Authorization Code + PKCE-ready flow, automatic token refresh, in-memory token store (swap for Redis/DB in production)
- Five realistic Xero tools โ list invoices, create invoice, list contacts, get account details, create payment
- Zod input validation โ every tool validates its arguments at the MCP layer before hitting the API
- Pino structured logging โ JSON output, configurable log level, per-request correlation IDs propagated through child loggers
- Rate limiting โ 100 requests/15 min window on the MCP endpoint, configurable via env vars
- Health endpoint โ
/healthreturns uptime, version, and live Xero token/API status - MCP endpoint authentication โ simple API key guard on the
/mcproute - Multi-stage Docker build โ builder stage compiles TypeScript, runtime stage contains only compiled JS
- docker-compose.yml โ single command local dev with automatic
.envloading - Jest + Supertest tests โ covers health 200, auth 401, and tool execution
- GitHub Actions CI โ lint, type-check, test, and Docker build on every push and PR
Works With
- Claude Desktop โ add server URL and API key to
claude_desktop_config.json - Claude Code โ use
--mcp-serverflag pointing at your deployed URL - Cursor โ configure under Settings โ MCP Servers
- Windsurf โ add to MCP server list in workspace settings
- Continue โ add as an MCP provider in
config.json
Claude Desktop Configuration Example
{
"mcpServers": {
"xero": {
"url": "http://localhost:3000/mcp",
"headers": {
"X-API-Key": "your-mcp-api-key"
}
}
}
}
Configuration
| Variable | Description | Required |
|---|---|---|
PORT | HTTP port the server listens on | Optional (default: 3000) |
NODE_ENV | Runtime environment (development/production) | Optional (default: development) |
LOG_LEVEL | Pino log level (trace/debug/info/warn/error) | Optional (default: info) |
MCP_API_KEY | Shared secret required in X-API-Key header for /mcp | Required |
XERO_CLIENT_ID | OAuth 2.0 Client ID from Xero Developer portal | Required |
XERO_CLIENT_SECRET | OAuth 2.0 Client Secret from Xero Developer portal | Required |
XERO_REDIRECT_URI | Callback URL registered in your Xero app (must match exactly) | Required |
XERO_SCOPES | Space-separated Xero OAuth scopes | Optional (default: openid profile email accounting.transactions accounting.contacts accounting.settings offline_access) |
RATE_LIMIT_WINDOW_MS | Rate limit window in milliseconds | Optional (default: 900000) |
RATE_LIMIT_MAX | Max requests per window per IP | Optional (default: 100) |
SERVER_BASE_URL | Public base URL of this server (used to build redirect URIs in logs) | Optional |
Deployment
Docker (local)
docker compose up --build
Docker (production build)
docker build -t xero-mcp-server:latest .
docker run -d \
--name xero-mcp \
-p 3000:3000 \
--env-file .env \
xero-mcp-server:latest
Railway
npm install -g @railway/cli
railway login
railway init
railway variables set XERO_CLIENT_ID=xxx XERO_CLIENT_SECRET=xxx MCP_API_KEY=xxx XERO_REDIRECT_URI=https://your-app.railway.app/auth/xero/callback
railway up
Fly.io
flyctl launch --name xero-mcp-server
flyctl secrets set XERO_CLIENT_ID=xxx XERO_CLIENT_SECRET=xxx MCP_API_KEY=xxx XERO_REDIRECT_URI=https://xero-mcp-server.fly.dev/auth/xero/callback
flyctl deploy
Production Checklist
-
MCP_API_KEYis at least 32 random characters and stored in a secrets manager -
XERO_CLIENT_SECRETis never committed to source control -
NODE_ENV=productionis set in the deployment environment -
XERO_REDIRECT_URIexactly matches the URI registered in the Xero Developer portal - Token storage is backed by a persistent store (Redis or database) rather than in-memory
- Rate limiting values are tuned to your expected load
-
LOG_LEVEL=infoorwarnin production (avoiddebug/trace) - HTTPS termination is handled by a reverse proxy (Railway/Fly.io do this automatically)
-
/healthendpoint is wired up to your load balancer or uptime monitor - Docker image is scanned for vulnerabilities (e.g.
docker scout cves) - GitHub Actions secrets
DOCKER_USERNAMEandDOCKER_PASSWORDare configured for image push - Xero app scopes are minimal โ only request scopes your tools actually use
- Token refresh errors trigger an alert so re-authorisation can happen before expiry
- Request correlation IDs are forwarded to any downstream services for tracing
- Dependency audit passes:
npm audit --audit-level=high
Testing
Automated tests
npm test # run all tests
npm run test:watch # watch mode
npm run test:coverage # coverage report
The test suite covers:
GET /healthreturns HTTP 200 with structured JSONPOST /mcpwithout a valid API key returns HTTP 401POST /mcpwith valid API key andtools/listreturns the registered toolsPOST /mcpcallingxero_list_invoiceswhile unauthorised returns a structured MCP error
Manual testing with curl
# List available tools
curl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-mcp-api-key' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | jq
# Call xero_list_contacts
curl -s -X POST http://localhost:3000/mcp \
-H 'Content-Type: application/json' \
-H 'X-API-Key: your-mcp-api-key' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"xero_list_contacts","arguments":{"page":1}}}' | jq
โญ GitHub Repository
The complete production-ready implementation is maintained on GitHub.
Repository:
https://github.com/kamolc4/xero-mcp-server
If this template saved you time, consider:
โญ Star the repository
๐ด Fork it
๐ Open an Issue
๐ Submit improvements
Keeping everything on GitHub makes it easier to receive updates, bug fixes, and new MCP features. express typescript utilities xero xero mcp server mcp typescript starter template production docker oauth claude cursor