13 KiB
Les 16 — Lesstof
MCP — Model Context Protocol
Vak: AI-Assisted Development Opleiding: NOVI Hogeschool Utrecht Vorige les: Les 15 — Cursor + Vercel deploy Volgende les: Les 17 — Externe APIs in diepte
Inhoud
- Wat is MCP en waarom
- Architectuur — client / server / transport
- De drie primitieven
- Bestaande MCP servers
- Eigen server bouwen — TypeScript SDK
- Laden in Cursor + Claude Desktop
- Resources + Prompts
- MCP Inspector
- MCP vs Tool Calling
- Productie + distributie
1. Wat is MCP en waarom
Model Context Protocol is een open standaard van Anthropic (gelanceerd november 2024) voor de communicatie tussen AI-assistenten en externe data/tools.
Het probleem dat MCP oplost
Stel je hebt een handige tool — bijvoorbeeld 'zoek bands in onze database'. Je wilt 'm gebruiken in:
- Je eigen Next.js chat-app
- Cursor (tijdens coden vragen stellen over je data)
- Claude Desktop (research-werkflow)
- ChatGPT plugins (in de toekomst)
Zonder MCP: vier integraties, vier configuraties, vier onderhouds-aspecten.
Met MCP: één server, alle clients begrijpen het protocol.
MCP is voor AI-tools wat USB-C is voor apparaten — één standaard connector.
Waarom dit nu hot is
- Anthropic Claude Desktop ondersteunt het out-of-the-box
- Cursor ondersteunt MCP sinds early 2025
- OpenAI heeft mei 2025 MCP-support in ChatGPT aangekondigd
- Honderden bedrijven publiceren MCP servers (GitHub, Stripe, Linear, Notion, ...)
Voor jouw eindopdracht: optionele bonus, maar erg handig om interne tools te bouwen die je in elke AI-client kunt gebruiken.
2. Architectuur — client / server / transport
┌──────────────┐ JSON-RPC ┌──────────────┐
│ MCP Client │ ◄────────────────────► │ MCP Server │
│ │ stdio / HTTP/SSE │ (jouw code) │
│ Cursor, │ │ │
│ Claude │ │ exposes: │
│ Desktop, │ │ - tools │
│ custom apps │ │ - resources │
│ │ │ - prompts │
└──────────────┘ └──────────────┘
Drie rollen
- MCP Client — de AI-assistent die jouw server gebruikt. Cursor, Claude Desktop, of een custom app die jij bouwt.
- MCP Server — JOUW code. Een proces dat tools, data, of prompts beschikbaar maakt.
- Transport — hoe ze communiceren. Twee opties:
- stdio (default) — server runt lokaal als child-process, communicatie via stdin/stdout
- HTTP/SSE — server runt remote, communicatie over HTTP met Server-Sent Events
Protocol
JSON-RPC 2.0 onder de motorkap. Je hoeft dat niet zelf te schrijven — de SDK regelt het. Wat je wel zou kunnen zien als je inspecteert:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "searchBands",
"arguments": { "day": "Vrijdag" }
},
"id": 1
}
3. De drie primitieven
Een MCP server kan drie soorten dingen exposeren.
3.1 Tools
Uitvoerbare functies. Vergelijkbaar met tool calling uit Les 12.
server.tool(
"searchBands", // naam
"Zoek bands op dag, stage of genre", // beschrijving
{ // input schema (Zod)
day: z.enum(["Vrijdag", "Zaterdag", "Zondag"]).optional(),
stage: z.string().optional(),
},
async ({ day, stage }) => { // execute
const bands = await query(day, stage);
return {
content: [{ type: "text", text: JSON.stringify(bands) }],
};
}
);
AI kan deze aanroepen tijdens een conversatie.
3.2 Resources
Read-only data die AI mag inladen als context.
server.resource(
"bands-list", // naam
"bands://list", // URI scheme
async () => ({
contents: [{
uri: "bands://list",
mimeType: "application/json",
text: JSON.stringify(allBands),
}],
})
);
In Cursor type je @polderfest:bands-list om resource toe te voegen aan context.
3.3 Prompts
Herbruikbare prompt-templates.
server.prompt(
"daily-recap",
"Genereer een samenvatting van een festivaldag",
{ day: z.string() },
({ day }) => ({
messages: [{
role: "user",
content: {
type: "text",
text: `Geef een gedetailleerde recap van ${day}:
- Top 3 acts
- Drukke / rustige momenten
- Bijzonderheden`,
},
}],
})
);
In Cursor commando palette of Claude Desktop slash-menu krijg je deze prompts.
4. Bestaande MCP servers
Officieel (Anthropic)
Geïnstalleerd via npm:
@modelcontextprotocol/server-filesystem— file system access@modelcontextprotocol/server-github— repos, issues, PRs@modelcontextprotocol/server-slack— Slack workspace@modelcontextprotocol/server-postgres— Postgres queries@modelcontextprotocol/server-puppeteer— browser automation@modelcontextprotocol/server-memory— persistent memory
Community
linear-mcp,notion-mcp,asana-mcp,figma-mcp,stripe-mcpyoutube-mcp,arxiv-mcp,wikipedia-mcp- Lijst: https://github.com/punkpeye/awesome-mcp-servers (~500+ servers)
Gebruik
Voorbeeld Cursor config (~/.cursor/mcp.json):
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_..." }
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/tim/Documents"]
}
}
}
Restart Cursor → servers actief.
5. Eigen server bouwen — TypeScript SDK
Setup
mkdir mcp-polderfest && cd mcp-polderfest
pnpm init
pnpm add @modelcontextprotocol/sdk zod
pnpm add -D typescript @types/node tsx
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}
Minimal server
src/index.ts:
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "polderfest-server",
version: "1.0.0",
});
server.tool(
"searchBands",
"Zoek bands op dag, stage, genre",
{
day: z.enum(["Vrijdag", "Zaterdag", "Zondag"]).optional(),
stage: z.string().optional(),
},
async ({ day, stage }) => {
// Echte DB query of mock
const bands = MOCK_BANDS.filter(b =>
(!day || b.day === day) && (!stage || b.stage === stage)
);
return {
content: [{ type: "text", text: JSON.stringify(bands, null, 2) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Build + run
pnpm tsc # build → dist/index.js
node dist/index.js # runt server (wacht op stdio input)
Eerste keer: niets gebeurt zichtbaar — server wacht op JSON-RPC over stdio. Test via Inspector (zie sectie 8).
Best practices
- Logging via
console.error—stdoutis gereserveerd voor protocol - Errors als data — return
{ content: [...], isError: true } - Schema's zo strict mogelijk — Zod schema = AI weet exact wat het mag invullen
- Description = belangrijk — AI kiest tools op basis van description
6. Laden in Cursor + Claude Desktop
Cursor
~/.cursor/mcp.json:
{
"mcpServers": {
"polderfest": {
"command": "node",
"args": ["/Users/jouw-naam/dev/mcp-polderfest/dist/index.js"],
"env": {
"SUPABASE_URL": "https://...supabase.co",
"SUPABASE_KEY": "..."
}
}
}
}
Belangrijk: gebruik absolute paths (geen ~ of relative).
Cursor → Settings → MCP → check server-status (groen = werkt).
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json (macOS):
{
"mcpServers": {
"polderfest": {
"command": "node",
"args": ["/Users/jouw-naam/dev/mcp-polderfest/dist/index.js"]
}
}
}
Op Windows: %APPDATA%\Claude\claude_desktop_config.json.
Restart Claude Desktop → MCP icon zichtbaar in chat input.
Beide tegelijk
Zelfde server, beide configs. Eén keer onderhouden, beide AI-clients gebruiken hem.
7. Resources + Prompts
Resources gebruik-cases
- Documentatie — handleiding, RFCs, design docs
- Database snapshots — read-only views
- API responses — gecachede externe data
- Logs — recent errors voor debugging
server.resource(
"recent-logs",
"logs://recent",
async () => ({
contents: [{
uri: "logs://recent",
mimeType: "text/plain",
text: await fs.readFile("/var/log/app.log", "utf-8"),
}],
})
);
In Cursor: @polderfest:recent-logs voegt logs toe aan chat context. AI kan vragen erover beantwoorden.
Prompts gebruik-cases
- Code review template
- Bug report opmaak
- Vergader-recap structuur
server.prompt(
"code-review",
"Doe een grondige code review",
{ file: z.string() },
({ file }) => ({
messages: [{
role: "user",
content: { type: "text", text: `Review ${file} op:
1. Bugs / edge cases
2. Naming / readability
3. Performance
4. Security
Geef per punt concrete suggesties.` },
}],
})
);
Cursor command palette → "MCP: code-review" → file selecteren → AI start met die exacte prompt.
8. MCP Inspector
Debug-tool om je server te testen zonder Cursor/Claude.
npx @modelcontextprotocol/inspector dist/index.js
Opent browser-UI op http://localhost:5173:
- Linker panel: server tools/resources/prompts lijst
- Hoofdvenster: parameters invullen + call uitvoeren
- Result + raw JSON-RPC zichtbaar
Workflow:
- Schrijf nieuwe tool
- Build (
pnpm tsc) - Test in Inspector
- Werkt? Restart Cursor en gebruik daar
- Werkt niet? Inspector toont errors direct
9. MCP vs Tool Calling
| Aspect | Tool Calling (Les 12) | MCP (Les 16) |
|---|---|---|
| Waar leeft tool | In jouw Next.js app | Aparte server, los proces |
| Clients | Alleen jouw chat | Alle MCP-clients |
| Setup | Function definitie in code | Server proces + config |
| Distributie | Niet — alleen in app | npm publish |
| State | Per-request, geen state | Server proces heeft state |
| Best voor | App-specifieke features | Herbruikbare tools |
Mentale model
- Tool calling = function in mijn app
- MCP server = library die elke AI-client kan importeren
Wanneer welke
- App-feature (alleen voor jouw users) → tool calling
- Interne dev-tools (jij + collega's) → MCP server
- Open-source tool voor de community → MCP server (publish naar npm)
- Beide patronen zijn complementair — productie-teams gebruiken vaak beide
10. Productie + distributie
npm publish
Maak je server beschikbaar voor anderen:
// package.json
{
"name": "@jouw-org/mcp-polderfest",
"version": "1.0.0",
"bin": {
"mcp-polderfest": "./dist/index.js"
},
"files": ["dist"]
}
pnpm publish --access public
Anderen installeren:
{
"mcpServers": {
"polderfest": {
"command": "npx",
"args": ["-y", "@jouw-org/mcp-polderfest"]
}
}
}
Remote servers (HTTP/SSE)
Voor cloud deploys — anders dan stdio:
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
const app = express();
app.get("/sse", (req, res) => {
const transport = new SSEServerTransport("/messages", res);
server.connect(transport);
});
app.listen(3001);
Cursor config:
{
"mcpServers": {
"polderfest-remote": {
"url": "https://mcp.example.com/sse"
}
}
}
Security
- Authenticate — als server gevoelige data exposeert, auth-token in env-var
- Validate input — Zod schema's strict
- Audit log — log elke tool-call
- Rate limit — server is gewoon een proces, gebruik standaard rate-limit libs
- Geen secrets in code — env vars only
Bronnen
- MCP officiële site: https://modelcontextprotocol.io/
- Spec: https://spec.modelcontextprotocol.io/
- TS SDK: https://github.com/modelcontextprotocol/typescript-sdk
- MCP Inspector: https://github.com/modelcontextprotocol/inspector
- Cursor MCP docs: https://docs.cursor.com/context/model-context-protocol
- Claude Desktop MCP: https://docs.anthropic.com/en/docs/build-with-claude/mcp
- Anthropic launch post: https://www.anthropic.com/news/model-context-protocol
- Awesome MCP: https://github.com/punkpeye/awesome-mcp-servers