MCP Server 指南
本指南介绍如何从您的 Finch 应用程序暴露一个 Model Context Protocol (MCP) 端点。MCP 是一种基于 JSON-RPC 的协议(版本 2025-11-25),专为 AI 代理和语言模型设计,用于发现和调用服务器端功能——工具、资源和提示词。
Finch 与 mcp_models 包集成,提供:
McpServerController— 一个抽象的 Finch 控制器,自动处理所有 MCP JSON-RPC 路由。McpBuilder— 用于声明式注册工具、资源、提示词和自定义方法处理器的构建器。mcp_models— 一个零依赖的 Dart 库,完整覆盖 MCP2025-11-25规范中的所有类型。
工作原理
当客户端(AI 代理、IDE 插件等)发送 MCP 请求时,它通过 HTTP 向您的 Finch 端点发送一个标准的 JSON-RPC 负载。McpServerController 解码该负载,将其路由到正确的内置处理器或您在 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)
工具是 AI 代理可以调用的可执行函数。使用 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 |
向 AI 代理展示的可读描述 |
inputSchema |
ToolSchema |
描述预期参数的 JSON Schema |
outputSchema |
ToolSchema? |
描述结构化输出的 JSON Schema(可选) |
handler |
Future<CallToolResult> Function(CallToolRequest) |
在 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) |
在 resources/read 时调用的处理器 |
您也可以在
configure()中使用rq.url('path')动态构建绝对 URI。
资源模板(Resource Templates)
对于参数化 URI(如 file:///books/{id}),使用 mcp.resourceTemplate():
mcp.resourceTemplate(
name: 'book',
uriTemplate: 'db:///books/{id}',
description: '通过 ID 获取单本书。',
);
提示词(Prompts)
提示词是 AI 代理可以检索的可复用消息模板:
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 |
唯一提示词标识符 |
description |
String? |
可选描述 |
handler |
Future<GetPromptResult> Function(GetPromptRequest) |
在 prompts/get 时调用的处理器 |
自定义方法处理器
使用 mcp.method() 覆盖或扩展任何 JSON-RPC 方法——包括内置方法:
mcp.method(
'notifications/initialized',
(Map<String, Object?> payload) async {
// 客户端初始化通知时的自定义逻辑。
return JSONRPCNotification(method: 'notifications/initialized');
},
);
通过 mcp.method() 注册的自定义处理器优先于内置分发器。
注册路由
使用 Methods.ALL 将 MCP 控制器添加到您的 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,
),
];
}
带身份验证
通过提供 AuthController 来保护您的 MCP 端点:
import 'controllers/mcp_auth_controller.dart';
FinchRoute(
key: 'mcp.my_server',
path: 'mcp/my-server',
methods: Methods.ALL,
index: MyMcpController().index,
auth: McpAuthController(),
),
典型的 MCP AuthController 检查 Bearer API 密钥:
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 为 MCP 2025-11-25 规范中的每种类型提供普通 Dart 类。无需代码生成——每个类都附带:
- 用于序列化的
toMap()方法。 - 用于反序列化的命名工厂
TypeName.toMCP(Map)。
主要类型
| 类别 | 类型 |
|---|---|
| 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, PromptMessage, GetPromptRequest, GetPromptResult, ListPromptsResult |
| 内容 | TextContent, ImageContent, AudioContent, EmbeddedResource |
| 功能 | 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: '2025-11-25',
capabilities: ClientCapabilities({}),
clientInfo: Implementation(name: 'my_client', version: '1.0.0'),
),
);
// 序列化为 Map(可直接进行 JSON 编码)。
final json = request.toMap();
// 反序列化回来。
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 基类
对于序列化形式就是底层 map 本身(而非包装对象)的类型,mcp_models 提供两个基类:
MapMC<K, V>— 继承MCP,直接序列化为其 map。MapModel<K, V>— 不带MCP基类的简单 map 类模型。
ServerCapabilities 和 ClientCapabilities 基于 MapMC 构建。
错误处理
在工具处理器中抛出任何 Exception——McpServerController 会将未处理的异常包装在代码为 -32600 的 JSONRPCErrorResponse 中,并向客户端流式传输 400 SSE 响应。对于结构化错误传播,返回带有 isError: true 的 CallToolResult:
handler: (req) async {
final id = req.params.arguments?['id'];
if (id == null) {
return CallToolResult(
isError: true,
content: [TextContent(text: '缺少必需参数:id')],
);
}
// ...
},