Uw eerste MCP-server bouwen
Gebruik de Python MCP SDK om een server te maken die resources, tools en prompts beschikbaar stelt, en verbind deze vervolgens met Claude Desktop om de volledige werking te bekijken.
Uw eerste MCP-server bouwen is een gratis AI Engineering Academy-les op CoddyKit. Dit is les 2 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject AI Engineering Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus AI Engineering Academy bevat in totaal 4 lessen.
Een project instellen voor een MCP-server
Voor het bouwen van een MCP-server in Python heb je de mcp-SDK en een Python-omgeving nodig. Je maakt een server die via stdio verbinding kan maken met Claude Desktop of een andere MCP-client. Begin met het installeren van het pakket en het maken van je serverbestand.
# Create a project directory
# mkdir my_mcp_server && cd my_mcp_server
# Create a virtual environment
# python -m venv venv && source venv/bin/activate
# Install the MCP SDK
# pip install mcp httpx
# Project structure:
# my_mcp_server/
# server.py <- Your MCP server
# requirements.txt
# README.mdHet serverobject maken
Importeer vanuit het pakket mcp en maak een Server-instantie met de naam van je server. De naam wordt aan clients getoond in hun lijst met MCP-servers — kies een duidelijke naam. Het serverobject is het toegangspunt voor het registreren van al je tools, resources en prompts.
# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types
import asyncio
import httpx
# Create the server — name is shown in Claude Desktop
app = Server('weather-server')
# --- Tool registrations go here ---
# Entry point
if __name__ == '__main__':
asyncio.run(stdio_server(app))Tools registreren met @app.list_tools()
De decorator @app.list_tools() registreert een handler die de lijst met beschikbare tools terugstuurt wanneer de client daarom vraagt. Elke tool wordt gedefinieerd met een naam, een beschrijving en een inputSchema — het JSON Schema waarin de parameters worden beschreven. De client stuurt deze lijst naar de LLM, zodat die weet welke tools hij kan aanroepen.
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name='get_weather',
description='Get current weather for a city. Use when the user asks about weather in a specific location.',
inputSchema={
'type': 'object',
'properties': {
'city': {
'type': 'string',
'description': 'City name, e.g. London or New York'
},
'units': {
'type': 'string',
'enum': ['metric', 'imperial'],
'description': 'Temperature units. Default is metric.'
}
},
'required': ['city']
}
),
types.Tool(
name='list_cities',
description='Return a list of major cities the user can query weather for.',
inputSchema={'type': 'object', 'properties': {}, 'required': []}
)
]Tooluitvoering implementeren
De decorator @app.call_tool() handelt de uitvoering van tools af. Wanneer de client een tool aanroept, ontvangt deze handler de naam en argumenten van de tool. Voer de juiste logica uit en retourneer een lijst met TextContent-objecten die de resultaattekst bevatten.
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == 'list_cities':
cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney']
return [types.TextContent(type='text', text=', '.join(cities))]
if name == 'get_weather':
city = arguments['city']
units = arguments.get('units', 'metric')
unit_symbol = 'C' if units == 'metric' else 'F'
# Call real weather API (stub here)
async with httpx.AsyncClient() as client:
# Replace with actual API call
result = f'{city}: 18{chr(176)}{unit_symbol}, partly cloudy, humidity 65%'
return [types.TextContent(type='text', text=result)]
raise ValueError(f'Unknown tool: {name}')Resources beschikbaar maken
Resources zijn statische of dynamische gegevens die het model kan lezen — zie ze als bestanden. Definieer resource-URI's en implementeer een resourcelezer. Resources verschijnen in de client als items waarnaar de AI kan verwijzen, wat handig is voor configuratie, documentatie of regelmatig gebruikte momentopnamen van gegevens.
@app.list_resources()
async def list_resources() -> list[types.Resource]:
return [
types.Resource(
uri='weather://supported-cities',
name='Supported Cities List',
description='Complete list of cities available in this weather server.',
mimeType='text/plain'
)
]
@app.read_resource()
async def read_resource(uri: str) -> str:
if uri == 'weather://supported-cities':
cities = ['London', 'New York', 'Tokyo', 'Paris', 'Sydney', 'Dubai', 'Singapore']
return '\n'.join(cities)
raise ValueError(f'Unknown resource URI: {uri}')Promptsjablonen registreren
Prompts zijn herbruikbare berichtsjablonen die clients aan gebruikers tonen als slashopdrachten of snelle acties. Ze accepteren parameters en retourneren een lijst met berichten die de initiële context voor een gesprek vormen. Prompts zijn ideaal om complexe instructies vast te leggen die gebruikers met één opdracht kunnen activeren.
@app.list_prompts()
async def list_prompts() -> list[types.Prompt]:
return [
types.Prompt(
name='weather-report',
description='Generate a formatted weather report for a city.',
arguments=[
types.PromptArgument(name='city', description='City name', required=True)
]
)
]
@app.get_prompt()
async def get_prompt(name: str, arguments: dict) -> types.GetPromptResult:
if name == 'weather-report':
city = arguments.get('city', 'London')
return types.GetPromptResult(
description=f'Weather report for {city}',
messages=[
types.PromptMessage(
role='user',
content=types.TextContent(
type='text',
text=f'Use the get_weather tool to look up {city} and give me a detailed weather report including what clothing I should wear.'
)
)
]
)
raise ValueError(f'Unknown prompt: {name}')Verbinding maken met Claude Desktop
Als je je MCP-server met Claude Desktop wilt gebruiken, voeg je deze toe aan het configuratiebestand van Claude Desktop. Op macOS staat dit bestand op ~/Library/Application Support/Claude/claude_desktop_config.json. Geef de opdracht op waarmee je server wordt gestart en alle omgevingsvariabelen die deze nodig heeft.
# ~/Library/Application Support/Claude/claude_desktop_config.json
# Add this JSON configuration:
# {
# "mcpServers": {
# "weather-server": {
# "command": "/path/to/venv/bin/python",
# "args": ["/path/to/my_mcp_server/server.py"],
# "env": {
# "WEATHER_API_KEY": "your_api_key_here"
# }
# }
# }
# }
# After saving, restart Claude Desktop.
# Your server's tools will appear in Claude's tool list.Je server testen met de MCP CLI
Voordat je verbinding maakt met Claude Desktop, test je je server met de MCP-inspector of CLI-tools. Met de opdracht mcp dev start je je server en open je een inspector in de browser waarin je tools handmatig kunt aanroepen en onbewerkte protocolberichten kunt bekijken. Zo kun je problemen eenvoudig opsporen.
# Install the MCP development tools
# pip install 'mcp[cli]'
# Start the inspector with your server
# mcp dev server.py
# The inspector opens at http://localhost:5173
# You can:
# - See all registered tools and their schemas
# - Call tools with custom arguments
# - Browse available resources
# - Test prompt templates
# - View the full JSON-RPC message exchangeFoutafhandeling in MCP-servers
MCP-servers mogen nooit crashen door ongeldige invoer. Plaats alle uitvoering van tools in try/except en retourneer foutmeldingen als TextContent in plaats van uitzonderingen op te werpen. Log fatale fouten, zoals configuratieproblemen en ontbrekende API-sleutels, bij het opstarten en werp ze op voordat de server zijn hoofdlus binnengaat.
@app.call_tool()
async def call_tool_safe(name: str, arguments: dict) -> list[types.TextContent]:
try:
if name == 'get_weather':
city = arguments.get('city')
if not city:
return [types.TextContent(type='text', text='Error: city argument is required.')]
result = await fetch_weather(city, arguments.get('units', 'metric'))
return [types.TextContent(type='text', text=result)]
raise ValueError(f'Unknown tool: {name}')
except httpx.TimeoutException:
return [types.TextContent(type='text', text='Error: Weather API timed out. Try again.')]
except Exception as e:
return [types.TextContent(type='text', text=f'Error: {str(e)}')]Logboekregistratie voor MCP-servers
Omdat MCP-servers via stdio met de client communiceren, verstoren printopdrachten het protocol. Gebruik voor logboekregistratie altijd stderr — dit gaat naar een aparte gegevensstroom die de uitwisseling van MCP-berichten niet verstoort. Configureer de module logging van Python zodat deze naar stderr schrijft.
import logging
import sys
# Configure logging to stderr (NOT stdout — that's the MCP channel)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
stream=sys.stderr
)
logger = logging.getLogger('weather-server')
# In your tool handler:
# logger.info(f'Getting weather for {city}')
# logger.error(f'API call failed: {e}')
# Claude Desktop captures stderr to a log file:
# ~/Library/Logs/Claude/mcp-server-weather-server.logJe MCP-server verpakken
Deel je MCP-server door deze te verpakken met een pyproject.toml en te publiceren op PyPI, of distribueer deze als Docker-container voor teams. Gebruik omgevingsvariabelen voor alle geheimen — API-sleutels en database-URL's — zodat de serverconfiguratie gescheiden blijft van de code. Documenteer de vereiste omgevingsvariabelen en voorbeeldconfiguratie in een duidelijke README.
Korte controle
Test je begrip van het bouwen van een MCP-server in Python.
Lesoverzicht
In deze les heb je geleerd: MCP-servers stellen hulpmiddelen beschikbaar via de decoratoren @app.list_tools() en @app.call_tool(), bronnen en prompts breiden de server uit met leesbare gegevens en herbruikbare sjablonen, en logboekregistratie moet stderr gebruiken om te voorkomen dat het stdio-MCP-protocolkanaal wordt beschadigd. Vervolgens verbinden we een MCP-server met een database en stellen we dynamische bronnen beschikbaar met paginering.
Leer Python met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 30
- Lessen
- 120
Veelgestelde vragen
Is de les “Uw eerste MCP-server bouwen” gratis?
Ja — de volledige tekst van “Uw eerste MCP-server bouwen” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus AI Engineering Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus AI Engineering Academy bevat in totaal 4 lessen.
Wat leer ik in “Uw eerste MCP-server bouwen”?
Gebruik de Python MCP SDK om een server te maken die resources, tools en prompts beschikbaar stelt, en verbind deze vervolgens met Claude Desktop om de volledige werking te bekijken. Je oefent met AI Engineering Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met AI Engineering Academy te beginnen?
Ervaring vooraf is niet nodig. AI Engineering Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 2 van 4.
Hoe lang duurt de les “Uw eerste MCP-server bouwen”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over AI Engineering Academy?
Ja. Elke les over AI Engineering Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Wat is MCP en waarom is het belangrijk
- Uw eerste MCP-server bouwen
- Databaseresources beschikbaar stellen via MCP
- MCP-beveiliging en authenticatie