返回市场
MCP服务器

MCP服务器

作者:app-appplayer8 星标更新:2025-10-07

项目介绍

MCP Server

🙌 支持此项目

如果你发现这个包有用,请考虑通过PayPal支持持续开发。

捐赠
通过 PayPal 支持makemind


🔗 MCP Dart 包家族

  • mcp_server: 向大型语言模型(LLM)提供工具、资源和提示。充当AI服务器。
  • mcp_client: 连接Flutter/Dart应用到MCP服务器。充当客户端接口。
  • mcp_llm: 桥接LLM(Claude、OpenAI等)到MCP客户端/服务器。充当LLM大脑。
  • flutter_mcp: 完整的Flutter插件,用于与平台特性集成的MCP。
  • flutter_mcp_ui_core: Flutter MCP UI系统的核心模型、常量和实用工具。
  • flutter_mcp_ui_runtime: 通过JSON规范构建动态、响应式UI的综合运行时。
  • flutter_mcp_ui_generator: 使用模板和流畅API创建UI定义的JSON生成工具包。
  • mcp_flow_runtime: 使用MCP Flow DSL进行硬件控制和IoT编排的声明性运行时。

这是一个实现模型上下文协议(MCP)服务器的Dart插件。该插件允许Flutter应用程序以标准化的方式向大型语言模型(LLM)应用程序暴露数据、功能和交互模式。

特性

  • 创建具有标准协议支持的MCP服务器
  • 通过资源暴露数据
  • 通过工具提供功能
  • 通过提示定义交互模式
  • 全面的会话管理,带有事件流
  • 内置资源缓存以优化性能
  • 长时间运行操作的进度报告和取消
  • 扩展的日志系统,具有可定制的级别和格式
  • 性能监控和指标跟踪
  • 多种传输层:
    • 标准I/O用于本地进程通信
    • 基于HTTP的通信的服务器发送事件(SSE)
  • 跨平台支持:Android、iOS、Web、Linux、Windows、macOS

协议版本

此包实现了模型上下文协议(MCP)规范版本2024-11-052025-03-26

协议版本对于确保MCP客户端和服务器之间的兼容性至关重要。此包的每个版本可能支持不同的协议版本,因此重要的是:

  • 查看CHANGELOG.md中的协议版本更新
  • 确保客户端和服务器的协议版本兼容
  • 保持最新的MCP规范

开始使用

安装

在你的pubspec.yaml中添加包:

dependencies:
  mcp_server: ^1.0.3

或者通过命令行安装:

dart pub add mcp_server

基本用法

import 'package:mcp_server/mcp_server.dart';

void main() {
  // 创建一个具有简单布尔能力的服务器
  final server = Server(
    name: '示例服务器',
    version: '1.0.0',
    capabilities: ServerCapabilities.simple(
      tools: true,
      resources: true,
      prompts: true,
    ),
  );

  // 添加一个简单的计算器工具
  server.addTool(
    name: '计算器',
    description: '执行基本计算',
    inputSchema: {
      'type': 'object',
      'properties': {
        'operation': {
          'type': 'string',
          'enum': ['加', '减', '乘', '除'],
          'description': '要执行的数学运算'
        },
        'a': {
          'type': 'number',
          'description': '第一个操作数'
        },
        'b': {
          'type': 'number',
          'description': '第二个操作数'
        }
      },
      'required': ['operation', 'a', 'b']
    },
    handler: (arguments) async {
      final operation = arguments['operation'] as String;
      final a = (arguments['a'] is int) ? (arguments['a'] as int).toDouble() : arguments['a'] as double;
      final b = (arguments['b'] is int) ? (arguments['b'] as int).toDouble() : arguments['b'] as double;
      
      double result;
      switch (operation) {
        case '加':
          result = a + b;
          break;
        case '减':
          result = a - b;
          break;
        case '乘':
          result = a * b;
          break;
        case '除':
          if (b == 0) {
            return CallToolResult(
              content: [TextContent(text: '除以零错误')],
              isError: true,
            );
          }
          result = a / b;
          break;
        default:
          return CallToolResult(
            content: [TextContent(text: '未知操作:$operation')],
            isError: true,
          );
      }
      
      return CallToolResult(content: [TextContent(text: '结果:$result')]);
    },
  );

  // 添加一个资源
  server.addResource(
    uri: 'time://当前',
    name: '当前时间',
    description: '获取当前日期和时间',
    mimeType: 'text/plain',
    handler: (uri, params) async {
      final now = DateTime.now().toString();
      
      return ReadResourceResult(
        contents: [
          ResourceContentInfo(
            uri: uri,
            text: now,
            mimeType: 'text/plain',
          ),
        ],
      );
    },
  );

  // 添加一个模板提示
  server.addPrompt(
    name: '问候',
    description: '生成自定义问候语',
    arguments: [
      PromptArgument(
        name: '姓名',
        description: '要问候的名字',
        required: true,
      ),
      PromptArgument(
        name: '正式',
        description: '是否使用正式问候风格',
        required: false,
      ),
    ],
    handler: (arguments) async {
      final name = arguments['姓名'] as String;
      final formal = arguments['正式'] as bool? ?? false;

      final String systemPrompt = formal
          ? '您是一个正式助手。以尊重和正式的方式称呼用户。'
          : '您是一个友好的助手。语气温暖而随意。';

      final messages = [
        Message(
          role: MessageRole.system.toString().split('.').last,
          content: TextContent(text: systemPrompt),
        ),
        Message(
          role: MessageRole.user.toString().split('.').last,
          content: TextContent(text: '请问候$name'),
        ),
      ];

      return GetPromptResult(
        description: '对$name的${formal ? '正式' : '随意'}问候',
        messages: messages,
      );
    },
  );

  // 连接到传输 - 旧方式(仍然支持)
  final transportResult = McpServer.createStdioTransport();
  final transport = transportResult.get();
  server.connect(transport);
}

// 新的:简化统一API(推荐)
void simplifiedMain() async {
  final serverResult = await McpServer.createAndStart(
    config: McpServer.simpleConfig(
      name: '示例服务器',
      version: '1.0.0',
    ),
    transportConfig: TransportConfig.stdio(),
  );

  await serverResult.fold(
    (server) async {
      // 如上所示添加工具、资源、提示
      
      // 服务器已经启动
      await Future.delayed(const Duration(hours: 24));
    },
    (error) => print('服务器失败:$error'),
  );
}

传输配置

MCP服务器现在支持统一的传输配置:

// STDIO传输
TransportConfig.stdio()

// SSE传输  
TransportConfig.sse(
  host: 'localhost',
  port: 8080,
  endpoint: '/sse',
  authToken: '可选令牌',
)

// 可流式传输的HTTP传输 - SSE流模式(默认)
TransportConfig.streamableHttp(
  host: 'localhost', 
  port: 8081,
  endpoint: '/mcp',
  isJsonResponseEnabled: false, // SSE流模式(默认)
)

// 可流式传输的HTTP传输 - JSON响应模式
TransportConfig.streamableHttp(
  host: 'localhost', 
  port: 8081,
  endpoint: '/mcp',
  isJsonResponseEnabled: true, // JSON响应模式
)

核心概念

服务器能力

MCP服务器支持两种方式来配置能力:

简单布尔配置

对于基本使用和测试,使用ServerCapabilities.simple()

final server = Server(
  name: '简单服务器',
  version: '1.0.0',
  capabilities: ServerCapabilities.simple(
    tools: true,
    toolsListChanged: true,
    resources: true,
    resourcesListChanged: true,
    prompts: true,
    promptsListChanged: true,
    sampling: true,
    logging: true,
    progress: true,
  ),
);

高级对象配置

对于生产使用,具有详细的控制能力:

final server = Server(
  name: '高级服务器',
  version: '1.0.0',
  capabilities: ServerCapabilities(
    tools: ToolsCapability(
      listChanged: true,
      supportsProgress: true,
      supportsCancellation: true,
    ),
    resources: ResourcesCapability(
      listChanged: true,
      subscribe: true,
    ),
    prompts: PromptsCapability(
      listChanged: true,
    ),
    sampling: SamplingCapability(),
    logging: LoggingCapability(),
    progress: ProgressCapability(
      supportsProgress: true,
    ),
  ),
);

服务器

Server类是您与MCP协议的核心接口。它处理连接管理、协议合规性和消息路由:

final server = Server(
  name: '我的应用',
  version: '1.0.0',
  capabilities: ServerCapabilities.simple(
    tools: true,
    resources: true,
    prompts: true,
  ),
);

资源

资源是您如何向LLM暴露数据。它们类似于REST API中的GET端点——它们提供数据但不应执行大量计算或产生副作用:

// 静态资源
server.addResource(
  uri: 'config://应用',
  name: '应用配置',
  description: '应用配置数据',
  mimeType: 'text/plain',
  handler: (uri, params) async {
    final configData = "app_name=我的应用\nversion=1.0.0\ndebug=false";
    
    return ReadResourceResult(
      contents: [
        ResourceContentInfo(
          uri: uri,
          text: configData,
          mimeType: 'text/plain',
        ),
      ],
    );
  },
);

// 带URI模板的资源
server.addResource(
  uri: 'file://{path}',
  name: '文件资源',
  description: '访问系统上的文件',
  mimeType: 'application/octet-stream',
  uriTemplate: {
    'type': 'object',
    'properties': {
      'path': {
        'type': 'string',
        'description': '文件路径'
      }
    }
  },
  handler: (uri, params) async {
    // 提取路径并读取文件内容
    final path = params['path'] ?? uri.substring('file://'.length);
    final content = await File(path).readAsString();
    
    return ReadResourceResult(
      contents: [
        ResourceContentInfo(
          uri: uri,
          text: content,
          mimeType: 'text/plain',
        ),
      ],
    );
  },
);

工具

工具让LLM通过您的服务器采取行动。与资源不同,工具预期执行计算并产生副作用:

server.addTool(
  name: '当前日期时间',
  description: '获取当前日期和时间',
  inputSchema: {
    'type': 'object',
    'properties': {
      'format': {
        'type': 'string',
        'description': '输出格式(完整、日期、时间)',
        'default': '完整'
      }
    },
    'required': []
  },
  handler: (args) async {
    final format = args['format'] as String? ?? '完整';
    final now = DateTime.now();
    
    String result;
    switch (format) {
      case '日期':
        result = '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
        break;
      case '时间':
        result = '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}';
        break;
      case '完整':
      default:
        result = now.toIso8601String();
        break;
    }
    
    return CallToolResult(content: [TextContent(text: result)]);
  },
);

提示

提示是可重用的模板,帮助LLM有效地与您的服务器交互:

server.addPrompt(
  name: '代码审查',
  description: '为代码片段生成代码审查',
  arguments: [
    PromptArgument(
      name: '代码',
      description: '要审查的代码',
      required: true,
    ),
    PromptArgument(
      name: '语言',
      description: '代码的编程语言',
      required: true,
    ),
  ],
  handler: (args) async {
    final code = args['代码'] as String;
    final language = args['语言'] as String;

    final systemPrompt = '''
您是一位经验丰富的代码审查员。根据以下指南审查提供的代码:
1. 识别潜在的错误或问题
2. 建议性能或可读性的优化
3. 强调代码中使用的良好实践
4. 提供建设性的改进反馈
在提供反馈时具体,并在建议更改时提供代码示例。
''';

    final messages = [
      Message(
        role: MessageRole.system.toString().split('.').last,
        content: TextContent(text: systemPrompt),
      ),
      Message(
        role: MessageRole.user.toString().split('.').last,
        content: TextContent(text: '请审查这段$language代码:\n\n```$language\n$code\n```'),
      ),
    ];

    return GetPromptResult(
      description: '$language代码的代码审查',
      messages: messages,
    );
  },
);

会话管理

MCP服务器提供了一个强大的基于流的系统来监控客户端会话事件:

// 创建日志器
final logger = Logger('mcp_server.sessions');

// 监听客户端连接
server.onConnect.listen((session) {
  logger.info('客户端已连接:${session.id}');
  // 初始化会话资源
});

// 监听客户端断开连接
server.onDisconnect.listen((session) {
  logger.info('客户端已断开:${session.id}');
  // 清理会话资源
});

ClientSession对象包含有用的信息:

  • 会话ID
  • 连接时间戳
  • 协议版本
  • 客户端能力
  • 客户端根目录

会话管理的最佳实践

  1. 始终处理两个事件:监听连接和断开连接事件以维护会话完整性。

  2. 为您的流订阅添加错误处理

    server.onConnect.listen(
      (session) {
        // 正常事件处理
      },
      onError: (error) {
        logger.severe('连接流中的错误:$error');
      },
    );
    
  3. 当不再需要时取消订阅

    final subscription = server.onConnect.listen((session) { /* ... */ });
    
    // 后来,当完成时:
    subscription.cancel();
    
  4. 使用会话事件进行状态管理:根据会话事件维护应用程序状态。

  5. 跟踪指标以进行监控:计数会话、持续时间和其他指标。

传输层

标准I/O

适用于命令行工具和直接集成:

final transportResult = McpServer.createStdioTransport();
final transport = transportResult.get();
server.connect(transport);

服务器发送事件(SSE)

适用于基于HTTP的通信:

final sseConfig = SseServerConfig(
  endpoint: '/sse',
  messagesEndpoint: '/messages',
  port: 8080,
);
final transportResult = McpServer.createSseTransport(sseConfig);
final transport = transportResult.get();
server.connect(transport);

可流式传输的HTTP传输

可流式传输的HTTP传输支持两种响应模式:

SSE流模式(默认)

用于实时响应流:

final serverResult = await McpServer.createAndStart(
  config: McpServer.simpleConfig(
    name: '我的服务器',
    version: '1.0.0',
  ),
  transportConfig: TransportConfig.streamableHttp(
    host: 'localhost',
    port: 8081,
    endpoint: '/mcp',
    isJsonResponseEnabled: false, // SSE流模式
  ),
);

JSON响应模式

用于单一JSON响应(更简单但无流):

final serverResult = await McpServer.createAndStart(
  config: McpServer.simpleConfig(
    name: '我的服务器',
    version: '1.0.0',
  ),
  transportConfig: TransportConfig.streamableHttp(
    host: 'localhost',
    port:  8081,
    endpoint: '/mcp',
    isJsonResponseEnabled: true, // JSON响应模式
  ),
);

重要注意事项:

  • 响应模式在服务器启动时固定,不能动态更改
  • 客户端必须在其Accept头中包含application/jsontext/event-stream,无论服务器模式如何
  • SSE模式允许