Spendesk MCP Server
An MCP server that exposes the Spendesk public API as tools and resources for automating ERP integrations and building dashboards from Spendesk data.
README
Spendesk MCP Server
Serveur MCP qui expose l'API publique Spendesk sous forme d'outils et de ressources pour :
- Automatiser des intégrations ERP (NetSuite, Xero, QuickBooks, DATEV, etc.)
- Créer des tableaux de bord à partir des données Spendesk (settlements, payables, fournisseurs, utilisateurs, etc.)
Prérequis
- Node.js ≥ 18
- Des identifiants OAuth2 client Spendesk (client id + client secret), créés dans Paramètres > Intégrations > Gestion d'accès API (compte Premium/Enterprise, statut Account Owner).
Installation
npm install
npm run build
Configuration
Variables d'environnement :
| Variable | Obligatoire | Description |
|---|---|---|
SPENDESK_ENV |
Non | Environnement API: production (défaut), demo ou trunk. |
SPENDESK_USE_DEMO |
Non | Legacy: true/1 est mappé vers trunk pour rétrocompatibilité. |
SPENDESK_CLIENT_ID |
Oui* | Client ID Spendesk (prod). *Requis pour stdio ; pour HTTP, requis sauf si chaque client envoie Bearer client_credentials ou les headers Spendesk. |
SPENDESK_CLIENT_SECRET |
Oui* | Client secret Spendesk (prod). Ne pas commiter. |
SPENDESK_CLIENT_ID_DEMO |
Non | Client ID Spendesk demo (SPENDESK_ENV=demo). Peut aussi servir de fallback non-prod. |
SPENDESK_CLIENT_SECRET_DEMO |
Non | Client secret Spendesk démo. |
SPENDESK_CLIENT_ID_TRUNK |
Non | Client ID Spendesk trunk (SPENDESK_ENV=trunk). |
SPENDESK_CLIENT_SECRET_TRUNK |
Non | Client secret Spendesk trunk. |
SPENDESK_BASE_URL |
Non | Surcharge de l’URL de l’API (sans / final). |
DB_PATH |
Non | Chemin de la base SQLite utilisée pour le monitoring (mcp_usage_events). Défaut : ./data/clients.db. |
DOCS_URL |
Non | URL de la documentation (Mintlify). Si définie, GET /doc redirige vers cette URL. |
USAGE_UI_SECRET |
Non | Si définie, la page GET /usage (dashboard MCP) exige ?secret=<valeur> ou Authorization: Bearer <valeur>. |
Le mode stdio (npm start) et le fallback HTTP utilisent uniquement OAuth2 client credentials : SPENDESK_ENV=production|demo|trunk + la paire d’identifiants correspondante. URLs par défaut: prod https://public-api.spendesk.com, demo https://public-api.demo.spendesk.com, trunk https://beta-sandbox.api.trunk.spendesk.services (historique).
Exemple avec un fichier .env (à ne pas commiter) :
# Prod
SPENDESK_CLIENT_ID=your_client_id
SPENDESK_CLIENT_SECRET=your_client_secret
# Demo (optionnel)
# SPENDESK_ENV=demo
# SPENDESK_CLIENT_ID_DEMO=your_demo_client_id
# SPENDESK_CLIENT_SECRET_DEMO=your_demo_client_secret
# Trunk (optionnel)
# SPENDESK_ENV=trunk
# SPENDESK_CLIENT_ID_TRUNK=your_trunk_client_id
# SPENDESK_CLIENT_SECRET_TRUNK=your_trunk_client_secret
Désactiver certains outils (expérimentaux)
Pour ne pas exposer certains tools (par exemple expérimentaux), créez un fichier de configuration. Les tools désactivés ne sont pas enregistrés et n’apparaissent pas dans la référence API (tool spendesk_get_api_reference et ressource spendesk://api-reference).
Fichier config/tools.config.json à la racine du projet (ou tools.config.json) :
{
"disabledTools": [
"spendesk_get_accruals",
"spendesk_get_purchase_orders"
]
}
Le serveur lit ce fichier au démarrage. Pour réactiver un tool, retirez son nom de disabledTools et redémarrez le serveur.
Doc Mintlify : pour que la doc (spendesk-mcp-docs) n’affiche que les tools activés, exécutez avant de lancer ou déployer la doc :
npm run docs:sync-tools
Puis lancez la doc en local avec npm run docs:dev (sync + mintlify dev). Les pages overview, bookkeeping, accounts-payable, reference-data et spend-analysis sont mises à jour pour masquer les tools désactivés.
Utilisation
Lancer le serveur (stdio)
# Client credentials (prod)
export SPENDESK_CLIENT_ID=...
export SPENDESK_CLIENT_SECRET=...
npm start
# Client credentials (demo)
export SPENDESK_ENV=demo
export SPENDESK_CLIENT_ID_DEMO=...
export SPENDESK_CLIENT_SECRET_DEMO=...
npm start
# Client credentials (trunk)
export SPENDESK_ENV=trunk
export SPENDESK_CLIENT_ID_TRUNK=...
export SPENDESK_CLIENT_SECRET_TRUNK=...
npm start
Smoke test stdio (après npm run build) : npm run test:mcp — vide SPENDESK_API_TOKEN et exige SPENDESK_CLIENT_ID + SPENDESK_CLIENT_SECRET (ou paire _DEMO).
Le serveur communique en stdio : un client MCP (Cursor, Claude Desktop, etc.) peut l'exécuter comme sous-processus et envoyer des requêtes JSON-RPC.
Configurer Cursor
Dans les paramètres MCP de Cursor (ou dans le fichier de config MCP) :
{
"mcpServers": {
"spendesk": {
"command": "node",
"args": ["/chemin/vers/spendesk-mcp-server/dist/index.js"],
"env": {
"SPENDESK_ENV": "demo",
"SPENDESK_CLIENT_ID_DEMO": "<client_id>",
"SPENDESK_CLIENT_SECRET_DEMO": "<client_secret>"
}
}
}
}
Ou avec npx depuis le répertoire du projet (même variables SPENDESK_CLIENT_ID / SPENDESK_CLIENT_SECRET ou démo) :
{
"mcpServers": {
"spendesk": {
"command": "npx",
"args": ["-y", "tsx", "src/index.ts"],
"cwd": "/chemin/vers/spendesk-mcp-server",
"env": {
"SPENDESK_ENV": "demo",
"SPENDESK_CLIENT_ID_DEMO": "<client_id>",
"SPENDESK_CLIENT_SECRET_DEMO": "<client_secret>"
}
}
}
}
(En production, privilégier node dist/index.js après npm run build.)
Configurer Claude Desktop
-
Compiler le projet (si ce n’est pas déjà fait) :
cd /chemin/vers/spendesk-mcp-server npm run build -
Ouvrir la config MCP de Claude :
- macOS :
~/Library/Application Support/Claude/claude_desktop_config.json - Ou dans Claude Desktop : Paramètres → Developer → Edit Config
- macOS :
-
Ajouter le serveur Spendesk dans
mcpServers(remplacer le chemin et le token) :{ "mcpServers": { "spendesk": { "command": "node", "args": ["/chemin/vers/spendesk-mcp-server/dist/index.js"], "env": { "SPENDESK_CLIENT_ID": "<client_id>", "SPENDESK_CLIENT_SECRET": "<client_secret>" } } } }Avec un chemin absolu réel, par exemple :
"args": ["/Users/julien.chriqui/spendesk-mcp-server/dist/index.js"] -
Redémarrer complètement Claude Desktop (quitter l’app puis la rouvrir). Les outils MCP apparaissent (icône 🔨 à côté de la zone de saisie).
Option multi-tenant : en HTTP, les clients peuvent envoyer Bearer client_credentials:... sans stocker d’identifiants sur le serveur ; en stdio, les credentials restent dans env comme ci-dessus.
Serveur HTTP (Streamable) — ChatGPT, etc.
Pour utiliser le MCP depuis ChatGPT, un client HTTP ou une plateforme qui parle MCP en Streamable HTTP, lancez le serveur HTTP :
export SPENDESK_CLIENT_ID=...
export SPENDESK_CLIENT_SECRET=...
npm run start:http
# écoute par défaut sur http://0.0.0.0:3000
Variables utiles pour l'HTTP :
| Variable | Défaut | Description |
|---|---|---|
PORT |
3000 |
Port d'écoute |
HOST |
0.0.0.0 |
Interface d'écoute (0.0.0.0 pour être joignable depuis l'extérieur) |
Endpoints :
- POST /mcp — JSON-RPC (initialisation + messages). Le serveur renvoie un en-tête
mcp-session-idà la première requête d'initialisation. - GET /mcp — Flux SSE (envoyer l'en-tête
mcp-session-id). - DELETE /mcp — Fermer la session (en-tête
mcp-session-id). - GET /doc — Redirection vers la documentation (Mintlify). Définir
DOCS_URLpour l’URL cible ; sinon une page d’information s’affiche. - GET /usage — Dashboard d’usage MCP (volume par jour, top tools, derniers appels). Optionnel : définir
USAGE_UI_SECRETpour protéger l’accès.
Authentification HTTP et déploiement
L’interface /ui et le mode multi-tenant (base SQLite + clés API) ont été retirés.
L’authentification se fait désormais uniquement via :
- des credentials fournis par le client (client credentials OAuth2 ou, pour une requête donnée, un Bearer token transmis par le client dans l’en-tête HTTP — pas via
SPENDESK_API_TOKENcôté serveur), ou - des variables d’environnement configurées sur le serveur.
Credentials fournis par le client (Dust / Claude)
Le client_id et le client_secret Spendesk peuvent être fournis par le client à la connexion, sans les mettre en dur sur Railway :
- Bearer :
Authorization: Bearer client_credentials:<base64(client_id:client_secret)>.
Générer avec :node scripts/generate-dust-bearer.mjs <client_id> <client_secret>et coller le résultat dans le champ Bearer (ex. dans Dust). - Headers :
X-Spendesk-Client-Id: <id>etX-Spendesk-Client-Secret: <secret>.
Le serveur appelle alors POST /v1/auth/token avec ces identifiants pour la session et renouvelle le token automatiquement sur 401.
Mode Fallback (variables d’environnement)
Si aucun header d’auth n’est fourni, le serveur utilise uniquement les variables d’environnement client credentials : selon SPENDESK_USE_DEMO, SPENDESK_CLIENT_ID + SPENDESK_CLIENT_SECRET (prod) ou SPENDESK_CLIENT_ID_DEMO + SPENDESK_CLIENT_SECRET_DEMO (démo). Le serveur appelle POST /v1/auth/token avec le flux client_credentials.
L’URL de l’API Spendesk est choisie selon SPENDESK_USE_DEMO (sandbox vs public-api). Cela permet d’exécuter le MCP en mode single-tenant, en prod ou en démo.
Déploiement (Docker, PaaS)
Docker
docker build -t spendesk-mcp-server .
docker run -p 3000:3000 \
-e SPENDESK_CLIENT_ID=your_client_id \
-e SPENDESK_CLIENT_SECRET=your_client_secret \
spendesk-mcp-server
Déployer sur Railway
- Créer un projet : railway.app → New Project → Deploy from GitHub repo.
- Build : Railway détecte le Dockerfile et build l'image, ou utilise
railway.json(build :npm ci && npm run build, start :node dist/server-http.js). Si un Dockerfile est présent, il est utilisé en priorité. - Variables d'environnement (Settings → Variables) :
- Environnement :
SPENDESK_USE_DEMO=truepour la sandbox démo,falseou non défini pour la prod. - Auth Spendesk (au moins une option pour l’environnement choisi) :
- Client credentials prod :
SPENDESK_CLIENT_ID+SPENDESK_CLIENT_SECRET(quandSPENDESK_USE_DEMOest faux). - Client credentials démo :
SPENDESK_CLIENT_ID_DEMO+SPENDESK_CLIENT_SECRET_DEMO(quandSPENDESK_USE_DEMO=true). - Ou aucune variable sur le serveur si chaque client envoie
Authorization: Bearer client_credentials:...(ou les headers Spendesk).
- Client credentials prod :
ALLOWED_HOSTS(recommandé) — host(s) autorisés pour la validation DNS rebinding, ex. :votre-service.railway.app(sanshttps://). Tu peux récupérer le domaine après le premier déploiement (Settings → Networking → Generate domain).
- Environnement :
- Domaine : Settings → Networking → Generate domain. L'URL MCP sera
https://<ton-domaine>.railway.app/mcp. - Vérifier :
MCP_BASE_URL=https://<ton-domaine>.railway.app node scripts/test-mcp-http.mjs
Render / Fly.io
- Déployer le dépôt (build :
npm ci && npm run build, start :node dist/server-http.js). - Définir
SPENDESK_CLIENT_ID+SPENDESK_CLIENT_SECRET(ou credentials par requête uniquement), et si besoinALLOWED_HOSTSpour le domaine public de l'app. - L'URL du serveur MCP :
https://votre-app.onrender.com/mcp(ou ton domaine +/mcp).
Déployer la documentation (Mintlify) sur Railway
La doc (Mintlify) est dans spendesk-mcp-docs/. Pour la déployer sur Railway en tant que second service : créer un nouveau service Railway à partir du même dépôt, définir Root Directory sur spendesk-mcp-docs, puis déployer. Le build exécute npm ci, le start lance un proxy qui relaie le serveur Mintlify. Générer un domaine dans Settings → Networking. Voir spendesk-mcp-docs/README.md pour le détail.
Configurer ChatGPT (ou un client MCP Streamable HTTP)
Dans l'interface ou la config du client MCP (ex. ChatGPT avec MCP, ou OpenAI Responses API) :
- Server URL :
https://votre-domaine.com/mcp(URL publique de votre déploiement +/mcp). - Authorization (au choix) :
- Client credentials (fournis par le client, sans rien en dur sur Railway) : pour que Dust/Claude envoie le client_id et client_secret Spendesk à la connexion :
- Option A — Bearer : Bearer =
client_credentials:<base64(client_id:client_secret)>. Générer le token avec :node scripts/generate-dust-bearer.mjs <client_id> <client_secret>puis coller la sortie dans le champ Bearer de Dust. - Option B — Headers : ajouter les headers
X-Spendesk-Client-IdetX-Spendesk-Client-Secret(section « Networking & Headers » dans Dust). Pas besoin de Bearer dans ce cas.
- Option A — Bearer : Bearer =
- Bearer token : le client peut envoyer un token API Spendesk en
Authorization: Bearer <token>pour cette session (sans variable d’environnement équivalente sur le serveur). - Mode fallback : si aucun header n’est envoyé, le serveur utilise
SPENDESK_CLIENT_ID+SPENDESK_CLIENT_SECRET(ou paire démo) en environnement. - Pour protéger l’accès, mettre un reverse proxy (auth, API key) devant
/mcp.
Le client envoie d'abord une requête POST avec le body JSON-RPC initialize, récupère le mcp-session-id dans les en-têtes de la réponse, puis réutilise ce session ID pour les requêtes suivantes et pour le flux GET SSE.
Dust (app.dust.tt) — la connexion liste les outils mais les appels Spendesk échouent
Checklist fréquente :
-
Sandbox vs prod (cause n°1) : les
client_id/client_secrettrunk / sandbox doivent parler àbeta-sandbox.api.trunk.spendesk.services. Sur Railway, siSPENDESK_USE_DEMOest absent oufalse, le serveur utilisait jusqu’ici public-api.spendesk.com même quand Dust envoyait des identifiants sandbox → token ou appels API invalides. Corriger par une des deux options :- A. Variables Railway :
SPENDESK_USE_DEMO=true(et éventuellementSPENDESK_BASE_URLsi tu utilises une autre URL de démo), ou - B. Dans Dust → Networking & Headers, ajouter un 3ᵉ header :
X-Spendesk-Use-Demo=true(ou1). Ainsi la session MCP utilise l’hôte sandbox pour le flux OAuth et les outils, sans changer le reste du déploiement. (Le serveur accepte aussiSpendesk-Use-Demosi l’UI Dust ne préfixe pas avecX-.)
- A. Variables Railway :
-
Bearer vide : tu peux laisser le champ Bearer Token vide si tu utilises
X-Spendesk-Client-Id+X-Spendesk-Client-Secret. Ne mets pas un espace ou un faux token. -
Scopes Spendesk : pour
spendesk_get_suppliers, l’application OAuth doit avoir au minimumsupplier:read(etexperimental:supplier:managepour création / archive). Vérifie dans Spendesk Paramètres → Intégrations → Gestion d’accès API. -
Test léger : demander à l’assistant d’appeler
spendesk_get_filter_optionsouspendesk_get_wallet_summary; si ça passe mais pas les suppliers, c’est presque sûrement un souci de scope ou d’URL d’API (point 1). -
Synchroniser : après changement d’URL ou de variables Railway, clique Sync sur l’outil MCP dans Dust puis réessaie.
Tester le MCP déployé (ou local)
Un script vérifie que le serveur HTTP répond correctement (GET /, initialize, tools/list, tools/call) :
# Test en local (serveur : npm run start:http ; .env avec SPENDESK_CLIENT_ID + SPENDESK_CLIENT_SECRET)
node scripts/test-mcp-http.mjs
# Test vers une URL déployée (même paire dans l’environnement pour construire Bearer client_credentials)
MCP_BASE_URL=https://votre-app.up.railway.app node scripts/test-mcp-http.mjs
En cas de succès : ✓ MCP HTTP test passed.
Tester les outils composites (analyse spend, bookkeeping, AP aging, cash flow)
Un script appelle les 5 outils composites pour vérifier qu’ils répondent correctement :
# 1. Démarrer le serveur (dans un premier terminal)
npm run start:http
# 2. Dans un second terminal (même .env / client id+secret que le serveur, ou headers client_credentials)
node scripts/test-composite-tools.mjs
Le script appelle successivement : spendesk_analyze_spend, spendesk_get_bookkeeping_pipeline, spendesk_get_payment_status, spendesk_get_ap_aging, spendesk_get_cash_flow_forecast sur une période exemple (janvier 2026). En cas d’erreur (token, 404, timeout snapshot), le message s’affiche pour chaque outil.
Pour tester les payables snapshot et la pagination :
FROM_DATE=2026-02-26 node scripts/test-payables-from-date.mjs
Ephemeral SQLite Analytics (pattern type Ramp)
Au lieu de renvoyer de gros JSON au LLM, vous pouvez charger les données Spendesk dans une base SQLite en mémoire par session, puis laisser le LLM exécuter des requêtes SQL en lecture seule. Utile pour des analyses croisées, agrégations et rapports comptables.
Workflow recommandé pour le LLM :
- Appeler
spendesk_load_sqlite_dataavec le jeu de données (payables,settlements,suppliers,purchase_orders) et, pour les payables, la plage de dates (from_date,to_date). - Appeler
spendesk_list_loaded_tablespour confirmer le schéma et le nombre de lignes. - Appeler
spendesk_execute_sql_queryavec des requêtes SQL analytiques (SELECT / WITH uniquement ; résultats plafonnés à 1000 lignes). - Appeler
spendesk_clear_sqlite_tablesen fin d’analyse pour libérer la mémoire.
Outils disponibles :
| Outil | Rôle |
|---|---|
spendesk_load_sqlite_data |
Charge un jeu (payables, settlements, suppliers, purchase_orders) dans une table SQLite en mémoire. Paramètres : dataset, from_date (optionnel), to_date (optionnel). |
spendesk_execute_sql_query |
Exécute une requête SQL en lecture seule (SELECT ou WITH). Paramètre : sql. |
spendesk_list_loaded_tables |
Liste les tables chargées avec colonnes, types et nombre de lignes. |
spendesk_clear_sqlite_tables |
Supprime des tables (table_names) ou toutes si la liste est vide. |
Sécurité : seules les requêtes commençant par SELECT ou WITH sont acceptées ; les mots-clés INSERT, UPDATE, DELETE, DROP, CREATE, ALTER sont refusés.
Exemples de requêtes SQL (après chargement des payables ou autres tables) :
-- 1. Top 10 fournisseurs par montant (EUR)
SELECT supplier_name, SUM(amount_eur) AS total_eur, COUNT(*) AS nb
FROM payables GROUP BY supplier_name ORDER BY total_eur DESC LIMIT 10;
-- 2. Dépenses par centre de coût
SELECT cost_center, SUM(amount_eur) AS total_eur FROM payables
WHERE cost_center IS NOT NULL AND cost_center != '' GROUP BY cost_center ORDER BY total_eur DESC;
-- 3. Factures non payées (unpaid)
SELECT id, supplier_name, amount_eur, payable_date, due_date FROM payables
WHERE payment_status = 'unpaid' ORDER BY due_date;
-- 4. Répartition par type de payable (invoice, card, expense…)
SELECT payable_type, COUNT(*) AS nb, SUM(amount_eur) AS total_eur
FROM payables GROUP BY payable_type ORDER BY total_eur DESC;
-- 5. Tendance mensuelle (montant par mois)
SELECT strftime('%Y-%m', payable_date) AS month, SUM(amount_eur) AS total_eur, COUNT(*) AS nb
FROM payables GROUP BY month ORDER BY month;
-- 6. Payables non encore exportés en compta (bookkeeping_status = created)
SELECT supplier_name, SUM(amount_eur) AS total_eur FROM payables
WHERE bookkeeping_status = 'created' GROUP BY supplier_name ORDER BY total_eur DESC;
-- 7. Montant par devise d’origine
SELECT original_currency, SUM(amount_eur) AS total_eur, COUNT(*) AS nb
FROM payables GROUP BY original_currency ORDER BY total_eur DESC;
-- 8. Settlements : montant par état (processing, completed, failed, pending)
SELECT state, COUNT(*) AS nb, SUM(amount_eur) AS total FROM settlements GROUP BY state;
-- 9. Top fournisseurs sur les purchase orders
SELECT supplier_name, COUNT(*) AS nb_po, SUM(total_amount) AS total FROM purchase_orders
GROUP BY supplier_name ORDER BY total DESC LIMIT 10;
-- 10. Jointure payables + type pour analyse détaillée
SELECT payable_type, bookkeeping_status, COUNT(*) AS nb, SUM(amount_eur) AS total_eur
FROM payables GROUP BY payable_type, bookkeeping_status ORDER BY total_eur DESC;
Outils (Tools)
Tous les endpoints principaux de l'API Spendesk sont exposés comme outils MCP :
Spend Data
spendesk_get_settlements– Liste des settlements (avec filtres viafilters)spendesk_update_settlement_state– Mise à jour de l'état d'un settlementspendesk_get_bank_fees– Frais bancaires (chargedFrom/chargedTopour limiter par date, oufilters). Voir Export bank fees → NetSuite pour le flow journalisation quotidienne.spendesk_create_payables_snapshot/spendesk_get_payables_snapshot– Snapshots de payables (création : filtres Public API ; récupération : paginationpage,perPagemax 100,filtersen camelCase)spendesk_get_payable/spendesk_get_payable_attachments– Détail payable et pièces jointesspendesk_update_payable_bookkeeping– Statut comptable d'un payable (sync ERP)- Report (réponses clés en main) :
spendesk_get_spend_dashboard– Dashboard spend (répartition par cost center / catégorie / compte de charge) ;spendesk_get_top_suppliers_by_spend– Top N fournisseurs par spend avec payables/settlements ;spendesk_get_purchase_orders_and_payables_export– Export POs + payables d'une période, liés par fournisseur spendesk_get_wallet_loads/spendesk_get_wallet_summary– Recharges et résumé wallet. Voir Export wallet loads → NetSuite pour le flow journalisation quotidienne.
Analytical
spendesk_get_analytical_fields/spendesk_get_analytical_values– Champs et valeurs analytiques (appeler d'abordspendesk_get_analytical_fieldspour obtenir lesfieldId, puisspendesk_get_analytical_valuesavec l'argumentfieldId)spendesk_get_cost_centers/spendesk_create_cost_center/spendesk_update_cost_center/spendesk_delete_cost_center– Centres de coûtspendesk_get_expense_categories– Catégories de dépenses
Accounting
spendesk_get_journal_csv– Contenu CSV d'un export comptablespendesk_create_accounting_export– Créer un export comptablespendesk_get_journal_templates– Modèles de journaux
Suppliers & Users
spendesk_get_suppliers/spendesk_get_supplier– Fournisseurs (filtres dédiés :ids,updatedBefore/After,createdBefore/After,bankCountry,iban,vatNumber,isArchived, +filtersgénérique)spendesk_create_suppliers– Création fournisseurs (POST/v1/suppliers, corps = tableau d’objetssupplierToCreate; scopeexperimental:supplier:manage)spendesk_update_supplier/spendesk_update_suppliers/spendesk_set_supplier_archive_status– Mise à jour / archivage fournisseurs (PATCH)- Démo cycle de vie :
node scripts/demo-supplier-lifecycle.mjs(création → mise à jour → archivage ; puis 2 fournisseurs même TVA, filtrevatNumber, archivage du plus récent). Nécessite client credentials avecexperimental:supplier:manageetSPENDESK_USE_DEMO=truesi trunk sandbox. - Smoke sandbox (API directe, trunk) :
npm run test:sandbox-apiounode scripts/test-sandbox-direct-api.mjs— appelleGET /v1/suppliers,GET /v1/purchase-orders,GET /v1/users,GET /v1/cost-centerssurSPENDESK_BASE_URLou la sandbox démo. Utilise client credentials (SPENDESK_API_TOKENvidé dans le script npm) : sur la sandbox, unSPENDESK_API_TOKEN“prod” ou expiré renvoie souvent401. Optionnel :SUPPLIER_IDS=id1,id2pour rejouer un filtreids=comme Postman. - Bulk suppliers (API directe) :
node scripts/test-supplier-bulk-operations.mjs— même flux que la collection Postmanpostman/Spendesk-Suppliers-Bulk.postman_collection.json(import + étapes :postman/README.md). - Liste + archive / désarchive :
npm run test:suppliers-archive-cycle— agrège des pagesGET /v1/suppliers?isArchived=false, prend les 10 plus récents parcreatedAt(tri client : l’API ne fournit pas desort), puisPATCH /v1/experimental/suppliers/:id/status(archiver puis désarchiver), commespendesk_set_supplier_archive_status. Variables :SUPPLIER_ARCHIVE_TEST_COUNT,SUPPLIER_ARCHIVE_TEST_MAX_PAGES,SUPPLIER_ARCHIVE_TEST_PATCH_DELAY_MS(défaut 800) pour limiter les 429,SUPPLIER_ARCHIVE_TEST_DRY_RUN=1pour liste seule. Scopessupplier:read+experimental:supplier:manage. - Désarchiver une liste d’IDs (ex. après un test interrompu) :
SUPPLIER_IDS=id1,id2,... node -r dotenv/config scripts/unarchive-suppliers-by-ids.mjs—PATCH .../statusavecisArchived: false, délais + retries 429 (SUPPLIER_UNARCHIVE_DELAY_MS). - Même flux archive / désarchive via MCP (stdio) :
npm run test:mcp-suppliers-archive-cycle—spendesk_get_suppliers→spendesk_set_supplier_archive_status→spendesk_get_supplier(variablesSUPPLIER_MCP_ARCHIVE_*,SUPPLIER_MCP_ARCHIVE_DRY_RUN=1). Rollback désarchive si erreur après archive partielle. - Fiche fournisseur complète (tous champs OpenAPI + analyse lecture/patch) :
node scripts/test-supplier-full-attributes.mjs— rapport et tableau attributs :docs/supplier-full-attributes.md. - Purchase Orders (liste, détail, création, cancel, close) :
npm run test:po-apiounode scripts/test-purchase-orders-api.mjs— rapport :docs/purchase-orders-api-test.md; outils MCP :npm run test:mcp-po; Postman :postman/Spendesk-Purchase-Orders.postman_collection.json+postman/README.md. spendesk_get_users/spendesk_get_user– Utilisateurs (avec filtres viafilters)
Webhooks
spendesk_create_webhook/spendesk_get_webhooks/spendesk_get_webhook/spendesk_update_webhook/spendesk_delete_webhook– Gestion des webhooks
Purchase Orders
spendesk_get_purchase_orders/spendesk_create_purchase_order– Liste et création. Liste : filtres alignés sur Get Purchase Orders. Création : Create a purchase order.spendesk_get_purchase_order– Détail par ID (Get purchase order), réponse sanitizée.spendesk_cancel_purchase_order/spendesk_close_purchase_order– Cancel / Close (scopesexperimental:purchase-order:write). Règles métier Spendesk : un PO ne peut être annulé que si aucune facture n’y est liée ; un PO ne peut être clos que si toutes les factures liées sont payées.- Édition (PATCH) : non exposée par l’API publique documentée dans ce dépôt — pas d’outil MCP d’update PO.
- Tests :
npm run test:mcp-po(stdio MCP : create → get → cancel → create B → close) avec.env;npm run test:po-apipour le même scénario en appels HTTP directs (scripts/test-purchase-orders-api.mjs).
Ephemeral SQLite (analyses SQL)
spendesk_load_sqlite_data– Charge un jeu (payables, settlements, suppliers, purchase_orders) dans une table SQLite en mémoire. Paramètres :dataset,from_date,to_date(obligatoires pour payables).spendesk_execute_sql_query– Exécute une requête SQL en lecture seule (SELECT ou WITH). Paramètre :sql. Résultats limités à 1000 lignes.spendesk_list_loaded_tables– Liste les tables chargées avec schéma (colonnes, types) et nombre de lignes.spendesk_clear_sqlite_tables– Supprime des tables (table_names) ou toutes si vide.
Flows / Intégrations
- Export bank fees → NetSuite — Journalisation quotidienne des frais bancaires (otherFee / fxFee) en écritures comptables NetSuite, avec batching par jour et idempotence.
- Export wallet loads → NetSuite — Journalisation des recharges wallet (virements bank → Spendesk) en écritures comptables NetSuite, une JE par load, avec idempotence et option
WALLET_LOAD_AMOUNT_UNIT(cents vs EUR).
Découverte / Référence API
spendesk_get_api_reference– Retourne la référence de l’API : liste des endpoints (méthode HTTP, path), paramètres (query, path, body), nom de l’outil MCP associé, et champsdocumentation(liens developer.spendesk.com) quand disponibles. Utiliser quand on demande « quels sont les endpoints ? », « quels paramètres pour les settlements ? », « filtres purchase orders », « structure de l’API ». Optionnel :mcpTool(ex.spendesk_get_settlements) oupath(ex.payables,purchase-orders) pour filtrer.
Les outils qui listent des éléments (settlements, suppliers, purchase orders, bank fees, wallet loads, etc.) acceptent une pagination explicite page (défaut 1) et perPage. Selon l’endpoint, le serveur mappe automatiquement vers le paramètre API attendu (perPage ou pageSize, ex. suppliers). Ils acceptent aussi des filtres génériques via le paramètre filters (objet avec n'importe quels query parameters de l'API Spendesk, ex. : { from: '2024-01-01', to: '2024-12-31', state: 'completed' }). spendesk_get_payables_snapshot supporte également page et perPage pour paginer les payables d’un snapshot (ex. 1 177 payables sur ~40 pages à 30/page).
Réponses clés en main (Claude / Dust)
Les clients peuvent poser une seule fois une des trois questions en langage naturel dans Claude ou Dust et recevoir une réponse correcte, précise et bien structurée (tableaux Markdown, sections), sans avoir à préciser les outils ou reformater.
Mapping question → outil
| Question type | Outil à utiliser | Paramètres |
|---|---|---|
| Dashboard spend, répartition des dépenses par cost center / catégorie / compte de charge pour une période (ex. Q1 2026, janvier 2026) | spendesk_get_spend_dashboard |
from, to (dates ISO) ; optionnel : groupBy (costCenter, expenseCategory, chargeAccount) |
| Top 10 (ou N) fournisseurs par spend, avec payables/settlements associés | spendesk_get_top_suppliers_by_spend |
from, to ; limit (défaut 10) |
| Export des purchase orders créés sur une période avec les payables associés | spendesk_get_purchase_orders_and_payables_export |
from, to |
Convention de dates : Q1 2026 = from: 2026-01-01, to: 2026-03-31 ; janvier 2026 = from: 2026-01-01, to: 2026-01-31.
Format de réponse recommandé
- Dashboard spend (après
spendesk_get_spend_dashboard) : résumé (période, total, devise) ; tableaux Markdown « Par cost center », « Par catégorie de dépense », « Par compte de charge » (colonnes : nom/id, montant, nombre d’éléments). - Top fournisseurs (après
spendesk_get_top_suppliers_by_spend) : tableau principal (rang, fournisseur, montant total, devise) ; pour chaque fournisseur (ou sur demande) : liste des payables/settlements (id, montant, date). - Export POs + payables (après
spendesk_get_purchase_orders_and_payables_export) : nombre de POs et de payables sur la période ; tableaux ou listes des POs et des payables ; regroupement par fournisseur si utile.
Instructions pour Claude / Dust (copier-coller)
Vous pouvez coller le bloc suivant dans les instructions du projet (Claude) ou dans le système du canal (Dust) pour que l’assistant choisisse le bon outil et formate la réponse :
Pour les questions sur le spend (dashboard, répartition par cost center / catégorie / compte de charge), utilise l’outil spendesk_get_spend_dashboard avec from/to selon la période demandée (Q1 = 2026-01-01 à 2026-03-31, janvier = 2026-01-01 à 2026-01-31). Présente le résultat en tableaux Markdown avec les sections par cost center, par catégorie, par compte de charge.
Pour le top 10 fournisseurs par spend, utilise spendesk_get_top_suppliers_by_spend. Affiche un tableau classé et les payables/settlements associés pour chaque fournisseur.
Pour l’export des POs et payables d’une période, utilise spendesk_get_purchase_orders_and_payables_export. Présente les POs et payables sous forme de tableaux, avec regroupement par fournisseur si utile.
Dépannage : « API Payables 404 » ou « Settlements 400 »
Si Dust ou Claude répond que l’API Payables retourne 404 ou que Settlements retourne 400 :
-
404 sur Payables
L’API Payables (snapshots / factures fournisseurs) n’est pas disponible pour ton compte. Causes possibles :- Plan Spendesk : Payables est souvent réservé aux offres Premium/Enterprise (module Invoices / Accounts Payable).
- Scopes : La clé API doit avoir le scope
payable:read(à activer dans Paramètres > Intégrations > Gestion d’accès API). - À vérifier dans l’interface Spendesk (Rapports, factures fournisseurs) : si tu n’as pas accès aux payables dans l’app, l’API les expose pas non plus.
-
400 sur Settlements
Requête invalide (paramètres ou format). Vérifier les valeurs passées (dates ISO, pas de paramètres inconnus) pour les filtrespaidFrom,clearedFrom,clearedTo,exportedAfter. Si l’erreur persiste, contacter le support Spendesk. -
500 Internal Server Error
Erreur côté serveur Spendesk (pas dans le MCP). Le serveur API a planté ou refuse la requête pour une raison interne. À faire :- Vérifier l’auth : client credentials ou token Bearer fourni par le client doit être valide et avoir les scopes nécessaires (ex. accès Purchase Orders si tu appelles les POs).
- Vérifier l’environnement : en démo (
SPENDESK_USE_DEMO=true), l’API démo peut ne pas supporter tous les endpoints (ex. purchase-orders). - Réessayer plus tard : un 500 peut être temporaire (incident Spendesk). Réessayer après quelques minutes.
- Regarder le détail : depuis la dernière version, le message d’erreur inclut un extrait du corps de réponse de l’API (
Body: ...) — cela peut indiquer la cause (ex. « feature not enabled », « invalid filter »). - Contacter Spendesk : si l’erreur est reproductible sur un compte prod avec un token valide, ouvrir un ticket au support Spendesk avec l’endpoint, les paramètres et le message/corps de la réponse.
Contournement : tant que Payables n’est pas disponible, les rapports « top fournisseurs par spend » ou « dashboard spend » ne peuvent pas être calculés par le MCP. Tu peux utiliser les rapports Spendesk (Rapports → filtrer sur la période) ou activer le module Payables / les scopes côté Spendesk pour débloquer l’API.
Découvrir la structure de l’API
Les clients (Claude, Dust, etc.) peuvent interroger le MCP pour connaître les endpoints, paramètres et structures de l’API Spendesk exposée :
- Outil
spendesk_get_api_reference: retourne la référence complète (baseUrl, endpoints avec method, path, queryParams, pathParams, bodyParams, mcpTool, responseNote). Paramètres optionnels :mcpToolpour un outil donné (ex.spendesk_get_settlements),pathpour filtrer par chemin (ex.settlements). - Ressource
spendesk://api-reference: même contenu en lecture seule (idéal pour l’injecter en contexte ou pour les clients qui lisent les ressources).
Exemples de questions que l’assistant peut résoudre en appelant l’outil ou en lisant la ressource : « Quels paramètres accepte l’API settlements ? », « Quel endpoint pour lister les payables ? », « Quelle est la structure des endpoints Purchase Orders ? ».
Ressources (Resources)
Données en lecture seule, utiles pour alimenter des dashboards ou du contexte :
| URI | Description |
|---|---|
spendesk://settlements |
Liste des settlements |
spendesk://suppliers |
Liste des fournisseurs |
spendesk://users |
Liste des utilisateurs |
spendesk://wallet-summary |
Résumé wallet |
spendesk://cost-centers |
Centres de coût |
spendesk://expense-categories |
Catégories de dépenses |
spendesk://analytical-fields |
Champs analytiques |
spendesk://bank-fees |
Frais bancaires |
spendesk://wallet-loads |
Recharges wallet |
spendesk://journal-templates |
Modèles de journaux comptables |
spendesk://api-reference |
Référence API : endpoints, paramètres, structures (pour découvrir comment utiliser l’API) |
Les ressources renvoient du JSON (UTF-8).
Référence API
Documentation officielle : Spendesk Public API.
Licence
MIT.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.