Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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: 59 additions & 17 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,20 @@ base mixin PubSupport on ToolsSupport, LoggingSupport, RootsTrackingSupport
inputSchema: Schema.object(
properties: {
ParameterNames.command: Schema.string(
title: 'The pub command to run.',
title: 'The pub subcommand to run.',
enumValues: SupportedPubCommand.values
.map<String>((e) => e.name)
.toList(),
description:
'Currently only ${SupportedPubCommand.listAll} are supported.',
'Only ${SupportedPubCommand.listAll} are supported.\n'
'${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 +111,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 +173,22 @@ enum SupportedPubCommand {
);
}

static String get commandDescriptions {
return _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