Description
When using the MCP server with a Xero Custom Connection (client_credentials grant type), all tool calls fail with:
Error listing invoices: Failed to get Xero token: [object Object]
The same credentials work correctly when calling the Xero identity endpoint directly:
bashcurl -s -X POST https://identity.xero.com/connect/token
-H "Content-Type: application/x-www-form-urlencoded"
-d "grant_type=client_credentials"
-d "client_id=<CLIENT_ID>"
-d "client_secret=<CLIENT_SECRET>"
This returns a valid access token, and subsequent API calls to https://api.xero.com/api.xro/2.0/Invoices (with the correct Xero-Tenant-Id header) succeed and return data.
Environment
@xeroapi/xero-mcp-server@latest (installed via npx -y)
Node.js / npx 10.9.4
Xero Custom Connection (AU organisation)
Scopes configured on app: accounting.transactions, accounting.transactions.read, accounting.contacts, accounting.contacts.read, accounting.settings, accounting.settings.read, accounting.reports.read, plus payroll and asset scopes
Steps to Reproduce
Create a Xero Custom Connection app at https://developer.xero.com/
Configure .mcp.json:
json {
"mcpServers": {
"xero": {
"command": "npx",
"args": ["-y", "@xeroapi/xero-mcp-server@latest"],
"env": {
"XERO_CLIENT_ID": "<your_client_id>",
"XERO_CLIENT_SECRET": "<your_client_secret>"
}
}
}
}
Call any tool (e.g. list-invoices)
Observe error: Failed to get Xero token: [object Object]
Expected Behaviour
The MCP server should use the client_credentials grant to obtain an access token from https://identity.xero.com/connect/token and use it for API calls, the same way a direct curl request works.
Workaround
Using XERO_CLIENT_BEARER_TOKEN with a manually obtained token works, but tokens expire every 30 minutes, making this impractical.
Additional Notes
The @beta tag referenced in some documentation does not exist on npm — npm error code ETARGET / No matching version found for @xeroapi/xero-mcp-server@beta
The error message [object Object] suggests the token exchange error response is not being serialised properly — likely needs JSON.stringify() or accessing a specific property on the error object
Description
When using the MCP server with a Xero Custom Connection (client_credentials grant type), all tool calls fail with:
Error listing invoices: Failed to get Xero token: [object Object]
The same credentials work correctly when calling the Xero identity endpoint directly:
bashcurl -s -X POST https://identity.xero.com/connect/token
-H "Content-Type: application/x-www-form-urlencoded"
-d "grant_type=client_credentials"
-d "client_id=<CLIENT_ID>"
-d "client_secret=<CLIENT_SECRET>"
This returns a valid access token, and subsequent API calls to https://api.xero.com/api.xro/2.0/Invoices (with the correct Xero-Tenant-Id header) succeed and return data.
Environment
@xeroapi/xero-mcp-server@latest (installed via npx -y)
Node.js / npx 10.9.4
Xero Custom Connection (AU organisation)
Scopes configured on app: accounting.transactions, accounting.transactions.read, accounting.contacts, accounting.contacts.read, accounting.settings, accounting.settings.read, accounting.reports.read, plus payroll and asset scopes
Steps to Reproduce
Create a Xero Custom Connection app at https://developer.xero.com/
Configure .mcp.json:
json {
"mcpServers": {
"xero": {
"command": "npx",
"args": ["-y", "@xeroapi/xero-mcp-server@latest"],
"env": {
"XERO_CLIENT_ID": "<your_client_id>",
"XERO_CLIENT_SECRET": "<your_client_secret>"
}
}
}
}
Call any tool (e.g. list-invoices)
Observe error: Failed to get Xero token: [object Object]
Expected Behaviour
The MCP server should use the client_credentials grant to obtain an access token from https://identity.xero.com/connect/token and use it for API calls, the same way a direct curl request works.
Workaround
Using XERO_CLIENT_BEARER_TOKEN with a manually obtained token works, but tokens expire every 30 minutes, making this impractical.
Additional Notes
The @beta tag referenced in some documentation does not exist on npm — npm error code ETARGET / No matching version found for @xeroapi/xero-mcp-server@beta
The error message [object Object] suggests the token exchange error response is not being serialised properly — likely needs JSON.stringify() or accessing a specific property on the error object