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
2 changes: 1 addition & 1 deletion pkgs/dart_mcp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
- This package still allows `reject` and treats it as an alias for`decline`.
- The old `reject` enum value was replaced with a static constant equal
exactly to `decline`, so switches are not affected.

- Add `title` parameter to `Prompt` constructor.

## 0.3.2

Expand Down
9 changes: 0 additions & 9 deletions pkgs/dart_mcp/lib/src/api/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -503,15 +503,6 @@ extension type ResourceLink.fromMap(Map<String, Object?> _value)
return type;
}

/// The name of the resource.
Copy link
Contributor Author

@jakemac53 jakemac53 Jul 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this was just duplicative, this getter exists in one of the implemented types already

String get name {
final name = _value['name'] as String?;
if (name == null) {
throw ArgumentError('Missing name field in $ResourceLink.');
}
return name;
}

/// The description of the resource.
String get description {
final description = _value['description'] as String?;
Expand Down
2 changes: 2 additions & 0 deletions pkgs/dart_mcp/lib/src/api/prompts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,12 @@ extension type Prompt.fromMap(Map<String, Object?> _value)
implements BaseMetadata {
factory Prompt({
required String name,
String? title,
String? description,
List<PromptArgument>? arguments,
}) => Prompt.fromMap({
'name': name,
if (title != null) 'title': title,
if (description != null) 'description': description,
if (arguments != null) 'arguments': arguments,
});
Expand Down
60 changes: 60 additions & 0 deletions pkgs/dart_mcp_server/lib/src/mixins/prompts.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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 'package:dart_mcp/server.dart';
import 'package:meta/meta.dart';

/// A mixin which adds support for various dart and flutter specific prompts.
base mixin DashPrompts on PromptsSupport {
@override
FutureOr<InitializeResult> initialize(InitializeRequest request) {
addPrompt(flutterDriverUserJourneyTest, _flutterDriverUserJourneyPrompt);
return super.initialize(request);
}

/// Creates the flutter driver user journey prompt based on a request.
GetPromptResult _flutterDriverUserJourneyPrompt(GetPromptRequest request) {
return GetPromptResult(
messages: [
PromptMessage(
role: Role.user,
content: flutterDriverUserJourneyPromptContent,
),
],
);
}

@visibleForTesting
static final flutterDriverUserJourneyTest = Prompt(
name: 'flutter_driver_user_journey_test',
title: 'User journey flutter driver test',
description: '''
Prompts the LLM to attempt to accomplish a user journey in the running app using
flutter driver. If successful, it will then translate the steps it followed into
a flutter driver test and write that to disk.
''',
);

@visibleForTesting
static final flutterDriverUserJourneyPromptContent = Content.text(
text: '''
Perform the following tasks in order:

- Prompt the user to navigate to the home page of the app.
- Prompt the user for a user journey that they would like to write a test for.
- Attempt to complete the given user journey using flutter driver to inspect the
widget tree and interact with the application. Only durable interactions
should be performed, do not use temporary IDs to select or interact with
widgets, but instead select them based on text, type, tooltip, etc. Avoid
reading in files to accomplish this task, just inspect the live state of the
app and widget tree. If you get stuck, feel free to ask the user for help.
- If you are able to successfully complete the journey, then create a flutter
driver based test with an appropriate name, which performs all the same
actions that you performed. Include the original user journey as a comment
in the test file.
''',
);
}
5 changes: 4 additions & 1 deletion pkgs/dart_mcp_server/lib/src/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'arg_parser.dart';
import 'mixins/analyzer.dart';
import 'mixins/dash_cli.dart';
import 'mixins/dtd.dart';
import 'mixins/prompts.dart';
import 'mixins/pub.dart';
import 'mixins/pub_dev_search.dart';
import 'mixins/roots_fallback_support.dart';
Expand All @@ -39,7 +40,9 @@ final class DartMCPServer extends MCPServer
DashCliSupport,
PubSupport,
PubDevSupport,
DartToolingDaemonSupport
DartToolingDaemonSupport,
PromptsSupport,
DashPrompts
implements
AnalyticsSupport,
ProcessManagerSupport,
Expand Down
2 changes: 1 addition & 1 deletion pkgs/dart_mcp_server/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies:
args: ^2.7.0
async: ^2.13.0
collection: ^1.19.1
dart_mcp: ^0.3.2
dart_mcp: ^0.3.3
dds_service_extensions: ^2.0.1
devtools_shared: ^12.0.0
dtd: ^4.0.0
Expand Down
51 changes: 51 additions & 0 deletions pkgs/dart_mcp_server/test/tools/prompts_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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 'package:dart_mcp/server.dart';
import 'package:dart_mcp_server/src/mixins/prompts.dart';
import 'package:test/test.dart';

import '../test_harness.dart';

void main() {
late TestHarness testHarness;

// TODO: Use setUpAll, currently this fails due to an apparent TestProcess
// issue.
setUp(() async {
testHarness = await TestHarness.start();
});

test('can list prompts', () async {
final server = testHarness.mcpServerConnection;
final promptsResult = await server.listPrompts(ListPromptsRequest());
expect(
promptsResult.prompts,
equals([
isA<GetPromptRequest>().having(
(p) => p.name,
'name',
DashPrompts.flutterDriverUserJourneyTest.name,
),
]),
);
});

test('can get the flutter driver user journey prompt', () async {
final server = testHarness.mcpServerConnection;
final prompt = await server.getPrompt(
GetPromptRequest(name: DashPrompts.flutterDriverUserJourneyTest.name),
);
expect(
prompt.messages.single,
isA<PromptMessage>()
.having((m) => m.role, 'role', Role.user)
.having(
(m) => m.content,
'content',
equals(DashPrompts.flutterDriverUserJourneyPromptContent),
),
);
});
}