Files
novi-lessons/Les16-MCP/Les16-Lesstof.md
2026-06-03 16:58:25 +02:00

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

  1. Wat is MCP en waarom
  2. Architectuur — client / server / transport
  3. De drie primitieven
  4. Bestaande MCP servers
  5. Eigen server bouwen — TypeScript SDK
  6. Laden in Cursor + Claude Desktop
  7. Resources + Prompts
  8. MCP Inspector
  9. MCP vs Tool Calling
  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:

{
  "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

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.errorstdout 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:

{
  "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:

  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:

// 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