Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkgs/dart_mcp_server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* Add an `--exclude-tool` command line flag to exclude tools by name.
* Add the abillity to limit the output of `analyze_files` to a set of paths.
* Stop reporting non-zero exit codes from command line tools as tool errors.
* Add descriptions for pub tools, add support for `pub deps` and `pub outdated`.

# 0.1.0 (Dart SDK 3.9.0)

Expand Down
76 changes: 58 additions & 18 deletions pkgs/dart_mcp_server/lib/src/mixins/pub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,18 @@ base mixin PubSupport on ToolsSupport, LoggingSupport, RootsTrackingSupport
inputSchema: Schema.object(
properties: {
ParameterNames.command: Schema.string(
title: 'The pub command to run.',
description:
'Currently only ${SupportedPubCommand.listAll} are supported.',
title: 'The pub subcommand to run.',
enumValues: SupportedPubCommand.values
.map<String>((e) => e.name)
.toList(),
description: SupportedPubCommand.commandDescriptions,
),
ParameterNames.packageNames: Schema.list(
title: 'The package names to run the command for.',
description:
'This is required for the '
'${SupportedPubCommand.listAllThatRequirePackageName} commands. ',
items: Schema.string(
title: 'A package to run the command for.',
description:
'When used with "add", prefix with "dev:" to add the package '
'as a dev dependency.',
),
items: Schema.string(title: 'A package to run the command for.'),
),
ParameterNames.roots: rootsSchema(),
},
Expand All @@ -112,22 +109,49 @@ base mixin PubSupport on ToolsSupport, LoggingSupport, RootsTrackingSupport

/// The set of supported `dart pub` subcommands.
enum SupportedPubCommand {
// This is supported in a simplified form: `dart pub add <package-name>`.
// TODO(https://github.com/dart-lang/ai/issues/77): add support for adding
// dev dependencies.
add(requiresPackageNames: true),

get,
add(
requiresPackageNames: true,
description: '''Add package dependencies.
- To add a package normally (typical): "pkg_name"
- Git reference: "pkg_name:{git:{url: https://github.com/pkg_name/pkg_name.git, ref: branch, path: subdir}}"
- ref and path are optional.
- From local path: "pkg_name:{path: ../pkg_name}"
- Dev Dependency: "dev:pkg_name"
- Dependency override: "override:pkg_name:1.0.0"
''',
),

deps(description: 'Print the dependency tree of the current package.'),

get(
description: "Fetch the current package's dependencies and install them.",
),

outdated(
description: 'Analyze dependencies to find which ones can be upgraded.',
),

// This is supported in a simplified form: `dart pub remove <package-name>`.
remove(requiresPackageNames: true),
remove(
requiresPackageNames: true,
description: 'Removes specified dependencies from `pubspec.yaml`.',
),

upgrade;
upgrade(
description:
"Upgrade the current package's dependencies to latest versions.",
);

const SupportedPubCommand({this.requiresPackageNames = false});
const SupportedPubCommand({
this.requiresPackageNames = false,
required this.description,
});

final bool requiresPackageNames;

/// The description to use in the subcommand help.
final String description;

static SupportedPubCommand? fromName(String name) {
for (final command in SupportedPubCommand.values) {
if (command.name == name) {
Expand All @@ -147,6 +171,22 @@ enum SupportedPubCommand {
);
}

static String get commandDescriptions {
return 'Available subcommands:\n${_getDescriptions(values)}';
}

static String _getDescriptions(Iterable<SupportedPubCommand> commands) {
final buffer = StringBuffer();
for (final command in commands) {
final commandName = command.name;
final description = command.description;
if (description.isNotEmpty) {
buffer.writeln('- `$commandName`: $description');
}
}
return buffer.toString();
}

static String _writeCommandsAsList(List<SupportedPubCommand> commands) {
final buffer = StringBuffer();
for (var i = 0; i < commands.length; i++) {
Expand Down
2 changes: 1 addition & 1 deletion pkgs/dart_mcp_server/test/tools/analyzer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ void main() {
],
},
);
final result = await testHarness.callToolWithRetry(request);
final result = await testHarness.callToolWithRetry(request, maxTries: 10);
expect(result.isError, isNot(true));
expect(result.content, hasLength(2));
expect(
Expand Down
46 changes: 45 additions & 1 deletion pkgs/dart_mcp_server/test/tools/pub_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,50 @@ void main() {
]);
});

test('deps', () async {
final request = CallToolRequest(
name: dartPubTool.name,
arguments: {
ParameterNames.command: 'deps',
ParameterNames.roots: [
{ParameterNames.root: testRoot.uri},
],
},
);
final result = await testHarness.callToolWithRetry(request);

// Verify the command was sent to the process manager without error.
expect(result.isError, isNot(true));
expect(testProcessManager.commandsRan, [
equalsCommand((
command: [endsWith(executableName), 'pub', 'deps'],
workingDirectory: testRoot.path,
)),
]);
});

test('outdated', () async {
final request = CallToolRequest(
name: dartPubTool.name,
arguments: {
ParameterNames.command: 'outdated',
ParameterNames.roots: [
{ParameterNames.root: testRoot.uri},
],
},
);
final result = await testHarness.callToolWithRetry(request);

// Verify the command was sent to the process manager without error.
expect(result.isError, isNot(true));
expect(testProcessManager.commandsRan, [
equalsCommand((
command: [endsWith(executableName), 'pub', 'outdated'],
workingDirectory: testRoot.path,
)),
]);
});

test('in a subdir of an a root', () async {
fileSystem.file(p.join(fakeAppPath, 'subdir', 'pubspec.yaml'))
..createSync(recursive: true)
Expand Down Expand Up @@ -214,7 +258,7 @@ void main() {

expect(
(result.content.single as TextContent).text,
contains('Unsupported pub command `publish`.'),
contains('String "publish" is not one of the allowed values:'),
);
expect(testProcessManager.commandsRan, isEmpty);
});
Expand Down
Loading