Skip to main content
Glama

Flutter Inspector MCP Server

server.dart17.4 kB
// Copyright (c) 2025, Flutter Inspector MCP Server authors. // Licensed under the MIT License. // ignore_for_file: avoid_catches_without_on_clauses import 'dart:async'; import 'package:dart_mcp/server.dart'; import 'package:flutter_inspector_mcp_server/src/base_server.dart'; import 'package:flutter_inspector_mcp_server/src/mixins/dynamic_registry_integration.dart'; import 'package:flutter_inspector_mcp_server/src/mixins/flutter_inspector.dart'; import 'package:flutter_inspector_mcp_server/src/mixins/vm_service_support.dart'; import 'package:stream_channel/stream_channel.dart'; // ignore: do_not_use_environment const kDebugMode = bool.fromEnvironment('kDebugMode'); /// Flutter Inspector MCP Server /// /// Provides tools and resources for Flutter app inspection and debugging final class MCPToolkitServer extends BaseMCPToolkitServer with VMServiceSupport, DynamicRegistryIntegration, FlutterInspector { MCPToolkitServer.fromStreamChannel( super.channel, { required super.configuration, }) : super.fromStreamChannel( implementation: Implementation( name: 'flutter-inspector', version: '1.0.0', ), instructions: ''' Flutter Inspector MCP Server - AI Agent Guide This server provides comprehensive tools for inspecting, debugging, and dynamically interacting with Flutter applications. It supports both static tools and **dynamic runtime tool registration** for advanced debugging workflows. ## Core Static Tools **Essential Tools:** - hot_reload_flutter: Hot reload the Flutter app for instant UI updates - get_vm: Get VM information and connection status - get_extension_rpcs: List available extension RPCs in the Flutter app ${configuration.dumpsSupported ? ''' **Debug Dump Tools (Heavy Operations - Use Sparingly):** - debug_dump_layer_tree: Dump complete layer tree structure - debug_dump_semantics_tree: Dump accessibility tree structure - debug_dump_semantics_tree_inverse: Dump semantics tree in inverse order - debug_dump_render_tree: Dump render tree for layout debugging - debug_dump_focus_tree: Dump focus tree for navigation debugging - get_active_ports: Get list of active Flutter/Dart process ports ''' : ''} ${configuration.resourcesSupported ? ''' **Resources:** - visual://localhost/app/errors/latest: Get latest app errors with stack traces - visual://localhost/app/errors/{count}: Get specific number of recent errors - visual://localhost/view/details: Get comprehensive view details and properties - visual://localhost/view/screenshots: Get screenshots of all app views ''' : ''' **Error & View Tools:** - get_view_errors: Get view errors with diagnostic information - get_view_details: Get detailed view information and widget tree - get_screenshots: Get screenshots of all views for visual debugging '''} ${configuration.dynamicRegistrySupported ? r''' ## Dynamic Runtime Tools - AI Agent Workflow **The dynamic registry enables you to interact with Flutter apps that register tools at runtime. This is particularly powerful for debugging and experimentation.** ### 1. Discovery Phase - Find Available Dynamic Tools **Use `listClientToolsAndResources` first to discover what's available:** Example: List all dynamic tools and resources registered by the Flutter app ```json {"name": "listClientToolsAndResources", "arguments": {}} ``` ### 2. Execution Phase - Run Dynamic Tools **Execute discovered tools using `runClientTool`:** Example: Execute a Fibonacci calculator tool ```json { "name": "runClientTool", "arguments": { "toolName": "calculate_fibonacci", "arguments": {"n": 10} } } ``` **Access dynamic resources using `runClientResource`:** Example: Get current app state ```json { "name": "runClientResource", "arguments": {"resourceUri": "visual://localhost/app/state"} } ``` ### 2.5. Creating Custom Tools (AI Agent Generates Dart Code for the Flutter App) **As an AI Agent, you can design custom debugging tools by generating the necessary Dart code. This Dart code, defining an `MCPCallEntry` and registering it with `addMcpTool()`, must then be integrated into the target Flutter application's source code (e.g., by a developer, or by you using file editing tools if available and appropriate).** **Step-by-step AI Workflow for Creating and Using a New Dynamic Tool:** 1. **Design & Generate Dart Code:** * Identify a need for a custom tool. * Generate the Dart code snippet that defines an `MCPCallEntry.tool(...)`. This includes its unique `name`, `description`, `inputSchema`, and the `handler` function (the tool's logic). * The generated code must also include the `addMcpTool(yourToolEntry)` call to register it with the `mcp_toolkit`. * *Example AI-Generated Dart Code Snippet (ensure necessary imports like `package:mcp_toolkit/mcp_toolkit.dart` are handled):* ```dart // This Dart code defines and registers a new dynamic tool. // It needs to be added to a relevant Dart file within the Flutter application's source // (e.g., inside main.dart during initialization, or a dedicated debug utilities file) // and ensured it runs when the app starts or when dynamic tools are set up. // Ensure necessary imports from mcp_toolkit: // import 'package:mcp_toolkit/mcp_toolkit.dart'; final MCPCallEntry myAICustomTool = MCPCallEntry.tool( handler: (request_params) { // Tool logic: access request_params['argument_name'] // Perform actions, gather data... return MCPCallResult( message: 'AI custom tool executed successfully!', parameters: {'result_key': 'some_value_from_tool'}, ); }, definition: MCPToolDefinition( name: 'ai_custom_tool_unique_name', // IMPORTANT: Must be unique description: 'An AI-generated tool that performs a specific debug action.', inputSchema: { 'type': 'object', 'properties': { 'argument_name': {'type': 'string', 'description': 'Description of argument'}, // Add other arguments as needed }, // 'required': ['argument_name'], // If arguments are required }, ), ); // This line makes the tool discoverable by the MCP server: addMcpTool(myAICustomTool); ``` 2. **Integrate and Ensure Execution:** * **Option A (AI integrates via `edit_file` - Preferred if capable):** * Use the `edit_file` tool to add the generated Dart code snippet into an appropriate file in the Flutter application's source code (e.g., `lib/main.dart` before `runApp()`, or a dedicated debug utilities file like `lib/debug_tools.dart` which is then imported and called during app initialization). * **Crucially, ensure the placement guarantees that the `addMcpTool(myAICustomTool);` line (or the function containing it) will be executed by the Flutter app *after* `MCPToolkitBinding.instance.initialize()` and (if used) `MCPToolkitBinding.instance.initializeFlutterToolkit()` have been called, and typically within a `if (kDebugMode) { ... }` block.** * You may need to define a new function in the Flutter code (e.g., `void registerMyAICustomTools() { ... }`) that contains your `MCPCallEntry` definitions and `addMcpTool` calls, and then use `edit_file` to ensure this new function is called from an existing part of the app's initialization sequence. * **Option B (Present for manual integration by a developer):** * Present the generated Dart code snippet to the user. * Clearly instruct the user where to add this code (e.g., `lib/main.dart` or a debug utilities file) and how to ensure it's called during app initialization, respecting the order relative to `MCPToolkitBinding` initialization and preferably within a `kDebugMode` check. 3. **Activate via Hot Reload:** * Once the code is integrated into the Flutter app (either by you via `edit_file` or manually by a developer), call the `hot_reload_flutter` tool. This allows the running Flutter app to execute the new `addMcpTool()` call, making the tool available to the server. 4. **Verify Registration:** * Use `listClientToolsAndResources` to check if your newly defined tool (e.g., 'ai_custom_tool_unique_name') now appears in the list of available dynamic tools. 5. **Execute the New Tool:** * Use `runClientTool` with the `toolName` matching your tool's definition and provide any necessary `arguments` according to its `inputSchema`. **Example Scenario: AI creates a simple widget property inspector** *AI generates the following Dart code and instructs it to be added to the Flutter app (e.g., in `main.dart` before `runApp` or in a debug initialization function):* ```dart // In the Flutter app's Dart code: // import 'package:mcp_toolkit/mcp_toolkit.dart'; // import 'package:flutter/widgets.dart'; // For Element, BuildContext etc. if needed for complex tools void registerAIWidgetInspector() { // Helper function for organization final aiWidgetInspector = MCPCallEntry.tool( handler: (request_params) { // final String widgetKeyString = request_params['widgetKey'] as String? ?? ''; // In a real scenario, you'd use the widgetKeyString to find a widget // and inspect its properties. This is a placeholder. // IMPORTANT: Accessing widget tree details might require context or specific Flutter knowledge. return MCPCallResult( message: 'AI Inspector: Info for widget key "${request_params['widgetKey'] ?? ''}". (Actual inspection logic TBD)', parameters: {'widgetKey': request_params['widgetKey'] ?? '', 'details': 'Placeholder details'}, ); }, definition: MCPToolDefinition( name: 'ai_inspect_widget_properties', description: 'AI-generated tool to inspect basic properties of a widget by its key.', inputSchema: { 'type': 'object', 'properties': { 'widgetKey': {'type': 'string', 'description': 'The string representation of the widget Key to inspect.'}, }, 'required': ['widgetKey'], }, ), ); addMcpTool(aiWidgetInspector); debugPrint('AI Widget Inspector tool registered with MCP toolkit.'); } // This function (registerAIWidgetInspector) would need to be called from somewhere // in the Flutter app's initialization sequence, for example: // void main() { // WidgetsFlutterBinding.ensureInitialized(); // MCPToolkitBinding.instance // ..initialize() // ..initializeFlutterToolkit(); // Includes standard Flutter tools // c // if (kDebugMode) { // Good practice to only register debug tools in debug mode // registerAIWidgetInspector(); // } // // runApp(const MyApp()); // } ``` *After the developer (or AI via `edit_file`) adds this to the Flutter app, the AI uses `hot_reload_flutter`, then `listClientToolsAndResources` to verify, then `runClientTool` to use `ai_inspect_widget_properties`.* ### 3. AI Agent Best Practices **For Runtime Debugging:** 1. Always start with `listClientToolsAndResources` to see what's available 2. Use `get_view_errors` if encountering UI issues 3. Take `get_screenshots` for visual debugging before and after changes 4. Use `hot_reload_flutter` after making code changes **For Dynamic Experimentation:** 1. Flutter apps can register new tools dynamically using `addMcpTool()` 2. These tools appear automatically in `listClientToolsAndResources` 3. Execute them immediately with `runClientTool` - no server restart needed 4. Perfect for testing new features, state inspection, or runtime modifications **Creating Custom Tools (When Working with Flutter Codebase):** 1. AI agents can write Dart code to create new MCP tools for specific debugging needs 2. Add tools using `addMcpTool(MCPCallEntry.tool(...))` in the Flutter app code 3. Use `hot_reload_flutter` to register the new tools without app restart 4. Execute the custom tools immediately with `runClientTool` **Common Debugging Workflow:** ``` 1. listClientToolsAndResources → discover available tools 2. get_screenshots → visual state before changes 3. runClientTool → execute debugging tool 4. hot_reload_flutter → apply any code changes 5. get_screenshots → visual state after changes 6. get_view_errors → check for any new issues ``` **Custom Tool Creation Workflow (When Working with Flutter Codebase):** ``` 1. listClientToolsAndResources → check existing tools 2. [Write Dart code to create custom MCPCallEntry.tool()] 3. [Add addMcpTool(customTool) to Flutter app code] 4. hot_reload_flutter → register new tool 5. listClientToolsAndResources → verify tool appeared 6. runClientTool → execute custom tool 7. [Iterate: modify tool logic if needed, hot reload, test] ``` ## Connection Requirements - Flutter app must be running in **debug mode** with `--enable-vm-service` `--host-vmservice-port=8182 --dds-port=8181 --disable-service-auth-codes` flags. - Default connection: localhost:8181 (Dart VM port) - Dynamic tools register automatically when app calls `addMcpTool()` - No server restart needed when new tools are registered ## Pro Tips for AI Agents - **Dynamic tools are ephemeral** - they disappear when the Flutter app restarts - **Always check tool availability** before execution with `listClientToolsAndResources` - **Combine static and dynamic tools** for comprehensive debugging - **Use hot reload liberally** - it's fast and preserves app state - **Screenshot before/after** any significant changes for visual verification Connect to a running Flutter app on debug mode to use these features. ''' : ''} ''', ); /// Create and connect a Flutter Inspector MCP Server factory MCPToolkitServer.connect( final StreamChannel<String> channel, { required final VMServiceConfigurationRecord configuration, }) => MCPToolkitServer.fromStreamChannel(channel, configuration: configuration); @override FutureOr<InitializeResult> initialize(final InitializeRequest request) async { log( LoggingLevel.info, 'Initializing Flutter Inspector MCP Server', logger: 'MCPToolkitServer', ); // Call parent initialize first which will trigger the mixin's initialize // This registers tools and resources regardless of VM service connection final result = await super.initialize(request); log( LoggingLevel.debug, () => 'Server capabilities: ${result.capabilities}', logger: 'MCPToolkitServer', ); // Try to initialize VM service connection (non-blocking) // This allows tools to be available even if no Flutter app is running try { if (configuration.awaitDndConnection) { await _initializeVMServiceAsync(); } else { await _initializeVMServiceAsync().timeout(const Duration(seconds: 10)); } log( LoggingLevel.info, 'VM service connected successfully', logger: 'VMService', ); } catch (e, s) { // Log but don't fail - tools should still be available log( LoggingLevel.warning, 'VM service initialization failed (this is normal if no ' 'Flutter app is running): $e ', logger: 'VMService', ); log(LoggingLevel.debug, () => 'Stack trace: $s', logger: 'VMService'); } // Start dynamic registry discovery if supported // // Warning! This may block the server from starting up // if --await-dynamics is set. // // This made is to fight current limitations of MCP Clients // which doesn't support tools updates. if (configuration.dynamicRegistrySupported) { if (configuration.awaitDndConnection) { await startRegistryDiscovery(mcpToolkitServer: this); } else { unawaited(startRegistryDiscovery(mcpToolkitServer: this)); } } log( LoggingLevel.info, 'Flutter Inspector MCP Server initialized successfully', logger: 'MCPToolkitServer', ); return result; } /// Initialize VM service connection asynchronously without blocking Future<void> _initializeVMServiceAsync() async { log( LoggingLevel.debug, 'Attempting VM service connection...', logger: 'VMService', ); try { await initializeVMService(); log( LoggingLevel.info, 'VM service initialization completed', logger: 'VMService', ); } on Exception catch (e, s) { // Log but don't fail - tools should still be available log( LoggingLevel.error, 'VM service initialization failed: $e', logger: 'VMService', ); log(LoggingLevel.debug, () => 'Stack trace: $s', logger: 'VMService'); } } @override Future<void> shutdown() async { log( LoggingLevel.info, 'Shutting down Flutter Inspector MCP Server', logger: 'MCPToolkitServer', ); try { await disconnectVMService(); await dispose(); await disposeDynamicRegistry(); log(LoggingLevel.debug, 'VM service disconnected', logger: 'VMService'); } on Exception catch (e) { log( LoggingLevel.warning, 'Error during VM service disconnect: $e', logger: 'VMService', ); } await super.shutdown(); log( LoggingLevel.info, 'Server shutdown complete', logger: 'MCPToolkitServer', ); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Arenukvern/mcp_flutter'

If you have feedback or need assistance with the MCP directory API, please join our Discord server