Skip to main content
Glama

manage_env

Boot, shutdown, or restart Android and iOS emulators/simulators for mobile app testing and development.

Instructions

Manage device environment: boot, shutdown, or restart emulators and simulators.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesAction to perform
platformYesTarget platform
deviceIdNoDevice ID, name, or AVD name (optional, uses first available)
waitForReadyNoWait for device to be fully ready after boot (default: true)
timeoutMsNoTimeout in milliseconds (default: 120000)

Implementation Reference

  • Main handler function for the 'manage_env' tool. Validates inputs and dispatches to platform-specific environment management functions (Android or iOS).
    export async function manageEnv(args: ManageEnvArgs): Promise<EnvironmentResult> {
      const {
        action,
        platform,
        deviceId,
        waitForReady = true,
        timeoutMs = 120000,
      } = args;
    
      // Validate platform
      if (!isPlatform(platform)) {
        throw Errors.invalidArguments(`Invalid platform: ${platform}. Must be 'android' or 'ios'`);
      }
    
      // Validate action
      if (!['boot', 'shutdown', 'restart'].includes(action)) {
        throw Errors.invalidArguments(`Invalid action: ${action}. Must be 'boot', 'shutdown', or 'restart'`);
      }
    
      const startTime = Date.now();
    
      if (platform === 'android') {
        return manageAndroidEnv(action, deviceId, waitForReady, timeoutMs, startTime);
      } else {
        return manageIOSEnv(action, deviceId, waitForReady, timeoutMs, startTime);
      }
    }
  • TypeScript interface defining the input parameters for the manage_env tool.
    export interface ManageEnvArgs {
      /** Action to perform */
      action: EnvironmentAction;
      /** Target platform */
      platform: string;
      /** Device ID or name */
      deviceId?: string;
      /** Wait for device to be ready after boot */
      waitForReady?: boolean;
      /** Timeout in milliseconds */
      timeoutMs?: number;
    }
  • Registers the 'manage_env' tool with the MCP tool registry, providing description, JSON input schema, and handler reference.
    export function registerManageEnvTool(): void {
      getToolRegistry().register(
        'manage_env',
        {
          description:
            'Manage device environment: boot, shutdown, or restart emulators and simulators.',
          inputSchema: createInputSchema(
            {
              action: {
                type: 'string',
                enum: ['boot', 'shutdown', 'restart'],
                description: 'Action to perform',
              },
              platform: {
                type: 'string',
                enum: ['android', 'ios'],
                description: 'Target platform',
              },
              deviceId: {
                type: 'string',
                description: 'Device ID, name, or AVD name (optional, uses first available)',
              },
              waitForReady: {
                type: 'boolean',
                description: 'Wait for device to be fully ready after boot (default: true)',
              },
              timeoutMs: {
                type: 'number',
                description: 'Timeout in milliseconds (default: 120000)',
              },
            },
            ['action', 'platform']
          ),
        },
        (args) => manageEnv(args as unknown as ManageEnvArgs)
      );
    }
  • Helper function implementing Android-specific logic for booting, shutting down, and restarting emulators.
    async function manageAndroidEnv(
      action: EnvironmentAction,
      deviceQuery: string | undefined,
      waitForReady: boolean,
      _timeoutMs: number,
      startTime: number
    ): Promise<EnvironmentResult> {
      // Get current devices
      const devices = await listAndroidDevices();
      let targetDevice: Device | undefined;
    
      if (deviceQuery) {
        // Find specific device
        const found = devices.find(
          (d) => d.id === deviceQuery || d.name === deviceQuery || d.model === deviceQuery
        );
        if (found) {
          targetDevice = fromAndroidDevice(found);
        }
      } else if (action !== 'boot') {
        // For shutdown/restart, use first booted device
        const booted = devices.find((d) => d.status === 'booted');
        if (booted) {
          targetDevice = fromAndroidDevice(booted);
        }
      }
    
      switch (action) {
        case 'boot': {
          // For boot, we need an AVD name
          let avdName = deviceQuery;
    
          if (!avdName) {
            // List available AVDs and use first one
            const avds = await listAvds();
            if (avds.length === 0) {
              return {
                success: false,
                action,
                error: 'No Android AVDs available. Create one in Android Studio.',
                durationMs: Date.now() - startTime,
              };
            }
            avdName = avds[0];
          }
    
          // Check if already running
          const existing = devices.find((d) => d.name === avdName || d.id.includes(avdName));
          if (existing && existing.status === 'booted') {
            return {
              success: true,
              action,
              device: fromAndroidDevice(existing),
              details: 'Device already running',
              durationMs: Date.now() - startTime,
            };
          }
    
          // Boot the emulator (fire and forget, returns void)
          await bootEmulator(avdName);
    
          // Wait for device to be ready if requested
          if (waitForReady) {
            // Find the booted device
            await new Promise((resolve) => setTimeout(resolve, 5000)); // Initial wait
            const updatedDevices = await listAndroidDevices();
            const bootedDevice = updatedDevices.find((d) => d.status === 'booted');
            if (bootedDevice) {
              await waitForDevice(bootedDevice.id);
            }
          }
    
          // Get updated device info
          const finalDevices = await listAndroidDevices();
          const bootedDevice = finalDevices.find((d) => d.status === 'booted');
    
          return {
            success: true,
            action,
            device: bootedDevice ? fromAndroidDevice(bootedDevice) : undefined,
            details: `Booted emulator: ${avdName}`,
            durationMs: Date.now() - startTime,
          };
        }
    
        case 'shutdown': {
          if (!targetDevice) {
            return {
              success: false,
              action,
              error: 'No running Android device found to shutdown',
              durationMs: Date.now() - startTime,
            };
          }
    
          await shutdownEmulator(targetDevice.id);
    
          return {
            success: true,
            action,
            device: targetDevice,
            details: `Shutdown device: ${targetDevice.name}`,
            durationMs: Date.now() - startTime,
          };
        }
    
        case 'restart': {
          if (!targetDevice) {
            return {
              success: false,
              action,
              error: 'No running Android device found to restart',
              durationMs: Date.now() - startTime,
            };
          }
    
          // Shutdown then boot
          await shutdownEmulator(targetDevice.id);
          await new Promise((resolve) => setTimeout(resolve, 2000));
    
          // Get AVD name from device
          const avdName = targetDevice.name;
          await bootEmulator(avdName);
    
          if (waitForReady) {
            await new Promise((resolve) => setTimeout(resolve, 5000));
            const updatedDevices = await listAndroidDevices();
            const bootedDevice = updatedDevices.find((d) => d.status === 'booted');
            if (bootedDevice) {
              await waitForDevice(bootedDevice.id);
            }
          }
    
          // Get updated device info
          const finalDevices = await listAndroidDevices();
          const restartedDevice = finalDevices.find((d) => d.status === 'booted');
    
          return {
            success: true,
            action,
            device: restartedDevice ? fromAndroidDevice(restartedDevice) : targetDevice,
            details: `Restarted device: ${targetDevice.name}`,
            durationMs: Date.now() - startTime,
          };
        }
      }
    }
  • Helper function implementing iOS-specific logic for booting, shutting down, and restarting simulators.
    async function manageIOSEnv(
      action: EnvironmentAction,
      deviceQuery: string | undefined,
      waitForReady: boolean,
      _timeoutMs: number,
      startTime: number
    ): Promise<EnvironmentResult> {
      // Get current devices
      const devices = await listIOSDevices();
      let targetDevice: Device | undefined;
    
      if (deviceQuery) {
        // Find specific device
        const found = devices.find((d) => d.id === deviceQuery || d.name === deviceQuery);
        if (found) {
          targetDevice = fromIOSDevice({
            id: found.id,
            name: found.name,
            status: found.status,
            runtime: found.runtime,
          });
        }
      } else if (action !== 'boot') {
        // For shutdown/restart, use first booted device
        const booted = await getIOSBootedDevice();
        if (booted) {
          targetDevice = fromIOSDevice({
            id: booted.id,
            name: booted.name,
            status: booted.status,
            runtime: booted.runtime,
          });
        }
      }
    
      switch (action) {
        case 'boot': {
          let udid = deviceQuery;
    
          if (!udid) {
            // Find first available simulator
            const available = devices.find((d) => d.isAvailable !== false);
            if (!available) {
              return {
                success: false,
                action,
                error: 'No iOS simulators available',
                durationMs: Date.now() - startTime,
              };
            }
            udid = available.id;
          }
    
          // Check if already running
          const existing = devices.find((d) => d.id === udid || d.name === udid);
          if (existing && existing.status.toLowerCase() === 'booted') {
            return {
              success: true,
              action,
              device: fromIOSDevice({
                id: existing.id,
                name: existing.name,
                status: existing.status,
                runtime: existing.runtime,
              }),
              details: 'Simulator already running',
              durationMs: Date.now() - startTime,
            };
          }
    
          // Get UDID if we have a name
          const targetSim = devices.find((d) => d.id === udid || d.name === udid);
          if (!targetSim) {
            return {
              success: false,
              action,
              error: `iOS simulator not found: ${udid}`,
              durationMs: Date.now() - startTime,
            };
          }
    
          // Boot the simulator
          await bootSimulator(targetSim.id);
    
          // Wait for device to be ready (simple delay since simctl boot is synchronous)
          if (waitForReady) {
            await new Promise((resolve) => setTimeout(resolve, 5000));
          }
    
          return {
            success: true,
            action,
            device: fromIOSDevice({
              id: targetSim.id,
              name: targetSim.name,
              status: 'Booted',
              runtime: targetSim.runtime,
            }),
            details: `Booted simulator: ${targetSim.name}`,
            durationMs: Date.now() - startTime,
          };
        }
    
        case 'shutdown': {
          if (!targetDevice) {
            return {
              success: false,
              action,
              error: 'No running iOS simulator found to shutdown',
              durationMs: Date.now() - startTime,
            };
          }
    
          await shutdownSimulator(targetDevice.id);
    
          return {
            success: true,
            action,
            device: targetDevice,
            details: `Shutdown simulator: ${targetDevice.name}`,
            durationMs: Date.now() - startTime,
          };
        }
    
        case 'restart': {
          if (!targetDevice) {
            return {
              success: false,
              action,
              error: 'No running iOS simulator found to restart',
              durationMs: Date.now() - startTime,
            };
          }
    
          // Shutdown then boot
          await shutdownSimulator(targetDevice.id);
          await new Promise((resolve) => setTimeout(resolve, 2000));
          await bootSimulator(targetDevice.id);
    
          if (waitForReady) {
            await new Promise((resolve) => setTimeout(resolve, 5000));
          }
    
          return {
            success: true,
            action,
            device: { ...targetDevice, status: 'booted' },
            details: `Restarted simulator: ${targetDevice.name}`,
            durationMs: Date.now() - startTime,
          };
        }
      }
    }
Behavior2/5

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

With no annotations provided, the description carries full burden but lacks critical behavioral details. It doesn't disclose that this is a potentially long-running operation (implied by timeout parameter), that it might require specific environment setup, or what happens on failure. The description mentions actions but not consequences like data persistence or state changes.

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 purpose. Every word earns its place: 'Manage device environment' establishes scope, 'boot, shutdown, or restart' specifies actions, and 'emulators and simulators' clarifies the target. No wasted words or redundant information.

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?

For a tool with 5 parameters, no annotations, and no output schema, the description is incomplete. It doesn't explain what 'managing' entails beyond the actions, what the expected outcomes are, or potential side effects. Given the complexity of device environment management and lack of structured behavioral hints, the description should provide more operational 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 already documents all 5 parameters thoroughly. The description adds no parameter-specific information beyond what's in the schema. It mentions 'emulators and simulators' which aligns with the platform enums, but this doesn't provide additional semantic value beyond the structured schema.

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 verb ('manage') and resource ('device environment') with specific actions ('boot, shutdown, or restart emulators and simulators'). It distinguishes from siblings like 'list_devices' or 'install_app' by focusing on environment lifecycle management. However, it doesn't explicitly differentiate from 'launch_app' which might overlap conceptually.

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. It doesn't mention prerequisites (e.g., needing emulators installed), exclusions (e.g., not for physical devices), or relationships to sibling tools like 'list_devices' (which might provide device IDs). Usage context is implied but not explicit.

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/abd3lraouf/specter-mcp'

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