Skip to main content
Glama
localstack
by localstack
localstack-management.ts7.36 kB
import { z } from "zod"; import { type ToolMetadata, type InferSchema } from "xmcp"; import { spawn } from "child_process"; import { getLocalStackStatus } from "../lib/localstack/localstack.utils"; import { runCommand } from "../core/command-runner"; import { runPreflights, requireLocalStackCli } from "../core/preflight"; import { ResponseBuilder } from "../core/response-builder"; export const schema = { action: z .enum(["start", "stop", "restart", "status"]) .describe("The LocalStack management action to perform"), envVars: z .record(z.string()) .optional() .describe("Additional environment variables as key-value pairs (only for start action)"), }; export const metadata: ToolMetadata = { name: "localstack-management", description: "Manage LocalStack lifecycle: start, stop, restart, or check status", annotations: { title: "LocalStack Management", readOnlyHint: false, destructiveHint: false, idempotentHint: false, }, }; export default async function localstackManagement({ action, envVars, }: InferSchema<typeof schema>) { const preflightError = await runPreflights([requireLocalStackCli()]); if (preflightError) return preflightError; switch (action) { case "start": return await handleStart({ envVars }); case "stop": return await handleStop(); case "restart": return await handleRestart(); case "status": return await handleStatus(); default: return ResponseBuilder.error( "Unknown action", `❌ Unknown action: ${action}. Supported actions: start, stop, restart, status` ); } } // Handle start action async function handleStart({ envVars }: { envVars?: Record<string, string> }) { const statusCheck = await getLocalStackStatus(); if (statusCheck.isRunning) { return ResponseBuilder.markdown( "⚠️ LocalStack is already running. Use 'restart' if you want to apply new configuration." ); } const environment = { ...process.env, ...(envVars || {}) } as Record<string, string>; if (process.env.LOCALSTACK_AUTH_TOKEN) { environment.LOCALSTACK_AUTH_TOKEN = process.env.LOCALSTACK_AUTH_TOKEN; } return new Promise((resolve) => { const child = spawn("localstack", ["start"], { env: environment, stdio: ["ignore", "ignore", "pipe"], }); let stderr = ""; child.stderr.on("data", (data) => { stderr += data.toString(); }); let earlyExit = false; let poll: NodeJS.Timeout; child.on("error", (err) => { earlyExit = true; if (poll) clearInterval(poll); resolve(ResponseBuilder.markdown(`❌ Failed to start LocalStack process: ${err.message}`)); }); child.on("close", (code) => { if (earlyExit) return; if (poll) clearInterval(poll); if (code !== 0) { resolve( ResponseBuilder.markdown( `❌ LocalStack process exited unexpectedly with code ${code}.\n\nStderr:\n${stderr}` ) ); } }); const pollInterval = 5000; const maxWaitTime = 120000; let timeWaited = 0; poll = setInterval(async () => { timeWaited += pollInterval; const status = await getLocalStackStatus(); if (status.isReady || status.isRunning) { clearInterval(poll); let resultMessage = "🚀 LocalStack started successfully!\n\n"; if (envVars) resultMessage += `✅ Custom environment variables applied: ${Object.keys(envVars).join(", ")}\n`; resultMessage += `\n**Status:**\n${status.statusOutput}`; resolve(ResponseBuilder.markdown(resultMessage)); } else if (timeWaited >= maxWaitTime) { clearInterval(poll); resolve( ResponseBuilder.markdown( `❌ LocalStack start timed out after ${maxWaitTime / 1000} seconds. It may still be starting in the background.` ) ); } }, pollInterval); }); } // Handle stop action async function handleStop() { const cmd = await runCommand("localstack", ["stop"]); let result = "🛑 LocalStack stop command executed successfully!\n"; if (cmd.stdout.trim()) result += `\nOutput:\n${cmd.stdout}`; if (cmd.stderr.trim()) result += `\nMessages:\n${cmd.stderr}`; const statusResult = await getLocalStackStatus(); if (!statusResult.isRunning) { result += "\n\n✅ LocalStack has been stopped successfully."; } else if (statusResult.errorMessage) { result += "\n\n✅ LocalStack appears to be stopped."; } else { result += "\n\n⚠️ LocalStack may still be running. Check the status manually if needed."; } if (cmd.error) { result = `❌ Failed to stop LocalStack: ${cmd.error.message}\n\nThis could happen if:\n- LocalStack is not currently running\n- There was an error executing the stop command\n- Permission issues\n\nYou can try checking the LocalStack status first to see if it's running.`; } return ResponseBuilder.markdown(result); } // Handle restart action async function handleRestart() { const cmd = await runCommand("localstack", ["restart"], { timeout: 30000 }); let result = "🔄 LocalStack restart command executed!\n\n"; if (cmd.stdout.trim()) result += `Output:\n${cmd.stdout}\n`; if (cmd.stderr.trim()) result += `Messages:\n${cmd.stderr}\n`; await new Promise((resolve) => setTimeout(resolve, 2000)); const statusResult = await getLocalStackStatus(); if (statusResult.statusOutput) { result += `\nStatus after restart:\n${statusResult.statusOutput}`; if (statusResult.isRunning) { result += "\n\n✅ LocalStack has been restarted successfully and is now running with a fresh state."; } else { result += "\n\n⚠️ LocalStack restart completed but may still be starting up. Check status again in a few moments."; } } else { result += "\n\n⚠️ Restart completed but unable to verify status. LocalStack may still be starting up."; } if (cmd.error) { result = `❌ Failed to restart LocalStack: ${cmd.error.message}\n\nThis could happen if:\n- LocalStack is not currently installed properly\n- There was an error executing the restart command\n- The restart process timed out (LocalStack can take time to restart)\n- Permission issues\n\nYou can try stopping and starting LocalStack manually using separate actions if the restart action continues to fail.`; } return ResponseBuilder.markdown(result); } // Handle status action async function handleStatus() { const statusResult = await getLocalStackStatus(); if (statusResult.statusOutput) { let result = "📊 LocalStack Status:\n\n"; result += statusResult.statusOutput; // Add helpful information based on the status if (statusResult.isRunning) { result += "\n\n✅ LocalStack is currently running and ready to accept requests."; } else { result += "\n\n⚠️ LocalStack is not currently running. Use the start action to start it."; } return ResponseBuilder.markdown(result); } else { const result = `❌ ${statusResult.errorMessage} This could happen if: - LocalStack is not installed properly - There was an error executing the status command - LocalStack services are not accessible Try running the CLI check first to verify your LocalStack installation.`; return ResponseBuilder.markdown(result); } }

Implementation Reference

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/localstack/localstack-mcp-server'

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