MCP-serverhandleiding
Deze handleiding legt uit hoe je een Model Context Protocol (MCP)-eindpunt vanuit je Finch-applicatie beschikbaar stelt. MCP is een op JSON-RPC gebaseerd protocol, ontworpen voor AI-agents en taalmodellen om server-side mogelijkheden — tools, resources en prompts — op een gestandaardiseerde manier te ontdekken en aan te roepen.
Finch's McpServerController ondersteunt twee MCP-protocolversies tegelijk — 2025-11-25 en 2026-07-28 — en bepaalt per verzoek automatisch welke van de twee wordt gesproken (zie Protocolversie-onderhandeling hieronder). Je registreert tools/resources/prompts één keer via McpBuilder; de controller zorgt voor de juiste routering, ongeacht welke versie de verbindende client spreekt.
Finch integreert met het mcp_models-pakket om het volgende te bieden:
McpServerController— een abstracte Finch-controller die alle MCP JSON-RPC-routering automatisch afhandelt.McpBuilder— een declaratieve builder voor het registreren van tools, resources, prompts en aangepaste methode-handlers.mcp_models— een Dart-bibliotheek zonder afhankelijkheden met volledige dekking van het MCP2026-07-28-schema.
Hoe het werkt
Wanneer een client (een AI-agent, IDE-plugin, enz.) een MCP-verzoek verstuurt, stuurt deze een standaard JSON-RPC-payload via HTTP naar je Finch-eindpunt. McpServerController decodeert de payload, stuurt deze door naar de juiste ingebouwde handler of naar een handler die je hebt geregistreerd met McpBuilder, en streamt de respons terug als Server-Sent Events (SSE).
Client ──POST /mcp/books──► McpServerController.index()
│
▼
_dispatch(method, id, payload)
│
┌──────────────┼──────────────────┐
▼ ▼ ▼
tools/call resources/read prompts/get
│ │ │
▼ ▼ ▼
McpBuilder McpBuilder McpBuilder
.toolHandler() .resourceHandler() .promptHandler()
Mogelijkheden registreren met McpBuilder
Tools
Een tool is een aanroepbare functie die een AI-agent kan aanroepen. Registreer er een met mcp.tool():
mcp.tool(
name: 'add',
description: 'Adds two integers and returns the sum.',
inputSchema: ToolSchema(
type: 'object',
properties: {
'a': Schema(type: 'integer', description: 'First operand', title: 'A'),
'b': Schema(type: 'integer', description: 'Second operand', title: 'B'),
},
required: ['a', 'b'],
),
outputSchema: ToolSchema(
type: 'object',
properties: {
'result': Schema(type: 'integer', description: 'The sum', title: 'Result'),
},
required: ['result'],
),
handler: (CallToolRequest req) async {
final args = req.params.arguments ?? {};
final sum = (args['a'] as int) + (args['b'] as int);
return CallToolResult(
content: [TextContent(text: '$sum', mimeType: 'text/plain')],
structuredContent: {'result': sum},
);
},
);
| Parameter | Type | Beschrijving |
|---|---|---|
name |
String |
Unieke tool-identificator |
description |
String |
Mensleesbare beschrijving die aan AI-agents wordt getoond |
inputSchema |
ToolSchema |
JSON Schema dat de verwachte argumenten beschrijft |
outputSchema |
ToolSchema? |
JSON Schema dat de gestructureerde output beschrijft (optioneel) |
handler |
Future<CallToolResult> Function(CallToolRequest) |
Asynchrone handler die wordt aangeroepen bij tools/call |
Resources
Een resource stelt een via URI adresseerbare gegevensbron beschikbaar (een bestand, een databaserecord, een API-respons, enz.):
mcp.resource(
name: 'config',
uri: 'file:///config.json',
description: 'Application configuration file.',
handler: (ReadResourceRequest req) async {
final content = await File('config.json').readAsString();
return ReadResourceResult(
contents: [
TextResourceContents(
uri: req.params.uri,
text: content,
mimeType: 'application/json',
),
],
);
},
);
| Parameter | Type | Beschrijving |
|---|---|---|
name |
String |
Unieke resourcenaam |
uri |
String |
De URI die deze resource identificeert |
description |
String? |
Optionele beschrijving |
handler |
Future<ReadResourceResult> Function(ReadResourceRequest) |
Handler die wordt aangeroepen bij resources/read |
Je kunt binnen
configure()ookrq.url('path')gebruiken om absolute URI's dynamisch op te bouwen, aangezienconfigurewordt aangeroepen met de live request-context.
Resource-templates
Gebruik voor geparametriseerde URI's (bijv. file:///books/{id}) mcp.resourceTemplate():
mcp.resourceTemplate(
name: 'book',
uriTemplate: 'db:///books/{id}',
description: 'Fetches a single book by ID.',
);
Prompts
Een prompt is een herbruikbare berichtsjabloon die een AI-agent kan opvragen:
mcp.prompt(
name: 'greet',
description: 'Returns a greeting message.',
handler: (GetPromptRequest req) async {
final name = req.params.arguments?['name'] ?? 'World';
return GetPromptResult(
messages: [
PromptMessage(
role: Role.assistant,
content: TextContent(text: 'Hello, $name!'),
),
],
);
},
);
| Parameter | Type | Beschrijving |
|---|---|---|
name |
String |
Unieke prompt-identificator |
description |
String? |
Optionele beschrijving |
handler |
Future<GetPromptResult> Function(GetPromptRequest) |
Handler die wordt aangeroepen bij prompts/get |
Aangepaste methode-handlers
Overschrijf of breid elke JSON-RPC-methode uit — inclusief ingebouwde — met mcp.method():
mcp.method(
'notifications/initialized',
(Map<String, Object?> payload) async {
// Aangepaste logica bij de initialisatienotificatie van de client.
return JSONRPCNotification(method: 'notifications/initialized');
},
);
Aangepaste handlers die via mcp.method() zijn geregistreerd, krijgen voorrang boven de ingebouwde dispatcher.
De route registreren
Voeg de MCP-controller toe aan je Finch-routeboom met Methods.ALL, zodat zowel GET- als POST-verzoeken worden geaccepteerd:
import 'package:finch/finch_route.dart';
import 'controllers/my_mcp_controller.dart';
List<FinchRoute> getRoutes() {
return [
FinchRoute(
key: 'mcp.my_server',
path: 'mcp/my-server',
methods: Methods.ALL,
index: MyMcpController().index,
),
];
}
Met authenticatie
Beveilig je MCP-eindpunt door een AuthController op te geven:
import 'controllers/mcp_auth_controller.dart';
FinchRoute(
key: 'mcp.my_server',
path: 'mcp/my-server',
methods: Methods.ALL,
index: MyMcpController().index,
auth: McpAuthController(),
),
Een typische AuthController voor MCP controleert een Bearer API-sleutel:
import 'package:finch/finch_route.dart';
class McpAuthController extends AuthController<String> {
final List<String> _allowedKeys = ['your-secret-api-key'];
@override
Future<bool> auth() async => (await checkLogin()).success;
@override
Future<bool> authApi() async {
final auth = rq.authorization;
if (auth.type == AuthType.bearer) {
return _allowedKeys.contains(auth.token);
}
return false;
}
@override
Future<String> loginForm() async => rq.renderJson(
data: {'error': 'Unauthorized'},
status: 401,
);
}
Het pakket mcp_models
mcp_models biedt gewone Dart-klassen voor elk type in het MCP 2026-07-28-schema. Er komt geen codegeneratie aan te pas — elke klasse levert:
- Een
toMap()-methode voor serialisatie. - Een genoemde factory
TypeName.toMCP(Map)voor deserialisatie.
Belangrijkste typen
| Categorie | Typen |
|---|---|
| JSON-RPC | JSONRPCRequest, JSONRPCResultResponse, JSONRPCErrorResponse, JSONRPCNotification |
| Levenscyclus | InitializeRequest, InitializeResult, InitializeResultResponse |
| Tools | Tool, ToolSchema, Schema, CallToolRequest, CallToolResult, CallToolResultResponse, ListToolsResult, ListToolsResultResponse |
| Resources | Resource, ResourceTemplate, ReadResourceRequest, ReadResourceResult, TextResourceContents, BlobResourceContents, ListResourcesResult, ListResourceTemplatesResult |
| Prompts | Prompt, PromptMessage, GetPromptRequest, GetPromptResult, ListPromptsResult |
| Inhoud | TextContent, ImageContent, AudioContent, EmbeddedResource |
| Capabilities | ServerCapabilities, ClientCapabilities, Implementation |
| Fouten | Error (JSON-RPC-foutobject) |
| Overig | Result, EmptyResult, Role, LoggingLevel |
Serialisatievoorbeeld
import 'package:mcp_models/mcp_models.dart';
// Bouw een initialize-request.
final request = InitializeRequest(
id: '1',
params: InitializeRequestParams(
protocolVersion: '2026-07-28',
capabilities: ClientCapabilities({}),
clientInfo: Implementation(name: 'my_client', version: '1.0.0'),
),
);
// Serialiseer naar een Map (klaar voor JSON-codering).
final json = request.toMap();
// Deserialiseer terug.
final restored = InitializeRequest.toMCP(json);
ToolSchema en Schema
ToolSchema en Schema beschrijven allebei JSON Schema-objecten die worden gebruikt voor validatie van tool-invoer/-uitvoer:
ToolSchema(
type: 'object',
properties: {
'name': Schema(
type: 'string',
description: 'The name of the item.',
defaultValue: '',
title: 'Name',
),
'count': Schema(
type: 'integer',
description: 'How many items.',
defaultValue: 1,
title: 'Count',
),
},
required: ['name'],
)
MapMC- en MapModel-basisklassen
Voor typen waarvan de geserialiseerde vorm de onderliggende map zelf is (in plaats van een wrapper-object), biedt mcp_models twee basisklassen:
MapMC<K, V>— breidtMCPuit, serialiseert direct als zijn map.MapModel<K, V>— een gewoon map-achtig model zonder deMCP-basis.
ServerCapabilities en ClientCapabilities zijn gebouwd op MapMC.
Foutafhandeling
Gooi een willekeurige Exception binnen een tool-handler — McpServerController verpakt onafgehandelde exceptions in een JSONRPCErrorResponse met code -32600 en streamt een 400-SSE-respons naar de client. Retourneer voor gestructureerde foutdoorgifte een CallToolResult met isError: true:
handler: (req) async {
final id = req.params.arguments?['id'];
if (id == null) {
return CallToolResult(
isError: true,
content: [TextContent(text: 'Missing required argument: id')],
);
}
// ...
},