-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathlocalstack-aws-client.ts
More file actions
80 lines (70 loc) · 3.16 KB
/
Copy pathlocalstack-aws-client.ts
File metadata and controls
80 lines (70 loc) · 3.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import { z } from "zod";
import { type ToolMetadata, type InferSchema } from "xmcp";
import { runPreflights, requireLocalStackRunning, requireAuthToken } from "../core/preflight";
import { ResponseBuilder } from "../core/response-builder";
import { withToolAnalytics } from "../core/analytics";
import { DockerApiClient } from "../lib/docker/docker.client";
import { sanitizeAwsCliCommand, splitAwsCliArgs } from "../lib/aws/aws-cli-sanitizer";
// Define the schema for tool parameters
export const schema = {
command: z
.string()
.trim()
.min(1, { message: "The command string cannot be empty." })
.describe(
"The AWS CLI command to execute (e.g., 's3 ls', 'dynamodb list-tables'). Do not include 'awslocal' or 'aws'."
),
};
// Define the metadata for the tool
export const metadata: ToolMetadata = {
name: "localstack-aws-client",
description:
"Executes an AWS CLI command against the running LocalStack container using the 'awslocal' wrapper.",
annotations: {
title: "LocalStack AWS Client",
},
};
export default async function localstackAwsClient({ command }: InferSchema<typeof schema>) {
return withToolAnalytics("localstack-aws-client", { command }, async () => {
const preflightError = await runPreflights([requireAuthToken(), requireLocalStackRunning()]);
if (preflightError) return preflightError;
try {
const dockerClient = new DockerApiClient();
const containerId = await dockerClient.findLocalStackContainer();
const sanitized = sanitizeAwsCliCommand(command);
const args = splitAwsCliArgs(sanitized);
const cmd = ["awslocal", ...args];
const result = await dockerClient.executeInContainer(containerId, cmd);
if (result.exitCode === 0) {
return ResponseBuilder.markdown(result.stdout || "");
}
// Coverage / unimplemented service hints
const stderr = result.stderr || "";
const actionMatch = stderr.match(
/The API action '([^']+)' for service '([^']+)' is either not available in your current license plan or has not yet been emulated by LocalStack/i
);
const serviceMatch = stderr.match(
/The API for service '([^']+)' is either not included in your current license plan or has not yet been emulated by LocalStack/i
);
if (actionMatch) {
const service = actionMatch[2];
const link = `https://docs.localstack.cloud/references/coverage/coverage_${service}`;
return ResponseBuilder.error(
"Service Not Implemented in LocalStack",
`The requested API action may not be implemented. Check coverage: ${link}\n\n${stderr}`
);
}
if (serviceMatch) {
const link = "https://docs.localstack.cloud/references/coverage";
return ResponseBuilder.error(
"Service Not Implemented in LocalStack",
`The requested service may not be implemented. Check coverage: ${link}\n\n${stderr}`
);
}
return ResponseBuilder.error("Command Failed", result.stderr || "Unknown error");
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
return ResponseBuilder.error("Execution Error", message);
}
});
}