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 库,完整覆盖 MCP 2025-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 路由树中,以便同时接受 GETPOST 请求:

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

ToolSchemaSchema 都描述用于工具输入/输出验证的 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 类模型。

ServerCapabilitiesClientCapabilities 基于 MapMC 构建。

错误处理

在工具处理器中抛出任何 Exception——McpServerController 会将未处理的异常包装在代码为 -32600JSONRPCErrorResponse 中,并向客户端流式传输 400 SSE 响应。对于结构化错误传播,返回带有 isError: trueCallToolResult

handler: (req) async {
  final id = req.params.arguments?['id'];
  if (id == null) {
    return CallToolResult(
      isError: true,
      content: [TextContent(text: '缺少必需参数:id')],
    );
  }
  // ...
},

另请参阅