From aa53b5cf54760ac736bb5c2f82f1abdff71ba4f6 Mon Sep 17 00:00:00 2001 From: Jake MacDonald Date: Wed, 12 Nov 2025 09:41:53 -0800 Subject: [PATCH 1/2] detect VM service connections and return a nicer error --- pkgs/dart_mcp_server/lib/src/mixins/dtd.dart | 31 ++++++++++++++++++- pkgs/dart_mcp_server/test/test_harness.dart | 17 ++++++---- pkgs/dart_mcp_server/test/tools/dtd_test.dart | 19 ++++++++++++ .../dart_cli_app/bin/infinite_wait.dart | 9 ++++++ 4 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 pkgs/dart_mcp_server/test_fixtures/dart_cli_app/bin/infinite_wait.dart diff --git a/pkgs/dart_mcp_server/lib/src/mixins/dtd.dart b/pkgs/dart_mcp_server/lib/src/mixins/dtd.dart index f8f27938..0a256b66 100644 --- a/pkgs/dart_mcp_server/lib/src/mixins/dtd.dart +++ b/pkgs/dart_mcp_server/lib/src/mixins/dtd.dart @@ -8,6 +8,7 @@ import 'dart:convert'; import 'package:dart_mcp/server.dart'; import 'package:dds_service_extensions/dds_service_extensions.dart'; import 'package:dtd/dtd.dart'; +import 'package:json_rpc_2/json_rpc_2.dart'; import 'package:meta/meta.dart'; import 'package:unified_analytics/unified_analytics.dart' as ua; import 'package:vm_service/vm_service.dart'; @@ -231,9 +232,23 @@ base mixin DartToolingDaemonSupport } try { - _dtd = await DartToolingDaemon.connect( + final dtd = _dtd = await DartToolingDaemon.connect( Uri.parse(request.arguments![ParameterNames.uri] as String), ); + try { + await dtd.call(null, 'getVM'); + // If the call above succeeds, we were connected to the vm service, and + // should error. + await _resetDtd(); + return _gotVmServiceUri; + } on RpcException catch (e) { + // Double check the failure was a method not found failure, if not + // rethrow it. + if (e.code != RpcErrorCodes.kMethodNotFound) { + await _resetDtd(); + rethrow; + } + } unawaited(_dtd!.done.then((_) async => await _resetDtd())); await _listenForServices(); @@ -1098,6 +1113,20 @@ base mixin DartToolingDaemonSupport isError: true, )..failureReason = CallToolFailureReason.flutterDriverNotEnabled; + static final _gotVmServiceUri = CallToolResult( + content: [ + Content.text( + text: + 'Connected to a VM Service but expected to connect to a Dart ' + 'Tooling Daemon service. When launching apps from an IDE you ' + 'should have a "Copy DTD URI to clipboard" command pallete option, ' + 'or when directly launching apps from a terminal you can pass the ' + '"--print-dtd" command line option in order to get the DTD URI.', + ), + ], + isError: true, + ); + static final runtimeErrorsScheme = 'runtime-errors'; static const _defaultTimeoutMs = 5000; diff --git a/pkgs/dart_mcp_server/test/test_harness.dart b/pkgs/dart_mcp_server/test/test_harness.dart index 0cc624dd..1d3b422c 100644 --- a/pkgs/dart_mcp_server/test/test_harness.dart +++ b/pkgs/dart_mcp_server/test/test_harness.dart @@ -143,11 +143,16 @@ class TestHarness { await AppDebugSession.kill(session.appProcess, session.isFlutter); } - /// Connects the MCP server to the dart tooling daemon at the `dtdUri` from - /// [fakeEditorExtension] using the "connectDartToolingDaemon" tool function. + /// Connects the MCP server to the dart tooling daemon at the [dtdUri] using + /// the "connectDartToolingDaemon" tool function. + /// + /// By default the DTD Uri will come from the [fakeEditorExtension]. /// /// This mimics a user using the "copy DTD Uri from clipboard" action. - Future connectToDtd() async { + Future connectToDtd({ + String? dtdUri, + bool expectError = false, + }) async { final tools = (await mcpServerConnection.listTools()).tools; final connectTool = tools.singleWhere( @@ -157,11 +162,11 @@ class TestHarness { final result = await callToolWithRetry( CallToolRequest( name: connectTool.name, - arguments: {ParameterNames.uri: fakeEditorExtension.dtdUri}, + arguments: {ParameterNames.uri: dtdUri ?? fakeEditorExtension.dtdUri}, ), + expectError: expectError, ); - - expect(result.isError, isNot(true), reason: result.content.join('\n')); + return result; } /// Helper to send [request] to [mcpServerConnection]. diff --git a/pkgs/dart_mcp_server/test/tools/dtd_test.dart b/pkgs/dart_mcp_server/test/tools/dtd_test.dart index 597091da..0b752dfe 100644 --- a/pkgs/dart_mcp_server/test/tools/dtd_test.dart +++ b/pkgs/dart_mcp_server/test/tools/dtd_test.dart @@ -794,6 +794,25 @@ void main() { expect(log.characters, 10); }); }); + + test('connect_to_dtd will reject a vm service URI', () async { + final testHarness = await TestHarness.start(inProcess: true); + final debugSession = await testHarness.startDebugSession( + dartCliAppsPath, + 'bin/infinite_wait.dart', + isFlutter: false, + ); + final connectResult = await testHarness.connectToDtd( + dtdUri: debugSession.vmServiceUri, + expectError: true, + ); + expect( + (connectResult.content.first as TextContent).text, + contains('Connected to a VM Service'), + ); + final retryResult = await testHarness.connectToDtd(); + expect(retryResult.isError, isNot(true)); + }); } extension on Iterable { diff --git a/pkgs/dart_mcp_server/test_fixtures/dart_cli_app/bin/infinite_wait.dart b/pkgs/dart_mcp_server/test_fixtures/dart_cli_app/bin/infinite_wait.dart new file mode 100644 index 00000000..9c1a6149 --- /dev/null +++ b/pkgs/dart_mcp_server/test_fixtures/dart_cli_app/bin/infinite_wait.dart @@ -0,0 +1,9 @@ +// 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. + +void main() async { + while (true) { + await Future.delayed(const Duration(seconds: 1)); + } +} From 7a2338f80677a82fe7125acd548729a3f029ee81 Mon Sep 17 00:00:00 2001 From: Jake MacDonald Date: Wed, 12 Nov 2025 09:46:13 -0800 Subject: [PATCH 2/2] update changelog also --- pkgs/dart_mcp_server/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/dart_mcp_server/CHANGELOG.md b/pkgs/dart_mcp_server/CHANGELOG.md index 558869f9..60f62612 100644 --- a/pkgs/dart_mcp_server/CHANGELOG.md +++ b/pkgs/dart_mcp_server/CHANGELOG.md @@ -3,6 +3,8 @@ - Add `--tools=dart|all` argument to allow enabling only vanilla Dart tools for non-flutter projects. - Include the device name and target platform in the list_devices tool. +- Fix erroneous SDK version error messages when connecting to a VM Service + instead of DTD URI. # 0.1.1 (Dart SDK 3.10.0)