Skip to main content
Glama
andreahaku

Expo iOS Development MCP Server

by andreahaku

expo.start

Launch the Expo/Metro development server for React Native applications, with optional cache clearing to resolve build issues.

Instructions

Start the Expo/Metro development server

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
clearCacheNoClear Metro cache before starting.

Implementation Reference

  • Core handler function that starts the Expo/Metro development server by executing the configured start command, managing the child process, parsing output for readiness detection, and updating state.
    export async function startExpo(options: ExpoStartOptions = {}): Promise<ExpoStartResult> {
      logger.info("expo", "Starting Expo/Metro...");
    
      if (expoProcess) {
        const status = detectMetroReady(outputBuffer);
        if (status.ready) {
          logger.info("expo", "Expo is already running");
          return {
            started: true,
            metroUrl: status.url,
            message: "Expo is already running",
          };
        }
        // Process exists but not ready - kill it and restart
        await stopExpo();
      }
    
      if (!hasConfig()) {
        throw createError("CONFIG_NOT_FOUND", "Configuration required to start Expo");
      }
    
      const config = getConfig();
      stateManager.updateExpo({ state: "starting" });
    
      // Build command
      let command = config.expo.startCommand;
    
      if (options.clearCache) {
        command += ` ${config.expo.clearCacheFlag}`;
      }
    
      if (options.port) {
        command += ` --port ${options.port}`;
      }
    
      const [cmd, ...args] = command.split(" ").filter(Boolean);
    
      logger.info("expo", `Executing: ${command}`, { cwd: config.projectPath });
    
      try {
        // Start Expo process
        expoProcess = execa(cmd, args, {
          cwd: config.projectPath,
          reject: false,
          env: {
            ...process.env,
            FORCE_COLOR: "1",
            CI: "false", // Ensure interactive mode
          },
        });
    
        startedAt = new Date().toISOString();
        outputBuffer = "";
    
        // Handle stdout
        expoProcess.stdout?.on("data", (chunk: Buffer) => {
          const text = chunk.toString();
          outputBuffer += text;
          processExpoOutput(text, "stdout");
    
          // Check for Metro ready
          const status = detectMetroReady(outputBuffer);
          if (status.ready && stateManager.getExpo().state !== "running") {
            stateManager.updateExpo({
              state: "running",
              metroUrl: status.url,
              processId: expoProcess?.pid,
            });
            logger.info("expo", `Metro ready at ${status.url}`);
          }
        });
    
        // Handle stderr
        expoProcess.stderr?.on("data", (chunk: Buffer) => {
          const text = chunk.toString();
          outputBuffer += text;
          processExpoOutput(text, "stderr");
    
          // Check for errors
          if (isMetroError(text)) {
            stateManager.updateExpo({ state: "crashed" });
            logger.error("expo", "Metro encountered an error", { output: text });
          }
        });
    
        // Handle process exit
        expoProcess.on("exit", (code: number | null) => {
          logger.info("expo", `Expo process exited with code ${code}`);
          if (code !== 0 && code !== null) {
            stateManager.updateExpo({ state: "crashed" });
          } else {
            stateManager.updateExpo({ state: "stopped" });
          }
          expoProcess = null;
        });
    
        // Wait for Metro to be ready (with timeout)
        const waitResult = await waitForMetroReady(30000);
    
        if (waitResult.ready) {
          return {
            started: true,
            metroUrl: waitResult.url,
            message: "Expo started successfully",
          };
        }
    
        return {
          started: true,
          message: "Expo started but Metro readiness not confirmed yet",
        };
      } catch (error) {
        stateManager.updateExpo({ state: "crashed" });
        throw createError("EXPO_START_FAILED", "Failed to start Expo", {
          details: error instanceof Error ? error.message : "Unknown error",
        });
      }
    }
  • Zod schema defining the input parameters for the expo.start tool, including optional clearCache flag.
    export const ExpoStartInputSchema = z.object({
      clearCache: z.boolean().optional().default(false).describe("Clear Metro cache before starting."),
    });
  • Registers the 'expo.start' tool in the MCP server, linking the schema and delegating to the startExpo handler function.
    server.tool(
      "expo.start",
      "Start the Expo/Metro development server",
      ExpoStartInputSchema.shape,
      async (args) => {
        try {
          const result = await startExpo({
            clearCache: args.clearCache,
          });
          return {
            content: [
              {
                type: "text",
                text: JSON.stringify(result, null, 2),
              },
            ],
          };
        } catch (error) {
          return handleToolError(error);
        }
      }
    );

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/andreahaku/expo_ios_development_mcp'

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