Skip to main content
Glama

flutter_run

Start a Flutter development session with hot reload enabled to run and test mobile applications on target devices during development.

Instructions

Start a Flutter development session (hot reload enabled)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
cwdYesWorking directory (Flutter project root)
deviceIdNoTarget device ID
targetNoTarget dart file (e.g., lib/main.dart)
flavorNoBuild flavor
debugPortNoDebug port number

Implementation Reference

  • The core handler function that executes the flutter_run tool. It validates inputs, verifies the Flutter project, checks for existing sessions, constructs flutter run command arguments, spawns the child process, tracks the session in runningFlutterSessions map, and returns session details including PID and status.
    handler: async (args: any) => {
      const validation = FlutterRunSchema.safeParse(args);
      if (!validation.success) {
        throw new Error(`Invalid request: ${validation.error.message}`);
      }
    
      const { cwd, deviceId, target, flavor, debugPort } = validation.data;
    
      // Validate that it's a Flutter project
      await validateFlutterProject(cwd);
    
      // Check if there's already a running session for this project
      const existingSession = Array.from(runningFlutterSessions.entries()).find(
        ([_, session]) => session.projectPath === cwd
      );
      
      if (existingSession) {
        return {
          success: true,
          data: {
            sessionId: existingSession[0],
            pid: existingSession[1].pid,
            status: 'already_running',
            projectPath: cwd,
            deviceId: existingSession[1].deviceId,
            message: 'Flutter run session is already active for this project',
          },
        };
      }
    
      const flutter_args = ['run'];
      
      if (deviceId) {
        flutter_args.push('-d', deviceId);
      }
      
      if (target) {
        // Validate target path (must be .dart file)
        if (!target.endsWith('.dart')) {
          throw new Error(`Target must be a .dart file. Invalid target: ${target}`);
        }
        flutter_args.push('--target', target);
      }
      
      if (flavor) {
        flutter_args.push('--flavor', flavor);
      }
      
      if (debugPort) {
        flutter_args.push('--debug-port', debugPort.toString());
      }
    
      // Start flutter run in background
      const flutterProcess = spawn('flutter', flutter_args, {
        cwd,
        detached: true,
        stdio: ['ignore', 'pipe', 'pipe'],
      });
    
      const sessionId = `flutter_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
      
      // Track the process
      runningFlutterSessions.set(sessionId, {
        pid: flutterProcess.pid!,
        deviceId: deviceId || 'default',
        projectPath: cwd,
      });
    
      // Handle process exit
      flutterProcess.on('exit', () => {
        runningFlutterSessions.delete(sessionId);
      });
    
      // Unref to allow the parent process to exit
      flutterProcess.unref();
    
      return {
        success: true,
        data: {
          sessionId,
          pid: flutterProcess.pid,
          status: 'started',
          projectPath: cwd,
          deviceId: deviceId || 'default',
          target: target || 'lib/main.dart',
          flavor,
          debugPort,
        },
      };
    }
  • Zod validation schema defining the input parameters for the flutter_run tool: cwd (required string), optional deviceId, target, flavor (strings), and debugPort (number 1024-65535). Used in the handler for input validation.
    const FlutterRunSchema = z.object({
      cwd: z.string().min(1),
      deviceId: z.string().optional(),
      target: z.string().optional(),
      flavor: z.string().optional(),
      debugPort: z.number().min(1024).max(65535).optional(),
    });
  • Registration of the flutter_run tool within the createFlutterTools factory function. Defines name, description, inputSchema (JSON schema version), and references the handler function. This map is later used in server registration.
     * Zod validation schema for flutter_build tool.
     * Validates Flutter build parameters for multiple platforms.
     *
     * @type {z.ZodObject}
     * @property {string} cwd - Working directory (Flutter project root)
     * @property {string} target - Build target (apk, appbundle, ipa, ios, android, web, etc.)
     * @property {string} buildMode - Build mode (debug, profile, release)
     * @property {string} [flavor] - Build flavor
     */
    const FlutterBuildSchema = z.object({
      cwd: z.string().min(1),
      target: z.enum(['apk', 'appbundle', 'ipa', 'ios', 'android', 'web', 'windows', 'macos', 'linux']),
      buildMode: z.enum(['debug', 'profile', 'release']).default('debug'),
      flavor: z.string().optional(),
    });
    
    /**
     * Zod validation schema for flutter_test tool.
     * Validates Flutter test execution parameters.
     *
     * @type {z.ZodObject}
     * @property {string} cwd - Working directory (Flutter project root)
     * @property {string} [testFile] - Specific test file to run
     * @property {boolean} coverage - Enable test coverage (default: false)
     */
    const FlutterTestSchema = z.object({
      cwd: z.string().min(1),
      testFile: z.string().optional(),
      coverage: z.boolean().default(false),
    });
    
    /**
     * Zod validation schema for flutter_clean tool.
     *
     * @type {z.ZodObject}
     * @property {string} cwd - Working directory (Flutter project root)
     */
    const FlutterCleanSchema = z.object({
      cwd: z.string().min(1),
    });
    
    /**
     * Zod validation schema for flutter_pub_get tool.
     *
     * @type {z.ZodObject}
     * @property {string} cwd - Working directory (Flutter project root)
     */
    const FlutterPubGetSchema = z.object({
      cwd: z.string().min(1),
    });
    
    /**
     * Zod validation schema for flutter_screenshot tool.
     *
     * @type {z.ZodObject}
     * @property {string} [deviceId] - Target device ID
     * @property {string} [outputPath] - Output PNG file path
     */
    const FlutterScreenshotSchema = z.object({
      deviceId: z.string().optional(),
      outputPath: z.string().optional(),
    });
    
    /**
     * Zod validation schema for flutter_stop_session tool.
     *
     * @type {z.ZodObject}
     * @property {string} sessionId - Flutter session ID to stop
     */
    const FlutterStopSessionSchema = z.object({
      sessionId: z.string().min(1),
    });
    
    /**
     * Validates that a directory is a valid Flutter project.
     * Checks for pubspec.yaml file and flutter section.
     *
     * @param {string} cwd - Directory path to validate
     * @returns {Promise<void>}
     * @throws {Error} If pubspec.yaml not found or flutter section missing
     *
     * @example
     * ```typescript
     * await validateFlutterProject('/path/to/flutter/project');
     * ```
     */
    const validateFlutterProject = async (cwd: string): Promise<void> => {
      const pubspecPath = path.join(cwd, 'pubspec.yaml');
      try {
        await fs.access(pubspecPath);
        const pubspecContent = await fs.readFile(pubspecPath, 'utf8');
        if (!pubspecContent.includes('flutter:')) {
          throw new Error(`Directory does not appear to be a Flutter project. No flutter section found in ${pubspecPath}`);
        }
      } catch {
        throw new Error(`pubspec.yaml not found. Flutter project must contain pubspec.yaml at: ${pubspecPath}`);
      }
    };
    
    /**
     * Creates and configures all Flutter tools for the MCP Mobile Server.
     *
     * This factory function initializes and returns a complete set of Flutter development tools,
     * providing comprehensive Flutter SDK integration for AI agents via the MCP protocol.
     *
     * **Tools Created:**
     * - `flutter_doctor`: System diagnostics and environment validation
     * - `flutter_version`: Get Flutter SDK version information
     * - `flutter_list_devices`: List connected devices and emulators
     * - `flutter_list_emulators`: List available emulators
     * - `flutter_launch_emulator`: Launch a Flutter emulator
     * - `flutter_run`: Start development session with hot reload
     * - `flutter_stop_session`: Stop running development session
     * - `flutter_list_sessions`: List active development sessions
     * - `flutter_build`: Build app for target platform
     * - `flutter_test`: Run tests with optional coverage
     * - `flutter_clean`: Clean build cache
     * - `flutter_pub_get`: Install project dependencies
     * - `flutter_screenshot`: Capture screenshot from device
     *
     * **Features:**
     * - Session management with hot reload support
     * - Multi-platform build support (Android, iOS, Web, Desktop)
     * - Comprehensive error handling and validation
     * - Process lifecycle tracking
     * - Automatic fallback for machine-readable output
     *
     * @param {Map<string, number>} globalProcessMap - Global map tracking all active processes
     * @returns {Map<string, any>} Map of tool names to tool configurations
     *
     * @example
     * ```typescript
     * const globalProcesses = new Map();
     * const tools = createFlutterTools(globalProcesses);
     *
     * // Use flutter_doctor
     * const doctor = tools.get('flutter_doctor');
     * const result = await doctor.handler({});
     * console.log(result.data.machineOutput);
     *
     * // Start development session
     * const run = tools.get('flutter_run');
     * const session = await run.handler({
     *   cwd: '/path/to/project',
     *   deviceId: 'emulator-5554'
     * });
     * console.log(session.data.sessionId);
     * ```
     *
     * @throws {Error} If Flutter SDK is not installed or not in PATH
     * @throws {Error} If validation schemas fail for any tool parameters
     *
     * @see {@link https://docs.flutter.dev/reference/flutter-cli|Flutter CLI Reference}
     */
    export function createFlutterTools(globalProcessMap: Map<string, number>): Map<string, any> {
      // Initialize local sessions tracking if not provided
      runningFlutterSessions = new Map();
    
      const tools = new Map<string, any>();
    
      // Flutter Doctor - System diagnostics
      tools.set('flutter_doctor', {
        name: 'flutter_doctor',
        description: 'Run Flutter doctor to check development environment setup',
        inputSchema: {
          type: 'object',
          properties: {},
          required: []
        },
        handler: async () => {
          const result = await processExecutor.execute('flutter', ['doctor', '--machine']);
          
          if (result.exitCode !== 0) {
            // Try fallback without --machine flag
            const fallbackResult = await processExecutor.execute('flutter', ['doctor', '-v']);
            
            return {
              success: true,
              data: {
                status: 'completed_with_issues',
                machineOutput: null,
                humanOutput: fallbackResult.stdout,
                issues: fallbackResult.stderr,
                rawExitCode: result.exitCode,
              },
            };
          }
    
          // Parse machine output if available
          let parsedOutput = null;
          try {
            parsedOutput = JSON.parse(result.stdout);
          } catch {
            // If JSON parsing fails, provide raw output
            parsedOutput = null;
          }
    
          return {
            success: true,
            data: {
              status: 'completed',
              machineOutput: parsedOutput,
              rawOutput: result.stdout,
              issues: result.stderr,
              exitCode: result.exitCode,
            },
          };
        }
      });
    
      // Flutter Version - Get version information
      tools.set('flutter_version', {
        name: 'flutter_version',
        description: 'Get Flutter SDK version information',
        inputSchema: {
          type: 'object',
          properties: {},
          required: []
        },
        handler: async () => {
          const result = await processExecutor.execute('flutter', ['--version', '--machine']);
          
          if (result.exitCode !== 0) {
            // Try fallback without --machine flag
            const fallbackResult = await processExecutor.execute('flutter', ['--version']);
            
            return {
              success: true,
              data: {
                machineOutput: null,
                humanOutput: fallbackResult.stdout,
                rawExitCode: result.exitCode,
              },
            };
          }
    
          // Parse machine output if available
          let parsedOutput = null;
          try {
            parsedOutput = JSON.parse(result.stdout);
          } catch {
            // Fallback to human readable
            const humanResult = await processExecutor.execute('flutter', ['--version']);
            return {
              success: true,
              data: {
                machineOutput: null,
                humanOutput: humanResult.stdout,
                rawExitCode: result.exitCode,
              },
            };
          }
    
          return {
            success: true,
            data: {
              machineOutput: parsedOutput,
              rawOutput: result.stdout,
              exitCode: result.exitCode,
            },
          };
        }
      });
    
      // Flutter Devices - List connected devices
      tools.set('flutter_list_devices', {
        name: 'flutter_list_devices',
        description: 'List connected devices and emulators available for Flutter development',
        inputSchema: {
          type: 'object',
          properties: {},
          required: []
        },
        handler: async () => {
          const result = await processExecutor.execute('flutter', ['devices', '--machine']);
          
          if (result.exitCode !== 0) {
            throw new Error(`Failed to list Flutter devices: ${result.stderr}`);
          }
    
          // Parse JSON output
          let devices = [];
          try {
            devices = JSON.parse(result.stdout);
          } catch (parseError) {
            throw new Error(`Failed to parse devices output: ${parseError}`);
          }
    
          // Enhance device info with running session status
          const enhancedDevices = devices.map((device: any) => ({
            ...device,
            hasRunningSession: Array.from(runningFlutterSessions.values()).some(
              session => session.deviceId === device.id
            ),
          }));
    
          return {
            success: true,
            data: {
              devices: enhancedDevices,
              totalCount: enhancedDevices.length,
              connectedCount: enhancedDevices.filter((d: any) => d.isDevice).length,
              simulatorCount: enhancedDevices.filter((d: any) => !d.isDevice).length,
              runningSessionsCount: runningFlutterSessions.size,
            },
          };
        }
      });
    
      // Flutter Emulators - List available emulators
      tools.set('flutter_list_emulators', {
        name: 'flutter_list_emulators',
        description: 'List available emulators for Flutter development',
        inputSchema: {
          type: 'object',
          properties: {},
          required: []
        },
        handler: async () => {
          const result = await processExecutor.execute('flutter', ['emulators']);
          
          if (result.exitCode !== 0) {
            throw new Error(`Failed to list Flutter emulators: ${result.stderr}`);
          }
    
          // Parse text output (emulators command doesn't support --machine flag)
          const emulators = result.stdout
            .split('\n')
            .filter((line: string) => line.trim() && !line.includes('No emulators available'))
            .map((line: string, index: number) => ({
              id: `emulator_${index}`,
              name: line.trim(),
              available: true
            }));
    
          return {
            success: true,
            data: {
              emulators,
              totalCount: emulators.length,
            },
          };
        }
      });
    
      // Flutter Emulators - Launch emulator
      tools.set('flutter_launch_emulator', {
        name: 'flutter_launch_emulator',
        description: 'Launch a Flutter emulator',
        inputSchema: {
          type: 'object',
          properties: {
            emulatorId: { type: 'string', minLength: 1, description: 'Emulator ID to launch' }
          },
          required: ['emulatorId']
        },
        handler: async (args: any) => {
          const validation = FlutterEmulatorLaunchSchema.safeParse(args);
          if (!validation.success) {
            throw new Error(`Invalid request: ${validation.error.message}`);
          }
    
          const { emulatorId } = validation.data;
    
          // Validate emulator ID format (alphanumeric, underscores, dots, dashes)
          if (!/^[a-zA-Z0-9._-]+$/.test(emulatorId)) {
            throw new Error(`Invalid emulator ID format. Emulator ID can only contain alphanumeric characters, dots, underscores, and dashes: ${emulatorId}`);
          }
    
          const result = await processExecutor.execute(
            'flutter', 
            ['emulators', '--launch', emulatorId],
            { timeout: 180000 } // 3 minutes timeout for emulator launch
          );
    
          if (result.exitCode !== 0) {
            throw new Error(`Failed to launch emulator: ${result.stderr || result.stdout}`);
          }
    
          return {
            success: true,
            data: {
              emulatorId,
              status: 'launched',
              output: result.stdout,
            },
          };
        }
      });
    
      // Flutter Run - Start development session
  • src/server.ts:43-71 (registration)
    Global MCP server registration in registerTools(). Calls createFlutterTools to get flutter tools including flutter_run, combines with other tool maps, and registers only those in TOOL_REGISTRY to the server's tools map used by MCP handlers.
    async function registerTools() {
    // Create tool handlers with shared process map
    const androidTools = createAndroidTools(globalProcessMap);
    const iosTools = createIOSTools(globalProcessMap);
    const flutterTools = createFlutterTools(globalProcessMap);
    const superTools = createSuperTools(globalProcessMap);
    const setupTools = createSetupTools(globalProcessMap);
    
    // Combine all tool sources
    const allAvailableTools = new Map<string, any>([
      ...androidTools.entries(),
      ...iosTools.entries(),
      ...flutterTools.entries(),
      ...superTools.entries(),
      ...setupTools.entries()
    ]);
    
    // Only register tools that are in TOOL_REGISTRY
    for (const toolName of Object.keys(TOOL_REGISTRY)) {
      if (allAvailableTools.has(toolName)) {
        tools.set(toolName, allAvailableTools.get(toolName));
      } else if (toolName === 'health_check') {
        // health_check is handled separately below
        continue;
      } else {
        // Tool is in registry but not implemented - log warning
        console.warn(`Warning: Tool '${toolName}' is in TOOL_REGISTRY but not implemented`);
      }
    }
  • Exported Zod schema for flutter_run inputs from types index, used potentially across the codebase for type inference and validation.
    export const FlutterRunSchema = z.object({
      cwd: z.string(),
      deviceId: z.string().optional(),
      target: z.string().optional(),
      flavor: z.string().optional(),
      debugPort: z.number().optional(),
    });
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions 'hot reload enabled', which hints at interactive development behavior, but lacks details on permissions, side effects (e.g., if it starts a server or modifies files), error handling, or runtime characteristics. This is inadequate for a tool that likely initiates a complex development process.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core action and key feature ('hot reload enabled') with zero wasted words. It is appropriately sized for the tool's complexity and easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of starting a Flutter development session, no annotations, and no output schema, the description is insufficient. It lacks details on what the tool returns (e.g., process ID, logs), how it behaves over time, or dependencies, making it incomplete for effective agent use in a development context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all five parameters. The description adds no parameter-specific information beyond what's in the schema, such as examples or constraints. Baseline 3 is appropriate as the schema handles the heavy lifting, but the description doesn't enhance parameter understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Start') and resource ('Flutter development session') with a specific feature ('hot reload enabled'), making the purpose understandable. However, it doesn't explicitly differentiate from sibling tools like 'flutter_dev_session' or 'flutter_build', which could have overlapping or related functionality.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With many sibling tools like 'flutter_dev_session', 'flutter_build', and 'flutter_test', there is no indication of context, prerequisites, or exclusions for using 'flutter_run', leaving the agent without usage direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

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/cristianoaredes/mcp-mobile-server'

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