Skip to content

Commit c8dc5da

Browse files
authored
don't bail early when running in multiple roots (#218)
Previously if a tool was ran in multiple roots and one of them failed, it would immediately return with just that failure output.
1 parent 2541b6c commit c8dc5da

File tree

4 files changed

+50
-34
lines changed

4 files changed

+50
-34
lines changed

pkgs/dart_mcp_server/CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
# 0.1.0 (Dart SDK 3.8.0) - WP
1+
# 0.1.1 (Dart SDK 3.10.0) - WIP
2+
3+
* Change tools that accept multiple roots to not return immediately on the first
4+
failure.
5+
6+
# 0.1.0 (Dart SDK 3.9.0)
27

38
* Add documentation/homepage/repository links to pub results.
49
* Handle relative paths under roots without trailing slashes.

pkgs/dart_mcp_server/lib/src/server.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ final class DartMCPServer extends MCPServer
5858
}) : super.fromStreamChannel(
5959
implementation: Implementation(
6060
name: 'dart and flutter tooling',
61-
version: '0.1.0',
61+
version: '0.1.1',
6262
),
6363
instructions:
6464
'This server helps to connect Dart and Flutter developers to '

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ Future<CallToolResult> runCommandInRoots(
9696
}
9797

9898
final outputs = <Content>[];
99+
var isError = false;
99100
for (var rootConfig in rootConfigs) {
100101
final result = await runCommandInRoot(
101102
request,
@@ -109,10 +110,10 @@ Future<CallToolResult> runCommandInRoots(
109110
defaultPaths: defaultPaths,
110111
sdk: sdk,
111112
);
112-
if (result.isError == true) return result;
113+
isError = isError || result.isError == true;
113114
outputs.addAll(result.content);
114115
}
115-
return CallToolResult(content: outputs);
116+
return CallToolResult(content: outputs, isError: isError);
116117
}
117118

118119
/// Runs [commandForRoot] in a single project root specified in the
@@ -233,7 +234,7 @@ Future<CallToolResult> runCommandInRoot(
233234
TextContent(
234235
text:
235236
'$commandDescription failed in ${projectRoot.path}:\n'
236-
'$output\n\nErrors\n$errors',
237+
'$output${errors.isEmpty ? '' : '\nErrors:\n$errors'}',
237238
),
238239
],
239240
isError: true,

pkgs/dart_mcp_server/test/utils/cli_utils_test.dart

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -195,39 +195,49 @@ void main() {
195195
});
196196

197197
group('cannot run commands', () {
198-
final processManager = FakeProcessManager();
199-
200198
test('with roots outside of known roots', () async {
201-
for (var invalidRoot in ['file:///bar/', 'file:///foo/../bar/']) {
202-
final result = await runCommandInRoots(
203-
CallToolRequest(
204-
name: 'foo',
205-
arguments: {
206-
ParameterNames.roots: [
207-
{ParameterNames.root: invalidRoot},
208-
],
209-
},
210-
),
211-
commandForRoot: (_, _, _) => 'fake',
212-
commandDescription: '',
213-
processManager: processManager,
214-
knownRoots: [Root(uri: 'file:///foo/')],
215-
fileSystem: fileSystem,
216-
sdk: Sdk(),
217-
);
218-
expect(result.isError, isTrue);
219-
expect(
220-
result.content.single,
221-
isA<TextContent>().having(
222-
(t) => t.text,
223-
'text',
224-
allOf(contains('Invalid root $invalidRoot')),
225-
),
226-
);
227-
}
199+
final processManager = TestProcessManager();
200+
final invalidRoots = ['file:///bar/', 'file:///foo/../bar/'];
201+
final allRoots = ['file:///foo/', ...invalidRoots];
202+
final result = await runCommandInRoots(
203+
CallToolRequest(
204+
name: 'foo',
205+
arguments: {
206+
ParameterNames.roots: [
207+
for (var root in allRoots) {ParameterNames.root: root},
208+
],
209+
},
210+
),
211+
commandForRoot: (_, _, _) => 'testProcess',
212+
commandDescription: 'Test process',
213+
processManager: processManager,
214+
knownRoots: [Root(uri: 'file:///foo/')],
215+
fileSystem: fileSystem,
216+
sdk: Sdk(),
217+
);
218+
expect(result.isError, isTrue);
219+
expect(
220+
result.content,
221+
unorderedEquals([
222+
for (var root in invalidRoots)
223+
isA<TextContent>().having(
224+
(t) => t.text,
225+
'text',
226+
contains('Invalid root $root'),
227+
),
228+
for (var root in allRoots)
229+
if (!invalidRoots.contains(root))
230+
isA<TextContent>().having(
231+
(t) => t.text,
232+
'text',
233+
allOf(contains('Test process'), contains(Uri.parse(root).path)),
234+
),
235+
]),
236+
);
228237
});
229238

230239
test('with paths outside of known roots', () async {
240+
final processManager = FakeProcessManager();
231241
final result = await runCommandInRoots(
232242
CallToolRequest(
233243
name: 'foo',

0 commit comments

Comments
 (0)