diff --git a/DEPS b/DEPS index baa4a3b4354..138b2322b95 100644 --- a/DEPS +++ b/DEPS @@ -129,6 +129,7 @@ vars = { ### /third_party/pkg dependencies # 'tools/rev_sdk_deps.dart' will rev pkg dependencies to their latest; put an # EOL comment after a dependency to instead pin at the current revision. + "ai_rev": "93a9191b6eed79d24fc5d3ec9b514f71c888fc41", "core_rev": "635dfa32c261ba078438b74de397f2207904ca78", # https://github.com/dart-lang/core/pull/734 "dartdoc_rev": "e38f392163a4738a21024acd370d104e0efe72f0", "ecosystem_rev": "815d4ba2e7d11f8695a26f6cbe1262e3b8ff8d0d", @@ -327,6 +328,8 @@ deps = { Var('chromium_git') + '/external/github.com/mdn/browser-compat-data' + "@" + Var("browser-compat-data_tag"), + Var("dart_root") + "/third_party/pkg/ai": + Var("dart_git") + "ai.git" + "@" + Var("ai_rev"), Var("dart_root") + "/third_party/pkg/core": Var("dart_git") + "core.git" + "@" + Var("core_rev"), Var("dart_root") + "/third_party/pkg/dart_style": diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart index aad08c812bb..b3fb052fd35 100644 --- a/pkg/dartdev/lib/dartdev.dart +++ b/pkg/dartdev/lib/dartdev.dart @@ -20,6 +20,7 @@ import 'src/commands/build.dart'; import 'src/commands/compilation_server.dart'; import 'src/commands/compile.dart'; import 'src/commands/create.dart'; +import 'src/commands/dart_mcp_server.dart'; import 'src/commands/debug_adapter.dart'; import 'src/commands/development_service.dart'; import 'src/commands/devtools.dart'; @@ -118,6 +119,7 @@ class DartdevRunner extends CommandRunner { addCommand(FormatCommand(verbose: verbose)); addCommand(InfoCommand(verbose: verbose)); addCommand(LanguageServerCommand(verbose: verbose)); + addCommand(DartMCPServerCommand(verbose: verbose)); addCommand(pubCommand(isVerbose: () => verbose)); addCommand(RunCommand( verbose: verbose, diff --git a/pkg/dartdev/lib/src/commands/dart_mcp_server.dart b/pkg/dartdev/lib/src/commands/dart_mcp_server.dart new file mode 100644 index 00000000000..dc453c039a1 --- /dev/null +++ b/pkg/dartdev/lib/src/commands/dart_mcp_server.dart @@ -0,0 +1,68 @@ +// 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 'dart:async'; + +import '../core.dart'; +import '../sdk.dart'; +import '../vm_interop_handler.dart'; + +class DartMCPServerCommand extends DartdevCommand { + static const String cmdName = 'mcp-server'; + + static const String cmdDescription = ''' +A stdio based Model Context Protocol (MCP) server to aid in Dart and Flutter development. + +EXPERIMENTAL: This tool may change dramatically or disappear at any time.'''; + + static const _forceRootsFallbackFlag = 'force-roots-fallback'; + static const _experimentFlag = 'experimental-mcp-server'; + + DartMCPServerCommand({bool verbose = false}) + : super(cmdName, cmdDescription, verbose, hidden: true) { + argParser + ..addFlag( + _forceRootsFallbackFlag, + negatable: true, + defaultsTo: false, + help: + 'Forces a behavior for project roots which uses MCP tools instead ' + 'of the native MCP roots. This can be helpful for clients like ' + 'Cursor which claim to have roots support but do not actually ' + 'support it.', + ) + ..addFlag(_experimentFlag, + defaultsTo: false, + help: 'A required flag in order to use this command. Passing this ' + 'flag is an acknowledgement that you understand it is an ' + 'experimental feature with no stability guarantees.'); + } + + @override + Future run() async { + final args = argResults!; + if (!args.flag(_experimentFlag)) { + log.stderr('Missing required flag --$_experimentFlag\n\n$usage'); + return 64; + } + try { + VmInteropHandler.run( + sdk.dartAotRuntime, + [ + sdk.dartMCPServerAotSnapshot, + if (args.flag(_forceRootsFallbackFlag)) '--$_forceRootsFallbackFlag' + ], + useExecProcess: true, + ); + return 0; + } catch (e, st) { + log.stderr('Error: launching Dart MCP server failed'); + log.stderr(e.toString()); + if (verbose) { + log.stderr(st.toString()); + } + return 255; + } + } +} diff --git a/pkg/dartdev/lib/src/sdk.dart b/pkg/dartdev/lib/src/sdk.dart index 20835bfb55e..845c38d7071 100644 --- a/pkg/dartdev/lib/src/sdk.dart +++ b/pkg/dartdev/lib/src/sdk.dart @@ -102,6 +102,10 @@ class Sdk { 'dart2wasm_product.snapshot', ); + String get dartMCPServerAotSnapshot => _snapshotPathFor( + 'dart_mcp_server_aot.dart.snapshot', + ); + String get ddsSnapshot => _snapshotPathFor( 'dds.dart.snapshot', ); diff --git a/pubspec.yaml b/pubspec.yaml index 10833799a69..7191316234d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -143,6 +143,10 @@ dependency_overrides: path: third_party/pkg/tools/pkgs/csslib dart_flutter_team_lints: path: third_party/pkg/ecosystem/pkgs/dart_flutter_team_lints + dart_mcp: + path: third_party/pkg/ai/pkgs/dart_mcp + dart_mcp_server: + path: third_party/pkg/ai/pkgs/dart_mcp_server dart_style: path: third_party/pkg/dart_style dartdoc: @@ -221,6 +225,8 @@ dependency_overrides: path: third_party/pkg/tools/pkgs/pool protobuf: path: third_party/pkg/protobuf/protobuf + process: + path: third_party/pkg/tools/pkgs/process protobuf_benchmarks: path: third_party/pkg/protobuf/benchmarks protoc_plugin: @@ -257,6 +263,8 @@ dependency_overrides: path: third_party/pkg/tools/pkgs/stack_trace stream_channel: path: third_party/pkg/tools/pkgs/stream_channel + stream_transform: + path: third_party/pkg/tools/pkgs/stream_transform string_scanner: path: third_party/pkg/tools/pkgs/string_scanner swift2objc: diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn index 0343253075e..37cadc80064 100644 --- a/sdk/BUILD.gn +++ b/sdk/BUILD.gn @@ -151,6 +151,11 @@ if (dart_target_arch != "ia32" && dart_target_arch != "x86") { "../utils/dtd:dtd_aot", "dart_tooling_daemon_aot", ], + [ + "dart_mcp_server_aot_product", + "../utils/dart_mcp_server:dart_mcp_server_aot", + "dart_mcp_server_aot", + ], ] } else { _platform_sdk_snapshots += [ diff --git a/utils/dart_mcp_server/BUILD.gn b/utils/dart_mcp_server/BUILD.gn new file mode 100644 index 00000000000..839ceb8d10a --- /dev/null +++ b/utils/dart_mcp_server/BUILD.gn @@ -0,0 +1,31 @@ +# 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("../../runtime/runtime_args.gni") +import("../aot_snapshot.gni") + +group("dart_mcp_server_aot") { + public_deps = [ + ":dart_mcp_server_aot_product_snapshot", + ":dart_mcp_server_aot_snapshot", + ] +} + +aot_snapshot("dart_mcp_server_aot_snapshot") { + main_dart = "../../third_party/pkg/ai/pkgs/dart_mcp_server/bin/main.dart" + output = "$root_gen_dir/dart_mcp_server_aot.dart.snapshot" +} + +aot_snapshot("dart_mcp_server_aot_product_snapshot") { + main_dart = "../../third_party/pkg/ai/pkgs/dart_mcp_server/bin/main.dart" + output = "$root_gen_dir/dart_mcp_server_aot_product.dart.snapshot" + + # dartaotruntime has dart_product_config applied to it, + # so it is built in product mode in both release and + # product builds, and is only built in debug mode in debug + # builds. The following line ensures that the dartaotruntime + # and dart_mcp_server_aot snapshot in an SDK build are + # always compatible with each other. + force_product_mode = !dart_debug +}