|
| 1 | +# Design Document: `flutter_launcher_mcp` |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This document describes the design of the Dart package, `flutter_launcher_mcp`, which exposes a Model Context Protocol (MCP) server. This server provides a tool to launch Flutter applications, manage their processes, and return their Dart Tooling Daemon (DTD) URI. |
| 6 | + |
| 7 | +This package provides a robust, MCP-based solution for launching Flutter applications. |
| 8 | + |
| 9 | +## Detailed Analysis of the Goal or Problem |
| 10 | + |
| 11 | +The primary goal is to enable external clients (such as AI agents or IDEs) to programmatically launch and interact with Flutter applications. A key piece of information for this interaction is the DTD URI, which is required for debugging, hot-reloading, and other development-time operations. |
| 12 | + |
| 13 | +This package solves these problems by: |
| 14 | + |
| 15 | +1. Directly launching and managing the Flutter process from within the MCP server. |
| 16 | +2. Capturing the DTD URI in real-time by listening to the process's `stdout` stream. |
| 17 | +3. Keeping the Flutter process alive and holding onto its I/O streams, paving the way for future tools to interact with the running application (e.g., sending commands to `stdin`). |
| 18 | +4. Exposing this functionality via the standardized MCP, allowing for seamless integration with various clients without requiring them to implement Flutter-specific process management logic. |
| 19 | +5. Using dependency injection for process and file system management to ensure the server is easily testable. |
| 20 | + |
| 21 | +## Detailed Design for the Package |
| 22 | + |
| 23 | +### Package Structure |
| 24 | + |
| 25 | +The package structure is a standard Dart package with a clear separation of concerns. |
| 26 | + |
| 27 | +```txt |
| 28 | +flutter_launcher_mcp/ |
| 29 | +├── lib/ |
| 30 | +│ ├── flutter_launcher_mcp.dart // Main library file |
| 31 | +│ └── src/ |
| 32 | +│ ├── server.dart // MCP server implementation |
| 33 | +│ ├── mixins/ |
| 34 | +│ │ └── flutter_launcher.dart |
| 35 | +│ └── utils/ |
| 36 | +│ ├── analytics.dart |
| 37 | +│ ├── cli_utils.dart |
| 38 | +│ ├── constants.dart |
| 39 | +│ ├── file_system.dart |
| 40 | +│ ├── process_manager.dart |
| 41 | +│ └── sdk.dart |
| 42 | +├── bin/ |
| 43 | +│ └── flutter_launcher_mcp.dart // Executable for the MCP server |
| 44 | +├── pubspec.yaml |
| 45 | +├── README.md |
| 46 | +├── CHANGELOG.md |
| 47 | +└── DESIGN.md |
| 48 | +``` |
| 49 | + |
| 50 | +### Dependencies |
| 51 | + |
| 52 | +- `dart_mcp`: For implementing the MCP server. |
| 53 | +- `async`: For stream manipulation and asynchronous operations. |
| 54 | +- `process`: For managing processes in a testable way. |
| 55 | +- `file`: For interacting with the file system in a testable way. |
| 56 | + |
| 57 | +### `bin/flutter_launcher_mcp.dart` (Executable) |
| 58 | + |
| 59 | +This file is the entry point for the MCP server. It creates an instance of `FlutterLauncherMCPServer` and connects it to standard I/O, providing concrete implementations for the `ProcessManager`, `FileSystem`, and `Sdk`. |
| 60 | + |
| 61 | +```dart |
| 62 | +// bin/flutter_launcher_mcp.dart |
| 63 | +import 'dart:io'; |
| 64 | +import 'package:dart_mcp/stdio.dart'; |
| 65 | +import 'package:file/local.dart'; |
| 66 | +import 'package:flutter_launcher_mcp/src/server.dart'; |
| 67 | +import 'package:flutter_launcher_mcp/src/utils/sdk.dart'; |
| 68 | +import 'package:process/process.dart'; |
| 69 | +
|
| 70 | +void main() { |
| 71 | + const processManager = LocalProcessManager(); |
| 72 | + const fileSystem = LocalFileSystem(); |
| 73 | + final sdk = Sdk.find(); |
| 74 | + FlutterLauncherMCPServer( |
| 75 | + stdioChannel(input: stdin, output: stdout), |
| 76 | + sdk: sdk, |
| 77 | + processManager: processManager, |
| 78 | + fileSystem: fileSystem, |
| 79 | + ); |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +### `lib/src/server.dart` and `lib/src/mixins/flutter_launcher.dart` |
| 84 | + |
| 85 | +The core logic is implemented in the `FlutterLauncherSupport` mixin, which is then applied to the main `FlutterLauncherMCPServer` class. This promotes separation of concerns and reusability. |
| 86 | + |
| 87 | +#### `FlutterLauncherMCPServer` Class |
| 88 | + |
| 89 | +This class extends `MCPServer` and composes functionality through mixins. It manages dependencies like `Sdk`, `ProcessManager`, and `FileSystem`. |
| 90 | + |
| 91 | +```dart |
| 92 | +// lib/src/server.dart |
| 93 | +import 'package:dart_mcp/server.dart'; |
| 94 | +import 'package:file/file.dart'; |
| 95 | +import 'package:process/process.dart'; |
| 96 | +
|
| 97 | +import 'utils/file_system.dart'; |
| 98 | +import 'utils/process_manager.dart'; |
| 99 | +import 'utils/sdk.dart'; |
| 100 | +import 'mixins/flutter_launcher.dart'; |
| 101 | +
|
| 102 | +final class FlutterLauncherMCPServer extends MCPServer |
| 103 | + with |
| 104 | + LoggingSupport, |
| 105 | + ToolsSupport, |
| 106 | + RootsTrackingSupport, |
| 107 | + FlutterLauncherSupport |
| 108 | + implements ProcessManagerSupport, FileSystemSupport, SdkSupport { |
| 109 | + @override |
| 110 | + Sdk sdk; |
| 111 | +
|
| 112 | + @override |
| 113 | + final ProcessManager processManager; |
| 114 | +
|
| 115 | + @override |
| 116 | + final FileSystem fileSystem; |
| 117 | +
|
| 118 | + FlutterLauncherMCPServer( |
| 119 | + super.channel, { |
| 120 | + required this.sdk, |
| 121 | + required this.processManager, |
| 122 | + required this.fileSystem, |
| 123 | + /* ... */ |
| 124 | + }) : super.fromStreamChannel(/* ... */); |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +#### `FlutterLauncherSupport` Mixin |
| 129 | + |
| 130 | +This mixin contains the tool definitions and implementations for interacting with Flutter applications. It manages running processes, their logs, and their DTD URIs. |
| 131 | + |
| 132 | +**Tools:** |
| 133 | + |
| 134 | +1. **`launch_app`**: Launches a Flutter application. |
| 135 | +2. **`stop_app`**: Stops a running Flutter application by its PID. |
| 136 | +3. **`list_devices`**: Lists available Flutter devices. |
| 137 | +4. **`get_app_logs`**: Retrieves the captured logs for a running application. |
| 138 | +5. **`list_running_apps`**: Lists all applications currently managed by the server. |
| 139 | + |
| 140 | +**Tool Definitions:** |
| 141 | + |
| 142 | +```dart |
| 143 | +// from lib/src/mixins/flutter_launcher.dart |
| 144 | +
|
| 145 | +// Tool definition for launching |
| 146 | +final launchAppTool = Tool( |
| 147 | + name: 'launch_app', |
| 148 | + description: 'Launches a Flutter application and returns its DTD URI.', |
| 149 | + inputSchema: Schema.object( |
| 150 | + properties: { |
| 151 | + 'root': Schema.string( |
| 152 | + description: 'The root directory of the Flutter project.', |
| 153 | + ), |
| 154 | + 'target': Schema.string( |
| 155 | + description: 'The main entry point file of the application.', |
| 156 | + ), |
| 157 | + 'device': Schema.string( |
| 158 | + description: 'The device ID to launch the application on.', |
| 159 | + ), |
| 160 | + }, |
| 161 | + required: ['root', 'device'], |
| 162 | + ), |
| 163 | + outputSchema: Schema.object( |
| 164 | + properties: { |
| 165 | + 'dtdUri': Schema.string( |
| 166 | + description: 'The DTD URI of the launched Flutter application.', |
| 167 | + ), |
| 168 | + 'pid': Schema.int( |
| 169 | + description: 'The process ID of the launched Flutter application.', |
| 170 | + ), |
| 171 | + }, |
| 172 | + required: ['dtdUri', 'pid'], |
| 173 | + ), |
| 174 | +); |
| 175 | +
|
| 176 | +// Tool definition for stopping an app |
| 177 | +final stopAppTool = Tool( |
| 178 | + name: 'stop_app', |
| 179 | + description: 'Kills a running Flutter process managed by this server.', |
| 180 | + inputSchema: Schema.object( |
| 181 | + properties: { |
| 182 | + 'pid': Schema.int( |
| 183 | + description: 'The process ID of the process to kill.', |
| 184 | + ), |
| 185 | + }, |
| 186 | + required: ['pid'], |
| 187 | + ), |
| 188 | + outputSchema: Schema.object( |
| 189 | + properties: { |
| 190 | + 'success': Schema.bool( |
| 191 | + description: 'Whether the process was killed successfully.', |
| 192 | + ), |
| 193 | + }, |
| 194 | + required: ['success'], |
| 195 | + ), |
| 196 | +); |
| 197 | +
|
| 198 | +// Other tools like list_devices, get_app_logs, list_running_apps are also defined here. |
| 199 | +``` |
| 200 | + |
| 201 | +The implementation of `_launchApp` starts the `flutter run` process with the `--print-dtd` flag, captures the DTD URI from the output, and stores the process and its logs. |
| 202 | + |
| 203 | +## Diagrams |
| 204 | + |
| 205 | +```mermaid |
| 206 | +graph TD |
| 207 | + A[Client Application] -->|"MCP Connection (stdio)"| B["flutter_launcher_mcp (MCP Server)"] |
| 208 | + B -->|Calls Tool: launch_app| C{Launches 'flutter run --print-dtd'} |
| 209 | + C -->|Manages Process| D[Flutter Application Process] |
| 210 | + D -->|stdout/stderr streams| B |
| 211 | + B -->|"Parses DTD URI & Captures Logs"| B |
| 212 | + B -->|"Returns CallToolResult (DTD URI, PID)"| A |
| 213 | +
|
| 214 | + subgraph "Other Tools" |
| 215 | + A2[Client Application] -->|"MCP Connection (stdio)"| B |
| 216 | + B -->|"Calls Tool: stop_app(PID)"| C |
| 217 | + B -->|"Calls Tool: list_devices"| E[flutter devices --machine] |
| 218 | + B -->|"Calls Tool: get_app_logs(PID)"| F[Returns stored logs] |
| 219 | + B -->|"Calls Tool: list_running_apps"| G[Returns running apps info] |
| 220 | + end |
| 221 | +``` |
| 222 | + |
| 223 | +## Summary of the Design |
| 224 | + |
| 225 | +The design for `flutter_launcher_mcp` creates a self-contained, efficient, and robust MCP server for managing Flutter application processes. By directly handling process creation and I/O streaming, it provides real-time DTD URI retrieval and eliminates the overhead of file-based communication. The architecture is modular, with core functionality encapsulated in a `FlutterLauncherSupport` mixin. |
| 226 | + |
| 227 | +The server provides a comprehensive set of tools for the entire application lifecycle, including launching (`launch_app`), stopping (`stop_app`), and monitoring (`list_devices`, `get_app_logs`, `list_running_apps`). This provides a solid foundation for sophisticated client integrations. The use of dependency injection for the SDK, process, and file system management ensures the server is easily testable. |
| 228 | + |
| 229 | +## References to Research URLs |
| 230 | + |
| 231 | +- [Model Context Protocol Specification](https://modelcontextprotocol.io/docs/concepts/overview) |
| 232 | +- [dart_mcp package on pub.dev](https://pub.dev/packages/dart_mcp) |
| 233 | +- [Dart `Process` class documentation](https://api.dart.dev/stable/dart-io/Process-class.html) |
| 234 | +- [process package on pub.dev](https://pub.dev/packages/process) |
| 235 | +- [file package on pub.dev](https://pub.dev/packages/file) |
0 commit comments