Resources versus tools
MCP onderscheidt twee manieren waarop een server data kan leveren: tools en resources. Het verschil is semantisch, maar belangrijk:
- Tools zijn acties die het model uitvoert. Ze kunnen side-effects hebben (schrijven, versturen, aanmaken).
- Resources zijn data die het model leest. Ze zijn read-only en identificeerbaar via een URI.
Resources lijken op bestanden of webpagina's: je haalt ze op via een adres. Het model kan een resource in zijn contextvenster laden om er vragen over te beantwoorden.
Resource subscriptions
MCP ondersteunt resource subscriptions: de client kan zich abonneren op wijzigingen en ontvangt een notificatie zodra een resource verandert. Dit is optioneel en niet elke client implementeert het.
Gebruik registerResource, niet de oude resource-vorm
In recente versies van de TypeScript SDK (geverifieerd tegen @modelcontextprotocol/sdk 1.29, juni 2026) is server.registerResource() de aanbevolen manier. Oudere voorbeelden gebruiken server.resource() met de beschrijving als tweede argument; dat patroon staat niet meer in de officiele docs. Gebruik voor nieuwe code de register*-methodes.
Statische resource registreren
registerResource neemt een naam, een vaste URI, een metadata-object en een async handler:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
const server = new McpServer({ name: "resource-demo", version: "1.0.0" });
server.registerResource(
"app-config",
"config://app",
{
title: "Applicatie-configuratie",
description: "Huidige applicatie-configuratie",
mimeType: "application/json",
},
async (uri) => ({
contents: [
{
uri: uri.href,
text: JSON.stringify({ environment: "production", version: "2.1.0" }),
mimeType: "application/json",
},
],
})
);
De URI config://app is een vaste identifier. Het model haalt deze resource op via een resources/read-verzoek met { uri: "config://app" }.
Dynamische resources met templates
Resource templates gebruiken URI-variabelen tussen accolades, volgens de RFC 6570 URI-template-standaard. De variabelen komen als tweede argument in de handler binnen:
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
server.registerResource(
"user-profile",
new ResourceTemplate("users://{userId}/profile", { list: undefined }),
{
title: "Gebruikersprofiel",
description: "Profiel van een specifieke gebruiker",
mimeType: "application/json",
},
async (uri, { userId }) => {
const user = await db.users.findById(userId);
if (!user) {
throw new Error(`Gebruiker ${userId} niet gevonden`);
}
return {
contents: [
{
uri: uri.href,
text: JSON.stringify({
id: user.id,
name: user.name,
email: user.email,
department: user.department,
}),
mimeType: "application/json",
},
],
};
}
);
Het model kan dan users://42/profile opvragen en krijgt het profiel van gebruiker 42.
Resources vindbaar maken met list
Geef een list-functie mee aan de ResourceTemplate zodat clients de beschikbare resources kunnen ontdekken:
server.registerResource(
"monthly-reports",
new ResourceTemplate("reports://{year}/{month}", {
list: async () => {
const reports = await db.reports.findAll();
return {
resources: reports.map((r) => ({
uri: `reports://${r.year}/${r.month}`,
name: `Rapport ${r.month}-${r.year}`,
description: `Maandrapport ${r.month} ${r.year}`,
mimeType: "application/pdf",
})),
};
},
}),
{
title: "Maandelijkse rapportages",
description: "Maandelijkse rapportages als PDF",
mimeType: "application/pdf",
},
async (uri, { year, month }) => {
const pdf = await getReportPdf(Number(year), Number(month));
return {
contents: [
{
uri: uri.href,
blob: pdf.toString("base64"),
mimeType: "application/pdf",
},
],
};
}
);
Binaire content stuur je als base64-gecodeerde string via het blob-veld in plaats van text.
Meerdere content-items per resource
Een resource kan meerdere content-items teruggeven. Handig voor documenten met bijlagen of gerelateerde bestanden:
server.registerResource(
"project-overview",
new ResourceTemplate("projects://{id}", { list: undefined }),
{
title: "Projectoverzicht",
description: "Projectoverzicht inclusief bijlagen",
mimeType: "application/json",
},
async (uri, { id }) => {
const project = await getProject(id);
return {
contents: [
{
uri: `${uri.href}/readme`,
text: project.description,
mimeType: "text/markdown",
},
{
uri: `${uri.href}/config`,
text: JSON.stringify(project.config),
mimeType: "application/json",
},
],
};
}
);
Resource subscriptions implementeren
Subscriptions verlopen via de low-level server-handlers. Let op: die bereik je via server.server, niet rechtstreeks op de McpServer. De resources.subscribe-capability wordt automatisch ingeschakeld zodra je resources registreert, dus je hoeft die niet handmatig te zetten:
import {
SubscribeRequestSchema,
UnsubscribeRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const subscriptions = new Set<string>();
server.server.setRequestHandler(SubscribeRequestSchema, async (request) => {
subscriptions.add(request.params.uri);
return {};
});
server.server.setRequestHandler(UnsubscribeRequestSchema, async (request) => {
subscriptions.delete(request.params.uri);
return {};
});
async function notifyResourceChange(uri: string) {
if (subscriptions.has(uri)) {
await server.server.sendResourceUpdated({ uri });
}
}
Roep notifyResourceChange aan zodra de data achter een resource wijzigt. De client kan de resource dan opnieuw ophalen. Gebruik server.server.sendResourceUpdated() en geen handmatig samengestelde notificatie, zodat je het juiste notifications/resources/updated-bericht stuurt.
Gebruik resources voor grote data
Wil je grote hoeveelheden data beschikbaar stellen, gebruik dan resources in plaats van tools. Een tool die een groot JSON-object teruggeeft, vult direct het contextvenster van het model. Met resources bepaalt de client zelf wanneer en wat hij laadt.
Wat is het verschil tussen een resource en een prompt?
Resources zijn data-objecten die het model leest. Prompts zijn herbruikbare instructie-templates met parameters die het model kan uitvoeren.
Moet ik registerResource of de oude resource-methode gebruiken?
Gebruik registerResource(). De SDK noemt de register*-methodes de aanbevolen aanpak voor nieuwe code en geeft je een metadata-object met title, description en mimeType.
Kan een resource andere resources bevatten?
Niet direct, maar een resource kan in zijn tekst-inhoud URI-referenties naar andere resources opnemen. Het model kan die dan afzonderlijk opvragen.
Hoe groot mag een resource-response zijn?
Er is geen formeel maximum, maar houd rekening met het contextvenster van het model. Grote resources boven ongeveer 100 KB deel je beter op in pagineerbare chunks.
Hoe stuur ik binaire data terug?
Zet de bytes als base64-gecodeerde string in het blob-veld van een content-item, samen met de juiste mimeType, in plaats van het text-veld.
Kan ik resources cachen op de client?
Dat is de verantwoordelijkheid van de client-implementatie. Het MCP-protocol definieert zelf geen caching-semantiek voor resources.