راهنمای 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')],
);
}
// ...
},