blob: 351dc72b38ff1d7e818e4fb9fe8731c532978d60 [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 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
import 'package:args/args.dart';
import 'package:async/async.dart';
import 'package:dart_mcp/server.dart';
import 'package:dart_mcp_server/dart_mcp_server.dart';
import 'package:stream_channel/stream_channel.dart';
void main(List<String> args) async {
final parsedArgs = argParser.parse(args);
if (parsedArgs.flag(help)) {
print(argParser.usage);
io.exit(0);
}
DartMCPServer? server;
final dartSdkPath =
parsedArgs.option(dartSdkOption) ?? io.Platform.environment['DART_SDK'];
final flutterSdkPath =
parsedArgs.option(flutterSdkOption) ??
io.Platform.environment['FLUTTER_SDK'];
runZonedGuarded(
() {
server = DartMCPServer(
StreamChannel.withCloseGuarantee(io.stdin, io.stdout)
.transform(StreamChannelTransformer.fromCodec(utf8))
.transformStream(const LineSplitter())
.transformSink(
StreamSinkTransformer.fromHandlers(
handleData: (data, sink) {
sink.add('$data\n');
},
),
),
forceRootsFallback: parsedArgs.flag(forceRootsFallback),
sdk: Sdk.find(dartSdkPath: dartSdkPath, flutterSdkPath: flutterSdkPath),
);
},
(e, s) {
if (server != null) {
try {
// Log unhandled errors to the client, if we managed to connect.
server!.log(LoggingLevel.error, '$e\n$s');
} catch (_) {}
} else {
// Otherwise log to stderr.
io.stderr
..writeln(e)
..writeln(s);
}
},
zoneSpecification: ZoneSpecification(
print: (_, _, _, value) {
if (server != null) {
try {
// Don't allow `print` since this breaks stdio communication, but if
// we have a server we do log messages to the client.
server!.log(LoggingLevel.info, value);
} catch (_) {}
}
},
),
);
}
final argParser =
ArgParser(allowTrailingOptions: false)
..addOption(
dartSdkOption,
help:
'The path to the root of the desired Dart SDK. Defaults to the '
'DART_SDK environment variable.',
)
..addOption(
flutterSdkOption,
help:
'The path to the root of the desired Flutter SDK. Defaults to '
'the FLUTTER_SDK environment variable, then searching up from the '
'Dart SDK.',
)
..addFlag(
forceRootsFallback,
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(help, abbr: 'h', help: 'Show usage text');
const dartSdkOption = 'dart-sdk';
const flutterSdkOption = 'flutter-sdk';
const forceRootsFallback = 'force-roots-fallback';
const help = 'help';