blob: e26546c63b0c84410da93a4fef696b04ef8ca01d [file] [view] [edit]
# Architecture of Dart MCP Server
This document outlines the high-level architecture of the `dart_mcp_server` and its underlying core library `dart_mcp`.
## High-Level Overview
The Dart MCP Server is an implementation of the Model Context Protocol (MCP). It acts as a bridge between an AI-assistant client (like the Antigravity CLI or an IDE plugin) and the Dart/Flutter development environment.
```mermaid
flowchart LR
Client[AI Client] <-->|JSON-RPC 2.0 over stdio| Server[Dart MCP Server]
subgraph Dart MCP Server
Router[JSON-RPC Router]
Tools[Tool Handlers]
Resources[Resource Handlers]
Prompts[Prompt Handlers]
Router <--> Tools
Router <--> Resources
Router <--> Prompts
end
Tools --> DartCLI[Dart CLI]
Tools --> FlutterCLI[Flutter CLI]
Tools --> DTD[Dart Tooling Daemon]
Tools --> Analyzer[Language Server Protocol]
```
## Package Structure
The project is split into two primary packages:
1. **`dart_mcp`**: The foundational library implementing the MCP specification. It provides base classes for both Clients and Servers, defining the API types, JSON-RPC communication, and standard MCP capabilities (Tools, Resources, Prompts, etc.) via mixins.
2. **`dart_mcp_server`**: The concrete implementation of a server tailored for Dart and Flutter. It extends the base `MCPServer` and implements specific tools (like `hotReload`, `analyzeFiles`, `launchApp`) by interacting with the Dart CLI, Flutter CLI, and the Dart Tooling Daemon (DTD).
## Core Class Architecture & Mixins
The server uses a heavily mixin-based architecture. This allows for clean separation of concerns, where each MCP capability and each set of tools can be developed and maintained independently.
```mermaid
classDiagram
class MCPBase {
<<abstract>>
+registerMethod()
+sendRequest()
+sendNotification()
}
class MCPServer {
<<abstract>>
+initialize()
}
class ToolsSupport {
<<mixin>>
+registerTool()
+unregisterTool()
}
class ResourcesSupport {
<<mixin>>
+addResource()
+updateResource()
+removeResource()
}
class PromptsSupport {
<<mixin>>
+registerPrompt()
+unregisterPrompt()
}
class LoggingSupport {
<<mixin>>
+log()
}
class RootsTrackingSupport {
<<mixin>>
+updateRoots()
}
class DartMCPServer {
+run()
}
class FlutterLauncherSupport {
<<mixin>>
+launchApp
+stopApp
+listDevices
+getAppLogs
+listRunningApps
}
class DartAnalyzerSupport {
<<mixin>>
+analyzeFiles
+lsp
}
class DartToolingDaemonSupport {
<<mixin>>
+dtd
+getRuntimeErrors
+getActiveLocation
+hotRestart
+hotReload
+widgetInspector
+flutterDriver
+callVmServiceMethod
}
class PubSupport {
<<mixin>>
+pub
}
class PubDevSearchSupport {
<<mixin>>
+pubDevSearch
}
class AnalyticsSupport {
<<mixin>>
+analytics
}
MCPBase <|-- MCPServer
MCPServer <|-- DartMCPServer
ToolsSupport <|-- DartMCPServer : with
ResourcesSupport <|-- DartMCPServer : with
PromptsSupport <|-- DartMCPServer : with
LoggingSupport <|-- DartMCPServer : with
RootsTrackingSupport <|-- DartMCPServer : with
FlutterLauncherSupport <|-- DartMCPServer : with
DartAnalyzerSupport <|-- DartMCPServer : with
DartToolingDaemonSupport <|-- DartMCPServer : with
PubSupport <|-- DartMCPServer : with
PubDevSearchSupport <|-- DartMCPServer : with
AnalyticsSupport <|-- DartMCPServer : with
```
### `dart_mcp` Mixins
The foundational package provides mixins corresponding to MCP specification features:
- **`ToolsSupport`**: Allows registering tools that the LLM can call.
- **`ResourcesSupport`**: Manages exposing local data and files to the client as resources.
- **`PromptsSupport`**: Provides a list of pre-defined prompts to the client.
- **`LoggingSupport`**: Structured server-to-client log messages.
- **`RootsTrackingSupport`**: Tracks the client's project roots to provide workspace context to tools.
### `dart_mcp_server` Mixins
The concrete server composes tools by mixing in specialized feature sets:
- **`FlutterLauncherSupport`**: App lifecycle management (`launchApp`, `stopApp`, `listDevices`).
- **`DartAnalyzerSupport`**: Static analysis of Dart code (`analyzeFiles`).
- **`DartToolingDaemonSupport`**: Dart Tooling Daemon integration for hot reload, hot restart, and widget tree inspection.
- **`PubSupport`**: Package management tools.
- **`PubDevSearchSupport`**: Querying packages from pub.dev.
- **`AnalyticsSupport`**: Telemetry and usage tracking.
## Deep Dive: Key Tooling Subsystems
### Dart Tooling Daemon (DTD) Integration
The `DartToolingDaemonSupport` mixin is responsible for bridging the MCP server with the [Dart Tooling Daemon](https://pub.dev/packages/dtd). DTD serves as a central hub for communication between various Dart tools, IDEs, and running Dart/Flutter applications.
**Auto-Discovery and Connection:**
When the MCP server connects to a DTD instance, it subscribes to DTD service events (like `ConnectedAppServiceConstants.vmServiceRegistered`) and Editor streams. This allows the MCP server to automatically discover any Dart or Flutter apps connected to that DTD instance, regardless of whether they were launched by an IDE, the CLI, or the MCP server itself. The server automatically establishes VM Service connections to these newly discovered apps and tracks them.
**Supported Features:**
- **App Lifecycle & Execution:** Tools to trigger `hotReload` and `hotRestart` on connected applications.
- **Diagnostics:** The `getRuntimeErrors` tool monitors the VM service error stream, capturing and providing access to recent runtime exceptions for the apps.
- **Widget Inspector:** The `widgetInspector` tool interacts with the `ext.flutter.inspector` service extension to fetch the widget tree, select widgets, and change the selection mode.
- **Flutter Driver:** Enables running Flutter Driver commands against connected applications for UI automation.
- **Direct VM Service access:** The `callVmServiceMethod` tool allows making arbitrary, raw RPC calls to the VM service of a connected app.
### Analyzer Integration
The `DartAnalyzerSupport` mixin integrates the Dart Language Server Protocol (LSP) into the MCP server. Rather than running a one-off analysis pass, it starts and manages a persistent `dart language-server` process.
**Persistent Workspace Analysis:**
When the client's project workspace roots change (tracked via the `RootsTrackingSupport` mixin), the analyzer mixin dynamically updates the LSP workspace folders. The server remains active in the background, listening for file changes and diagnostics.
**Supported Features:**
- **Diagnostics & Quick Fixes:** The `analyzeFiles` tool queries the LSP server for current workspace diagnostics. It can also request the LSP server to apply quick fixes (`dart.edit.fixAllInWorkspace`), which the MCP server translates into filesystem edits via `workspace/applyEdit` events from the language server.
- **Language Intelligence:** The `lsp` tool exposes deep IDE-like capabilities, including:
- `hover`: Provides documentation and type information for a specific position in the code.
- `signatureHelp`: Returns parameter information for function calls.
- `resolveWorkspaceSymbol`: Enables searching for classes, methods, and variables across the entire workspace.
## Request Lifecycle
When a client wants to invoke a tool, the request flows through the base communication layer into the specific mixin that registered the tool.
```mermaid
sequenceDiagram
participant AI as AI Client
participant Base as MCPBase (JSON-RPC)
participant Server as DartMCPServer
participant Mixin as Tool Mixin (e.g., Dtd)
participant Exec as External Process (e.g., DTD)
AI->>Base: Request `tools/call` (name: "hotReload")
Base->>Server: Route request to Tool Handler
Server->>Mixin: Invoke registered callback
Mixin->>Exec: Call actual service (e.g., ext.flutter.app.hotReload)
Exec-->>Mixin: Return Result / Status
Mixin-->>Server: Return MCP ToolResult
Server-->>Base: Format as JSON-RPC Response
Base-->>AI: Response
```
## Transport Mechanism
By default, the `dart_mcp_server` communicates with clients over standard input/output (`stdio`).
The `lib/stdio.dart` utility in `dart_mcp` creates a `StreamChannel` wrapping `stdin` and `stdout`, which the `MCPBase` uses for JSON-RPC 2.0 message passing. This allows the server to be seamlessly spawned as a subprocess by clients like the Antigravity CLI or IDE extensions.