mcpField
M
MCP Server

MCP Server

本地服务
github.com/app-appplayer开发工具92 周前更新

关于 MCP Server

MCP Server 是供 Flutter 应用开发者构建 MCP 服务的 Dart 插件。它实现多版 MCP 协议及其核心原语,并提供可配置传输、授权、任务处理和会话监控。

主要功能

  1. 01支持资源、工具、提示词和补全
  2. 02支持多个 MCP 协议版本
  3. 03支持 stdio、SSE 和 Streamable HTTP
  4. 04授权与 Schema 验证
  5. 05任务与会话管理
  6. 06健康检查与指标

适用场景

  1. 01向 LLM 客户端公开 Flutter 应用数据和操作
  2. 02通过 stdio 或 HTTP 传输提供 MCP 服务
  3. 03将长时间运行的工具调用作为任务处理

MCP 相关

它实现 MCP 服务,向 MCP 客户端公开资源、工具和提示词,并支持 stdio、SSE 与 Streamable HTTP 传输。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

MCP 服务

用于实现 模型上下文协议(MCP) 服务的 Dart 插件。此插件允许 Flutter 应用以标准化方式向大语言模型(LLM)应用公开数据、功能和交互模式。

功能

  • 支持所有 MCP 修订版本,从 2024-11-05 到 2026-07-28,以及 2026-07-28 tasks 扩展版——详见下文的 协议版本
  • 核心原语——Resources、Tools、Prompts,以及 Completions(2025-06-18 起)
  • 由服务端发起的请求——requestClientSampling、requestClientRoots、requestClientElicitation,支持响应路由和超时
  • 授权——每个 HTTP 请求都会在分发前进行判定(401 / 403,附带 WWW-Authenticate),支持受保护资源元数据(RFC 9728)、受众和作用域检查
  • 结构化工具输出(2025-06-18)——outputSchema + structuredContent + resource_link 内容类型
  • 图标 + 采样工具调用(2025-11-25)——Tool.icons / Resource.icons / Prompt.icons,以及 sampling/createMessage 中新增的 tools / toolChoice 字段
  • 列表变更通知——符合规范的 notifications/{tools,resources,prompts}/list_changed
  • 进度 + 取消——出站 notifyProgress;入站 notifications/cancelled 和 notifications/progress 监听器
  • 会话事件——onConnect / onDisconnect 流、每个会话的协议版本 + 功能
  • 缓存提示(2026-07-28)——discover、列表和读取操作中的 ttlMs / cacheScope(cacheTtl、publicLists)
  • 运行状况 + 指标——getHealth()、命名计时器、自定义计数器
  • 传输方式——stdio、SSE(旧版)、Streamable HTTP(2025-03-26 起的规范方式)
  • 平台:支持 dart:io 的平台(Android、iOS、Linux、Windows、macOS)

协议版本

实现模型上下文协议的所有修订版本;每个客户端都会使用其连接时指定的版本。

版本说明
2024-11-05原始版本;HTTP+SSE 传输
2025-03-26Streamable HTTP、授权、JSON-RPC 批处理
2025-06-18Elicitation、结构化工具输出、资源链接、MCP-Protocol-Version 标头;移除批处理
2025-11-25图标、带工具的采样、URL elicitation、tasks
2026-07-28无状态:无需握手、server/discover、每个请求的 _meta、subscriptions/listen、多轮请求、缓存提示;tasks 扩展版

initialize 会在 McpProtocol.supportedVersions(2025-11-25 及更早版本)中进行协商。2026-07-28 无需握手:在 stdio 上,会根据请求在 _meta 中指定的版本提供服务;通过 Streamable HTTP 时,则在与会话版本相同的端点上提供服务。保留每会话状态的服务会设置 enableStateless: false(TransportConfig.streamableHttp 或 StreamableHttpServerConfig),而 2026-07-28 客户端会回退到 initialize。

从 2.x 迁移:MIGRATION-3.0.md。

任务

工具的 execution.taskSupport(optional、required、forbidden) 决定调用是否作为任务运行。

  • 2025-11-25(请求方驱动):声明 ServerCapabilities(tasks: {'list': {}, 'cancel': {}, 'requests': {'tools': {'call': {}}}})。 携带 task 的调用会收到 CreateTaskResult,并在后台运行;随后可调用 tasks/get、tasks/result(等待任务结束)、tasks/list 和 tasks/cancel。服务器也可以请求客户端将 sampling/createMessage / elicitation/create 作为任务运行 (requestClientTask、clientTaskResult 等)。
  • 2026-07-28(tasks 扩展,服务器定向):声明 extensions: {tasksExtensionId: {}}。客户端声明了该扩展后,其调用工具时,如果工具允许,则会作为任务运行;随后可调用 tasks/get、 tasks/update 和 tasks/cancel。
  • 在任务执行过程中,server.currentTask.requestInput({...}) 会向客户端请求输入(2025-11-25 使用带标签的请求,扩展中使用 inputRequests)。

任务归属于经过身份验证的用户;否则(2025-11-25)归属于创建任务的会话,其他人无法读取、列出或取消这些任务。如果两者都不存在——即未进行身份验证的 2026-07-28 路径——任务仅由其 ID(128 位随机数)保护;如果任务结果敏感,请使用身份验证。限制:McpTaskStore(maxTtl:, defaultTtl:, maxTasksPerOwner:)。

JSON Schema

JsonSchemaValidator(由此软件包导出)会评估 MCP 携带的架构——工具的 inputSchema / outputSchema、elicitation 的 requestedSchema。

  • 方言:2020-12(架构未指定 $schema 时使用)、2019-09、 draft-07、draft-06、draft-04。若架构指定其他方言,则会以 JsonSchemaException 拒绝。
  • 引用会在架构内部、传入 registry 的文档中,以及内置元架构中解析。不会从网络获取内容;无法解析的引用会导致架构被拒绝。
  • 评估受到限制(JsonSchemaLimits:深度、子架构数量、评估次数、时间)。
  • format 默认为注解;设置 assertFormat: true 后会检查 email、uri、date 和 date-time。
  • 一致性:官方 JSON-Schema-Test-Suite(涵盖全部五种方言的必需测试,以及上述功能的可选测试)。

开始使用

基本用法
import 'package:mcp_server/mcp_server.dart';

void main() {
  // Create a server with simple boolean capabilities
  final server = Server(
    name: 'Example Server',
    version: '1.0.0',
    capabilities: ServerCapabilities.simple(
      tools: true,
      resources: true,
      prompts: true,
    ),
  );

  // Add a simple calculator tool
  server.addTool(
    name: 'calculator',
    description: 'Perform basic calculations',
    inputSchema: {
      'type': 'object',
      'properties': {
        'operation': {
          'type': 'string',
          'enum': ['add', 'subtract', 'multiply', 'divide'],
          'description': 'Mathematical operation to perform'
        },
        'a': {
          'type': 'number',
          'description': 'First operand'
        },
        'b': {
          'type': 'number',
          'description': 'Second operand'
        }
      },
      '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 'add':
          result = a + b;
          break;
        case 'subtract':
          result = a - b;
          break;
        case 'multiply':
          result = a * b;
          break;
        case 'divide':
          if (b == 0) {
            return CallToolResult(
              content: [TextContent(text: 'Division by zero error')],
              isError: true,
            );
          }
          result = a / b;
          break;
        default:
          return CallToolResult(
            content: [TextContent(text: 'Unknown operation: $operation')],
            isError: true,
          );
      }
      
      return CallToolResult(content: [TextContent(text: 'Result: $result')]);
    },
  );

  // Add a resource
  server.addResource(
    uri: 'time://current',
    name: 'Current Time',
    description: 'Get the current date and time',
    mimeType: 'text/plain',
    handler: (uri, params) async {
      final now = DateTime.now().toString();
      
      return ReadResourceResult(
        contents: [
          TextResourceContents(
            uri: uri,
            text: now,
            mimeType: 'text/plain',
          ),
        ],
      );
    },
  );

  // Add a template prompt
  server.addPrompt(
    name: 'greeting',
    description: 'Generate a customized greeting',
    arguments: [
      PromptArgument(
        name: 'name',
        description: 'Name to greet',
        required: true,
      ),
      PromptArgument(
        name: 'formal',
        description: 'Whether to use formal greeting style',
        required: false,
      ),
    ],
    handler: (arguments) async {
      final name = arguments['name'] as String;
      final formal = arguments['formal'] as bool? ?? false;

      final String systemPrompt = formal
          ? 'You are a formal assistant. Address the user with respect and formality.'
          : 'You are a friendly assistant. Be warm and casual in your tone.';

      // Prompt messages are `user` or `assistant`; instructions go in the
      // text (the specification has no system role here).
      final messages = [
        PromptMessage(
          role: Role.user,
          content: TextContent(text: '$systemPrompt\n\nPlease greet $name'),
        ),
      ];

      return GetPromptResult(
        description: 'A ${formal ? 'formal' : 'casual'} greeting for $name',
        messages: messages,
      );
    },
  );

  // Connect to transport - old way (still supported)
  final transportResult = McpServer.createStdioTransport();
  final transport = transportResult.get();
  server.connect(transport);
}

// NEW: Simplified unified API (recommended)
void simplifiedMain() async {
  final serverResult = await McpServer.createAndStart(
    config: McpServer.simpleConfig(
      name: 'Example Server',
      version: '1.0.0',
    ),
    transportConfig: TransportConfig.stdio(),
  );

  await serverResult.fold(
    (server) async {
      // Add tools, resources, prompts as shown above
      
      // Server is already running
      await Future.delayed(const Duration(hours: 24));
    },
    (error) => print('Server failed: $error'),
  );
}
传输配置

MCP Server 现在支持统一的传输配置:

// STDIO Transport
TransportConfig.stdio()

// SSE Transport  
TransportConfig.sse(
  host: 'localhost',
  port: 8080,
  endpoint: '/sse',
  authToken: 'optional-token',
)

// Streamable HTTP Transport - SSE Streaming Mode (default)
TransportConfig.streamableHttp(
  host: 'localhost', 
  port: 8081,
  endpoint: '/mcp',
  isJsonResponseEnabled: false, // SSE streaming mode (default)
)

// Streamable HTTP Transport - JSON Response Mode
TransportConfig.streamableHttp(
  host: 'localhost', 
  port: 8081,
  endpoint: '/mcp',
  isJsonResponseEnabled: true, // JSON response mode
)

核心概念

服务器能力

MCP Server 支持两种配置能力的方式:

简单布尔配置

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

final server = Server(
  name: 'Simple Server',
  version: '1.0.0',
  capabilities: ServerCapabilities.simple(
    tools: true,
    toolsListChanged: true,
    resources: true,
    resourcesSubscribe: true,
    resourcesListChanged: true,
    prompts: true,
    promptsListChanged: true,
    logging: true,
    completions: true,
  ),
);
基于对象的配置

架构中的能力对象,以及 tasks 和 extensions:

final server = Server(
  name: 'Advanced Server',
  version: '1.0.0',
  capabilities: ServerCapabilities(
    tools: ToolsCapability(listChanged: true),
    resources: ResourcesCapability(listChanged: true, subscribe: true),
    prompts: PromptsCapability(listChanged: true),
    logging: LoggingCapability(),
    completions: CompletionsCapability(),
    // 2025-11-25 tasks; 2026-07-28 tasks extension
    tasks: {'list': {}, 'cancel': {}, 'requests': {'tools': {'call': {}}}},
    extensions: {tasksExtensionId: {}},
  ),
);

采样、根目录和 elicitation 是客户端能力:如果客户端声明了这些能力,服务器便会请求使用它们(通过 requestClientSampling、requestClientRoots、requestClientElicitation,或在 2026-07-28 通过 McpMrtr 输入请求)。

服务器

Server 类是你与 MCP 协议交互的核心接口。它负责连接管理、协议合规性和消息路由:

final server = Server(
  name: 'My App',
  version: '1.0.0',
  capabilities: ServerCapabilities.simple(
    tools: true,
    resources: true,
    prompts: true,
  ),
);
资源

资源是向大模型公开数据的方式。它们类似于 REST API 中的 GET 端点——提供数据,但不应执行大量计算或产生副作用:

// Static resource
server.addResource(
  uri: 'config://app',
  name: 'App Configuration',
  description: 'Application configuration data',
  mimeType: 'text/plain',
  handler: (uri, params) async {
    final configData = "app_name=MyApp\nversion=1.0.0\ndebug=false";
    
    return ReadResourceResult(
      contents: [
        TextResourceContents(
          uri: uri,
          text: configData,
          mimeType: 'text/plain',
        ),
      ],
    );
  },
);

// Resource with URI template
server.addResource(
  uri: 'file://{path}',
  name: 'File Resource',
  description: 'Access files on the system',
  mimeType: 'application/octet-stream',
  uriTemplate: {
    'type': 'object',
    'properties': {
      'path': {
        'type': 'string',
        'description': 'Path to the file'
      }
    }
  },
  handler: (uri, params) async {
    // Extract path and read file content
    final path = params['path'] ?? uri.substring('file://'.length);
    final content = await File(path).readAsString();
    
    return ReadResourceResult(
      contents: [
        TextResourceContents(
          uri: uri,
          text: content,
          mimeType: 'text/plain',
        ),
      ],
    );
  },
);
工具

工具让大模型能够通过服务器执行操作。与资源不同,工具通常会执行计算并产生副作用:

server.addTool(
  name: 'currentDateTime',
  description: 'Get the current date and time',
  inputSchema: {
    'type': 'object',
    'properties': {
      'format': {
        'type': 'string',
        'description': 'Output format (full, date, time)',
        'default': 'full'
      }
    },
    'required': []
  },
  handler: (args) async {
    final format = args['format'] as String? ?? 'full';
    final now = DateTime.now();
    
    String result;
    switch (format) {
      case 'date':
        result = '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
        break;
      case 'time':
        result = '${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}';
        break;
      case 'full':
      default:
        result = now.toIso8601String();
        break;
    }
    
    return CallToolResult(content: [TextContent(text: result)]);
  },
);
提示词

提示词是可复用的模板,可帮助大模型有效地与服务器交互:

server.addPrompt(
  name: 'codeReview',
  description: 'Generate a code review for a code snippet',
  arguments: [
    PromptArgument(
      name: 'code',
      description: 'Code to review',
      required: true,
    ),
    PromptArgument(
      name: 'language',
      description: 'Programming language of the code',
      required: true,
    ),
  ],
  handler: (args) async {
    final code = args['code'] as String;
    final language = args['language'] as String;

    final systemPrompt = '''
You are an expert code reviewer. Review the provided code with these guidelines:
1. Identify potential bugs or issues
2. Suggest optimizations for performance or readability
3. Highlight good practices used in the code
4. Provide constructive feedback for improvements
Be specific in your feedback and provide code examples when suggesting changes.
''';

    final messages = [
      PromptMessage(
        role: Role.user,
        content: TextContent(
            text: '$systemPrompt\nPlease review this $language code:\n\n```$language\n$code\n```'),
      ),
    ];

    return GetPromptResult(
      description: 'Code review for $language code',
      messages: messages,
    );
  },
);

会话管理

MCP Server 提供了强大的基于流的系统,用于监控客户端会话事件:

// Create a logger
final logger = Logger('mcp_server.sessions');

// Listen for client connections
server.onConnect.listen((session) {
  logger.info('Client connected: ${session.id}');
  // Initialize session resources
});

// Listen for client disconnections
server.onDisconnect.listen((session) {
  logger.info('Client disconnected: ${session.id}');
  // Clean up session resources
});

ClientSession 对象包含以下有用信息:

  • 会话 ID
  • 连接时间戳
  • 协议版本
  • 客户端能力
  • 客户端根目录
会话管理最佳实践
  1. 始终处理这两种事件:监听连接和断开事件,以维护会话完整性。

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

    server.onConnect.listen(
      (session) {
        // Normal event handling
      },
      onError: (error) {
        logger.severe('Error in connection stream: $error');
      },
    );
    
  3. 不再需要时取消订阅:

    final subscription = server.onConnect.listen((session) { /* ... */ });
    
    // Later, when done:
    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: 'My Server',
    version: '1.0.0',
  ),
  transportConfig: TransportConfig.streamableHttp(
    host: 'localhost',
    port: 8081,
    endpoint: '/mcp',
    isJsonResponseEnabled: false, // SSE streaming mode
  ),
);
JSON 响应模式

用于单个 JSON 响应(更简单,但不支持流式传输):

final serverResult = await McpServer.createAndStart(
  config: McpServer.simpleConfig(
    name: 'My Server',
    version: '1.0.0',
  ),
  transportConfig: TransportConfig.streamableHttp(
    host: 'localhost',
    port: 8081,
    endpoint: '/mcp',
    isJsonResponseEnabled: true, // JSON response mode
  ),
);

重要说明:

  • 响应模式在服务启动时固定,无法动态更改
  • 无论服务使用哪种模式,客户端都必须在 Accept 标头中同时包含 application/json 和 text/event-stream
  • SSE 模式允许流式传输多个响应,而 JSON 模式只返回单个响应

日志记录

该软件包包含全面的日志记录工具,支持自定义级别和格式:

// Create a named logger
final logger = Logger('mcp_server.example');

// Log at different levels
logger.severe('Connection failed: unable to connect to transport');
logger.warning('Resource cache is approaching capacity limit');
logger.info('Server started successfully on port 8080');
logger.fine('Session initialized with protocol version');

// Send structured logs to MCP client
server.sendLog(
  McpLogLevel.info, 
  'Operation registered',
  data: {
    'operationId': 'op-123',
    'sessionId': 'session-456',
    'type': 'tool:calculator',
    'timestamp': DateTime.now().toIso8601String(),
  }
);

MCP 基元

MCP 协议定义了服务可实现的三个核心基元:

基元控制方描述示例用途
提示词用户控制由用户选择调用的交互式模板斜杠命令、菜单选项
资源应用程序控制由客户端应用程序管理的上下文数据文件内容、API 响应
工具模型控制提供给大模型以执行操作的函数API 调用、数据更新

其他功能

进度与取消

需要进度信息的客户端会发送 progressToken;服务会持续报告进度, 直到调用结束(报告频率受 progressMinInterval 限制):

server.addToolWithProgress(
  'longRunningOperation',
  'Perform a long-running operation with progress reporting',
  {'type': 'object'},
  (args, {onProgress}) async {
    for (var i = 0; i < 10; i++) {
      // The client cancelled: stop (no response is sent for a cancelled call).
      if (server.currentRequestCancelled) break;
      onProgress?.call(i / 10, 'Step ${i + 1} of 10');
      await Future<void>.delayed(const Duration(seconds: 1));
    }
    return CallToolResult(content: [TextContent(text: 'Done')]);
  },
);
资源更新
// To the sessions subscribed to the URI (and, on 2026-07-28, the
// subscriptions/listen streams watching it)
server.notifyResourceUpdated('config://app');
缓存提示(2026-07-28)

server/discover、四种列表方法和 resources/read 支持 ttlMs (Server(cacheTtl: ...))和 cacheScope(publicLists),因此客户端可以 复用结果。cacheResource / getCachedResource / invalidateCache 是独立的服务端缓存,应用程序可在处理程序中使用。

授权(HTTP)
server.enableAuthentication(
  myTokenValidator, // TokenValidator: decides each token, reports its scopes and audience
  requiredScopes: (method, params) =>
      method == 'tools/call' && params?['name'] == 'delete' ? ['files:write'] : const [],
);
server.configureProtectedResource(
  resource: 'https://api.example.com/mcp',
  authorizationServers: ['https://auth.example.com'],
  scopesSupported: ['files:read', 'files:write'],
);

每个 HTTP 请求都会在分发前接受检查:没有令牌 → 401;令牌无效或面向其他资源 → 401 invalid_token; 缺少作用域 → 403 insufficient_scope;每个响应都包含带有 resource_metadata 的 WWW-Authenticate。处理程序可通过 McpCaller.current 获取调用方信息。捆绑提供的 OAuthTokenValidator(RFC 7662 内省)会拒绝内省结果中未列出任何受众的令牌。

健康监控
final health = server.getHealth();
Logger('mcp_server.health').info('Running: ${health.isRunning}, '
    'sessions: ${health.connectedSessions}, uptime: ${health.uptime}, '
    'tools: ${health.registeredTools}, resources: ${health.registeredResources}');

server.incrementMetric('api_calls');
server.startTimer('operation_duration');
// ... perform operation
server.stopTimer('operation_duration');

错误处理

工具失败会作为结果返回,而不是协议错误:处理程序抛出异常时, 响应会包含一个 isError 结果;不符合 inputSchema 的参数也会如此 (2025-11-25 及之后版本;此前为 -32602)。未知的工具、提示词或 资源属于协议错误,错误代码由协商后的修订版指定。 处理程序也可以直接返回错误结果:

try {
  // Attempt an operation
  final result = await performOperation();
  return CallToolResult(content: [TextContent(text: result)]);
} catch (e) {
  // Return an error result
  return CallToolResult(
    content: [TextContent(text: 'Operation failed: ${e.toString()}')],
    isError: true,
  );
}

示例

请查看 示例 目录,了解完整的示例应用程序。

资源

问题与反馈

请在我们的问题跟踪器中提交任何问题、错误或功能请求。

许可证

本项目采用 MIT 许可证授权,详情请参阅 LICENSE 文件。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。