blob: f61c81fe870fdbe92db26855f83df2e2d7c5263c [file] [log] [blame]
// 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, readResource }
/// 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 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;
CallToolMetrics({
required this.tool,
required this.success,
required this.elapsedMilliseconds,
required this.failureReason,
});
@override
Map<String, Object> toMap() => {
_tool: tool,
_success: success,
_elapsedMilliseconds: elapsedMilliseconds,
_failureReason: ?failureReason?.name,
};
}
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 {
argumentError,
connectedAppServiceNotSupported,
dtdAlreadyConnected,
dtdNotConnected,
invalidPath,
invalidRootPath,
invalidRootScheme,
noActiveDebugSession,
noRootGiven,
noRootsSet,
noSuchCommand,
nonZeroExitCode,
webSocketException,
}
const _elapsedMilliseconds = 'elapsedMilliseconds';
const _failureReason = 'failureReason';
const _kind = 'kind';
const _length = 'length';
const _success = 'success';
const _tool = 'tool';