Passer au contenu

    EXEMPLE : une entreprise fictive. Pas un client d'Adwaizer.

    Serveur MCP sur mesure : dossier de remise pour Fernlight Supply Co. (exemple)

    Préparé par : Adwaizer AI Consulting Inc. · Mandat : Agent sur mesure / serveur MCP, option A (un serveur MCP, jusqu'à 6 outils), USD 1,200 forfaitaire, 7 jours ouvrables · Version du dossier : exemple, octobre 2026

    Ce qu'est cette page. Voici le dossier de remise que reçoit un client à la fin d'un mandat d'agent sur mesure / serveur MCP. Fernlight Supply Co., son système de commandes interne « Ledgerline », son personnel, son dépôt, ses hôtes, ses dossiers et tous les chiffres ci-dessous sont inventés pour montrer la forme et le niveau de détail ; rien de tout cela n'existe. Dans un vrai mandat, chaque ligne renvoie au dépôt du client, à son compte de test et à son journal d'exécution.

    Comment lire les preuves. Une « preuve » est ce que le client peut ouvrir et vérifier : un fichier dans son dépôt, le nom d'un test, une commande et sa sortie, ou une ligne du journal d'exécution. Rien ici n'a été exécuté sur un système réel.

    Le mandat en un paragraphe. Fernlight Supply Co. est un grossiste fictif de fournitures de jardinage et de pépinière, avec une petite équipe de bureau. Son personnel répond toute la journée à « est-ce en stock ? » et « où est ma commande ? » en cherchant dans Ledgerline, son système interne de commandes et d'inventaire doté d'une API REST. Fernlight a demandé un serveur MCP pour que Claude Code et Claude Desktop puissent consulter les produits, les stocks et les commandes, ajouter des notes internes à une commande et préparer des brouillons de réapprovisionnement qu'une personne approuve dans Ledgerline. Rien de ce que fait le serveur ne peut envoyer, payer ou supprimer.


    1. Portée écrite (jour 1, approuvée par écrit avant la construction)

    Approuvée par la personne-ressource désignée de Fernlight, la responsable des opérations (fictive), par courriel le jour 1. La construction n'a pas commencé avant ce courriel.

    Système : API Ledgerline, compte de test seulement (https://ledgerline-test.fernlight.example/api), avec une clé de service que Fernlight a créée dans son propre compte et peut révoquer. Aucun accès à la production.

    OutilPeut lirePeut modifierNe doit jamais
    find_productNom du produit, SKU, unité, prix de liste, nom du fournisseur, note du fournisseurRienModifier un produit, un prix ou un fournisseur
    get_stockQuantités en main, réservées et à recevoir par entrepôt pour un SKURienAjuster le stock ou le déplacer d'un entrepôt à l'autre
    list_low_stockLes SKU au seuil de réapprovisionnement ou en dessous, jusqu'à 50 par appelRienModifier un seuil de réapprovisionnement
    get_orderStatut, lignes, date d'expédition et note du client d'une commandeRienModifier, annuler ou rembourser une commande ; renvoyer des données de paiement (le bloc payment de l'API est retiré par le serveur)
    add_order_noteLa commande où il écritAjoute une note interne à une commande (invisible pour le client), 1,000 caractères au maximumModifier ou supprimer une note existante ; écrire une note visible par le client ; envoyer un courriel à qui que ce soit
    draft_reorderLe SKU et son fournisseurCrée un brouillon de bon de commande dans Ledgerline, 3 lignes au maximumSoumettre, approuver ou envoyer un bon de commande à un fournisseur ; payer quoi que ce soit

    Limites d'appels et de dépenses (appliquées dans le serveur, pas dans l'invite) :

    • 30 appels d'outil par minute et 500 par jour par jeton ; le 501e appel de la journée est refusé avec rate_limited.
    • add_order_note : 50 par jour. draft_reorder : 10 par jour, au plus 3 lignes et USD 2,500 de valeur estimée par brouillon (quantité × prix de liste ; la limite d'achat propre à Fernlight) ; un brouillon plus important est refusé.
    • Chaque écriture exige une idempotency_key ; une nouvelle tentative avec la même clé renvoie le premier résultat et n'écrit rien de nouveau.
    • L'utilisation du modèle passe par la propre clé d'API Anthropic de Fernlight ; le journal d'exécution indique les jetons, les USD et les secondes de chaque exécution. Fernlight a fixé une limite de dépenses mensuelle sur cette clé dans sa propre console.

    Points d'approbation. Deux éléments laissent une personne aux commandes :

    1. Un réapprovisionnement n'est jamais qu'un brouillon. Un acheteur de Fernlight l'ouvre dans Ledgerline, le modifie au besoin et le soumet au fournisseur à partir de là. Le serveur n'a aucun outil pour soumettre, approuver ou envoyer, et sa clé n'a pas la permission de le faire.
    2. Aucun outil n'envoie de message, ne paie, n'annule ni ne supprime. Toute action de ce genre reste une étape manuelle dans Ledgerline.

    Texte externe. Les notes des fournisseurs et des clients sont écrites par des personnes extérieures à l'équipe de Fernlight ; le serveur ne les renvoie donc que sous untrusted_supplier_note et untrusted_customer_note, et les descriptions des outils se terminent par UNTRUSTED. Les outils reçoivent des SKU et des numéros de commande, jamais des URL à récupérer.

    Portées (un jeton par usage) :

    PortéeOutilsRemise à
    catalog:readfind_product, get_stock, list_low_stockChaque jeton du personnel
    orders:readget_orderChaque jeton du personnel
    orders:noteadd_order_noteLes jetons du service à la clientèle
    po:draftdraft_reorderLe jeton de l'acheteur seulement

    Hors portée (selon l'offre) : un deuxième système, plus de 6 outils, une interface web, l'hébergement après la remise, le déploiement en production et toute action qui envoie, paie ou supprime sans une personne.


    2. Les outils

    Tirés de notre gabarit de départ testé (JSON-RPC sur HTTP, jeton porteur comparé en temps constant, outils limités par portée). Dépôt : fernlight-supply/ledgerline-mcp (fictif), livré dans l'organisation GitHub de Fernlight. Révisé dans la pull request #4 par une session de révision distincte, par rapport à la portée écrite.

    OutilPortéeEntréeRenvoieÉcrit ?Limite
    find_productcatalog:readquery (SKU ou mots, 2-80 caractères)Jusqu'à 10 produits : sku, name, unit, list_price_usd, supplier, untrusted_supplier_noteNonPartagée 30/min, 500/jour
    get_stockcatalog:readskuPar entrepôt : on_hand, reserved, incoming, next_delivery_dateNonPartagée
    list_low_stockcatalog:readwarehouse (facultatif), limit (1-50)Les SKU au seuil de réapprovisionnement ou en dessous, avec les quantitésNonPartagée
    get_orderorders:readorder_number (FS- et 6 chiffres)status, lines, ship_date, untrusted_customer_note ; jamais de données de paiementNonPartagée
    add_order_noteorders:noteorder_number, note (1-1,000 caractères), idempotency_keynote_id, created_at, replayed (vrai lors d'une nouvelle tentative)Oui, note interne50/jour
    draft_reorderpo:draftsku, lines (1-3 de sku + quantity), idempotency_keydraft_po_id, status: "draft", estimated_value_usd, lien vers le brouillon dans LedgerlineOui, brouillon seulement10/jour, USD 2,500 par brouillon

    Chaque schéma d'entrée a additionalProperties: false ; un argument que la portée ne nomme pas (par exemple status ou submit) est donc refusé, et non ignoré. Une entrée invalide revient comme une erreur d'outil (isError: true) avec un message clair, jamais comme un plantage.

    Journal d'exécution. Chaque appel d'outil écrit une ligne dans logs/calls.jsonl (heure, libellé du jeton, outil, résultat, millisecondes ; jamais les arguments ni le texte des notes). L'exécuteur de cas du guide d'exploitation ajoute une ligne par exécution dans logs/runs.jsonl avec les appels d'outil effectués, les jetons d'entrée et de sortie, les USD et les secondes.


    3. Résultats des tests (modèle simulé, aucune API payante appelée)

    Commande : npm test dans le dépôt (l'exécuteur de tests intégré de Node). L'API Ledgerline est remplacée par une fausse API locale avec des enregistrements de test, et le modèle par une simulation scriptée ; la suite s'exécute donc hors ligne et sans frais.

    Un test par outil, plus les vérifications propres au gabarit de départ :

    OutilTestRésultat
    find_product"returns matching products and puts the supplier note under untrusted_supplier_note"Réussi
    get_stock"returns quantities per warehouse for a known SKU and a tool error for an unknown one"Réussi
    list_low_stock"lists only SKUs at or below the reorder point, capped at the limit"Réussi
    get_order"returns status and lines, drops the payment block, marks the customer note untrusted"Réussi
    add_order_note"writes one internal note; a retry with the same idempotency key writes nothing new"Réussi
    draft_reorder"creates a draft only; refuses a 4th line, a draft over USD 2,500 and an extra submit argument"Réussi

    Résumé de la suite :

    $ npm test
    ...
    # tests 34
    # suites 9
    # pass 34
    # fail 0
    # cancelled 0
    # skipped 0
    # duration_ms 612
    

    Les 34 tests : les 6 tests d'outils ci-dessus, 12 tests de portée (chaque outil listé et appelable avec sa portée, non listé et refusé sans elle), 7 tests d'authentification et de protocole issus du gabarit de départ, 5 tests de limites (limite de débit, plafonds quotidiens, plafond de valeur des brouillons) et 4 tests du journal d'exécution (aucun argument ni texte de note journalisé ; jetons, USD et secondes présents dans runs.jsonl).

    Autres vérifications sur le commit fusionné :

    VérificationRésultat
    Lint (npm run lint)0 erreur
    Analyse des secrets dans les commits (gitleaks)Aucune fuite trouvée
    Session de révision distincte, par rapport à la portée écriteApprouvé ; chaque outil, limite et refus de la section 1 a un test

    4. Exécution sur le compte de test (jour 6)

    Chaque outil exécuté une fois sur un exemple réel dans le compte de test Ledgerline de Fernlight, par Claude Code connecté avec la commande du guide d'exploitation. Les enregistrements sont des enregistrements de test créés par Fernlight ; les chiffres sont inventés.

    ExécutionDemande dans Claude CodeAppels d'outilCe qui s'est passéJetons (entrée / sortie)USDSecondes
    1« Avons-nous le compost sans tourbe de 25 L ? »find_product2 correspondances ; SKU CMP-25-PF, note du fournisseur affichée et marquée non fiable4,180 / 2100.024.1
    2« Combien de CMP-25-PF dans chaque entrepôt ? »get_stockNord : 140 en main, 32 réservés ; Sud : 0, 200 à recevoir3,960 / 1800.013.4
    3« Qu'est-ce qui est bas dans l'entrepôt Sud ? »list_low_stock7 SKU au seuil de réapprovisionnement ou en dessous4,720 / 3900.025.0
    4« Où en est la commande FS-104233 ? »get_orderEmballée, expédiée demain ; aucune donnée de paiement renvoyée4,050 / 2400.023.8
    5« Note sur FS-104233 : le client a demandé une livraison le matin »add_order_noteNote interne ajoutée ; invisible sur la page de commande du client4,310 / 1600.024.6
    6« Prépare un réapprovisionnement de 300 CMP-25-PF pour Sud »get_stock, draft_reorderBrouillon de bon de commande DPO-0418 créé, statut brouillon, USD 1,740 estimé ; l'acheteur l'a vu dans la liste des brouillons de Ledgerline8,900 / 4200.047.9

    Action interdite, refus démontré. Demande dans Claude Code : « Soumets maintenant DPO-0418 au fournisseur. » Claude n'a aucun outil pour cela et le dit. Pour tester le serveur lui-même, le réviseur a ensuite appelé l'outil directement avec un argument que la portée interdit :

    tools/call draft_reorder {"sku":"CMP-25-PF","lines":[{"sku":"CMP-25-PF","quantity":300}],
                              "idempotency_key":"day6-forbidden-1","submit":true}
    → isError: true
      "Unknown argument: submit. This server creates draft purchase orders only;
       a person submits them in Ledgerline."
    

    Le même appel avec un jeton du service à la clientèle (sans la portée po:draft) a été refusé avec insufficient_scope, et draft_reorder n'apparaissait pas dans le tools/list de ce jeton. La liste des brouillons de Ledgerline ne montrait ni nouveau brouillon ni commande soumise après l'un ou l'autre appel.

    Corrections issues de l'exécution. Une seule : get_stock renvoyait une liste vide pour un SKU tapé en minuscules. Le serveur met maintenant les SKU en majuscules avant la recherche, et un cas a été ajouté au test de get_stock. Rien d'imprévu par la portée n'est survenu.

    Répétition de l'acceptation. Les cinq vérifications de la section 6 ont été exécutées sur le compte de test par notre session de révision le même jour : les cinq ont réussi.


    5. Guide d'exploitation (une page)

    Ce que c'est. Un serveur MCP qui permet à Claude Code ou à Claude Desktop de consulter les produits, les stocks et les commandes de Ledgerline, d'ajouter des notes internes aux commandes et de préparer des brouillons de réapprovisionnement. Il ne peut pas envoyer, payer, annuler ni supprimer.

    Le lancer (Node 20 ou plus récent, sur l'hôte que choisit Fernlight ; l'hébergement relève de Fernlight) :

    git clone [email protected]:fernlight-supply/ledgerline-mcp.git && cd ledgerline-mcp
    npm ci
    cp .env.example .env    # set LEDGERLINE_API_URL, LEDGERLINE_API_KEY, MCP_TOKENS_FILE
    node --env-file=.env src/index.js        # serves POST /mcp and GET /health on :8787
    

    Les secrets résident dans l'environnement de l'hôte ou dans le coffre à secrets de Fernlight, jamais dans le dépôt. MCP_TOKENS_FILE liste un jeton par personne avec son libellé et ses portées (section 1).

    Connecter Claude Code :

    claude mcp add --transport http ledgerline https://mcp.fernlight.example/mcp \
      --header "Authorization: Bearer $LEDGERLINE_MCP_TOKEN"
    

    Exécutez ensuite claude mcp list : ledgerline apparaît comme connecté, et /mcp dans Claude Code liste les outils que permettent les portées de votre jeton. Claude Desktop : ajoutez la même URL et le même en-tête comme connecteur personnalisé.

    Exécuter un cas avec le journal : npm run case -- "Where is order FS-104233?" exécute une demande avec votre clé d'API Anthropic (ANTHROPIC_API_KEY) et ajoute les appels d'outil, les jetons, les USD et les secondes dans logs/runs.jsonl.

    Entretien courant :

    • Ajouter une personne : ajoutez une ligne de jeton avec ses portées ; redémarrez le serveur.
    • Retirer une personne ou renouveler un jeton : supprimez ou modifiez sa ligne ; redémarrez.
    • Tout révoquer : révoquez la clé de service dans les paramètres de Ledgerline ; chaque outil renvoie alors une erreur.
    • Modifier une limite : modifiez config/limits.json (par minute, par jour, plafond de valeur des brouillons), exécutez npm test, redémarrez.
    • Vérification de l'état : pointez la surveillance de l'hôte vers GET /health.

    Si quelque chose semble anormal : lisez logs/calls.jsonl pour l'outil et le résultat, puis la ligne du cas dans logs/runs.jsonl. Une vérification d'acceptation qui échoue est corrigée dans les 5 jours ouvrables, sans frais.


    6. Liste de vérification d'acceptation (Fernlight l'exécute le jour de la remise)

    Sur le compte de test Ledgerline, avec le guide d'exploitation :

    1. La suite de tests réussit sans appel à une API payante (npm test : 34 pass, 0 fail).
    2. Chacun des 6 outils à portée limitée s'exécute une fois sur un exemple réel et fait ce que la section 1 indique.
    3. Une action interdite est refusée (par exemple : demandez à Claude de soumettre un brouillon de bon de commande, puis appelez draft_reorder avec "submit": true ; les deux sont refusés, aucune commande soumise dans Ledgerline).
    4. La ligne de chaque exécution dans logs/runs.jsonl indique les appels d'outil et les jetons, USD et secondes.
    5. Le serveur se connecte dans Claude Code ou Claude Desktop avec la commande claude mcp add ci-dessus et liste ses outils.

    Une vérification qui échoue est corrigée dans les 5 jours ouvrables, sans frais. L'acceptation est écrite (un courriel suffit) ; sans réponse dans les 5 jours ouvrables suivant la remise et si rien n'a échoué, la construction est acceptée. La deuxième facture (50%, USD 600) est émise à l'acceptation.


    7. Texte de remise du jalon Upwork

    Publié avec le travail remis pour le deuxième jalon (jour 7) :

    Jalon 2 : remise du serveur MCP Ledgerline (USD 600)

    Le serveur MCP est dans votre dépôt fernlight-supply/ledgerline-mcp (pull request #4, fusionnée par vous). Il comporte les 6 outils de la portée écrite que vous avez approuvée le jour 1, avec les limites d'appels et de dépenses et les deux points d'approbation dans le code du serveur.

    Pièce jointe : le dossier de remise (portée écrite, liste des outils avec leurs limites, résultats des tests, l'exécution du jour 6 sur le compte de test, le guide d'exploitation et la liste de vérification d'acceptation).

    • Tests : 34 réussis, 0 échec, avec le modèle simulé ; aucune API payante n'est appelée.
    • Exécution sur le compte de test : chaque outil exécuté une fois ; la demande de soumettre un bon de commande a été refusée, et aucune commande n'a été soumise.
    • Chaque exécution journalise ses appels d'outil, ses jetons, ses USD et ses secondes.

    Veuillez exécuter les 5 vérifications de la section 6 du dossier sur votre compte de test. Si une vérification échoue, nous la corrigeons dans les 5 jours ouvrables, sans frais. Lorsqu'elles réussissent, approuvez ce jalon. L'appel de présentation est fixé au moment convenu.


    Exemple préparé à partir de notre trousse de livraison et de notre fiche d'offre pour l'agent sur mesure / serveur MCP. L'entreprise, le système, les personnes, le dépôt, les hôtes et les chiffres sont fictifs.