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
| Name | Required | Description | Default |
|---|---|---|---|
| arguments | No | Arguments to pass to the executable | |
| background | No | Run in background and return immediately (default: false) | |
| configuration | No | Build configuration: 'debug' (default) or 'release' | |
| executableName | No | Name of executable to run (defaults to package name) | |
| packagePath | Yes | Path to the Swift package root (Required) | |
| parseAsLibrary | No | Add -parse-as-library flag for @main support (default: false) | |
| timeout | No | Timeout 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)'), });
- src/mcp/tools/swift-package/swift_package_run.ts:222-231 (registration)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, ), };