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 tegelijk2025-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 MCP 2026-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() ook rq.url('path') gebruiken om absolute URI's dynamisch op te bouwen, aangezien configure wordt 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> — breidt MCP uit, serialiseert direct als zijn map.
  • MapModel<K, V> — een gewoon map-achtig model zonder de MCP-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')],
    );
  }
  // ...
},

Zie ook