524 lines
13 KiB
Markdown
524 lines
13 KiB
Markdown
# 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
|
|
|
|
1. [Wat is MCP en waarom](#1-wat-is-mcp-en-waarom)
|
|
2. [Architectuur — client / server / transport](#2-architectuur--client--server--transport)
|
|
3. [De drie primitieven](#3-de-drie-primitieven)
|
|
4. [Bestaande MCP servers](#4-bestaande-mcp-servers)
|
|
5. [Eigen server bouwen — TypeScript SDK](#5-eigen-server-bouwen--typescript-sdk)
|
|
6. [Laden in Cursor + Claude Desktop](#6-laden-in-cursor--claude-desktop)
|
|
7. [Resources + Prompts](#7-resources--prompts)
|
|
8. [MCP Inspector](#8-mcp-inspector)
|
|
9. [MCP vs Tool Calling](#9-mcp-vs-tool-calling)
|
|
10. [Productie + distributie](#10-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:
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```typescript
|
|
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.
|
|
|
|
```typescript
|
|
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.
|
|
|
|
```typescript
|
|
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-mcp`
|
|
- `youtube-mcp`, `arxiv-mcp`, `wikipedia-mcp`
|
|
- Lijst: https://github.com/punkpeye/awesome-mcp-servers (~500+ servers)
|
|
|
|
### Gebruik
|
|
|
|
Voorbeeld Cursor config (`~/.cursor/mcp.json`):
|
|
|
|
```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
|
|
|
|
```bash
|
|
mkdir mcp-polderfest && cd mcp-polderfest
|
|
pnpm init
|
|
pnpm add @modelcontextprotocol/sdk zod
|
|
pnpm add -D typescript @types/node tsx
|
|
```
|
|
|
|
`tsconfig.json`:
|
|
```json
|
|
{
|
|
"compilerOptions": {
|
|
"target": "ES2022",
|
|
"module": "Node16",
|
|
"moduleResolution": "Node16",
|
|
"outDir": "./dist",
|
|
"strict": true,
|
|
"esModuleInterop": true
|
|
},
|
|
"include": ["src/**/*"]
|
|
}
|
|
```
|
|
|
|
### Minimal server
|
|
|
|
`src/index.ts`:
|
|
```typescript
|
|
#!/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
|
|
|
|
```bash
|
|
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`** — `stdout` is 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`:
|
|
```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):
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```typescript
|
|
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**
|
|
|
|
```typescript
|
|
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.
|
|
|
|
```bash
|
|
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:**
|
|
1. Schrijf nieuwe tool
|
|
2. Build (`pnpm tsc`)
|
|
3. Test in Inspector
|
|
4. Werkt? Restart Cursor en gebruik daar
|
|
5. 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:
|
|
|
|
```json
|
|
// package.json
|
|
{
|
|
"name": "@jouw-org/mcp-polderfest",
|
|
"version": "1.0.0",
|
|
"bin": {
|
|
"mcp-polderfest": "./dist/index.js"
|
|
},
|
|
"files": ["dist"]
|
|
}
|
|
```
|
|
|
|
```bash
|
|
pnpm publish --access public
|
|
```
|
|
|
|
Anderen installeren:
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"polderfest": {
|
|
"command": "npx",
|
|
"args": ["-y", "@jouw-org/mcp-polderfest"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Remote servers (HTTP/SSE)
|
|
|
|
Voor cloud deploys — anders dan stdio:
|
|
|
|
```typescript
|
|
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:
|
|
```json
|
|
{
|
|
"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
|