Spendesk MCP Server

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.

Category
Visit Server

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

  1. Compiler le projet (si ce n’est pas déjà fait) :

    cd /chemin/vers/spendesk-mcp-server
    npm run build
    
  2. Ouvrir la config MCP de Claude :

    • macOS : ~/Library/Application Support/Claude/claude_desktop_config.json
    • Ou dans Claude Desktop : ParamètresDeveloperEdit Config
  3. 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"]
    
  4. 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_URL pour 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_SECRET pour 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_TOKEN cô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> et X-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

  1. Créer un projet : railway.app → New Project → Deploy from GitHub repo.
  2. 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é.
  3. Variables d'environnement (Settings → Variables) :
    • Environnement : SPENDESK_USE_DEMO = true pour la sandbox démo, false ou 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 (quand SPENDESK_USE_DEMO est faux).
      • Client credentials démo : SPENDESK_CLIENT_ID_DEMO + SPENDESK_CLIENT_SECRET_DEMO (quand SPENDESK_USE_DEMO=true).
      • Ou aucune variable sur le serveur si chaque client envoie Authorization: Bearer client_credentials:... (ou les headers Spendesk).
    • ALLOWED_HOSTS (recommandé) — host(s) autorisés pour la validation DNS rebinding, ex. : votre-service.railway.app (sans https://). Tu peux récupérer le domaine après le premier déploiement (Settings → Networking → Generate domain).
  4. Domaine : Settings → Networking → Generate domain. L'URL MCP sera https://<ton-domaine>.railway.app/mcp.
  5. 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 besoin ALLOWED_HOSTS pour 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) :

  1. Server URL : https://votre-domaine.com/mcp (URL publique de votre déploiement + /mcp).
  2. 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-Id et X-Spendesk-Client-Secret (section « Networking & Headers » dans Dust). Pas besoin de Bearer dans ce cas.
  • 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 :

  1. Sandbox vs prod (cause n°1) : les client_id / client_secret trunk / sandbox doivent parler à beta-sandbox.api.trunk.spendesk.services. Sur Railway, si SPENDESK_USE_DEMO est absent ou false, 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 éventuellement SPENDESK_BASE_URL si tu utilises une autre URL de démo), ou
    • B. Dans Dust → Networking & Headers, ajouter un 3ᵉ header : X-Spendesk-Use-Demo = true (ou 1). 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 aussi Spendesk-Use-Demo si l’UI Dust ne préfixe pas avec X-.)
  2. 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.

  3. Scopes Spendesk : pour spendesk_get_suppliers, l’application OAuth doit avoir au minimum supplier:read (et experimental:supplier:manage pour création / archive). Vérifie dans Spendesk Paramètres → Intégrations → Gestion d’accès API.

  4. Test léger : demander à l’assistant d’appeler spendesk_get_filter_options ou spendesk_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).

  5. 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 :

  1. Appeler spendesk_load_sqlite_data avec le jeu de données (payables, settlements, suppliers, purchase_orders) et, pour les payables, la plage de dates (from_date, to_date).
  2. Appeler spendesk_list_loaded_tables pour confirmer le schéma et le nombre de lignes.
  3. Appeler spendesk_execute_sql_query avec des requêtes SQL analytiques (SELECT / WITH uniquement ; résultats plafonnés à 1000 lignes).
  4. Appeler spendesk_clear_sqlite_tables en 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 via filters)
  • spendesk_update_settlement_state – Mise à jour de l'état d'un settlement
  • spendesk_get_bank_fees – Frais bancaires (chargedFrom / chargedTo pour limiter par date, ou filters). 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 : pagination page, perPage max 100, filters en camelCase)
  • spendesk_get_payable / spendesk_get_payable_attachments – Détail payable et pièces jointes
  • spendesk_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'abord spendesk_get_analytical_fields pour obtenir les fieldId, puis spendesk_get_analytical_values avec l'argument fieldId)
  • spendesk_get_cost_centers / spendesk_create_cost_center / spendesk_update_cost_center / spendesk_delete_cost_center – Centres de coût
  • spendesk_get_expense_categories – Catégories de dépenses

Accounting

  • spendesk_get_journal_csv – Contenu CSV d'un export comptable
  • spendesk_create_accounting_export – Créer un export comptable
  • spendesk_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, + filters générique)
  • spendesk_create_suppliers – Création fournisseurs (POST /v1/suppliers, corps = tableau d’objets supplierToCreate ; scope experimental: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, filtre vatNumber, archivage du plus récent). Nécessite client credentials avec experimental:supplier:manage et SPENDESK_USE_DEMO=true si trunk sandbox.
  • Smoke sandbox (API directe, trunk) : npm run test:sandbox-api ou node scripts/test-sandbox-direct-api.mjs — appelle GET /v1/suppliers, GET /v1/purchase-orders, GET /v1/users, GET /v1/cost-centers sur SPENDESK_BASE_URL ou la sandbox démo. Utilise client credentials (SPENDESK_API_TOKEN vidé dans le script npm) : sur la sandbox, un SPENDESK_API_TOKEN “prod” ou expiré renvoie souvent 401. Optionnel : SUPPLIER_IDS=id1,id2 pour rejouer un filtre ids= comme Postman.
  • Bulk suppliers (API directe) : node scripts/test-supplier-bulk-operations.mjs — même flux que la collection Postman postman/Spendesk-Suppliers-Bulk.postman_collection.json (import + étapes : postman/README.md).
  • Liste + archive / désarchive : npm run test:suppliers-archive-cycle — agrège des pages GET /v1/suppliers?isArchived=false, prend les 10 plus récents par createdAt (tri client : l’API ne fournit pas de sort), puis PATCH /v1/experimental/suppliers/:id/status (archiver puis désarchiver), comme spendesk_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=1 pour liste seule. Scopes supplier: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.mjsPATCH .../status avec isArchived: false, délais + retries 429 (SUPPLIER_UNARCHIVE_DELAY_MS).
  • Même flux archive / désarchive via MCP (stdio) : npm run test:mcp-suppliers-archive-cyclespendesk_get_suppliersspendesk_set_supplier_archive_statusspendesk_get_supplier (variables SUPPLIER_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-api ou node 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 via filters)

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_orderCancel / Close (scopes experimental: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-api pour 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 champs documentation (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) ou path (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 filtres paidFrom, 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 :

    1. 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).
    2. 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).
    3. Réessayer plus tard : un 500 peut être temporaire (incident Spendesk). Réessayer après quelques minutes.
    4. 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 »).
    5. 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 : mcpTool pour un outil donné (ex. spendesk_get_settlements), path pour 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

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured