Skip to content

Commit f2b48c6

Browse files
authored
Retain compatibility with 3.7 SDKs (#163)
The code will only ship with newer SDKs but keep compatibility for easier testing from source with older SDKs. Remove use of null aware elements.
1 parent 52adf08 commit f2b48c6

File tree

12 files changed

+179
-152
lines changed

12 files changed

+179
-152
lines changed

pkgs/dart_mcp_server/bin/main.dart

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,31 +69,32 @@ void main(List<String> args) async {
6969
);
7070
}
7171

72-
final argParser = ArgParser(allowTrailingOptions: false)
73-
..addOption(
74-
dartSdkOption,
75-
help:
76-
'The path to the root of the desired Dart SDK. Defaults to the '
77-
'DART_SDK environment variable.',
78-
)
79-
..addOption(
80-
flutterSdkOption,
81-
help:
82-
'The path to the root of the desired Flutter SDK. Defaults to '
83-
'the FLUTTER_SDK environment variable, then searching up from the '
84-
'Dart SDK.',
85-
)
86-
..addFlag(
87-
forceRootsFallback,
88-
negatable: true,
89-
defaultsTo: false,
90-
help:
91-
'Forces a behavior for project roots which uses MCP tools instead '
92-
'of the native MCP roots. This can be helpful for clients like '
93-
'cursor which claim to have roots support but do not actually '
94-
'support it.',
95-
)
96-
..addFlag(help, abbr: 'h', help: 'Show usage text');
72+
final argParser =
73+
ArgParser(allowTrailingOptions: false)
74+
..addOption(
75+
dartSdkOption,
76+
help:
77+
'The path to the root of the desired Dart SDK. Defaults to the '
78+
'DART_SDK environment variable.',
79+
)
80+
..addOption(
81+
flutterSdkOption,
82+
help:
83+
'The path to the root of the desired Flutter SDK. Defaults to '
84+
'the FLUTTER_SDK environment variable, then searching up from the '
85+
'Dart SDK.',
86+
)
87+
..addFlag(
88+
forceRootsFallback,
89+
negatable: true,
90+
defaultsTo: false,
91+
help:
92+
'Forces a behavior for project roots which uses MCP tools instead '
93+
'of the native MCP roots. This can be helpful for clients like '
94+
'cursor which claim to have roots support but do not actually '
95+
'support it.',
96+
)
97+
..addFlag(help, abbr: 'h', help: 'Show usage text');
9798

9899
const dartSdkOption = 'dart-sdk';
99100
const flutterSdkOption = 'flutter-sdk';

pkgs/dart_mcp_server/lib/src/mixins/analyzer.dart

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,19 @@ base mixin DartAnalyzerSupport
109109
log(LoggingLevel.warning, line, logger: 'DartLanguageServer');
110110
});
111111

112-
_lspConnection = Peer(lspChannel(_lspServer.stdout, _lspServer.stdin))
113-
..registerMethod(
114-
lsp.Method.textDocument_publishDiagnostics.toString(),
115-
_handleDiagnostics,
116-
)
117-
..registerMethod(r'$/analyzerStatus', _handleAnalyzerStatus)
118-
..registerFallback((Parameters params) {
119-
log(
120-
LoggingLevel.debug,
121-
() => 'Unhandled LSP message: ${params.method} - ${params.asMap}',
122-
);
123-
});
112+
_lspConnection =
113+
Peer(lspChannel(_lspServer.stdout, _lspServer.stdin))
114+
..registerMethod(
115+
lsp.Method.textDocument_publishDiagnostics.toString(),
116+
_handleDiagnostics,
117+
)
118+
..registerMethod(r'$/analyzerStatus', _handleAnalyzerStatus)
119+
..registerFallback((Parameters params) {
120+
log(
121+
LoggingLevel.debug,
122+
() => 'Unhandled LSP message: ${params.method} - ${params.asMap}',
123+
);
124+
});
124125

125126
unawaited(_lspConnection.listen());
126127

@@ -354,9 +355,8 @@ base mixin DartAnalyzerSupport
354355
diagnostics[diagnosticParams.uri] = diagnosticParams.diagnostics;
355356
log(LoggingLevel.debug, {
356357
ParameterNames.uri: diagnosticParams.uri,
357-
'diagnostics': diagnosticParams.diagnostics
358-
.map((d) => d.toJson())
359-
.toList(),
358+
'diagnostics':
359+
diagnosticParams.diagnostics.map((d) => d.toJson()).toList(),
360360
});
361361
}
362362

@@ -368,18 +368,16 @@ base mixin DartAnalyzerSupport
368368
final newRoots = await roots;
369369

370370
final oldWorkspaceFolders = _currentWorkspaceFolders;
371-
final newWorkspaceFolders = _currentWorkspaceFolders =
372-
HashSet<lsp.WorkspaceFolder>(
371+
final newWorkspaceFolders =
372+
_currentWorkspaceFolders = HashSet<lsp.WorkspaceFolder>(
373373
equals: (a, b) => a.uri == b.uri,
374374
hashCode: (a) => a.uri.hashCode,
375375
)..addAll(newRoots.map((r) => r.asWorkspaceFolder));
376376

377-
final added = newWorkspaceFolders
378-
.difference(oldWorkspaceFolders)
379-
.toList();
380-
final removed = oldWorkspaceFolders
381-
.difference(newWorkspaceFolders)
382-
.toList();
377+
final added =
378+
newWorkspaceFolders.difference(oldWorkspaceFolders).toList();
379+
final removed =
380+
oldWorkspaceFolders.difference(newWorkspaceFolders).toList();
383381

384382
// This can happen in the case of multiple notifications in quick
385383
// succession, the `roots` future will complete only after the state has

pkgs/dart_mcp_server/lib/src/mixins/dash_cli.dart

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,10 @@ base mixin DashCliSupport on ToolsSupport, LoggingSupport, RootsTrackingSupport
109109
// Platforms are ignored for Dart, so no need to validate them.
110110
final invalidPlatforms = platforms.difference(_allowedFlutterPlatforms);
111111
if (invalidPlatforms.isNotEmpty) {
112-
final plural = invalidPlatforms.length > 1
113-
? 'are not valid platforms'
114-
: 'is not a valid platform';
112+
final plural =
113+
invalidPlatforms.length > 1
114+
? 'are not valid platforms'
115+
: 'is not a valid platform';
115116
errors.add(
116117
ValidationError(
117118
ValidationErrorType.itemInvalid,
@@ -152,13 +153,14 @@ base mixin DashCliSupport on ToolsSupport, LoggingSupport, RootsTrackingSupport
152153
return runCommandInRoot(
153154
request,
154155
arguments: commandArgs,
155-
commandForRoot: (_, _, sdk) =>
156-
switch (projectType) {
157-
'dart' => sdk.dartExecutablePath,
158-
'flutter' => sdk.flutterExecutablePath,
159-
_ => StateError('Unknown project type: $projectType'),
160-
}
161-
as String,
156+
commandForRoot:
157+
(_, _, sdk) =>
158+
switch (projectType) {
159+
'dart' => sdk.dartExecutablePath,
160+
'flutter' => sdk.flutterExecutablePath,
161+
_ => StateError('Unknown project type: $projectType'),
162+
}
163+
as String,
162164
commandDescription: '$projectType create',
163165
fileSystem: fileSystem,
164166
processManager: processManager,

pkgs/dart_mcp_server/lib/src/mixins/dtd.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,10 @@ base mixin DartToolingDaemonSupport
9696
continue;
9797
}
9898
if (debugSession.vmServiceUri case final vmServiceUri?) {
99-
final vmServiceFuture = activeVmServices[debugSession.id] =
100-
vmServiceConnectUri(vmServiceUri);
99+
final vmServiceFuture =
100+
activeVmServices[debugSession.id] = vmServiceConnectUri(
101+
vmServiceUri,
102+
);
101103
final vmService = await vmServiceFuture;
102104
// Start listening for and collecting errors immediately.
103105
final errorService = await _AppErrorsListener.forVmService(

pkgs/dart_mcp_server/lib/src/mixins/pub_dev_search.dart

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,11 @@ base mixin PubDevSupport on ToolsSupport {
4343
try {
4444
result = jsonDecode(await _client.read(searchUrl));
4545

46-
final packageNames = dig<List>(result, [
47-
'packages',
48-
]).take(_resultsLimit).map((p) => dig<String>(p, ['package'])).toList();
46+
final packageNames =
47+
dig<List>(result, ['packages'])
48+
.take(_resultsLimit)
49+
.map((p) => dig<String>(p, ['package']))
50+
.toList();
4951

5052
if (packageNames.isEmpty) {
5153
return CallToolResult(
@@ -69,17 +71,18 @@ base mixin PubDevSupport on ToolsSupport {
6971
}
7072

7173
// Retrieve information about all the packages in parallel.
72-
final subQueryFutures = packageNames
73-
.map(
74-
(packageName) => (
75-
versionListing: retrieve('api/packages/$packageName'),
76-
score: retrieve('api/packages/$packageName/score'),
77-
docIndex: retrieve(
78-
'documentation/$packageName/latest/index.json',
79-
),
80-
),
81-
)
82-
.toList();
74+
final subQueryFutures =
75+
packageNames
76+
.map(
77+
(packageName) => (
78+
versionListing: retrieve('api/packages/$packageName'),
79+
score: retrieve('api/packages/$packageName/score'),
80+
docIndex: retrieve(
81+
'documentation/$packageName/latest/index.json',
82+
),
83+
),
84+
)
85+
.toList();
8386

8487
// Aggregate the retrieved information about each package into a
8588
// TextContent.
@@ -94,10 +97,11 @@ base mixin PubDevSupport on ToolsSupport {
9497
?.cast<Map<String, Object?>>() ??
9598
<Map<String, Object?>>[])
9699
if (!object.containsKey('enclosedBy'))
97-
object['name'] as String: Uri.https(
98-
'pub.dev',
99-
'documentation/$packageName/latest/${object['href']}',
100-
).toString(),
100+
object['name'] as String:
101+
Uri.https(
102+
'pub.dev',
103+
'documentation/$packageName/latest/${object['href']}',
104+
).toString(),
101105
};
102106
results.add(
103107
TextContent(
@@ -108,26 +112,34 @@ base mixin PubDevSupport on ToolsSupport {
108112
'latest',
109113
'version',
110114
]),
111-
'description': ?dig<String?>(versionListing, [
112-
'latest',
113-
'pubspec',
114-
'description',
115-
]),
116-
'homepage': ?dig<String?>(versionListing, [
117-
'latest',
118-
'pubspec',
119-
'homepage',
120-
]),
121-
'repository': ?dig<String?>(versionListing, [
122-
'latest',
123-
'pubspec',
124-
'repository',
125-
]),
126-
'documentation': ?dig<String?>(versionListing, [
127-
'latest',
128-
'pubspec',
129-
'documentation',
130-
]),
115+
if (dig<String?>(versionListing, [
116+
'latest',
117+
'pubspec',
118+
'description',
119+
])
120+
case final description?)
121+
'description': description,
122+
if (dig<String?>(versionListing, [
123+
'latest',
124+
'pubspec',
125+
'homepage',
126+
])
127+
case final homepage?)
128+
'homepage': homepage,
129+
if (dig<String?>(versionListing, [
130+
'latest',
131+
'pubspec',
132+
'repository',
133+
])
134+
case final repository?)
135+
'repository': repository,
136+
if (dig<String?>(versionListing, [
137+
'latest',
138+
'pubspec',
139+
'documentation',
140+
])
141+
case final documentation?)
142+
'documentation': documentation,
131143
},
132144
if (libraryDocs.isNotEmpty) ...{'libraries': libraryDocs},
133145
if (scoreResult != null) ...{
@@ -139,15 +151,19 @@ base mixin PubDevSupport on ToolsSupport {
139151
'downloadCount30Days',
140152
]),
141153
},
142-
'topics': dig<List>(scoreResult, [
143-
'tags',
144-
]).where((t) => (t as String).startsWith('topic:')).toList(),
145-
'licenses': dig<List>(scoreResult, [
146-
'tags',
147-
]).where((t) => (t as String).startsWith('license')).toList(),
148-
'publisher': dig<List>(scoreResult, ['tags'])
149-
.where((t) => (t as String).startsWith('publisher:'))
150-
.firstOrNull,
154+
'topics':
155+
dig<List>(
156+
scoreResult,
157+
['tags'],
158+
).where((t) => (t as String).startsWith('topic:')).toList(),
159+
'licenses':
160+
dig<List>(scoreResult, ['tags'])
161+
.where((t) => (t as String).startsWith('license'))
162+
.toList(),
163+
'publisher':
164+
dig<List>(scoreResult, ['tags'])
165+
.where((t) => (t as String).startsWith('publisher:'))
166+
.firstOrNull,
151167
},
152168
}),
153169
),

pkgs/dart_mcp_server/lib/src/mixins/roots_fallback_support.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ base mixin RootsFallbackSupport on ToolsSupport, RootsTrackingSupport {
5050
// If the client supports roots, just use their stream (or lack thereof).
5151
// If they don't, use our own stream.
5252
_fallbackEnabled
53-
? _rootsListChangedFallbackController?.stream
54-
: super.rootsListChanged;
53+
? _rootsListChangedFallbackController?.stream
54+
: super.rootsListChanged;
5555

5656
StreamController<RootsListChangedNotification>?
5757
_rootsListChangedFallbackController;
@@ -76,8 +76,8 @@ base mixin RootsFallbackSupport on ToolsSupport, RootsTrackingSupport {
7676
@override
7777
Future<ListRootsResult> listRoots(ListRootsRequest request) async =>
7878
_fallbackEnabled
79-
? ListRootsResult(roots: _customRoots.toList())
80-
: super.listRoots(request);
79+
? ListRootsResult(roots: _customRoots.toList())
80+
: super.listRoots(request);
8181

8282
/// Adds the roots in [request] the custom roots and calls [updateRoots].
8383
///

pkgs/dart_mcp_server/lib/src/utils/cli_utils.dart

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ Future<CallToolResult> runCommandInRoots(
8585
List<String> defaultPaths = const <String>[],
8686
required Sdk sdk,
8787
}) async {
88-
var rootConfigs = (request.arguments?[ParameterNames.roots] as List?)
89-
?.cast<Map<String, Object?>>();
88+
var rootConfigs =
89+
(request.arguments?[ParameterNames.roots] as List?)
90+
?.cast<Map<String, Object?>>();
9091

9192
// Default to use the known roots if none were specified.
9293
if (rootConfigs == null || rootConfigs.isEmpty) {
@@ -256,12 +257,13 @@ Future<String> defaultCommandForRoot(
256257
) async => switch (await inferProjectKind(rootUri, fileSystem)) {
257258
ProjectKind.dart => sdk.dartExecutablePath,
258259
ProjectKind.flutter => sdk.flutterExecutablePath,
259-
ProjectKind.unknown => throw ArgumentError.value(
260-
rootUri,
261-
'rootUri',
262-
'Unknown project kind at root $rootUri. All projects must have a '
263-
'pubspec.',
264-
),
260+
ProjectKind.unknown =>
261+
throw ArgumentError.value(
262+
rootUri,
263+
'rootUri',
264+
'Unknown project kind at root $rootUri. All projects must have a '
265+
'pubspec.',
266+
),
265267
};
266268

267269
/// Returns whether [uri] is under or exactly equal to [root].

pkgs/dart_mcp_server/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ description: >-
66
publish_to: none
77

88
environment:
9-
sdk: ^3.8.0
9+
sdk: ^3.7.0
1010

1111
executables:
1212
dart_mcp_server: main

0 commit comments

Comments
 (0)