| // Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:dart_mcp/server.dart'; |
| import 'package:unified_analytics/unified_analytics.dart'; |
| |
| /// An interface class that provides a access to an [Analytics] instance, if |
| /// enabled. |
| /// |
| /// The `DartMCPServer` class implements this class so that [Analytics] |
| /// methods can be easily mocked during testing. |
| abstract interface class AnalyticsSupport { |
| Analytics? get analytics; |
| } |
| |
| enum AnalyticsEvent { |
| callTool, |
| initialize, |
| listPrompts, |
| listResources, |
| listResourceTemplates, |
| listTools, |
| readResource, |
| getPrompt, |
| } |
| |
| /// The metrics for an initialize MCP handler. |
| final class InitializeMetrics extends CustomMetrics { |
| final bool supportsElicitation; |
| final bool supportsRoots; |
| final bool supportsSampling; |
| |
| InitializeMetrics({ |
| required this.supportsElicitation, |
| required this.supportsRoots, |
| required this.supportsSampling, |
| }); |
| |
| @override |
| Map<String, Object> toMap() => { |
| _supportsElicitation: supportsElicitation, |
| _supportsRoots: supportsRoots, |
| _supportsSampling: supportsSampling, |
| }; |
| } |
| |
| /// The metrics for a resources/read MCP handler. |
| final class ReadResourceMetrics extends CustomMetrics { |
| /// The kind of resource that was read. |
| /// |
| /// We don't want to record the full URI. |
| final ResourceKind kind; |
| |
| /// The length of the resource. |
| final int length; |
| |
| /// The time it took to read the resource. |
| final int elapsedMilliseconds; |
| |
| ReadResourceMetrics({ |
| required this.kind, |
| required this.length, |
| required this.elapsedMilliseconds, |
| }); |
| |
| @override |
| Map<String, Object> toMap() => { |
| _kind: kind.name, |
| _length: length, |
| _elapsedMilliseconds: elapsedMilliseconds, |
| }; |
| } |
| |
| /// The metrics for a prompts/get MCP handler. |
| final class GetPromptMetrics extends CustomMetrics { |
| /// The name of the prompt that was retrieved. |
| final String name; |
| |
| /// Whether or not the prompt was given with arguments. |
| final bool withArguments; |
| |
| /// The time it took to generate the prompt. |
| final int elapsedMilliseconds; |
| |
| /// Whether or not the prompt call succeeded. |
| final bool success; |
| |
| GetPromptMetrics({ |
| required this.name, |
| required this.withArguments, |
| required this.elapsedMilliseconds, |
| required this.success, |
| }); |
| |
| @override |
| Map<String, Object> toMap() => { |
| _name: name, |
| _withArguments: withArguments, |
| _elapsedMilliseconds: elapsedMilliseconds, |
| _success: success, |
| }; |
| } |
| |
| /// The metrics for a tools/call MCP handler. |
| final class CallToolMetrics extends CustomMetrics { |
| /// The name of the tool that was invoked. |
| final String tool; |
| |
| /// Whether or not the tool call succeeded. |
| final bool success; |
| |
| /// The time it took to invoke the tool. |
| final int elapsedMilliseconds; |
| |
| /// The reason for the failure, if [success] is `false`. |
| final CallToolFailureReason? failureReason; |
| |
| /// Extra metrics reported by the given tool that was called. |
| final CustomMetrics? extraToolMetrics; |
| |
| CallToolMetrics({ |
| required this.tool, |
| required this.success, |
| required this.elapsedMilliseconds, |
| required this.failureReason, |
| required this.extraToolMetrics, |
| }); |
| |
| @override |
| Map<String, Object> toMap() => { |
| _tool: tool, |
| _success: success, |
| _elapsedMilliseconds: elapsedMilliseconds, |
| _failureReason: ?failureReason?.name, |
| ...?extraToolMetrics?.toMap(), |
| }; |
| } |
| |
| enum ResourceKind { runtimeErrors } |
| |
| /// Extension which tracks failure reasons for [CallToolResult] objects in an |
| /// [Expando]. |
| extension WithFailureReason on CallToolResult { |
| static final _expando = Expando<CallToolFailureReason>(); |
| |
| CallToolFailureReason? get failureReason => _expando[this as Object]; |
| |
| set failureReason(CallToolFailureReason? value) => |
| _expando[this as Object] = value; |
| } |
| |
| /// Known reasons for failed tool calls. |
| enum CallToolFailureReason { |
| alreadyDisconnected, |
| applicationNotFound, |
| argumentError, |
| connectedAppServiceNotSupported, |
| dtdAlreadyConnected, |
| dtdNotConnected, |
| flutterDriverNotEnabled, |
| givenVmServiceUri, |
| httpClientException, |
| invalidPath, |
| invalidRootPath, |
| invalidRootScheme, |
| lspStartupFailed, |
| noActiveDebugSession, |
| noPackageConfigFound, |
| noRootGiven, |
| noRootsSet, |
| noSuchCommand, |
| nonZeroExitCode, |
| mustSpecifyDtdUri, |
| processException, |
| rpcError, |
| timeout, |
| unhandledError, |
| webSocketException, |
| wrappedServiceIssue, |
| } |
| |
| extension WithCustomMetrics on CallToolResult { |
| static final _expando = Expando<CustomMetrics>(); |
| |
| CustomMetrics? get customMetrics => _expando[this as Object]; |
| |
| set customMetrics(CustomMetrics? value) => _expando[this as Object] = value; |
| } |
| |
| const _elapsedMilliseconds = 'elapsedMilliseconds'; |
| const _failureReason = 'failureReason'; |
| const _kind = 'kind'; |
| const _length = 'length'; |
| const _name = 'name'; |
| const _success = 'success'; |
| const _tool = 'tool'; |
| const _withArguments = 'withArguments'; |
| const _supportsElicitation = 'supportsElicitation'; |
| const _supportsRoots = 'supportsRoots'; |
| const _supportsSampling = 'supportsSampling'; |