@yawlabs/aws-mcp 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +219 -147
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30054,6 +30054,9 @@ var require_turndown_cjs = __commonJS({
|
|
|
30054
30054
|
}
|
|
30055
30055
|
});
|
|
30056
30056
|
|
|
30057
|
+
// src/index.ts
|
|
30058
|
+
import { pathToFileURL } from "node:url";
|
|
30059
|
+
|
|
30057
30060
|
// node_modules/zod/v3/helpers/util.js
|
|
30058
30061
|
var util;
|
|
30059
30062
|
(function(util2) {
|
|
@@ -50924,11 +50927,11 @@ var Protocol = class {
|
|
|
50924
50927
|
*
|
|
50925
50928
|
* The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
|
|
50926
50929
|
*/
|
|
50927
|
-
async connect(
|
|
50930
|
+
async connect(transport) {
|
|
50928
50931
|
if (this._transport) {
|
|
50929
50932
|
throw new Error("Already connected to a transport. Call close() before connecting to a new transport, or use a separate Protocol instance per connection.");
|
|
50930
50933
|
}
|
|
50931
|
-
this._transport =
|
|
50934
|
+
this._transport = transport;
|
|
50932
50935
|
const _onclose = this.transport?.onclose;
|
|
50933
50936
|
this._transport.onclose = () => {
|
|
50934
50937
|
_onclose?.();
|
|
@@ -52518,8 +52521,8 @@ var McpServer = class {
|
|
|
52518
52521
|
*
|
|
52519
52522
|
* The `server` object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward.
|
|
52520
52523
|
*/
|
|
52521
|
-
async connect(
|
|
52522
|
-
return await this.server.connect(
|
|
52524
|
+
async connect(transport) {
|
|
52525
|
+
return await this.server.connect(transport);
|
|
52523
52526
|
}
|
|
52524
52527
|
/**
|
|
52525
52528
|
* Closes the connection.
|
|
@@ -54207,6 +54210,8 @@ function doStartSsoLogin(profile, opts) {
|
|
|
54207
54210
|
sessionId,
|
|
54208
54211
|
verificationUrl: urlSeen,
|
|
54209
54212
|
userCode: codeSeen,
|
|
54213
|
+
// `|| "default"` is vestigial -- see the empty-profile note above;
|
|
54214
|
+
// `profile` is always non-empty here (startSsoLogin rejects "").
|
|
54210
54215
|
profile: profile || "default"
|
|
54211
54216
|
});
|
|
54212
54217
|
}
|
|
@@ -54307,7 +54312,7 @@ async function waitForLogin(sessionId) {
|
|
|
54307
54312
|
return {
|
|
54308
54313
|
ok: false,
|
|
54309
54314
|
exitCode: null,
|
|
54310
|
-
error: `No active login session with id '${sessionId}'.
|
|
54315
|
+
error: `No active login session with id '${sessionId}'. It may have already completed (waitForLogin is fire-once -- the session is dropped after the first call resolves) or it may never have started. If a prior aws_login_complete already returned success for this id, the login is done; run aws_whoami to confirm rather than starting over. Otherwise call aws_login_start first.`
|
|
54311
54316
|
};
|
|
54312
54317
|
}
|
|
54313
54318
|
try {
|
|
@@ -54431,6 +54436,7 @@ var profilesTools = [
|
|
|
54431
54436
|
|
|
54432
54437
|
// src/tools/auth.ts
|
|
54433
54438
|
var MAX_SSO_CACHE_FILE_BYTES = 64 * 1024;
|
|
54439
|
+
var _startSsoLoginImpl = (profile) => startSsoLogin(profile);
|
|
54434
54440
|
function findCachedSsoToken(cacheDir = join3(homedir3(), ".aws", "sso", "cache"), opts = {}) {
|
|
54435
54441
|
try {
|
|
54436
54442
|
const files = readdirSync(cacheDir).filter((f) => f.endsWith(".json"));
|
|
@@ -54462,6 +54468,14 @@ function findCachedSsoToken(cacheDir = join3(homedir3(), ".aws", "sso", "cache")
|
|
|
54462
54468
|
}
|
|
54463
54469
|
return null;
|
|
54464
54470
|
}
|
|
54471
|
+
function projectSsoToken(cachedToken) {
|
|
54472
|
+
if (!cachedToken) return null;
|
|
54473
|
+
return {
|
|
54474
|
+
expiresAt: cachedToken.expiresAt,
|
|
54475
|
+
minutesLeft: cachedToken.minutesLeft,
|
|
54476
|
+
startUrl: cachedToken.startUrl
|
|
54477
|
+
};
|
|
54478
|
+
}
|
|
54465
54479
|
function startUrlForProfile(profile) {
|
|
54466
54480
|
try {
|
|
54467
54481
|
const text = readFileSync3(join3(homedir3(), ".aws", "config"), "utf-8");
|
|
@@ -54526,11 +54540,7 @@ var authTools = [
|
|
|
54526
54540
|
arn: identity.arn,
|
|
54527
54541
|
profile: useProfile,
|
|
54528
54542
|
region: useRegion,
|
|
54529
|
-
ssoToken: cachedToken
|
|
54530
|
-
expiresAt: cachedToken.expiresAt,
|
|
54531
|
-
minutesLeft: cachedToken.minutesLeft,
|
|
54532
|
-
startUrl: cachedToken.startUrl
|
|
54533
|
-
} : null
|
|
54543
|
+
ssoToken: projectSsoToken(cachedToken)
|
|
54534
54544
|
}
|
|
54535
54545
|
};
|
|
54536
54546
|
}
|
|
@@ -54565,7 +54575,7 @@ var authTools = [
|
|
|
54565
54575
|
}
|
|
54566
54576
|
};
|
|
54567
54577
|
}
|
|
54568
|
-
const result = await
|
|
54578
|
+
const result = await _startSsoLoginImpl(useProfile);
|
|
54569
54579
|
if (!result.ok) {
|
|
54570
54580
|
return {
|
|
54571
54581
|
ok: false,
|
|
@@ -54630,7 +54640,7 @@ var authTools = [
|
|
|
54630
54640
|
arn: identity.arn,
|
|
54631
54641
|
profile: useProfile,
|
|
54632
54642
|
region: useRegion,
|
|
54633
|
-
ssoToken: cachedToken
|
|
54643
|
+
ssoToken: projectSsoToken(cachedToken)
|
|
54634
54644
|
}
|
|
54635
54645
|
};
|
|
54636
54646
|
}
|
|
@@ -54682,7 +54692,7 @@ var authTools = [
|
|
|
54682
54692
|
}
|
|
54683
54693
|
};
|
|
54684
54694
|
}
|
|
54685
|
-
const loginResult = await
|
|
54695
|
+
const loginResult = await _startSsoLoginImpl(useProfile);
|
|
54686
54696
|
if (!loginResult.ok) {
|
|
54687
54697
|
return { ok: false, error: loginResult.error, rawBody: loginResult.rawOutput };
|
|
54688
54698
|
}
|
|
@@ -54750,7 +54760,14 @@ var callTools = [
|
|
|
54750
54760
|
return {
|
|
54751
54761
|
ok: false,
|
|
54752
54762
|
error: result.error,
|
|
54753
|
-
|
|
54763
|
+
// Treat an empty-string rawStderr as "no stderr" so a nonzero exit
|
|
54764
|
+
// that wrote its diagnostic to stdout (rare but observed: some
|
|
54765
|
+
// `aws` operations route through stdout when stderr is closed or
|
|
54766
|
+
// when a wrapper script swallows stderr) still surfaces the
|
|
54767
|
+
// stdout body. Pinned by call.test.ts -- the failure-shape
|
|
54768
|
+
// contract is "diagnostic text first, stdout second" rather
|
|
54769
|
+
// than "stderr always wins even when empty".
|
|
54770
|
+
rawBody: result.rawStderr ? result.rawStderr : result.rawStdout
|
|
54754
54771
|
};
|
|
54755
54772
|
}
|
|
54756
54773
|
return {
|
|
@@ -55321,6 +55338,12 @@ var logsTools = [
|
|
|
55321
55338
|
}
|
|
55322
55339
|
}
|
|
55323
55340
|
}
|
|
55341
|
+
if (i.logStreamNamePrefix && !isValidLogStreamName(i.logStreamNamePrefix)) {
|
|
55342
|
+
return {
|
|
55343
|
+
ok: false,
|
|
55344
|
+
error: `Invalid logStreamNamePrefix '${i.logStreamNamePrefix}'. Must be 1-512 chars, not start with '-', and contain no ':', '*', or control characters.`
|
|
55345
|
+
};
|
|
55346
|
+
}
|
|
55324
55347
|
const extraFlags = [i.logGroupName, "--format", "json", "--since", i.since ?? "10m"];
|
|
55325
55348
|
if (i.filterPattern) extraFlags.push("--filter-pattern", i.filterPattern);
|
|
55326
55349
|
if (i.logStreamNames && i.logStreamNames.length > 0) {
|
|
@@ -55358,6 +55381,87 @@ var logsTools = [
|
|
|
55358
55381
|
}
|
|
55359
55382
|
];
|
|
55360
55383
|
|
|
55384
|
+
// src/tools/paginate.ts
|
|
55385
|
+
function extractNextToken(data) {
|
|
55386
|
+
if (data && typeof data === "object" && "NextToken" in data) {
|
|
55387
|
+
const token = data.NextToken;
|
|
55388
|
+
if (typeof token === "string" && token.length > 0) return token;
|
|
55389
|
+
}
|
|
55390
|
+
return null;
|
|
55391
|
+
}
|
|
55392
|
+
function wrapQueryForPagination(userQuery) {
|
|
55393
|
+
return `{NextToken: NextToken, items: ${userQuery}}`;
|
|
55394
|
+
}
|
|
55395
|
+
var paginateTools = [
|
|
55396
|
+
{
|
|
55397
|
+
name: "aws_paginate",
|
|
55398
|
+
description: "Fetch one page of a paginated AWS list/describe operation. Identical to aws_call plus `maxItems` (page size) and `startingToken` (resume cursor). Returns the parsed response, a `nextToken` (null when the list is exhausted), and `hasMore`. Call again with the returned nextToken as startingToken until hasMore is false. Use this instead of aws_call for operations that might exceed the 5 MB stdout cap: list-objects-v2, describe-instances, describe-log-streams, list-roles, etc.",
|
|
55399
|
+
annotations: {
|
|
55400
|
+
title: "Fetch one page of a paginated AWS operation",
|
|
55401
|
+
readOnlyHint: true,
|
|
55402
|
+
destructiveHint: false,
|
|
55403
|
+
idempotentHint: true,
|
|
55404
|
+
openWorldHint: true
|
|
55405
|
+
},
|
|
55406
|
+
inputSchema: external_exports3.object({
|
|
55407
|
+
service: external_exports3.string().describe("AWS service in kebab-case: 's3api', 'ec2', 'iam', 'logs', etc."),
|
|
55408
|
+
operation: external_exports3.string().describe("Paginated operation: 'list-objects-v2', 'describe-instances', 'list-roles', etc."),
|
|
55409
|
+
params: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Operation parameters (PascalCase keys) passed via --cli-input-json."),
|
|
55410
|
+
query: external_exports3.string().optional().describe(
|
|
55411
|
+
"JMESPath expression to extract fields from each page (--query). The query is wrapped server-side as {NextToken, items: <query>} so pagination still works even when the projection drops NextToken; the handler unwraps `items` before returning."
|
|
55412
|
+
),
|
|
55413
|
+
maxItems: external_exports3.number().int().positive().optional().describe("Items per page. Default 100. Lower this if hitting the 5 MB output cap."),
|
|
55414
|
+
startingToken: external_exports3.string().optional().describe("Resume cursor from the previous call's `nextToken`. Omit for the first page."),
|
|
55415
|
+
profile: external_exports3.string().optional().describe("Override session profile for this call."),
|
|
55416
|
+
region: external_exports3.string().optional().describe("Override session region for this call."),
|
|
55417
|
+
timeoutMs: external_exports3.number().int().positive().optional().describe("Timeout in milliseconds. Default 60000.")
|
|
55418
|
+
}),
|
|
55419
|
+
handler: async (input) => {
|
|
55420
|
+
const i = input;
|
|
55421
|
+
const maxItems = i.maxItems ?? 100;
|
|
55422
|
+
const extraFlags = ["--max-items", String(maxItems)];
|
|
55423
|
+
if (i.startingToken) {
|
|
55424
|
+
extraFlags.push("--starting-token", i.startingToken);
|
|
55425
|
+
}
|
|
55426
|
+
const userQuery = i.query?.trim();
|
|
55427
|
+
const queryWrapped = userQuery ? wrapQueryForPagination(userQuery) : void 0;
|
|
55428
|
+
const result = await runAwsCall({
|
|
55429
|
+
service: i.service,
|
|
55430
|
+
operation: i.operation,
|
|
55431
|
+
params: i.params,
|
|
55432
|
+
query: queryWrapped,
|
|
55433
|
+
profile: i.profile,
|
|
55434
|
+
region: i.region,
|
|
55435
|
+
outputFormat: "json",
|
|
55436
|
+
timeoutMs: i.timeoutMs,
|
|
55437
|
+
extraFlags
|
|
55438
|
+
});
|
|
55439
|
+
if (!result.ok) {
|
|
55440
|
+
return { ok: false, error: result.error, rawBody: result.rawStderr ?? result.rawStdout };
|
|
55441
|
+
}
|
|
55442
|
+
let resultBody;
|
|
55443
|
+
let nextToken;
|
|
55444
|
+
if (queryWrapped) {
|
|
55445
|
+
const wrapped = result.data ?? {};
|
|
55446
|
+
nextToken = extractNextToken(wrapped);
|
|
55447
|
+
resultBody = wrapped.items ?? null;
|
|
55448
|
+
} else {
|
|
55449
|
+
nextToken = extractNextToken(result.data);
|
|
55450
|
+
resultBody = result.data;
|
|
55451
|
+
}
|
|
55452
|
+
return {
|
|
55453
|
+
ok: true,
|
|
55454
|
+
data: {
|
|
55455
|
+
command: result.command,
|
|
55456
|
+
result: resultBody,
|
|
55457
|
+
nextToken,
|
|
55458
|
+
hasMore: nextToken !== null
|
|
55459
|
+
}
|
|
55460
|
+
};
|
|
55461
|
+
}
|
|
55462
|
+
}
|
|
55463
|
+
];
|
|
55464
|
+
|
|
55361
55465
|
// src/tools/metrics.ts
|
|
55362
55466
|
var SIMPLE_STATS = ["Average", "Sum", "Maximum", "Minimum", "SampleCount"];
|
|
55363
55467
|
var EXTENDED_STAT_RE = /^(p|tm|tc|wm|pr|ts|iqm)(\d{1,3}(\.\d{1,3})?)?$/i;
|
|
@@ -55376,6 +55480,7 @@ function canonicalizeStatistic(s) {
|
|
|
55376
55480
|
}
|
|
55377
55481
|
var QUERY_ID_RE = /^[a-z][A-Za-z0-9_]*$/;
|
|
55378
55482
|
var MAX_QUERIES = 100;
|
|
55483
|
+
var CLOUDWATCH_MAX_DATAPOINTS = 100800;
|
|
55379
55484
|
var PERIOD_3H_MS = 3 * 60 * 60 * 1e3;
|
|
55380
55485
|
var PERIOD_24H_MS = 24 * 60 * 60 * 1e3;
|
|
55381
55486
|
var PERIOD_15D_MS = 15 * 24 * 60 * 60 * 1e3;
|
|
@@ -55471,7 +55576,7 @@ var metricsTools = [
|
|
|
55471
55576
|
endTime: external_exports3.string().optional().describe("ISO 8601 timestamp or relative shorthand. Default 'now'."),
|
|
55472
55577
|
scanBy: external_exports3.enum(["TimestampAscending", "TimestampDescending"]).optional().describe("Sort order for returned datapoints. Default 'TimestampDescending' (matches CloudWatch's default)."),
|
|
55473
55578
|
maxDataPoints: external_exports3.number().int().positive().optional().describe(
|
|
55474
|
-
"
|
|
55579
|
+
"Target datapoint count. CloudWatch does not truncate to the first N points -- it widens (coarsens) the period server-side so the series aggregates down to fit this many points. CloudWatch's own ceiling is ~100,800; lower this to make CloudWatch return a coarser, smaller series. Forwarded as CloudWatch's MaxDatapoints (single 'p') field; the camelCase schema name follows this server's convention."
|
|
55475
55580
|
),
|
|
55476
55581
|
nextToken: external_exports3.string().optional().describe(
|
|
55477
55582
|
"Resume cursor from a previous call's `nextToken`. Omit for the first page. Forwarded as CloudWatch's NextToken; only meaningful when a prior call returned `hasMore: true`."
|
|
@@ -55543,6 +55648,23 @@ var metricsTools = [
|
|
|
55543
55648
|
error: `endTime (${endDate.toISOString()}) must be after startTime (${startDate.toISOString()}).`
|
|
55544
55649
|
};
|
|
55545
55650
|
}
|
|
55651
|
+
const rangeSeconds = (endDate.getTime() - startDate.getTime()) / 1e3;
|
|
55652
|
+
for (const q of i.queries) {
|
|
55653
|
+
if (q.period === void 0) continue;
|
|
55654
|
+
if (q.period <= 0 || q.period % 60 !== 0) {
|
|
55655
|
+
return {
|
|
55656
|
+
ok: false,
|
|
55657
|
+
error: `Query '${q.id}' has invalid period ${q.period}. CloudWatch requires period to be a positive multiple of 60 (seconds).`
|
|
55658
|
+
};
|
|
55659
|
+
}
|
|
55660
|
+
const datapoints = Math.ceil(rangeSeconds / q.period);
|
|
55661
|
+
if (datapoints > CLOUDWATCH_MAX_DATAPOINTS) {
|
|
55662
|
+
return {
|
|
55663
|
+
ok: false,
|
|
55664
|
+
error: `Query '${q.id}' with period ${q.period}s over the requested range (${startDate.toISOString()} to ${endDate.toISOString()}) would request ${datapoints} datapoints, exceeding CloudWatch's per-request cap of ${CLOUDWATCH_MAX_DATAPOINTS}. Widen the period or narrow the time range.`
|
|
55665
|
+
};
|
|
55666
|
+
}
|
|
55667
|
+
}
|
|
55546
55668
|
const periodSeconds = pickAutoPeriodSeconds(startDate.getTime(), endDate.getTime());
|
|
55547
55669
|
const metricDataQueries = buildMetricDataQueries(i.queries, periodSeconds);
|
|
55548
55670
|
const params = {
|
|
@@ -55579,7 +55701,7 @@ var metricsTools = [
|
|
|
55579
55701
|
code: m.Code,
|
|
55580
55702
|
value: m.Value
|
|
55581
55703
|
}));
|
|
55582
|
-
const nextToken =
|
|
55704
|
+
const nextToken = extractNextToken(raw);
|
|
55583
55705
|
return {
|
|
55584
55706
|
ok: true,
|
|
55585
55707
|
data: {
|
|
@@ -55634,7 +55756,7 @@ var multiRegionTools = [
|
|
|
55634
55756
|
service: external_exports3.string().describe("AWS service in kebab-case: 's3api', 'ec2', 'iam', etc."),
|
|
55635
55757
|
operation: external_exports3.string().describe("Operation in kebab-case: 'describe-instances', 'list-buckets', etc."),
|
|
55636
55758
|
regions: external_exports3.array(external_exports3.string().min(1)).min(1).max(MAX_REGIONS).describe(
|
|
55637
|
-
`Region IDs (e.g. ['us-east-1','us-west-2','eu-west-1']). 1-${MAX_REGIONS}. Validated for argv-safety; bad region
|
|
55759
|
+
`Region IDs (e.g. ['us-east-1','us-west-2','eu-west-1']). 1-${MAX_REGIONS}. Validated for argv-safety; a bad region name yields a clear per-region error and skips its CLI spawn (per-region isolation comes from each region being a separate call, not from this pre-check).`
|
|
55638
55760
|
),
|
|
55639
55761
|
params: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Operation parameters (PascalCase keys) -- same shape as aws_call."),
|
|
55640
55762
|
query: external_exports3.string().optional().describe("JMESPath expression for --query (server-side trimming per region)."),
|
|
@@ -55701,87 +55823,6 @@ var multiRegionTools = [
|
|
|
55701
55823
|
}
|
|
55702
55824
|
];
|
|
55703
55825
|
|
|
55704
|
-
// src/tools/paginate.ts
|
|
55705
|
-
function extractNextToken(data) {
|
|
55706
|
-
if (data && typeof data === "object" && "NextToken" in data) {
|
|
55707
|
-
const token = data.NextToken;
|
|
55708
|
-
if (typeof token === "string" && token.length > 0) return token;
|
|
55709
|
-
}
|
|
55710
|
-
return null;
|
|
55711
|
-
}
|
|
55712
|
-
function wrapQueryForPagination(userQuery) {
|
|
55713
|
-
return `{NextToken: NextToken, items: ${userQuery}}`;
|
|
55714
|
-
}
|
|
55715
|
-
var paginateTools = [
|
|
55716
|
-
{
|
|
55717
|
-
name: "aws_paginate",
|
|
55718
|
-
description: "Fetch one page of a paginated AWS list/describe operation. Identical to aws_call plus `maxItems` (page size) and `startingToken` (resume cursor). Returns the parsed response, a `nextToken` (null when the list is exhausted), and `hasMore`. Call again with the returned nextToken as startingToken until hasMore is false. Use this instead of aws_call for operations that might exceed the 5 MB stdout cap: list-objects-v2, describe-instances, describe-log-streams, list-roles, etc.",
|
|
55719
|
-
annotations: {
|
|
55720
|
-
title: "Fetch one page of a paginated AWS operation",
|
|
55721
|
-
readOnlyHint: true,
|
|
55722
|
-
destructiveHint: false,
|
|
55723
|
-
idempotentHint: true,
|
|
55724
|
-
openWorldHint: true
|
|
55725
|
-
},
|
|
55726
|
-
inputSchema: external_exports3.object({
|
|
55727
|
-
service: external_exports3.string().describe("AWS service in kebab-case: 's3api', 'ec2', 'iam', 'logs', etc."),
|
|
55728
|
-
operation: external_exports3.string().describe("Paginated operation: 'list-objects-v2', 'describe-instances', 'list-roles', etc."),
|
|
55729
|
-
params: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional().describe("Operation parameters (PascalCase keys) passed via --cli-input-json."),
|
|
55730
|
-
query: external_exports3.string().optional().describe(
|
|
55731
|
-
"JMESPath expression to extract fields from each page (--query). The query is wrapped server-side as {NextToken, items: <query>} so pagination still works even when the projection drops NextToken; the handler unwraps `items` before returning."
|
|
55732
|
-
),
|
|
55733
|
-
maxItems: external_exports3.number().int().positive().optional().describe("Items per page. Default 100. Lower this if hitting the 5 MB output cap."),
|
|
55734
|
-
startingToken: external_exports3.string().optional().describe("Resume cursor from the previous call's `nextToken`. Omit for the first page."),
|
|
55735
|
-
profile: external_exports3.string().optional().describe("Override session profile for this call."),
|
|
55736
|
-
region: external_exports3.string().optional().describe("Override session region for this call."),
|
|
55737
|
-
timeoutMs: external_exports3.number().int().positive().optional().describe("Timeout in milliseconds. Default 60000.")
|
|
55738
|
-
}),
|
|
55739
|
-
handler: async (input) => {
|
|
55740
|
-
const i = input;
|
|
55741
|
-
const maxItems = i.maxItems ?? 100;
|
|
55742
|
-
const extraFlags = ["--max-items", String(maxItems)];
|
|
55743
|
-
if (i.startingToken) {
|
|
55744
|
-
extraFlags.push("--starting-token", i.startingToken);
|
|
55745
|
-
}
|
|
55746
|
-
const userQuery = i.query?.trim();
|
|
55747
|
-
const queryWrapped = userQuery ? wrapQueryForPagination(userQuery) : void 0;
|
|
55748
|
-
const result = await runAwsCall({
|
|
55749
|
-
service: i.service,
|
|
55750
|
-
operation: i.operation,
|
|
55751
|
-
params: i.params,
|
|
55752
|
-
query: queryWrapped,
|
|
55753
|
-
profile: i.profile,
|
|
55754
|
-
region: i.region,
|
|
55755
|
-
outputFormat: "json",
|
|
55756
|
-
timeoutMs: i.timeoutMs,
|
|
55757
|
-
extraFlags
|
|
55758
|
-
});
|
|
55759
|
-
if (!result.ok) {
|
|
55760
|
-
return { ok: false, error: result.error, rawBody: result.rawStderr ?? result.rawStdout };
|
|
55761
|
-
}
|
|
55762
|
-
let resultBody;
|
|
55763
|
-
let nextToken;
|
|
55764
|
-
if (queryWrapped) {
|
|
55765
|
-
const wrapped = result.data ?? {};
|
|
55766
|
-
nextToken = typeof wrapped.NextToken === "string" && wrapped.NextToken.length > 0 ? wrapped.NextToken : null;
|
|
55767
|
-
resultBody = wrapped.items ?? null;
|
|
55768
|
-
} else {
|
|
55769
|
-
nextToken = extractNextToken(result.data);
|
|
55770
|
-
resultBody = result.data;
|
|
55771
|
-
}
|
|
55772
|
-
return {
|
|
55773
|
-
ok: true,
|
|
55774
|
-
data: {
|
|
55775
|
-
command: result.command,
|
|
55776
|
-
result: resultBody,
|
|
55777
|
-
nextToken,
|
|
55778
|
-
hasMore: nextToken !== null
|
|
55779
|
-
}
|
|
55780
|
-
};
|
|
55781
|
-
}
|
|
55782
|
-
}
|
|
55783
|
-
];
|
|
55784
|
-
|
|
55785
55826
|
// src/tools/resource.ts
|
|
55786
55827
|
var TYPE_NAME_RE = /^[A-Z][A-Za-z0-9]*::[A-Z][A-Za-z0-9]*::[A-Z][A-Za-z0-9]*$/;
|
|
55787
55828
|
function isValidIdentifier(id) {
|
|
@@ -56065,7 +56106,7 @@ var resourceTools = [
|
|
|
56065
56106
|
const p = parseResourceProperties(d);
|
|
56066
56107
|
return { identifier: p.Identifier, properties: p.Properties };
|
|
56067
56108
|
});
|
|
56068
|
-
const nextToken =
|
|
56109
|
+
const nextToken = extractNextToken(raw);
|
|
56069
56110
|
return {
|
|
56070
56111
|
ok: true,
|
|
56071
56112
|
data: {
|
|
@@ -56755,10 +56796,10 @@ var scriptTools = [
|
|
|
56755
56796
|
},
|
|
56756
56797
|
inputSchema: external_exports3.object({
|
|
56757
56798
|
code: external_exports3.string().min(1).describe(
|
|
56758
|
-
"JavaScript snippet evaluated inside `(async () => { ... })()`. Use `return <value>` to surface a result. Bound globals: aws.call, aws.paginate, aws.paginateAll, aws.resource.{get,list,create,update,delete,status}, aws.logsTail, aws.metricsQuery, aws.iamSimulate, aws.multiRegion, aws.assumeRole, aws.docs.{search,read}, console (capture), JSON, Math, Date, Promise, Array, Object, String, Number, Boolean, Error, Intl, Atomics, SharedArrayBuffer, WebAssembly (compile blocked). Intentionally NOT bound (call as sibling MCP tools instead): aws_list_profiles, the auth/session tools, and aws_script itself. Shadowed (undefined): require,
|
|
56799
|
+
"JavaScript snippet evaluated inside `(async () => { ... })()`. Use `return <value>` to surface a result. Bound globals: aws.call, aws.paginate, aws.paginateAll, aws.resource.{get,list,create,update,delete,status}, aws.logsTail, aws.metricsQuery, aws.iamSimulate, aws.multiRegion, aws.assumeRole, aws.docs.{search,read}, console (capture), JSON, Math, Date, Promise, Array, Object, String, Number, Boolean, Error, Intl, Atomics, SharedArrayBuffer, WebAssembly (compile blocked). Intentionally NOT bound (call as sibling MCP tools instead): aws_list_profiles, the auth/session tools, and aws_script itself. Shadowed (undefined): require, process, fetch + family, BroadcastChannel, setTimeout/Interval, queueMicrotask, Buffer, global, globalThis. NOT available (ReferenceError if used): URL, URLSearchParams, TextEncoder, TextDecoder, crypto, structuredClone, EventTarget, MessageChannel, performance, fs, import. eval/Function are disabled (codeGeneration off). Tool helpers throw on failure -- wrap in try/catch when you want to handle errors per-call."
|
|
56759
56800
|
),
|
|
56760
56801
|
timeoutMs: external_exports3.number().int().positive().max(MAX_TIMEOUT_MS).optional().describe(
|
|
56761
|
-
`Wall-clock timeout in milliseconds. Default ${DEFAULT_TIMEOUT_MS2}; max ${MAX_TIMEOUT_MS}.
|
|
56802
|
+
`Wall-clock timeout in milliseconds. Default ${DEFAULT_TIMEOUT_MS2}; max ${MAX_TIMEOUT_MS}. Best-effort across evaluation plus awaited aws.* calls -- it fires on synchronous spin before the first await and on async wall-clock once the script has yielded, but a synchronous infinite loop BETWEEN awaits can outrun the timer and is not guaranteed to be interrupted. On timeout the script stops being awaited and the tool returns an error, but any aws.* call already in flight is NOT cancelled -- it continues until its own per-call timeout (default 60s). Plan retries accordingly: a script that timed out mid 'resource.delete' may have completed the delete; re-issuing the same script can double-mutate.`
|
|
56762
56803
|
)
|
|
56763
56804
|
}),
|
|
56764
56805
|
handler: async (input) => {
|
|
@@ -56807,12 +56848,32 @@ var sessionTools = [
|
|
|
56807
56848
|
error: "Nothing to set \u2014 pass at least one of 'profile' or 'region'. Use aws_session_get to read current values."
|
|
56808
56849
|
};
|
|
56809
56850
|
}
|
|
56810
|
-
|
|
56811
|
-
|
|
56812
|
-
if (
|
|
56813
|
-
|
|
56814
|
-
|
|
56851
|
+
if (profile !== void 0) {
|
|
56852
|
+
const trimmed = profile.trim();
|
|
56853
|
+
if (!trimmed) {
|
|
56854
|
+
return { ok: false, error: "Profile name cannot be empty" };
|
|
56855
|
+
}
|
|
56856
|
+
if (!isValidProfileName(trimmed)) {
|
|
56857
|
+
return {
|
|
56858
|
+
ok: false,
|
|
56859
|
+
error: `Invalid profile name '${trimmed}'. Must be 1-128 chars from [A-Za-z0-9_+=,.@:-], must not start with '-' or '=', no whitespace or shell metacharacters.`
|
|
56860
|
+
};
|
|
56861
|
+
}
|
|
56862
|
+
}
|
|
56863
|
+
if (region !== void 0) {
|
|
56864
|
+
const trimmed = region.trim();
|
|
56865
|
+
if (!trimmed) {
|
|
56866
|
+
return { ok: false, error: "Region cannot be empty" };
|
|
56867
|
+
}
|
|
56868
|
+
if (!isValidRegionName(trimmed)) {
|
|
56869
|
+
return {
|
|
56870
|
+
ok: false,
|
|
56871
|
+
error: `Invalid region '${trimmed}'. Must match /^[a-z][a-z0-9-]{2,30}$/ (e.g. 'us-east-1', 'eu-west-3').`
|
|
56872
|
+
};
|
|
56873
|
+
}
|
|
56815
56874
|
}
|
|
56875
|
+
if (profile !== void 0) setProfile(profile);
|
|
56876
|
+
if (region !== void 0) setRegion(region);
|
|
56816
56877
|
return { ok: true, data: getSessionState() };
|
|
56817
56878
|
}
|
|
56818
56879
|
},
|
|
@@ -56856,9 +56917,40 @@ var sessionTools = [
|
|
|
56856
56917
|
];
|
|
56857
56918
|
|
|
56858
56919
|
// src/index.ts
|
|
56859
|
-
|
|
56920
|
+
function toMcpResult(response) {
|
|
56921
|
+
if (!response.ok) {
|
|
56922
|
+
const baseError = `Error: ${response.error || "Unknown error"}`;
|
|
56923
|
+
const errorText = response.rawBody ? `${baseError}
|
|
56924
|
+
|
|
56925
|
+
${response.rawBody}` : baseError;
|
|
56926
|
+
return {
|
|
56927
|
+
content: [{ type: "text", text: errorText }],
|
|
56928
|
+
isError: true
|
|
56929
|
+
};
|
|
56930
|
+
}
|
|
56931
|
+
const text = response.rawBody ?? JSON.stringify(response.data ?? { success: true }, null, 2);
|
|
56932
|
+
return {
|
|
56933
|
+
content: [{ type: "text", text }]
|
|
56934
|
+
};
|
|
56935
|
+
}
|
|
56936
|
+
function errorToMcpResult(err, toolName) {
|
|
56937
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56938
|
+
const stack = err instanceof Error ? err.stack : void 0;
|
|
56939
|
+
console.error(`[aws-mcp] handler '${toolName}' threw: ${message}`);
|
|
56940
|
+
if (stack) console.error(stack);
|
|
56941
|
+
return {
|
|
56942
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
56943
|
+
isError: true
|
|
56944
|
+
};
|
|
56945
|
+
}
|
|
56946
|
+
var version2 = true ? "1.4.0" : (await null).createRequire(import.meta.url)("../package.json").version;
|
|
56947
|
+
var isEntryPoint = (() => {
|
|
56948
|
+
const entry = process.argv[1];
|
|
56949
|
+
if (!entry) return false;
|
|
56950
|
+
return import.meta.url === pathToFileURL(entry).href;
|
|
56951
|
+
})();
|
|
56860
56952
|
var subcommand = process.argv[2];
|
|
56861
|
-
if (subcommand === "version" || subcommand === "--version") {
|
|
56953
|
+
if (isEntryPoint && (subcommand === "version" || subcommand === "--version")) {
|
|
56862
56954
|
console.log(version2);
|
|
56863
56955
|
process.exit(0);
|
|
56864
56956
|
}
|
|
@@ -56877,49 +56969,29 @@ var allTools = [
|
|
|
56877
56969
|
...docsTools,
|
|
56878
56970
|
...scriptTools
|
|
56879
56971
|
];
|
|
56880
|
-
|
|
56881
|
-
|
|
56882
|
-
|
|
56883
|
-
|
|
56884
|
-
|
|
56885
|
-
|
|
56886
|
-
tool.name,
|
|
56887
|
-
tool.description,
|
|
56888
|
-
tool.inputSchema.shape,
|
|
56889
|
-
tool.annotations,
|
|
56890
|
-
async (input) => {
|
|
56972
|
+
if (isEntryPoint) {
|
|
56973
|
+
const server = new McpServer({
|
|
56974
|
+
name: "@yawlabs/aws-mcp",
|
|
56975
|
+
version: version2
|
|
56976
|
+
});
|
|
56977
|
+
for (const tool of allTools) {
|
|
56978
|
+
server.tool(tool.name, tool.description, tool.inputSchema.shape, tool.annotations, async (input) => {
|
|
56891
56979
|
try {
|
|
56892
|
-
|
|
56893
|
-
if (!response.ok) {
|
|
56894
|
-
const baseError = `Error: ${response.error || "Unknown error"}`;
|
|
56895
|
-
const errorText = response.rawBody ? `${baseError}
|
|
56896
|
-
|
|
56897
|
-
${response.rawBody}` : baseError;
|
|
56898
|
-
return {
|
|
56899
|
-
content: [{ type: "text", text: errorText }],
|
|
56900
|
-
isError: true
|
|
56901
|
-
};
|
|
56902
|
-
}
|
|
56903
|
-
const text = response.rawBody ?? JSON.stringify(response.data ?? { success: true }, null, 2);
|
|
56904
|
-
return {
|
|
56905
|
-
content: [{ type: "text", text }]
|
|
56906
|
-
};
|
|
56980
|
+
return toMcpResult(await tool.handler(input));
|
|
56907
56981
|
} catch (err) {
|
|
56908
|
-
|
|
56909
|
-
const stack = err instanceof Error ? err.stack : void 0;
|
|
56910
|
-
console.error(`[aws-mcp] handler '${tool.name}' threw: ${message}`);
|
|
56911
|
-
if (stack) console.error(stack);
|
|
56912
|
-
return {
|
|
56913
|
-
content: [{ type: "text", text: `Error: ${message}` }],
|
|
56914
|
-
isError: true
|
|
56915
|
-
};
|
|
56982
|
+
return errorToMcpResult(err, tool.name);
|
|
56916
56983
|
}
|
|
56917
|
-
}
|
|
56918
|
-
|
|
56984
|
+
});
|
|
56985
|
+
}
|
|
56986
|
+
const transport = new StdioServerTransport();
|
|
56987
|
+
await server.connect(transport);
|
|
56988
|
+
console.error(`@yawlabs/aws-mcp v${version2} ready (${allTools.length} tools)`);
|
|
56919
56989
|
}
|
|
56920
|
-
|
|
56921
|
-
|
|
56922
|
-
|
|
56990
|
+
export {
|
|
56991
|
+
allTools,
|
|
56992
|
+
errorToMcpResult,
|
|
56993
|
+
toMcpResult
|
|
56994
|
+
};
|
|
56923
56995
|
/*! Bundled license information:
|
|
56924
56996
|
|
|
56925
56997
|
he/he.js:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yawlabs/aws-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"mcpName": "io.github.YawLabs/aws-mcp",
|
|
5
5
|
"description": "AWS MCP server — call any AWS API from AI assistants, with first-class SSO re-login (no more 'browser won't open' dead ends)",
|
|
6
6
|
"license": "MIT",
|