Skip to content

Commit 1484f81

Browse files
authored
Add flutter_launcher MCP server (#55)
1 parent ff1cfb6 commit 1484f81

23 files changed

+2979
-15
lines changed

.github/workflows/release.yml

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,30 +13,50 @@ on:
1313
- "v*"
1414

1515
jobs:
16-
release:
17-
runs-on: ubuntu-latest
18-
permissions:
19-
contents: write
16+
build:
17+
strategy:
18+
matrix:
19+
os: [ubuntu-latest, macos-latest, windows-latest]
20+
runs-on: ${{ matrix.os }}
2021
steps:
2122
- name: Checkout code
2223
uses: actions/checkout@v4
2324

24-
- name: Create archive
25+
- name: Create archive (Linux/macOS)
26+
if: runner.os != 'Windows'
2527
run: |
26-
tag_name=${GITHUB_REF#refs/tags/}
27-
archive_name="flutter.tar.gz"
28-
git archive --format=tar.gz -o $archive_name $tag_name \
29-
gemini-extension.json \
30-
commands/ \
31-
LICENSE \
32-
README.md \
33-
flutter.md
34-
echo "ARCHIVE_NAME=$archive_name" >> $GITHUB_ENV
28+
bash ./scripts/build_release.sh
29+
shell: bash
30+
31+
- name: Create archive (Windows)
32+
if: runner.os == 'Windows'
33+
run: |
34+
./scripts/build_release.ps1
35+
shell: pwsh
36+
37+
- name: Upload artifact
38+
uses: actions/upload-artifact@v4
39+
with:
40+
name: release-archive-${{ matrix.os }}
41+
path: ${{ env.ARCHIVE_NAME }}
42+
43+
release:
44+
needs: build
45+
runs-on: ubuntu-latest
46+
permissions:
47+
contents: write
48+
steps:
49+
- name: Download all artifacts
50+
uses: actions/download-artifact@v4
51+
with:
52+
path: artifacts
3553

3654
- name: Create GitHub Release with GH CLI
3755
env:
3856
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3957
run: |
4058
gh release create ${{ github.ref_name }} \
4159
--generate-notes \
42-
${{ env.ARCHIVE_NAME }}
60+
artifacts/release-archive-ubuntu-latest/* \
61+
artifacts/release-archive-macos-latest/* \
62+
artifacts/release-archive-windows-latest/*

flutter_launcher_mcp/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# https://dart.dev/guides/libraries/private-files
2+
# Created by `dart pub`
3+
.dart_tool/
4+
flutter_launcher_mcp.dill

flutter_launcher_mcp/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## 0.2.0
2+
3+
- Added the `flutter_launcher_mcp` package, an MCP server installed with the
4+
extension that allows launching and controlling Flutter apps.
5+
6+
## 0.1.0
7+
8+
- Initial version of the package.

flutter_launcher_mcp/DESIGN.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
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

Comments
 (0)