Skip to content

disable the screenshots tool by default #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 30, 2025
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: 2 additions & 0 deletions pkgs/dart_mcp_server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@
* Add `--log-file` argument to log all protocol traffic to a file.
* Improve error text for failed DTD connections as well as the tool description.
* Add support for injecting an `Analytics` instance to track usage.
* Screenshot tool disabled until
https://github.com/flutter/flutter/issues/170357 is resolved.
* Add `arg_parser.dart` public library with minimal deps to be used by the dart tool.
3 changes: 1 addition & 2 deletions pkgs/dart_mcp_server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ for configuring the server in Android Studio. -->
[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=dart&config=eyJ0eXBlIjoic3RkaW8iLCJjb21tYW5kIjoiZGFydCBtY3Atc2VydmVyIC0tZXhwZXJpbWVudGFsLW1jcC1zZXJ2ZXIgLS1mb3JjZS1yb290cy1mYWxsYmFjayJ9)

The easiest way to configure the Dart MCP server with Cursor is by clicking the "Add to Cursor"
button above.
button above.

Alternatively, you can configure the server manually. Go to **Cursor -> Settings -> Cursor Settings > Tools & Integrations**, and then click **"Add Custom MCP"** or **"New MCP Server"**
depending on whether you already have other MCP servers configured. Edit the `.cursor/mcp.json` file in your local project (configuration will only apply to this project) or
Expand Down Expand Up @@ -155,7 +155,6 @@ For more information, see the official VS Code documentation for
| `pub` | `static tool` | Runs a `dart pub` command for the given project roots. |
| `pub_dev_search` | `package search` | Searches pub.dev for packages relevant to a given search query. |
| `get_runtime_errors` | `runtime analysis` | Retrieves the list of runtime errors that have occurred in the active Dart or Flutter application. |
| `take_screenshot` | `runtime analysis` | Takes a screenshot of the active Flutter application in its current state. |
| `get_widget_tree` | `runtime analysis` | Retrieves the widget tree from the active Flutter application. |
| `get_selected_widget` | `runtime analysis` | Retrieves the selected widget from the active Flutter application. |
| `hot_reload` | `runtime tool` | Performs a hot reload of the active Flutter application. |
Expand Down
5 changes: 4 additions & 1 deletion pkgs/dart_mcp_server/lib/src/mixins/dtd.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ base mixin DartToolingDaemonSupport
/// for full list of available Flutter Widget Inspector service extensions.
static const _inspectorServiceExtensionPrefix = 'ext.flutter.inspector';

/// Whether or not to enable the screenshot tool.
bool get enableScreenshots;
Copy link
Contributor

Choose a reason for hiding this comment

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

should this default to false instead of being an abstract getter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is overridden always so the default would be meaningless here


/// Called when the DTD connection is lost, resets all associated state.
Future<void> _resetDtd() async {
_dtd = null;
Expand Down Expand Up @@ -165,7 +168,7 @@ base mixin DartToolingDaemonSupport
// they should return an error when used against a pure Dart app (or a
// Flutter app that does not support the operation, e.g. hot reload is not
// supported in profile mode).
registerTool(screenshotTool, takeScreenshot);
if (enableScreenshots) registerTool(screenshotTool, takeScreenshot);
registerTool(hotReloadTool, hotReload);
registerTool(getWidgetTreeTool, widgetTree);
registerTool(getSelectedWidgetTool, selectedWidget);
Expand Down
5 changes: 5 additions & 0 deletions pkgs/dart_mcp_server/lib/src/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ final class DartMCPServer extends MCPServer
@visibleForTesting this.processManager = const LocalProcessManager(),
@visibleForTesting this.fileSystem = const LocalFileSystem(),
this.forceRootsFallback = false,
// Disabled due to https://github.com/flutter/flutter/issues/170357
this.enableScreenshots = false,
super.protocolLogSink,
}) : super.fromStreamChannel(
implementation: Implementation(
Expand Down Expand Up @@ -156,6 +158,9 @@ final class DartMCPServer extends MCPServer
@override
final Analytics? analytics;

@override
final bool enableScreenshots;

@override
/// Automatically logs all tool calls via analytics by wrapping the [impl],
/// if [analytics] is not `null`.
Expand Down
2 changes: 2 additions & 0 deletions pkgs/dart_mcp_server/test/test_harness.dart
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,8 @@ Future<ServerConnectionPair> _initializeMCPServer(
fileSystem: fileSystem,
sdk: sdk,
analytics: analytics,
// So we can test them.
enableScreenshots: true,
);
addTearDown(server.shutdown);
connection = client.connectServer(clientChannel);
Expand Down
41 changes: 20 additions & 21 deletions pkgs/dart_mcp_server/test/tools/dtd_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,6 @@ void main() {
});

group('flutter tests', () {
test('can take a screenshot', () async {
await testHarness.startDebugSession(
counterAppPath,
'lib/main.dart',
isFlutter: true,
);
final tools =
(await testHarness.mcpServerConnection.listTools()).tools;
final screenshotTool = tools.singleWhere(
(t) => t.name == DartToolingDaemonSupport.screenshotTool.name,
);
final screenshotResult = await testHarness.callToolWithRetry(
CallToolRequest(name: screenshotTool.name),
);
expect(screenshotResult.content.single, {
'data': anything,
'mimeType': 'image/png',
'type': ImageContent.expectedType,
});
});

test('can get the widget tree', () async {
await testHarness.startDebugSession(
counterAppPath,
Expand Down Expand Up @@ -225,6 +204,26 @@ void main() {
});
});

test('can take a screenshot', () async {
await testHarness.startDebugSession(
counterAppPath,
'lib/main.dart',
isFlutter: true,
);
final tools = (await testHarness.mcpServerConnection.listTools()).tools;
final screenshotTool = tools.singleWhere(
(t) => t.name == DartToolingDaemonSupport.screenshotTool.name,
);
final screenshotResult = await testHarness.callToolWithRetry(
CallToolRequest(name: screenshotTool.name),
);
expect(screenshotResult.content.single, {
'data': anything,
'mimeType': 'image/png',
'type': ImageContent.expectedType,
});
});

group('get selected widget', () {
test('when a selected widget exists', () async {
final server = testHarness.serverConnectionPair.server!;
Expand Down