Voor bedrijven Voor ondernemingen Oplossingen Apps Prijzen Ontwikkelaars Blog Documentatie Start een werkruimte
Blog / MCP voor bedrijfssoftware

MCP voor ERP: een praktische gids

Een gids voor bouwers. Het gaat ervan uit dat je de specificatie of het bijbehorende artikel over wat een ERP MCP-server is, hebt gelezen, en het concentreert zich op de beslissingen die de specificatie aan jou overlaat: welke acties te publiceren, hoe ze te noemen, hoe de identiteit van de aanroeper elke aanroep bereikt, en wat een schrijfactie moet doen wanneer een model het opnieuw probeert. De voorbeeldtool is een echte.

7 minuten lezenBijgewerkt 4 september 2026Sois engineering, het team dat het platform bouwt

Een werkbank in een kleine ingenieursstudio: gelabelde onderdelenladen, een afgedrukte schema onder een clip, een gesloten laptop en een soldeerlamp uitgeschakeld.
Korte antwoord

Het bouwen van MCP voor ERP komt neer op vier beslissingen. Exposeer transacties, niet tabellen: een tool moet iets zijn dat een persoon in het systeem kan doen, zoals een factuur aanmaken of een betaling registreren, met de bedrijfsregels erin. Benoem en beschrijf elke tool voor het model dat de lijst zal lezen, met de beperkingen die het moet respecteren in de beschrijving in plaats van in documentatie die het nooit zal zien. Laat de OAuth-identiteit bij elk verzoek beslissen welke tools worden vermeld en of elke oproep wordt uitgevoerd. En ontwerp elke schrijfactie zodat een herhaling, een weigering of een vraag veilig is, omdat een model alledrie zal produceren.

Het transport, de ontdekking en de aanmelding zijn gespecificeerd en elke SDK handelt deze af. De waarde van de server ligt in die vier beslissingen, en een zakelijk systeem dat ze goed krijgt, is bedienbaar door Claude, ChatGPT of een andere client zonder dat die client er iets van af weet.

Begin bij de transactie, niet bij de tabel

De eerste instinct bij het blootstellen van een ERP is om een tool per tabel te genereren met aanmaken, lezen, bijwerken en verwijderen op elk. Dit produceert een grote, uniforme lijst die een model slecht kan verwerken, omdat de bedrijfsregel dat een factuur een belastingtarief op elke regel nodig heeft, of dat voorraad niet kan worden verzonden voordat deze is gereserveerd, nergens leeft waar het model het kan zien. Exposeer in plaats daarvan de acties. Een nuttige test is of een persoon de tool kan beschrijven als iets dat ze vandaag hebben gedaan: een factuur opgemaakt, een betaling geregistreerd, een deal verplaatst, een achtervolging uitgesteld. Elk van deze draagt zijn regels, valideert zijn invoer en retourneert een leesbaar resultaat.

Voeg naast de acties een klein aantal samenvattende tools toe die de vragen beantwoorden die een model stelt voordat het handelt. Eén oproep die het profiel, de gezondheid, open items en recente geschiedenis van een account retourneert, bespaart het model vier oproepen en enkele duizenden tokens aan context, en het maakt de volgende actie beter geïnformeerd. Sois noemt deze examiner tools; examineerContact en krijg boekhoudsamenvatting er zijn er twee. Houd ook de totale oppervlakte in het oog: Claude Code beperkt de output van een server per oproep standaard, en zowel Claude als OpenAI bieden uitgestelde laadtijd of toolzoekfunctie voor grote lijsten, zodat een server met enkele honderden tools deze in een deterministische volgorde moet retourneren (de specificatie vraagt hierom zodat klanten kunnen cachen) en moet filteren op rol voordat deze wordt weergegeven.

Tools benoemen zodat een model de juiste kiest

De specificatie beperkt namen licht: één tot 128 tekens, letters, cijfers, underscore, koppelteken en punt, hoofdlettergevoelig, uniek binnen de server. Alles daarbuiten is conventie, en de conventie die werkt is een werkwoord gevolgd door het zakelijke zelfstandig naamwoord in een consistente schrijfwijze, met dezelfde werkwoorden die overal dezelfde dingen betekenen. Een model dat kiest tussen zoekFacturen, krijgFactuur en maakFactuur kiest tussen een lijst, één record en een schrijfopdracht, en leert dat patroon eenmaal voor de hele server.

ZwakBeterWaarom
factuurmaakFactuurEen zelfstandig naamwoord alleen zegt niet of het leest of schrijft; een cliënt kan het niet annoteren en een model kan het niet rangschikken ten opzichte van zijn broers en zussen.
factuurAanmakenV2EindmaakFactuurVersie en status horen in de server, niet in de naam. Namen die veranderen breken gecachte toollijsten en promptcaches.
boekhouding doenregistreerBetaling, stuurFactuurherinneringenEen catch-all met een modusargument verbergt de transactie. Eén naam per transactie stelt de klant in staat om bevestiging per tool toe te passen.
krijg_factuur en krijgContact gemengdÉén geval doorheenCliënten samenvoegen met prefixnamen per server; consistentie binnen een server is waar het model op vertrouwt.

De beschrijving bevat de rest: wanneer de tool te gebruiken, wanneer niet, en welke regels het model moet respecteren voordat het deze aanroept.

Beschrijvingen worden gelezen door een model dat onder druk staat om te handelen, dus schrijf ze als instructies. Geef in de eerste zin aan wat de tool doet, gevolgd door de voorwaarden. Als een zuster-tool de juiste keuze is voor een nabijgelegen verzoek, noem deze dan bij naam. Als een veld moet worden ingesteld voor het resultaat om correct te zijn, zeg dat dan in HOOFDLETTERS als het moet; de factuurtool van Sois vertelt het model dat het btw-tarief op elke regel moet worden ingesteld en dat de exacte tarieven komen van , omdat de beschrijving van de factuurtool zegt dat het btw-tarief op elke regel moet worden ingesteld en dat de exacte tarieven van de werkruimte uit die aanroep komen; een model leest beschrijvingen, en een goede beschrijving voorkomt dat een factuur zonder btw wordt verzonden. Het maakte de factuur aan met één regel, hoeveelheid twaalf, tegen het tarief dat op de voorwaarden van de klant stond, en ontving de identificatie en het nummer van de nieuwe factuur in het resultaat. Het gaf die identificatie door aan, because an invoice with no VAT is a worse failure than a refused call. Include one example call. Everything the model needs to call the tool correctly should be in the tool, because it will never open your documentation.

Een voorbeeld van een tooldefinitie

Dit is een Sois factuurtool zoals een klant deze ontvangt van tools/lijst, beperkt tot de velden die ertoe doen, met annotaties en een uitvoerschema toegevoegd in de vorm die de huidige specificatie definieert. Het toont het patroon: een werkwoord-zelfstandig naamwoord naam, een instructieve beschrijving, een schema waarvan de eigenschapsbeschrijvingen de foutpreventie van het model doen, en hints die een klant kan gebruiken om te beslissen of te bevestigen.

{
  "name": "createInvoice",
  "title": "Create invoice",
  "description": "Create a new invoice of any type and return the draft with its auto-generated number. TAX: set tax_rate on each line (for example 20 for 20% VAT); call listTaxTypes for this workspace's exact rates. If the user says 'plus VAT' you MUST set tax_rate or the invoice goes out with no VAT. To email the result use sendInvoice. Example: createInvoice({ type: \"sales_invoice\", contact_id: \"uuid\", currency: \"GBP\", lines: [{ description: \"Consulting\", quantity: 10, unit_price: 150 }] })",
  "inputSchema": {
    "type": "object",
    "properties": {
      "type": { "type": "string", "description": "sales_invoice, purchase_invoice, sales_credit_note or purchase_credit_note" },
      "contact_id": { "type": "string", "description": "Contact UUID (bill-to for sales, bill-from for purchases)" },
      "currency": { "type": "string", "description": "ISO code, for example GBP. Uses the workspace default if omitted" },
      "invoice_date": { "type": "string", "description": "ISO date. Defaults to today" },
      "reference": { "type": "string" },
      "lines": {
        "type": "array",
        "description": "Line items. Every line MUST carry the numeric unit_price the user asked for",
        "items": {
          "type": "object",
          "properties": {
            "description": { "type": "string" },
            "quantity": { "type": "number", "description": "Defaults to 1" },
            "unit_price": { "type": "number", "description": "NUMBER only: no currency symbols, no thousands separators. Use the exact amount stated; never guess or round" },
            "tax_rate": { "type": "number" },
            "discount_percent": { "type": "number" }
          },
          "required": ["description", "unit_price"]
        }
      }
    },
    "required": ["type"]
  },
  "outputSchema": {
    "type": "object",
    "properties": {
      "invoice_id": { "type": "string" },
      "number": { "type": "string" },
      "status": { "type": "string" },
      "total": { "type": "number" }
    },
    "required": ["invoice_id", "number", "status"]
  },
  "annotations": {
    "readOnlyHint": false,
    "destructiveHint": false,
    "idempotentHint": false,
    "openWorldHint": false
  }
}

De annotaties zeggen: dit schrijft, het voegt alleen toe (een concept), het twee keer aanroepen maakt twee concepten, en het raakt niets buiten het systeem aan. Klanten moeten annotaties als onbetrouwbaar beschouwen tenzij de server vertrouwd is, dus ze zijn aanwijzingen voor bevestigingsgedrag, geen vervanging voor de eigen controles van de server.

Drie keuzes in die definitie zijn opzettelijk. Het resultaat retourneert een identificatie die het model moet meenemen naar , dat de PDF bijvoegt en deze per e-mail verzendt, en uiteindelijk aan en registreerBetaling, which is the specification's recommended way to relate calls now that servers hold no session state. The tool creates a draft, not a posted invoice, so the write is additive and a person or a separate approval tool finalises it. And the output schema means an integration can read the number and total as data while the model reads the same result as text.

Tools afstemmen op de gebruiker

Over HTTP arriveert de oproeper met een OAuth-toegangstoken dat aan uw server is gebonden, en dat token identificeert een persoon. De specificatie staat toe dat het resultaat van tools/lijst kan variëren op basis van de referenties in het verzoek, dus de eerste afwegingsbeslissing is om de lijst te filteren op basis van de rol van die persoon voordat deze wordt geretourneerd: een magazijngebruiker ontvangt geen factuur goedkeurenDe lijst mag niet variëren per verbinding of als een neveneffect van andere aanroepen, alleen door autorisatie, wat het cachebaar maakt.

De tweede beslissing is om opnieuw te controleren bij uitvoering. Een klant kan elke oproep doen die hij wil, en een model kan worden gemanipuleerd door tekst in een toolresultaat om er een te proberen. Los de gebruiker op van het token bij elke oproep, controleer de toestemming die de tool vereist, en weiger met een fout bij de uitvoering van de tool die het model kan lezen. Houd OAuth-scopes grof (Sois geeft lees-, schrijf- en offline-scopes uit) en laat de eigen rollen van de ERP de fijnmazige grens zijn, omdat die rollen al bestaan, al worden onderhouden en al iets betekenen voor de business. Ken elke oproep toe aan de persoon in het logboek met zijn argumenten en resultaat, zodat het werk van een agent precies kan worden beoordeeld zoals dat van een persoon.

  1. Token arriveertValideer de handtekening en dat het publiek deze server is, zoals RFC 8707 vereist; wijs alles anders af met 401.
  2. Los de persoon opKoppel het token aan een gebruiker in de werkruimte en laad hun rol en geïnstalleerde apps.
  3. Filter de lijstGeef alleen de tools terug die deze rol mag gebruiken, in een stabiele volgorde, van tools/lijst.
  4. Controleer de oproepControleer op tools/oproep opnieuw de toestemming en weiger met isError als deze ontbreekt; niets wordt uitgevoerd.
  5. Voer uit en logVoer de transactie uit, meet deze als jouw eigen agent de redenering heeft gedaan, en schrijf de oproep naar het auditlog onder die persoon.

Schrijfacties afhandelen

Een model dat een ambigu resultaat ontvangt, zal opnieuw aanroepen, en een model dat een fout ontvangt, zal proberen een gecorrigeerde invoer. Ontwerp daarvoor. Schrijfacties die creëren, moeten een handle retourneren en, waar mogelijk, een idempotentie-sleutel of een natuurlijke sleutel accepteren, zodat een herhaling wordt gedetecteerd. Schrijfacties die de status wijzigen, moeten expliciet zijn over de overgang die ze uitvoeren en onmogelijke overgangen weigeren met een leesbare reden: het registreren van een betaling tegen een geannuleerde factuur is een isFout resultaat dat dit aangeeft, niet een stille no-op en niet een stacktrace. Laat nooit een gedeeltelijke schrijfactie achter; als een multi-stap tool niet kan worden voltooid, maak dan ongedaan en rapporteer.

  • Geef de voorkeur aan concepten en goedkeuringen. Maak creatie additief (een concept) en geef finalisatie zijn eigen tool met zijn eigen toestemming, zodat de destructieve stap degene is die een klant bevestigt en een rol controleert.
  • Markeer destructieve tools. Stel in destructiveHint over annuleringen en verwijderingen en vermeld dit in de beschrijving; Claude en ChatGPT gebruiken dergelijke signalen bij het beslissen om te vragen voordat ze bellen.
  • Vraag in plaats van te raden. Wanneer een oproep een beslissing vereist die de tool niet kan maken, geef dan een resultaat dat invoer vereist met een verzoek om informatie; de klant stelt de vraag aan de persoon en probeert de oproep opnieuw met het antwoord.
  • Beperk de impact. Beperk de snelheid per verbinding, stel een limiet in voor uitgaven per integratie waar uw eigen agent redeneringen maakt, en valideer elke invoer server-side ongeacht het schema, omdat het schema advies is voor het model, niet handhaving.

De interface testen met een echte klant

De MCP-inspecteur zal uitoefenen tools/lijst en tools/oproep en doorloop de OAuth-stroom. De echte test is een model. Verbind Claude als een aangepaste connector, of ChatGPT in ontwikkelaarsmodus, log in als een gebruiker met een beperkte rol, en vraag om één routinematig resultaat dat drie of vier tools nodig heeft. Kijk welke tools het kiest en waarom; een verkeerde keuze is bijna altijd een beschrijvingsprobleem. Log vervolgens in als een gebruiker zonder een van de machtigingen en bevestig dat de uitvoering stopt bij de juiste oproep met een reden die het model herhaalt.

Dit is hoe de Sois-werkruimte server is gebouwd en gecontroleerd: transacties als tools, instructieve beschrijvingen, een rol-gefilterde lijst, een tweede controle op elke oproep, concepten voor goedkeuringen, en een log die een persoon kan lezen. Ontwikkelaars die apps voor de marketplace bouwen, publiceren tools in dezelfde lijst onder dezelfde regels, zodat een app door elke agent kan worden bediend op het moment dat deze is geïnstalleerd. Het patroon is niet specifiek voor één product; elke ERP die het aanneemt, wordt iets dat een agent kan uitvoeren.

Vragen die mensen stellen

Hoeveel tools moet een ERP MCP-server blootstellen?

Zoveel als er transacties zijn die het waard zijn om te automatiseren, gefilterd per gebruiker zodat elke beller een werkset ziet. Enkele honderden is normaal voor een volledig systeem; wat belangrijk is, is dat de lijst stabiel is, gefilterd op rol, en georganiseerd is met consistente werkwoorden zodat het model kandidaten kan rangschikken.

Moet ik OAuth-scopes gebruiken voor gedetailleerde machtigingen?

Gebruik grove scopes voor de verbinding en de eigen rollen van de ERP voor de fijnmazige grens, gecontroleerd bij elke oproep. Rollen bestaan al en worden onderhouden door het bedrijf; een parallelle scope-schema zou daarvan afwijken.

Hoe moet een schrijfoperatie zich gedragen als het model deze twee keer aanroept?

Detecteer de herhaling ofwel via een idempotentie of natuurlijke sleutel en geef het bestaande record terug, of maak de schrijfoperatie additief en duidelijk gerapporteerd zodat de duplicaat zichtbaar is. Faail nooit stilletjes, en laat nooit een gedeeltelijke schrijfoperatie achter.

Worden toolannotaties afgedwongen door de cliënt?

Nee. Het zijn aanwijzingen, en de specificatie zegt tegen cliënten dat ze deze als onbetrouwbaar moeten beschouwen tenzij de server vertrouwd is. Cliënten gebruiken ze om het bevestigingsgedrag te kiezen; de eigen toestemming en validatiecontroles van de server zijn wat schade voorkomt.

Bronnen
  1. Model Context Protocol specificatie (2026-07-28): tools toolnamen, schema's, annotaties, gestructureerde resultaten, foutafhandeling en de richtlijnen voor stateful-handle
  2. Model Context Protocol specificatie: autorisatie token audience validatie, scope-uitdagingen en het autorisatiemodel per verzoek
  3. OpenAI Apps SDK: bouw een MCP-server hoe ChatGPT readOnlyHint, destructiveHint en openWorldHint gebruikt voor bevestigingsgedrag
  4. Sois documentatie: de werkruimte MCP-server de toolreferentie waar het voorbeeld uit is getrokken, rolfiltering, limieten en foutcodes

Dit artikel wordt herzien wanneer de producten die het beschrijft veranderen. Volgende geplande herziening: 4 december 2026.

Start

Bouw op Sois.

Verbind je eigen agent met de bouwtools, beschrijf de app, valideer deze, publiceer het en verdien aan het gebruik.

  • Gratis om te beginnen
  • Breng uw eigen agent mee
  • Geen vendor lock-in