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, }; } } }

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