راهنمای سرور MCP

این راهنما توضیح می‌دهد چگونه یک endpoint Model Context Protocol (MCP) را از اپلیکیشن فینچ خود در معرض دید قرار دهید. MCP یک پروتکل مبتنی بر JSON-RPC است که برای عامل‌های هوش مصنوعی (AI agents) و مدل‌های زبانی طراحی شده تا قابلیت‌های سمت سرور — ابزارها (tools)، منابع (resources) و promptها — را به‌شکلی استاندارد کشف و فراخوانی کنند.

McpServerController فینچ به‌طور هم‌زمان از دو نسخه پروتکل MCP پشتیبانی می‌کند — 2025-11-25 و 2026-07-28 — و به‌ازای هر درخواست به‌صورت خودکار تعیین می‌کند کدام نسخه صحبت شود (به Protocol Version Negotiation در ادامه مراجعه کنید). شما tool ها/resource ها/prompt ها را فقط یک‌بار از طریق McpBuilder ثبت می‌کنید؛ کنترلر مسیریابی را برای هر نسخه‌ای که کلاینت متصل صحبت می‌کند به‌درستی مدیریت می‌کند.

فینچ با پکیج mcp_models یکپارچه می‌شود تا موارد زیر را فراهم کند:

  • McpServerController — یک کنترلر انتزاعی فینچ که تمام مسیریابی JSON-RPC مربوط به MCP را به‌صورت خودکار مدیریت می‌کند.
  • McpBuilder — یک builder اعلانی (declarative) برای ثبت tool ها، resource ها، prompt ها و handlerهای متد سفارشی.
  • mcp_models — یک کتابخانه Dart بدون وابستگی (zero-dependency) با پوشش کامل schema نسخه 2026-07-28 مربوط به MCP.

نحوه عملکرد

وقتی یک کلاینت (یک عامل هوش مصنوعی، افزونه IDE و غیره) یک درخواست MCP ارسال می‌کند، یک payload استاندارد JSON-RPC را از طریق HTTP به endpoint فینچ شما ارسال می‌کند. McpServerController این payload را decode می‌کند، آن را به handler داخلی مناسب یا به handlerی که با McpBuilder ثبت کرده‌اید مسیریابی می‌کند، و پاسخ را به‌صورت 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()

ثبت قابلیت‌ها با McpBuilder

Tool ها

یک tool یک تابع قابل‌فراخوانی است که یک عامل هوش مصنوعی می‌تواند آن را invoke کند. یکی را با 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},
    );
  },
);
پارامتر نوع توضیح
name String شناسه یکتای tool
description String توضیح قابل‌خوانش برای انسان که به عامل‌های هوش مصنوعی نمایش داده می‌شود
inputSchema ToolSchema JSON Schema که آرگومان‌های مورد انتظار را توصیف می‌کند
outputSchema ToolSchema? JSON Schema که خروجی ساختاریافته را توصیف می‌کند (اختیاری)
handler Future<CallToolResult> Function(CallToolRequest) handler ناهمگام (async) که هنگام tools/call فراخوانی می‌شود

Resource ها

یک resource یک منبع داده قابل‌آدرس‌دهی با URI را در معرض دید قرار می‌دهد (یک فایل، یک رکورد دیتابیس، یک پاسخ API و غیره):

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',
        ),
      ],
    );
  },
);
پارامتر نوع توضیح
name String نام یکتای resource
uri String URI‌ای که این resource را شناسایی می‌کند
description String? توضیح اختیاری
handler Future<ReadResourceResult> Function(ReadResourceRequest) handler که هنگام resources/read فراخوانی می‌شود

همچنین می‌توانید از rq.url('path') درون configure() برای ساخت URIهای مطلق به‌صورت پویا استفاده کنید، زیرا configure با context زنده درخواست فراخوانی می‌شود.

Resource Template ها

برای URIهای پارامتری‌شده (مثلاً file:///books/{id})، از mcp.resourceTemplate() استفاده کنید:

mcp.resourceTemplate(
  name: 'book',
  uriTemplate: 'db:///books/{id}',
  description: 'Fetches a single book by ID.',
);

Prompt ها

یک prompt یک قالب پیام قابل‌استفاده‌مجدد است که یک عامل هوش مصنوعی می‌تواند آن را دریافت کند:

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!'),
        ),
      ],
    );
  },
);
پارامتر نوع توضیح
name String شناسه یکتای prompt
description String? توضیح اختیاری
handler Future<GetPromptResult> Function(GetPromptRequest) handler که هنگام prompts/get فراخوانی می‌شود

Handlerهای متد سفارشی

هر متد JSON-RPC — از جمله متدهای داخلی — را با mcp.method() override یا گسترش دهید:

mcp.method(
  'notifications/initialized',
  (Map<String, Object?> payload) async {
    // منطق سفارشی برای اعلان (notification) مربوط به initialisation کلاینت.
    return JSONRPCNotification(method: 'notifications/initialized');
  },
);

handlerهای سفارشی ثبت‌شده از طریق mcp.method() نسبت به dispatcher داخلی اولویت دارند.

ثبت مسیر (Route)

کنترلر MCP را با Methods.ALL به route tree فینچ خود اضافه کنید تا هر دو درخواست GET و POST پذیرفته شوند:

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,
    ),
  ];
}

با احراز هویت

با فراهم کردن یک AuthController، از endpoint مربوط به MCP خود محافظت کنید:

import 'controllers/mcp_auth_controller.dart';

FinchRoute(
  key: 'mcp.my_server',
  path: 'mcp/my-server',
  methods: Methods.ALL,
  index: MyMcpController().index,
  auth: McpAuthController(),
),

یک AuthController معمولی برای MCP یک Bearer API key را بررسی می‌کند:

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,
      );
}

پکیج mcp_models

mcp_models کلاس‌های ساده Dart برای هر نوع در schema نسخه 2026-07-28 مربوط به MCP فراهم می‌کند. هیچ code generation‌ای دخیل نیست — هر کلاس همراه با موارد زیر ارائه می‌شود:

  • یک متد toMap() برای سریالایز کردن.
  • یک factory با نام TypeName.toMCP(Map) برای deserialize کردن.

انواع کلیدی

دسته‌بندی انواع
JSON-RPC JSONRPCRequest, JSONRPCResultResponse, JSONRPCErrorResponse, JSONRPCNotification
چرخه عمر (Lifecycle) InitializeRequest, InitializeResult, InitializeResultResponse
Tool ها Tool, ToolSchema, Schema, CallToolRequest, CallToolResult, CallToolResultResponse, ListToolsResult, ListToolsResultResponse
Resource ها Resource, ResourceTemplate, ReadResourceRequest, ReadResourceResult, TextResourceContents, BlobResourceContents, ListResourcesResult, ListResourceTemplatesResult
Prompt ها Prompt, PromptMessage, GetPromptRequest, GetPromptResult, ListPromptsResult
محتوا (Content) TextContent, ImageContent, AudioContent, EmbeddedResource
قابلیت‌ها (Capabilities) ServerCapabilities, ClientCapabilities, Implementation
خطاها Error (شیء خطای JSON-RPC)
متفرقه Result, EmptyResult, Role, LoggingLevel

نمونه سریالایز کردن

import 'package:mcp_models/mcp_models.dart';

// یک درخواست initialize بساز.
final request = InitializeRequest(
  id: '1',
  params: InitializeRequestParams(
    protocolVersion: '2026-07-28',
    capabilities: ClientCapabilities({}),
    clientInfo: Implementation(name: 'my_client', version: '1.0.0'),
  ),
);

// به یک Map سریالایز کن (آماده برای JSON encoding).
final json = request.toMap();

// دوباره deserialize کن.
final restored = InitializeRequest.toMCP(json);

ToolSchema و Schema

ToolSchema و Schema هر دو یک شیء JSON Schema را توصیف می‌کنند که برای اعتبارسنجی ورودی/خروجی tool استفاده می‌شود:

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 و MapModel

برای انواعی که شکل سریالایز‌شده آن‌ها خودِ map زیرین است (به‌جای یک شیء wrapper)، mcp_models دو کلاس پایه ارائه می‌دهد:

  • MapMC<K, V> — از MCP extend می‌شود و مستقیماً به‌صورت map خودش سریالایز می‌شود.
  • MapModel<K, V> — یک مدل ساده شبیه‌به‌map بدون کلاس پایه MCP.

ServerCapabilities و ClientCapabilities بر پایه MapMC ساخته شده‌اند.

مدیریت خطا

هر Exception درون یک tool handler پرتاب کنید — McpServerController استثناهای مدیریت‌نشده را در یک JSONRPCErrorResponse با کد -32600 می‌پیچد و یک پاسخ SSE با کد 400 به کلاینت استریم می‌کند. برای انتشار خطای ساختاریافته، یک CallToolResult با 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')],
    );
  }
  // ...
},

مطالب مرتبط