Skip to main content
Glama

swift_package_run

Execute an executable target from a Swift Package by specifying the package path, executable name, and optional arguments. Supports debug or release configurations, background execution, and custom timeouts.

Instructions

Runs an executable target from a Swift Package with swift run

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
argumentsNoArguments to pass to the executable
backgroundNoRun in background and return immediately (default: false)
configurationNoBuild configuration: 'debug' (default) or 'release'
executableNameNoName of executable to run (defaults to package name)
packagePathYesPath to the Swift package root (Required)
parseAsLibraryNoAdd -parse-as-library flag for @main support (default: false)
timeoutNoTimeout in seconds (default: 30, max: 300)

Implementation Reference

  • The main handler function `swift_package_runLogic` that executes the `swift run` command, supports foreground and background modes, handles timeouts, process tracking, and error cases.
    export async function swift_package_runLogic(
      params: SwiftPackageRunParams,
      executor: CommandExecutor,
    ): Promise<ToolResponse> {
      const resolvedPath = path.resolve(params.packagePath);
      const timeout = Math.min(params.timeout ?? 30, 300) * 1000; // Convert to ms, max 5 minutes
    
      // Detect test environment to prevent real spawn calls during testing
      const isTestEnvironment = process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';
    
      const swiftArgs = ['run', '--package-path', resolvedPath];
    
      if (params.configuration && params.configuration.toLowerCase() === 'release') {
        swiftArgs.push('-c', 'release');
      } else if (params.configuration && params.configuration.toLowerCase() !== 'debug') {
        return createTextResponse("Invalid configuration. Use 'debug' or 'release'.", true);
      }
    
      if (params.parseAsLibrary) {
        swiftArgs.push('-Xswiftc', '-parse-as-library');
      }
    
      if (params.executableName) {
        swiftArgs.push(params.executableName);
      }
    
      // Add double dash before executable arguments
      if (params.arguments && params.arguments.length > 0) {
        swiftArgs.push('--');
        swiftArgs.push(...params.arguments);
      }
    
      log('info', `Running swift ${swiftArgs.join(' ')}`);
    
      try {
        if (params.background) {
          // Background mode: Use CommandExecutor but don't wait for completion
          if (isTestEnvironment) {
            // In test environment, return mock response without real process
            const mockPid = 12345;
            return {
              content: [
                createTextContent(
                  `🚀 Started executable in background (PID: ${mockPid})\n` +
                    `💡 Process is running independently. Use swift_package_stop with PID ${mockPid} to terminate when needed.`,
                ),
              ],
            };
          } else {
            // Production: use CommandExecutor to start the process
            const command = ['swift', ...swiftArgs];
            // Filter out undefined values from process.env
            const cleanEnv = Object.fromEntries(
              Object.entries(process.env).filter(([, value]) => value !== undefined),
            ) as Record<string, string>;
            const result = await executor(
              command,
              'Swift Package Run (Background)',
              true,
              cleanEnv,
              true,
            );
    
            // Store the process in active processes system if available
            if (result.process?.pid) {
              addProcess(result.process.pid, {
                process: {
                  kill: (signal?: string) => {
                    // Adapt string signal to NodeJS.Signals
                    if (result.process) {
                      result.process.kill(signal as NodeJS.Signals);
                    }
                  },
                  on: (event: string, callback: () => void) => {
                    if (result.process) {
                      result.process.on(event, callback);
                    }
                  },
                  pid: result.process.pid,
                },
                startedAt: new Date(),
              });
    
              return {
                content: [
                  createTextContent(
                    `🚀 Started executable in background (PID: ${result.process.pid})\n` +
                      `💡 Process is running independently. Use swift_package_stop with PID ${result.process.pid} to terminate when needed.`,
                  ),
                ],
              };
            } else {
              return {
                content: [
                  createTextContent(
                    `🚀 Started executable in background\n` +
                      `💡 Process is running independently. PID not available for this execution.`,
                  ),
                ],
              };
            }
          }
        } else {
          // Foreground mode: use CommandExecutor but handle long-running processes
          const command = ['swift', ...swiftArgs];
    
          // Create a promise that will either complete with the command result or timeout
          const commandPromise = executor(command, 'Swift Package Run', true, undefined);
    
          const timeoutPromise = new Promise<{
            success: boolean;
            output: string;
            error: string;
            timedOut: boolean;
          }>((resolve) => {
            setTimeout(() => {
              resolve({
                success: false,
                output: '',
                error: `Process timed out after ${timeout / 1000} seconds`,
                timedOut: true,
              });
            }, timeout);
          });
    
          // Race between command completion and timeout
          const result = await Promise.race([commandPromise, timeoutPromise]);
    
          if ('timedOut' in result && result.timedOut) {
            // For timeout case, the process may still be running - provide timeout response
            if (isTestEnvironment) {
              // In test environment, return mock response
              const mockPid = 12345;
              return {
                content: [
                  createTextContent(
                    `⏱️ Process timed out after ${timeout / 1000} seconds but may continue running.`,
                  ),
                  createTextContent(`PID: ${mockPid} (mock)`),
                  createTextContent(
                    `💡 Process may still be running. Use swift_package_stop with PID ${mockPid} to terminate when needed.`,
                  ),
                  createTextContent(result.output || '(no output so far)'),
                ],
              };
            } else {
              // Production: timeout occurred, but we don't start a new process
              return {
                content: [
                  createTextContent(`⏱️ Process timed out after ${timeout / 1000} seconds.`),
                  createTextContent(
                    `💡 Process execution exceeded the timeout limit. Consider using background mode for long-running executables.`,
                  ),
                  createTextContent(result.output || '(no output so far)'),
                ],
              };
            }
          }
    
          if (result.success) {
            return {
              content: [
                createTextContent('✅ Swift executable completed successfully.'),
                createTextContent('💡 Process finished cleanly. Check output for results.'),
                createTextContent(result.output || '(no output)'),
              ],
            };
          } else {
            const content = [
              createTextContent('❌ Swift executable failed.'),
              createTextContent(result.output || '(no output)'),
            ];
            if (result.error) {
              content.push(createTextContent(`Errors:\n${result.error}`));
            }
            return { content };
          }
        }
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        log('error', `Swift run failed: ${message}`);
        return createErrorResponse('Failed to execute swift run', message);
      }
    }
  • Zod schema defining input parameters for the `swift_package_run` tool, including package path, executable, arguments, config, timeout, background mode, and parse-as-library flag.
    const swiftPackageRunSchema = z.object({
      packagePath: z.string().describe('Path to the Swift package root (Required)'),
      executableName: z
        .string()
        .optional()
        .describe('Name of executable to run (defaults to package name)'),
      arguments: z.array(z.string()).optional().describe('Arguments to pass to the executable'),
      configuration: z
        .enum(['debug', 'release'])
        .optional()
        .describe("Build configuration: 'debug' (default) or 'release'"),
      timeout: z.number().optional().describe('Timeout in seconds (default: 30, max: 300)'),
      background: z
        .boolean()
        .optional()
        .describe('Run in background and return immediately (default: false)'),
      parseAsLibrary: z
        .boolean()
        .optional()
        .describe('Add -parse-as-library flag for @main support (default: false)'),
    });
  • Module export default object registering the tool with name, description, schema, and typed handler created via `createTypedTool`.
    export default {
      name: 'swift_package_run',
      description: 'Runs an executable target from a Swift Package with swift run',
      schema: swiftPackageRunSchema.shape, // MCP SDK compatibility
      handler: createTypedTool(
        swiftPackageRunSchema,
        swift_package_runLogic,
        getDefaultCommandExecutor,
      ),
    };

Tool Definition Quality

Score is being calculated. Check back soon.

Install Server

Other Tools

Related 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/getsentry/XcodeBuildMCP'

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