راهنمای MCP Server

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

Finch با بسته mcp_models ادغام شده و موارد زیر را فراهم می‌کند:

  • McpServerController — یک کنترلر انتزاعی Finch که تمام مسیریابی JSON-RPC پروتکل MCP را به صورت خودکار مدیریت می‌کند.
  • McpBuilder — یک سازنده اعلانی برای ثبت ابزارها، منابع، prompt‌ها و handler‌های سفارشی.
  • mcp_models — یک کتابخانه Dart بدون وابستگی با پوشش کامل از schema پروتکل MCP نسخه 2025-11-25.

نحوه کار

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

ابزارها (Tools)

یک ابزار یک تابع قابل فراخوانی است که یک عامل هوش مصنوعی می‌تواند آن را اجرا کند. با mcp.tool() یکی را ثبت کنید:

mcp.tool(
  name: 'add',
  description: 'دو عدد صحیح را جمع کرده و حاصل جمع را برمی‌گرداند.',
  inputSchema: ToolSchema(
    type: 'object',
    properties: {
      'a': Schema(type: 'integer', description: 'عملوند اول', title: 'A'),
      'b': Schema(type: 'integer', description: 'عملوند دوم', title: 'B'),
    },
    required: ['a', 'b'],
  ),
  outputSchema: ToolSchema(
    type: 'object',
    properties: {
      'result': Schema(type: 'integer', description: 'حاصل جمع', 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 شناسه منحصر به فرد ابزار
description String توضیح قابل خواندن برای عوامل هوش مصنوعی
inputSchema ToolSchema JSON Schema که آرگومان‌های مورد انتظار را توصیف می‌کند
outputSchema ToolSchema? JSON Schema که خروجی ساختاریافته را توصیف می‌کند (اختیاری)
handler Future<CallToolResult> Function(CallToolRequest) handler ناهمزمان که با tools/call فراخوانی می‌شود

منابع (Resources)

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

mcp.resource(
  name: 'config',
  uri: 'file:///config.json',
  description: 'فایل پیکربندی برنامه.',
  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 نام منحصر به فرد منبع
uri String URI که این منبع را شناسایی می‌کند
description String? توضیح اختیاری
handler Future<ReadResourceResult> Function(ReadResourceRequest) handler فراخوانی شده با resources/read

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

قالب‌های منبع (Resource Templates)

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

mcp.resourceTemplate(
  name: 'book',
  uriTemplate: 'db:///books/{id}',
  description: 'یک کتاب خاص را با ID واکشی می‌کند.',
);

Prompt‌ها

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

mcp.prompt(
  name: 'greet',
  description: 'یک پیام خوش‌آمدگویی برمی‌گرداند.',
  handler: (GetPromptRequest req) async {
    final name = req.params.arguments?['name'] ?? 'World';
    return GetPromptResult(
      messages: [
        PromptMessage(
          role: Role.assistant,
          content: TextContent(text: 'سلام، $name!'),
        ),
      ],
    );
  },
);
پارامتر نوع توضیح
name String شناسه منحصر به فرد prompt
description String? توضیح اختیاری
handler Future<GetPromptResult> Function(GetPromptRequest) handler فراخوانی شده با prompts/get

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

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

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

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

ثبت مسیر

کنترلر MCP را با Methods.ALL به درخت مسیر Finch خود اضافه کنید تا هر دو درخواست 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,
    ),
  ];
}

با احراز هویت

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

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 یک کلید API Bearer را بررسی می‌کند:

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 پروتکل MCP نسخه 2025-11-25 ارائه می‌دهد. هیچ تولید کدی وجود ندارد — هر کلاس با این موارد ارائه می‌شود:

  • یک متد toMap() برای serialization.
  • یک factory نام‌گذاری شده TypeName.toMCP(Map) برای deserialization.

انواع کلیدی

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

مثال Serialization

import 'package:mcp_models/mcp_models.dart';

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

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

// Deserialize برگشت.
final restored = InitializeRequest.toMCP(json);

ToolSchema و Schema

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

ToolSchema(
  type: 'object',
  properties: {
    'name': Schema(
      type: 'string',
      description: 'نام مورد.',
      defaultValue: '',
      title: 'Name',
    ),
    'count': Schema(
      type: 'integer',
      description: 'چند مورد.',
      defaultValue: 1,
      title: 'Count',
    ),
  },
  required: ['name'],
)

کلاس‌های پایه MapMC و MapModel

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

  • MapMC<K, V> — کلاس MCP را گسترش می‌دهد، مستقیماً به عنوان map خود serialize می‌شود.
  • MapModel<K, V> — یک مدل map-مانند ساده بدون کلاس پایه MCP.

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

مدیریت خطا

هر Exception را در داخل یک 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: 'آرگومان مورد نیاز وجود ندارد: id')],
    );
  }
  // ...
},

همچنین ببینید