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,
      ),
    };
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 offers minimal behavioral insight. It implies execution but doesn't disclose critical traits: whether it runs synchronously or asynchronously (hinted by 'background' parameter but not explained), potential side effects (e.g., process creation, resource usage), error handling, or output format (stdout/stderr capture). For a tool with 7 parameters and no annotation coverage, this is inadequate.

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 directly states the tool's purpose without redundancy. It uses precise terminology ('executable target', 'Swift Package', 'swift run') and avoids unnecessary elaboration, making it easy to parse and front-loaded with essential information.

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

Completeness3/5

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

Given the tool's complexity (7 parameters, execution-focused) and lack of annotations/output schema, the description is minimally adequate but incomplete. It covers the basic action but misses behavioral context (e.g., how output is returned, error scenarios) and usage guidance. Without annotations, it should provide more detail on execution behavior to fully inform the agent.

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%, providing clear documentation for all 7 parameters (e.g., 'arguments' as command-line args, 'background' for async execution). The description adds no parameter-specific information beyond the schema, such as examples or usage tips. According to guidelines, high schema coverage sets a baseline of 3, and the description doesn't enhance this further.

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 ('Runs') and target ('executable target from a Swift Package'), using the specific command 'swift run'. It distinguishes from sibling tools like swift_package_build or swift_package_test by focusing on execution rather than building, cleaning, or testing. However, it doesn't explicitly differentiate from other run-related tools (e.g., launch_app_sim), which prevents a perfect score.

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 a valid Swift package), compare to sibling tools like swift_package_build (for building without running) or launch_app_sim (for running on simulators), or specify use cases (e.g., testing executables locally). This leaves the agent without context for tool selection.

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

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