MCPPythonIntegraties

Een MCP-server bouwen met één eenvoudige voorraadtool

5 november 20265 min leestijdDoor Albert Barth
Een technische verbinding wordt zorgvuldig aan een proefopstelling bevestigd
Gegenereerde conceptillustratie bij de werkwijze; geen productscreenshot, praktijkmeting of klantfoto.

Een MCP-server bouwen wordt overzichtelijk zodra je één vraag kiest. In dit voorbeeld: hoeveel schriften liggen er volgens een fictieve voorraadlijst? De server krijgt één tool, leest twee zelfbedachte artikelen en kan geen voorraad wijzigen. Er is geen database, account of externe API nodig.

Het codevoorbeeld hieronder is niet uitgevoerd. Ik heb voor deze uitleg geen SDK geïnstalleerd of server gestart. Je krijgt wel de volledige voorbeeldcode en een controleplan waarmee je straks onderscheid maakt tussen “het programma start” en “de MCP-tool geeft het juiste antwoord”.

Spreek het gedrag af voordat je code toevoegt

Onze oefenwinkel verkoopt A5-schriften en blauwe pennen. De voorraadlijst bevat voor de schriften twaalf stuks en voor de pennen nul. De tool krijgt een artikelcode en geeft de code, naam en hoeveelheid terug. Nul betekent uitverkocht. Een onbekende code betekent dat het artikel niet in deze lijst bestaat. Die antwoorden mogen niet door elkaar lopen.

Dit is een bewuste kleine grens. Een functie die “iets met voorraad” doet, kan later ook inkopen of reserveren gaan betekenen. Met alleen lees_voorraad kun je precies beoordelen wat een client vraagt. De naam beschrijft een handeling die je ook buiten AI begrijpt. Het model mag een code kiezen; het programma bepaalt welke codes werkelijk bestaan.

Bewaar de artikelcode ook bij een vervolgvraag. “En hoeveel zijn er daarvan?” kan voor een mens duidelijk lijken, maar een toolaanroep moet nog steeds naar het bedoelde artikel verwijzen. Controleer daarom zowel de invoer als de teruggegeven code. Zo kun je een verkeerde modelkeuze onderscheiden van een fout in je voorraadfunctie.

Gebruik de huidige officiële SDK

De officiële MCP Python-SDK documenteert momenteel de stabiele v2-lijn, met Python 3.11 of hoger. Daar heet de serverklasse MCPServer. Oudere v1-voorbeelden gebruiken andere imports; meng die niet in hetzelfde voorbeeld.

Als je dit zelf uitvoert, maak dan een aparte oefenmap en een eigen virtuele omgeving. Installeer daarin volgens de SDK-documentatie mcp[cli]. Dat is een beschreven voorbereidingsstap, hier niet uitgevoerd. Leg bij een herhaalbare oefening de werkelijk gebruikte dependencyversies vast. Je hebt voor deze lokale voorraadlijst geen Anthropic-sleutel of winkelaccount nodig.

Maak op macOS of Linux eerst de virtuele omgeving en installeer de SDK in je aparte oefenmap. Bewaar daarna server.py uit de volgende sectie. Start de Inspector pas na het opslaan van dat bestand; daarvoor heb je ook Node.js met npx nodig:

python3 -m venv .venv
.venv/bin/python -m pip install "mcp[cli]"

Het volledige bestand server.py

Bewaar onderstaande code als server.py in de oefenmap. De voorraad staat bewust in hetzelfde bestand, zodat je de verwachte antwoorden naast de code kunt controleren.

from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
from mcp.types import ToolAnnotations

mcp = MCPServer("Oefenvoorraad")

VOORRAAD = {
    "SCHRIFT-A5": {"naam": "Schrift A5", "aantal": 12},
    "PEN-BLAUW": {"naam": "Blauwe pen", "aantal": 0},
}

@mcp.tool(
    annotations=ToolAnnotations(
        read_only_hint=True,
        open_world_hint=False,
    )
)
def lees_voorraad(artikelcode: str) -> dict:
    """Lees fictieve voorraad voor precies één artikelcode."""
    code = artikelcode.strip().upper()
    if not code:
        raise ToolError("Vul een artikelcode in.")
    if code not in VOORRAAD:
        raise ToolError("Artikelcode niet gevonden in de oefenlijst.")
    return {"artikelcode": code, **VOORRAAD[code]}

if __name__ == "__main__":
    mcp.run()

De functie verandert de lijst nergens. De read-only-annotatie vertelt een client wat de bedoeling is, maar dwingt die beperking niet af. Hier kun je het gedrag zelf zien: er staat alleen een lookup en een nieuw antwoordobject. Er wordt niets opgeslagen of verzonden naar een leverancier.

Wat de client over je tool te zien krijgt

De SDK maakt op basis van de functienaam, docstring en typehint de toolbeschrijving en het invoerschema. De SDK-documentatie over tools legt dat contract uit. In ons voorbeeld is artikelcode een verplichte string. Een relevante schemavorm is:

{
  "type": "object",
  "properties": {"artikelcode": {"type": "string"}},
  "required": ["artikelcode"]
}

Dit toont de kern van het invoercontract, niet een opgenomen antwoord van een draaiende server. Het schema voorkomt ook niet dat een bestaande code verkeerd wordt gekozen. Daarom blijft de gevraagde artikelcode in het resultaat staan. Wie naar schriften vraagt en een antwoord over pennen krijgt, kan de verwisseling herkennen.

Een onbekend artikel is een fout, geen lege voorraad

Volgens de officiële foutafhandeling maakt ToolError een herkenbaar toolfoutresultaat. Dat past hier bij een ontbrekende artikelcode. Een client kan de vraag verbeteren. Een tekst zoals “artikel niet gevonden” als gewoon succesvol resultaat teruggeven zou het onderscheid vertroebelen.

Pas die gedachte straks ook toe bij een echte gegevensbron. Is de voorraadservice onbereikbaar, dan weet je de voorraad niet. Maak daar geen nul van. Anders kan een technisch probleem ongemerkt een inkoopadvies worden. Laat in zo'n situatie een duidelijke fout terugkomen, zonder geheime configuratie of interne details in het modelantwoord te stoppen.

Starten en controleren zijn aparte stappen

Nu server.py is opgeslagen, kun je vanuit dezelfde oefenmap de Inspector starten:

.venv/bin/mcp dev server.py

Het entrypoint staat onder de main-guard, zodat importeren het programma niet meteen laat draaien. mcp.run() gebruikt standaard stdio. De documentatie over starten en transport beschrijft hoe een host het proces met zijn invoer- en uitvoerkanalen verbindt. Een stil wachtend proces is op zichzelf nog geen geslaagde MCP-test.

Gebruik in je eigen geïsoleerde omgeving de SDK-ontwikkelroute met de MCP Inspector. Controleer eerst of lees_voorraad zichtbaar is. Roep daarna SCHRIFT-A5, PEN-BLAUW, een onbekende code en een lege string aan. Verwacht respectievelijk twaalf, nul en twee fouten. Probeer ook schrift-a5 : onze normalisatie moet diezelfde twaalf opleveren. Vergelijk de waarden rechtstreeks met de lijst.

Breid pas uit na een aantoonbaar resultaat

Een volgende stap kan een read-only bron met oefengegevens zijn. Houd het invoercontract klein en voeg een peilmoment toe als voorraad kan veranderen. Zodra meerdere gebruikers of echte bedrijfsgegevens meedoen, komen toegangscontrole, logging en beheer erbij. Die worden niet opgelost door de server op een andere poort te starten.

Voor de verbinding met de CLI kun je verder met MCP koppelen aan Claude Code. De gids over AI coding helpt je daarna een volgende bouwstap afbakenen. Bewaar bij je eigen proef de echte resultaten, inclusief fouten. Daar kun je op voortbouwen.

Zelf aan de slag met Vibe Coding?

Leer hoe je met AI-tools werkende apps bouwt. Onze praktische cursussen nemen je stap voor stap mee.

Blijf op de hoogte

Ontvang maandelijks de nieuwste tips over vibe coding, AI tools en exclusieve content.

In welke taal wil je door ons benaderd worden?