Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ terraform.tfstate*
.terraform.lock.hcl
.mcp-test-results/
.DS_Store
cdk.out/
44 changes: 38 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,41 @@ Prompts are user-selected workflow templates exposed by MCP clients as slash com

## Installation

| Editor | Installation |
| :--------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cursor** | [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=localstack-mcp-server&config=eyJjb21tYW5kIjoibnB4IC15IEBsb2NhbHN0YWNrL2xvY2Fsc3RhY2stbWNwLXNlcnZlciJ9) |
### Quick Setup (Wizard)

The fastest way to install the MCP server is the interactive setup wizard:

```bash
npx -y @localstack/localstack-mcp-server init
```

The wizard:

- lets you choose how to run the server (`npx` on your machine, or the self-contained Docker image),
- checks the prerequisites (Node.js, LocalStack CLI, Docker) and tells you how to fix anything missing,
- picks up your `LOCALSTACK_AUTH_TOKEN` from the environment, or asks for it,
- lets you pass extra LocalStack config (e.g. `DEBUG=1,PERSISTENCE=1`),
- detects your installed MCP clients (Cursor, Claude Code, Claude Desktop, VS Code, Codex, OpenCode, Amazon Q CLI) and writes the right configuration for each one you select.

It can also run fully non-interactively, e.g. in dotfiles or scripts:

```bash
npx -y @localstack/localstack-mcp-server init --method npx --client cursor,claude-code --yes
```

To remove the server from your clients again:

```bash
npx -y @localstack/localstack-mcp-server remove
```

Run `npx -y @localstack/localstack-mcp-server init --help` for all options.

### Manual Setup

| Editor | Installation |
| :--------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cursor** | [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=localstack&config=eyJjb21tYW5kIjoibnB4IC15IEBsb2NhbHN0YWNrL2xvY2Fsc3RhY2stbWNwLXNlcnZlciJ9) |

For other MCP Clients, refer to the [configuration guide](#configuration).

Expand All @@ -65,7 +97,7 @@ For other MCP Clients, refer to the [configuration guide](#configuration).
- [LocalStack CLI](https://docs.localstack.cloud/getting-started/installation/#localstack-cli) and Docker installed in your system path
- [`cdklocal`](https://github.com/localstack/aws-cdk-local), [`tflocal`](https://github.com/localstack/terraform-local), or [`samlocal`](https://github.com/localstack/aws-sam-cli-local) installed in your system path for running infrastructure deployment tooling
- A [valid LocalStack Auth Token](https://docs.localstack.cloud/aws/getting-started/auth-token/) configured as `LOCALSTACK_AUTH_TOKEN` (**required for all MCP tools**)
- [Node.js v22.x](https://nodejs.org/en/download/) or higher installed in your system path
- [Node.js v20](https://nodejs.org/en/download/) or higher installed in your system path

### Configuration

Expand All @@ -74,7 +106,7 @@ Add the following to your MCP client's configuration file (e.g., `~/.cursor/mcp.
```json
{
"mcpServers": {
"localstack-mcp-server": {
"localstack": {
"command": "npx",
"args": ["-y", "@localstack/localstack-mcp-server"],
"env": {
Expand All @@ -92,7 +124,7 @@ If you installed from source, change `command` and `args` to point to your local
```json
{
"mcpServers": {
"localstack-mcp-server": {
"localstack": {
"command": "node",
"args": ["/path/to/your/localstack-mcp-server/dist/stdio.js"],
"env": {
Expand Down
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
"node": ">=20.0.0"
},
"scripts": {
"build": "xmcp build",
"build": "xmcp build && yarn build:cli",
"build:cli": "esbuild src/cli/index.ts --bundle --platform=node --format=cjs --target=node20 --tsconfig=tsconfig.cli.json --alias:jsonc-parser=jsonc-parser/lib/esm/main.js --outfile=dist/cli.js --external:./stdio.js \"--banner:js=#!/usr/bin/env node\" --log-level=warning",
"dev": "xmcp dev",
"start": "node dist/stdio.js",
"prepack": "yarn build",
"format": "prettier --write .",
"test": "jest",
"test:mcp:direct": "yarn build && playwright test -c playwright.config.mjs tests/mcp/direct.spec.mjs",
Expand All @@ -23,12 +25,15 @@
"zod": "4.3.6"
},
"devDependencies": {
"@clack/prompts": "^1.5.1",
"@gleanwork/mcp-server-tester": "1.0.0-beta.6",
"@playwright/test": "^1.58.2",
"@types/dockerode": "^3.3.43",
"@types/jest": "^30.0.0",
"esbuild": "^0.28.0",
"eslint-config-prettier": "^10.1.8",
"jest": "^30.1.3",
"jsonc-parser": "^3.3.1",
"prettier": "^3.6.2",
"swc-loader": "^0.2.6",
"ts-jest": "^29.4.1",
Expand All @@ -39,7 +44,7 @@
"dist"
],
"bin": {
"localstack-mcp-server": "./dist/stdio.js"
"localstack-mcp-server": "./dist/cli.js"
},
"author": "@localstack",
"license": "Apache-2.0",
Expand Down
41 changes: 41 additions & 0 deletions src/cli/help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { ALL_CLIENT_IDS } from "../lib/wizard/clients/registry";

export const HELP_TEXT = `LocalStack MCP Server

Usage:
npx -y @localstack/localstack-mcp-server Start the MCP server (stdio)
npx -y @localstack/localstack-mcp-server init Set up the server in your MCP clients
npx -y @localstack/localstack-mcp-server remove Remove the server from your MCP clients

init options:
--method <npx|docker> How the MCP server should run (default: npx)
--client <ids> MCP clients to configure, comma-separated or repeated.
Valid: ${ALL_CLIENT_IDS.join(", ")}
--token <token> LocalStack Auth Token (default: $LOCALSTACK_AUTH_TOKEN)
--config <pairs> Extra LocalStack config vars, e.g. "DEBUG=1,PERSISTENCE=1"
--cache-dir <path> [docker] State/cache dir mounted into the container
(default: ~/.localstack-mcp)
--workspace <path> [docker] Workspace dir to mount for IaC deployments
(default: current directory; pass "" to skip)
--image-tag <tag> [docker] Image tag for localstack/localstack-mcp-server
(default: latest)
--force Overwrite an existing "localstack" entry without asking
-y, --yes Accept defaults for everything not provided via flags;
existing entries are kept unless --force is also given
-h, --help Show this help

remove options:
--client <ids> Clients to remove "localstack" from (default: all with an entry)
--force, -y, --yes Don't ask for confirmation

Examples:
npx -y @localstack/localstack-mcp-server init
npx -y @localstack/localstack-mcp-server init --method npx --client cursor,claude-code
npx -y @localstack/localstack-mcp-server init --method docker --client cursor --yes
npx -y @localstack/localstack-mcp-server remove --client cursor

The auth token is read from $LOCALSTACK_AUTH_TOKEN when --token is not given.
Get yours at https://app.localstack.cloud/workspace/auth-tokens

The wizard writes and removes only the MCP server entry named "localstack".
`;
65 changes: 65 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Launcher for the published bin. With no recognized subcommand it starts the
* MCP server exactly as before (dist/stdio.js), so every existing client
* config keeps working. `init`/`remove` run the setup wizard.
*/
import * as fs from "fs";
import * as path from "path";

function getVersion(): string {
try {
const packageJson = JSON.parse(
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")
);
return packageJson.version ?? "unknown";
} catch {
return "unknown";
}
}

async function main(): Promise<void> {
const command = process.argv[2];

switch (command) {
case "init": {
const { runInit } = await import("./init");
process.exit(await runInit(process.argv.slice(3)));
break;
}
case "remove": {
const { runRemove } = await import("./remove");
process.exit(await runRemove(process.argv.slice(3)));
break;
}
case "help":
case "--help":
case "-h": {
const { HELP_TEXT } = await import("./help");
console.log(HELP_TEXT);
process.exit(0);
break;
}
case "version":
case "--version":
case "-v": {
console.log(getVersion());
process.exit(0);
break;
}
default:
// Anything else (including no args) starts the MCP server, matching the
// pre-wizard behavior for MCP clients that launch this bin. A stderr
// hint covers typos like "innit" — MCP hosts ignore stderr.
if (command !== undefined) {
console.error(
`Unknown command "${command}" — starting the MCP server. Did you mean "init"? See --help for setup commands.`
);
}
require("./stdio.js");
}
}

main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});
Loading
Loading