cdk-local 0.41.0 → 0.43.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/README.md +38 -200
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{local-list-DDsa8FJV.js → local-list-bwZnI2pV.js} +466 -48
- package/dist/local-list-bwZnI2pV.js.map +1 -0
- package/package.json +1 -1
- package/dist/local-list-DDsa8FJV.js.map +0 -1
|
@@ -1196,11 +1196,17 @@ function notFoundError$3(target, stack, resources) {
|
|
|
1196
1196
|
*/
|
|
1197
1197
|
const AGENTCORE_RUNTIME_TYPE = "AWS::BedrockAgentCore::Runtime";
|
|
1198
1198
|
/**
|
|
1199
|
-
*
|
|
1200
|
-
*
|
|
1201
|
-
*
|
|
1199
|
+
* AgentCore Runtime protocols `cdkl invoke-agentcore` can serve.
|
|
1200
|
+
*
|
|
1201
|
+
* - `HTTP` — the agent contract (`POST /invocations` + `GET /ping` on 8080).
|
|
1202
|
+
* - `MCP` — Model Context Protocol over Streamable HTTP (`POST /mcp` on 8000).
|
|
1203
|
+
*
|
|
1204
|
+
* `A2A` / `AGUI` declare yet other wire contracts and are not served yet.
|
|
1202
1205
|
*/
|
|
1203
1206
|
const AGENTCORE_HTTP_PROTOCOL = "HTTP";
|
|
1207
|
+
const AGENTCORE_MCP_PROTOCOL = "MCP";
|
|
1208
|
+
/** Protocols this CLI can run a container for. */
|
|
1209
|
+
const SUPPORTED_AGENTCORE_PROTOCOLS = [AGENTCORE_HTTP_PROTOCOL, "MCP"];
|
|
1204
1210
|
var AgentCoreResolutionError = class AgentCoreResolutionError extends Error {
|
|
1205
1211
|
constructor(message) {
|
|
1206
1212
|
super(message);
|
|
@@ -1272,7 +1278,7 @@ function notFoundError$2(target, stack, resources) {
|
|
|
1272
1278
|
function extractRuntimeProperties(stack, logicalId, resource, resources, imageContext) {
|
|
1273
1279
|
const props = resource.Properties ?? {};
|
|
1274
1280
|
const protocol = extractProtocol(props["ProtocolConfiguration"], logicalId, stack.stackName);
|
|
1275
|
-
const
|
|
1281
|
+
const artifact = extractArtifact(props["AgentRuntimeArtifact"], logicalId, stack.stackName, resources, stack.region, imageContext);
|
|
1276
1282
|
const environmentVariables = props["EnvironmentVariables"] && typeof props["EnvironmentVariables"] === "object" && !Array.isArray(props["EnvironmentVariables"]) ? props["EnvironmentVariables"] : {};
|
|
1277
1283
|
const roleArn = typeof props["RoleArn"] === "string" ? props["RoleArn"] : void 0;
|
|
1278
1284
|
const jwtAuthorizer = extractJwtAuthorizer(props["AuthorizerConfiguration"], logicalId);
|
|
@@ -1280,7 +1286,7 @@ function extractRuntimeProperties(stack, logicalId, resource, resources, imageCo
|
|
|
1280
1286
|
stack,
|
|
1281
1287
|
logicalId,
|
|
1282
1288
|
resource,
|
|
1283
|
-
containerUri,
|
|
1289
|
+
...artifact.kind === "container" ? { containerUri: artifact.containerUri } : { codeArtifact: artifact.codeArtifact },
|
|
1284
1290
|
environmentVariables,
|
|
1285
1291
|
protocol,
|
|
1286
1292
|
...roleArn !== void 0 && { roleArn },
|
|
@@ -1313,30 +1319,61 @@ function extractJwtAuthorizer(authorizerConfig, logicalId) {
|
|
|
1313
1319
|
};
|
|
1314
1320
|
}
|
|
1315
1321
|
/**
|
|
1316
|
-
* Validate `ProtocolConfiguration`.
|
|
1317
|
-
*
|
|
1318
|
-
*
|
|
1322
|
+
* Validate `ProtocolConfiguration`. Serves `HTTP` (the default when absent)
|
|
1323
|
+
* and `MCP`; `A2A` / `AGUI` speak other wire contracts and hard-error with a
|
|
1324
|
+
* pointer to the follow-up.
|
|
1319
1325
|
*/
|
|
1320
1326
|
function extractProtocol(value, logicalId, stackName) {
|
|
1321
1327
|
if (value === void 0 || value === null) return AGENTCORE_HTTP_PROTOCOL;
|
|
1322
|
-
if (typeof value !== "string") throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a non-string ProtocolConfiguration. ${getEmbedConfig().cliName} invoke-agentcore supports the ${
|
|
1323
|
-
if (value
|
|
1328
|
+
if (typeof value !== "string") throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a non-string ProtocolConfiguration. ${getEmbedConfig().cliName} invoke-agentcore supports the ${SUPPORTED_AGENTCORE_PROTOCOLS.join(" / ")} protocols.`);
|
|
1329
|
+
if (!SUPPORTED_AGENTCORE_PROTOCOLS.includes(value)) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} uses the ${value} protocol. ${getEmbedConfig().cliName} invoke-agentcore supports the ${SUPPORTED_AGENTCORE_PROTOCOLS.join(" / ")} protocols (A2A / AGUI speak different wire contracts and are not served yet).`);
|
|
1324
1330
|
return value;
|
|
1325
1331
|
}
|
|
1326
1332
|
/**
|
|
1327
|
-
*
|
|
1328
|
-
*
|
|
1329
|
-
*
|
|
1333
|
+
* Resolve `AgentRuntimeArtifact` to either a container image URI or a code
|
|
1334
|
+
* artifact (managed runtime). A `ContainerConfiguration` yields the resolved
|
|
1335
|
+
* `ContainerUri`; a `CodeConfiguration` yields its `Runtime` / `EntryPoint` +
|
|
1336
|
+
* the cdk.out asset hash the command uses to locate the bundle source.
|
|
1330
1337
|
*/
|
|
1331
|
-
function
|
|
1338
|
+
function extractArtifact(artifact, logicalId, stackName, resources, region, imageContext) {
|
|
1332
1339
|
if (!artifact || typeof artifact !== "object" || Array.isArray(artifact)) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has no AgentRuntimeArtifact.`);
|
|
1333
1340
|
const art = artifact;
|
|
1334
|
-
if (art["CodeConfiguration"] && !art["ContainerConfiguration"])
|
|
1341
|
+
if (art["CodeConfiguration"] && !art["ContainerConfiguration"]) return {
|
|
1342
|
+
kind: "code",
|
|
1343
|
+
codeArtifact: extractCodeArtifact(art["CodeConfiguration"], logicalId, stackName)
|
|
1344
|
+
};
|
|
1335
1345
|
const container = art["ContainerConfiguration"];
|
|
1336
1346
|
if (!container || typeof container !== "object" || Array.isArray(container)) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has no ContainerConfiguration in its AgentRuntimeArtifact.`);
|
|
1337
1347
|
const uri = resolveImageUri(container["ContainerUri"], resources, region, imageContext);
|
|
1338
1348
|
if (uri === void 0) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a ContainerConfiguration.ContainerUri that ${getEmbedConfig().cliName} invoke-agentcore cannot resolve. v1 resolves a literal image URI, an Fn::Sub asset URI (the fromAsset / Dockerfile path), and an imported-ECR Fn::Join. A same-stack AWS::ECR::Repository reference is not supported — build the agent as a fromAsset image, or pin a literal / imported ECR image URI.`);
|
|
1339
|
-
return
|
|
1349
|
+
return {
|
|
1350
|
+
kind: "container",
|
|
1351
|
+
containerUri: uri
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Extract a `CodeConfiguration` (managed-runtime) artifact. Reads `Runtime`,
|
|
1356
|
+
* `EntryPoint`, and the cdk.out file-asset hash from `Code.S3.Prefix`
|
|
1357
|
+
* (`<hash>.zip`, the `fromCodeAsset` shape). A non-literal `Code.S3.Prefix`
|
|
1358
|
+
* (an intrinsic, or a `fromS3` object key the command can't map to a local
|
|
1359
|
+
* asset) hard-errors — downloading a pre-existing S3 bundle is not supported
|
|
1360
|
+
* locally yet.
|
|
1361
|
+
*/
|
|
1362
|
+
function extractCodeArtifact(codeConfig, logicalId, stackName) {
|
|
1363
|
+
const cfg = codeConfig && typeof codeConfig === "object" && !Array.isArray(codeConfig) ? codeConfig : {};
|
|
1364
|
+
const runtime = cfg["Runtime"];
|
|
1365
|
+
if (typeof runtime !== "string" || runtime.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a CodeConfiguration with no string Runtime.`);
|
|
1366
|
+
const entryPointRaw = cfg["EntryPoint"];
|
|
1367
|
+
const entryPoint = Array.isArray(entryPointRaw) ? entryPointRaw.filter((x) => typeof x === "string") : [];
|
|
1368
|
+
if (entryPoint.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a CodeConfiguration with no EntryPoint.`);
|
|
1369
|
+
const s3 = cfg["Code"] && typeof cfg["Code"] === "object" ? cfg["Code"]["S3"] : void 0;
|
|
1370
|
+
const prefix = s3 && typeof s3 === "object" ? s3["Prefix"] : void 0;
|
|
1371
|
+
if (typeof prefix !== "string" || prefix.length === 0) throw new AgentCoreResolutionError(`AgentCore Runtime '${logicalId}' in ${stackName} has a CodeConfiguration whose Code.S3.Prefix is not a literal string. ${getEmbedConfig().cliName} invoke-agentcore runs a local from-source build of the fromCodeAsset bundle; downloading a pre-existing S3 bundle (fromS3) is not supported yet.`);
|
|
1372
|
+
return {
|
|
1373
|
+
runtime,
|
|
1374
|
+
entryPoint,
|
|
1375
|
+
codeAssetHash: prefix.replace(/^.*\//, "").replace(/\.zip$/, "")
|
|
1376
|
+
};
|
|
1340
1377
|
}
|
|
1341
1378
|
/**
|
|
1342
1379
|
* Resolve a `ContainerUri` value to a string. Handles a literal string,
|
|
@@ -6308,7 +6345,7 @@ async function runDetached(opts) {
|
|
|
6308
6345
|
if (opts.platform) args.push("--platform", opts.platform);
|
|
6309
6346
|
if (opts.extraHosts) for (const entry of opts.extraHosts) args.push("--add-host", `${entry.host}:${entry.ip}`);
|
|
6310
6347
|
const host = opts.host ?? "127.0.0.1";
|
|
6311
|
-
args.push("-p", `${host}:${opts.hostPort}
|
|
6348
|
+
args.push("-p", `${host}:${opts.hostPort}:${opts.containerPort ?? 8080}`);
|
|
6312
6349
|
if (opts.debugPort !== void 0) args.push("-p", `${host}:${opts.debugPort}:${opts.debugPort}`);
|
|
6313
6350
|
for (const mount of opts.mounts) {
|
|
6314
6351
|
const ro = mount.readOnly ? ":ro" : "";
|
|
@@ -7022,7 +7059,7 @@ async function httpProbe(host, port, timeoutMs) {
|
|
|
7022
7059
|
})).text().catch(() => void 0);
|
|
7023
7060
|
return true;
|
|
7024
7061
|
} catch (err) {
|
|
7025
|
-
if (isTransientNetworkError$
|
|
7062
|
+
if (isTransientNetworkError$2(err)) return false;
|
|
7026
7063
|
throw err;
|
|
7027
7064
|
} finally {
|
|
7028
7065
|
clearTimeout(timer);
|
|
@@ -7036,7 +7073,7 @@ async function httpProbe(host, port, timeoutMs) {
|
|
|
7036
7073
|
* between Docker's port forwarder accepting a TCP connection and the
|
|
7037
7074
|
* container's RIE process being ready for HTTP.
|
|
7038
7075
|
*/
|
|
7039
|
-
function isTransientNetworkError$
|
|
7076
|
+
function isTransientNetworkError$2(err) {
|
|
7040
7077
|
if (!(err instanceof Error)) return false;
|
|
7041
7078
|
if (err.name === "AbortError") return true;
|
|
7042
7079
|
if (err.name === "TypeError" && err.message === "fetch failed") return true;
|
|
@@ -8136,6 +8173,116 @@ function createLocalInvokeCommand(opts = {}) {
|
|
|
8136
8173
|
return invoke;
|
|
8137
8174
|
}
|
|
8138
8175
|
|
|
8176
|
+
//#endregion
|
|
8177
|
+
//#region src/local/agentcore-code-build.ts
|
|
8178
|
+
/**
|
|
8179
|
+
* Local from-source build for an AgentCore Runtime `CodeConfiguration`
|
|
8180
|
+
* (managed-runtime) artifact.
|
|
8181
|
+
*
|
|
8182
|
+
* Unlike the container artifact (which ships its own Dockerfile/image), a code
|
|
8183
|
+
* artifact is just source + an `EntryPoint` + a `Runtime`; AWS's managed
|
|
8184
|
+
* runtime runs the entrypoint, which self-serves the AgentCore HTTP contract
|
|
8185
|
+
* (`POST /invocations` + `GET /ping` on 8080) — typically via the
|
|
8186
|
+
* `bedrock-agentcore` SDK. We replicate that locally: generate a Dockerfile
|
|
8187
|
+
* for the runtime's base image, install the bundle's dependencies, and run the
|
|
8188
|
+
* entrypoint. The resulting container speaks the same 8080 contract, so the
|
|
8189
|
+
* existing HTTP client drives it unchanged.
|
|
8190
|
+
*/
|
|
8191
|
+
/** AgentCore CodeConfiguration `Runtime` enum → local Docker base image. */
|
|
8192
|
+
const RUNTIME_BASE_IMAGES = {
|
|
8193
|
+
PYTHON_3_10: "public.ecr.aws/docker/library/python:3.10-slim",
|
|
8194
|
+
PYTHON_3_11: "public.ecr.aws/docker/library/python:3.11-slim",
|
|
8195
|
+
PYTHON_3_12: "public.ecr.aws/docker/library/python:3.12-slim",
|
|
8196
|
+
PYTHON_3_13: "public.ecr.aws/docker/library/python:3.13-slim",
|
|
8197
|
+
PYTHON_3_14: "public.ecr.aws/docker/library/python:3.14-slim",
|
|
8198
|
+
NODE_22: "public.ecr.aws/docker/library/node:22-slim"
|
|
8199
|
+
};
|
|
8200
|
+
/** Runtimes this CLI can build a from-source image for. */
|
|
8201
|
+
const SUPPORTED_CODE_RUNTIMES = Object.keys(RUNTIME_BASE_IMAGES);
|
|
8202
|
+
/**
|
|
8203
|
+
* Build (or, with `noBuild`, verify) a local image for a code artifact and
|
|
8204
|
+
* return its tag. The generated Dockerfile is written to a temp dir and built
|
|
8205
|
+
* with the source dir as the context, so the cdk.out asset is never mutated.
|
|
8206
|
+
*/
|
|
8207
|
+
async function buildAgentCoreCodeImage(options) {
|
|
8208
|
+
const logger = getLogger();
|
|
8209
|
+
const base = RUNTIME_BASE_IMAGES[options.runtime];
|
|
8210
|
+
if (!base) throw new LocalInvokeBuildError(`AgentCore CodeConfiguration runtime '${options.runtime}' is not supported for local execution. Supported runtimes: ${SUPPORTED_CODE_RUNTIMES.join(", ")}.`);
|
|
8211
|
+
const isNode = options.runtime.startsWith("NODE");
|
|
8212
|
+
const dockerfile = renderCodeDockerfile(base, options.entryPoint, isNode);
|
|
8213
|
+
const tag = computeCodeImageTag(options.sourceDir, options.runtime, options.entryPoint, dockerfile);
|
|
8214
|
+
const platform = options.architecture === "x86_64" ? "linux/amd64" : "linux/arm64";
|
|
8215
|
+
if (options.noBuild === true) {
|
|
8216
|
+
logger.info(`Skipping docker build (--no-build). Verifying ${tag} is in local registry...`);
|
|
8217
|
+
if (!await isImageInLocalCache(tag)) throw new LocalInvokeBuildError(`image '${tag}' not in local registry and --no-build is set; remove --no-build or run the build manually first.`);
|
|
8218
|
+
return tag;
|
|
8219
|
+
}
|
|
8220
|
+
logger.info(`Building agent image from source (runtime=${options.runtime}, platform=${platform})...`);
|
|
8221
|
+
logger.debug(`Local tag: ${tag}`);
|
|
8222
|
+
const buildDir = await mkdtemp(join(tmpdir(), `${getEmbedConfig().resourceNamePrefix}-agentcore-code-`));
|
|
8223
|
+
const dockerfilePath = join(buildDir, "Dockerfile");
|
|
8224
|
+
try {
|
|
8225
|
+
await writeFile(dockerfilePath, dockerfile, "utf-8");
|
|
8226
|
+
await runDockerStreaming([
|
|
8227
|
+
"build",
|
|
8228
|
+
"--platform",
|
|
8229
|
+
platform,
|
|
8230
|
+
"--tag",
|
|
8231
|
+
tag,
|
|
8232
|
+
"--file",
|
|
8233
|
+
dockerfilePath,
|
|
8234
|
+
options.sourceDir
|
|
8235
|
+
]);
|
|
8236
|
+
} catch (err) {
|
|
8237
|
+
const stderr = err.stderr?.trim();
|
|
8238
|
+
throw new LocalInvokeBuildError(`docker build failed for AgentCore code artifact (${options.sourceDir})${stderr ? `: ${stderr}` : ""}`);
|
|
8239
|
+
} finally {
|
|
8240
|
+
await rm(buildDir, {
|
|
8241
|
+
recursive: true,
|
|
8242
|
+
force: true
|
|
8243
|
+
}).catch(() => void 0);
|
|
8244
|
+
}
|
|
8245
|
+
return tag;
|
|
8246
|
+
}
|
|
8247
|
+
/**
|
|
8248
|
+
* Render the generated Dockerfile. Dependencies are installed conditionally
|
|
8249
|
+
* (a bundle may vendor them or ship none), and the EntryPoint is mapped to a
|
|
8250
|
+
* CMD: a bare script (`app.py` / `server.js`) is run by the interpreter, while
|
|
8251
|
+
* an explicit launcher (e.g. `opentelemetry-instrument`) is run verbatim.
|
|
8252
|
+
*/
|
|
8253
|
+
function renderCodeDockerfile(base, entryPoint, isNode) {
|
|
8254
|
+
const installStep = isNode ? "RUN if [ -f package.json ]; then npm install --omit=dev; fi" : "RUN if [ -f requirements.txt ]; then pip install --no-cache-dir -r requirements.txt; elif [ -f pyproject.toml ]; then pip install --no-cache-dir .; fi";
|
|
8255
|
+
return [
|
|
8256
|
+
`FROM ${base}`,
|
|
8257
|
+
"WORKDIR /app",
|
|
8258
|
+
"COPY . /app",
|
|
8259
|
+
installStep,
|
|
8260
|
+
"EXPOSE 8080",
|
|
8261
|
+
`CMD ${JSON.stringify(toCmdArgv(entryPoint, isNode))}`
|
|
8262
|
+
].join("\n") + "\n";
|
|
8263
|
+
}
|
|
8264
|
+
/**
|
|
8265
|
+
* Map the EntryPoint argv to a Docker CMD argv. The managed runtime execs the
|
|
8266
|
+
* entrypoint as the program; a bare script file is run by the language
|
|
8267
|
+
* interpreter (`python` / `node`), while a non-script first token (a launcher
|
|
8268
|
+
* already on PATH, e.g. `opentelemetry-instrument`) is run verbatim.
|
|
8269
|
+
*/
|
|
8270
|
+
function toCmdArgv(entryPoint, isNode) {
|
|
8271
|
+
const first = entryPoint[0] ?? "";
|
|
8272
|
+
if (!(isNode ? /\.[cm]?js$/.test(first) : /\.py$/.test(first))) return entryPoint;
|
|
8273
|
+
return [isNode ? "node" : "python", ...entryPoint];
|
|
8274
|
+
}
|
|
8275
|
+
/** Deterministic local tag, stable for identical source + runtime + entrypoint. */
|
|
8276
|
+
function computeCodeImageTag(sourceDir, runtime, entryPoint, dockerfile) {
|
|
8277
|
+
const hash = createHash("sha256").update([
|
|
8278
|
+
sourceDir,
|
|
8279
|
+
runtime,
|
|
8280
|
+
entryPoint.join(" "),
|
|
8281
|
+
dockerfile
|
|
8282
|
+
].join("\0")).digest("hex").slice(0, 16);
|
|
8283
|
+
return `${getEmbedConfig().resourceNamePrefix}-agentcore-code-${hash}`;
|
|
8284
|
+
}
|
|
8285
|
+
|
|
8139
8286
|
//#endregion
|
|
8140
8287
|
//#region src/local/agentcore-client.ts
|
|
8141
8288
|
/**
|
|
@@ -8208,7 +8355,7 @@ async function pingProbe(host, port, timeoutMs) {
|
|
|
8208
8355
|
await response.text().catch(() => void 0);
|
|
8209
8356
|
return response.status;
|
|
8210
8357
|
} catch (err) {
|
|
8211
|
-
if (isTransientNetworkError(err)) return void 0;
|
|
8358
|
+
if (isTransientNetworkError$1(err)) return void 0;
|
|
8212
8359
|
throw err;
|
|
8213
8360
|
} finally {
|
|
8214
8361
|
clearTimeout(timer);
|
|
@@ -8220,7 +8367,7 @@ async function pingProbe(host, port, timeoutMs) {
|
|
|
8220
8367
|
* `ECONNRESET` / `ECONNREFUSED` / `UND_ERR_SOCKET`. Treat all of those —
|
|
8221
8368
|
* plus an `AbortError` from the per-probe timeout — as "not ready, retry".
|
|
8222
8369
|
*/
|
|
8223
|
-
function isTransientNetworkError(err) {
|
|
8370
|
+
function isTransientNetworkError$1(err) {
|
|
8224
8371
|
if (!(err instanceof Error)) return false;
|
|
8225
8372
|
if (err.name === "AbortError") return true;
|
|
8226
8373
|
if (err.name === "TypeError" && err.message === "fetch failed") return true;
|
|
@@ -8305,6 +8452,191 @@ async function streamBody(body, onChunk) {
|
|
|
8305
8452
|
}
|
|
8306
8453
|
}
|
|
8307
8454
|
|
|
8455
|
+
//#endregion
|
|
8456
|
+
//#region src/local/agentcore-mcp-client.ts
|
|
8457
|
+
/**
|
|
8458
|
+
* Client for the Bedrock AgentCore Runtime MCP protocol contract.
|
|
8459
|
+
*
|
|
8460
|
+
* An MCP-protocol AgentCore Runtime container listens on `0.0.0.0:8000` and
|
|
8461
|
+
* serves the Model Context Protocol over **Streamable HTTP** at `POST /mcp`
|
|
8462
|
+
* (no `GET /ping` — unlike the HTTP protocol). Each JSON-RPC message is its
|
|
8463
|
+
* own POST. `cdkl invoke-agentcore` performs the minimal session lifecycle:
|
|
8464
|
+
*
|
|
8465
|
+
* 1. `initialize` — negotiate; the server MAY return an
|
|
8466
|
+
* `Mcp-Session-Id` header to echo thereafter.
|
|
8467
|
+
* 2. `notifications/initialized` — required before any request (202, no body).
|
|
8468
|
+
* 3. one request — `tools/list` by default, or the method/params
|
|
8469
|
+
* from `--event` (e.g. `tools/call`).
|
|
8470
|
+
*
|
|
8471
|
+
* Talking to the local container is **vanilla MCP**: the
|
|
8472
|
+
* `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header and the inbound OAuth
|
|
8473
|
+
* bearer are AgentCore managed-plane concerns the front door maps to MCP's own
|
|
8474
|
+
* `Mcp-Session-Id`, so a direct local client does not send them.
|
|
8475
|
+
*/
|
|
8476
|
+
/** Container port an MCP-protocol AgentCore Runtime listens on. */
|
|
8477
|
+
const MCP_CONTAINER_PORT = 8e3;
|
|
8478
|
+
/** HTTP path of the MCP Streamable-HTTP endpoint. */
|
|
8479
|
+
const MCP_PATH = "/mcp";
|
|
8480
|
+
/** MCP protocol version this client negotiates. */
|
|
8481
|
+
const MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
8482
|
+
const SESSION_ID_HEADER = "mcp-session-id";
|
|
8483
|
+
const PROTOCOL_VERSION_HEADER = "MCP-Protocol-Version";
|
|
8484
|
+
/**
|
|
8485
|
+
* Run the MCP session lifecycle against a local container and return the
|
|
8486
|
+
* single request's JSON-RPC response. The initial `initialize` POST is
|
|
8487
|
+
* retried for `readyTimeoutMs` to absorb container boot (there is no separate
|
|
8488
|
+
* readiness endpoint), so this also serves as the wait-for-ready step.
|
|
8489
|
+
*/
|
|
8490
|
+
async function mcpInvokeOnce(host, port, request, options = {}) {
|
|
8491
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
8492
|
+
const url = `http://${host}:${port}${MCP_PATH}`;
|
|
8493
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? 12e4;
|
|
8494
|
+
const sessionId = await initializeWithRetry(fetchImpl, url, options.readyTimeoutMs ?? 3e4);
|
|
8495
|
+
await postMcp(fetchImpl, url, {
|
|
8496
|
+
jsonrpc: "2.0",
|
|
8497
|
+
method: "notifications/initialized"
|
|
8498
|
+
}, sessionId, requestTimeoutMs);
|
|
8499
|
+
const message = (await postMcp(fetchImpl, url, {
|
|
8500
|
+
jsonrpc: "2.0",
|
|
8501
|
+
id: 1,
|
|
8502
|
+
method: request.method,
|
|
8503
|
+
...request.params !== void 0 && { params: request.params }
|
|
8504
|
+
}, sessionId, requestTimeoutMs)).message;
|
|
8505
|
+
return {
|
|
8506
|
+
ok: !(message !== null && typeof message === "object" && "error" in message),
|
|
8507
|
+
raw: JSON.stringify(message ?? null, null, 2)
|
|
8508
|
+
};
|
|
8509
|
+
}
|
|
8510
|
+
/**
|
|
8511
|
+
* POST `initialize`, retrying transient connect failures until the container
|
|
8512
|
+
* is up or `readyTimeoutMs` elapses. Returns the `Mcp-Session-Id` the server
|
|
8513
|
+
* assigned (undefined for a stateless server that omits it).
|
|
8514
|
+
*/
|
|
8515
|
+
async function initializeWithRetry(fetchImpl, url, readyTimeoutMs) {
|
|
8516
|
+
const deadline = Date.now() + readyTimeoutMs;
|
|
8517
|
+
const body = {
|
|
8518
|
+
jsonrpc: "2.0",
|
|
8519
|
+
id: 0,
|
|
8520
|
+
method: "initialize",
|
|
8521
|
+
params: {
|
|
8522
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
8523
|
+
capabilities: {},
|
|
8524
|
+
clientInfo: {
|
|
8525
|
+
name: "cdkl",
|
|
8526
|
+
version: "1"
|
|
8527
|
+
}
|
|
8528
|
+
}
|
|
8529
|
+
};
|
|
8530
|
+
let lastDetail = "";
|
|
8531
|
+
while (Date.now() < deadline) {
|
|
8532
|
+
const controller = new AbortController();
|
|
8533
|
+
const timer = setTimeout(() => controller.abort(), 5e3);
|
|
8534
|
+
try {
|
|
8535
|
+
const response = await fetchImpl(url, {
|
|
8536
|
+
method: "POST",
|
|
8537
|
+
headers: {
|
|
8538
|
+
"Content-Type": "application/json",
|
|
8539
|
+
Accept: "application/json, text/event-stream"
|
|
8540
|
+
},
|
|
8541
|
+
body: JSON.stringify(body),
|
|
8542
|
+
signal: controller.signal
|
|
8543
|
+
});
|
|
8544
|
+
await response.text().catch(() => void 0);
|
|
8545
|
+
if (response.status >= 200 && response.status < 300) return response.headers.get(SESSION_ID_HEADER) ?? void 0;
|
|
8546
|
+
lastDetail = `initialize returned HTTP ${response.status}`;
|
|
8547
|
+
throw new Error(lastDetail);
|
|
8548
|
+
} catch (err) {
|
|
8549
|
+
if (!isTransientNetworkError(err)) {
|
|
8550
|
+
if (lastDetail) throw new Error(`MCP initialize at ${url} failed: ${lastDetail}. Check 'docker logs' output.`);
|
|
8551
|
+
throw err;
|
|
8552
|
+
}
|
|
8553
|
+
lastDetail = err instanceof Error ? err.message : String(err);
|
|
8554
|
+
} finally {
|
|
8555
|
+
clearTimeout(timer);
|
|
8556
|
+
}
|
|
8557
|
+
await setTimeout$1(150);
|
|
8558
|
+
}
|
|
8559
|
+
throw new Error(`MCP server did not become ready on ${url} within ${readyTimeoutMs}ms${lastDetail ? `: ${lastDetail}` : ""}. The container may have exited early or may not serve POST ${MCP_PATH} — check 'docker logs'.`);
|
|
8560
|
+
}
|
|
8561
|
+
/**
|
|
8562
|
+
* POST one JSON-RPC message and, for a request (one with an `id`), return the
|
|
8563
|
+
* parsed JSON-RPC response — handling both an `application/json` body and a
|
|
8564
|
+
* `text/event-stream` (the server picks either per the Streamable-HTTP spec).
|
|
8565
|
+
* A notification (no `id`) returns 202 with no body and yields no message.
|
|
8566
|
+
*/
|
|
8567
|
+
async function postMcp(fetchImpl, url, body, sessionId, timeoutMs) {
|
|
8568
|
+
const controller = new AbortController();
|
|
8569
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
8570
|
+
try {
|
|
8571
|
+
const response = await fetchImpl(url, {
|
|
8572
|
+
method: "POST",
|
|
8573
|
+
headers: {
|
|
8574
|
+
"Content-Type": "application/json",
|
|
8575
|
+
Accept: "application/json, text/event-stream",
|
|
8576
|
+
[PROTOCOL_VERSION_HEADER]: MCP_PROTOCOL_VERSION,
|
|
8577
|
+
...sessionId && { "Mcp-Session-Id": sessionId }
|
|
8578
|
+
},
|
|
8579
|
+
body: JSON.stringify(body),
|
|
8580
|
+
signal: controller.signal
|
|
8581
|
+
});
|
|
8582
|
+
const text = await response.text();
|
|
8583
|
+
if (body.id === void 0) return { status: response.status };
|
|
8584
|
+
const message = (response.headers.get("content-type") ?? "").includes("text/event-stream") ? parseSseForJsonRpc(text, body.id) : text ? safeJsonParse$1(text) : void 0;
|
|
8585
|
+
return {
|
|
8586
|
+
status: response.status,
|
|
8587
|
+
message
|
|
8588
|
+
};
|
|
8589
|
+
} catch (err) {
|
|
8590
|
+
if (err.name === "AbortError") throw new Error(`MCP request '${body.method}' at ${url} timed out after ${timeoutMs}ms. The server may be hung; check container logs.`);
|
|
8591
|
+
throw err;
|
|
8592
|
+
} finally {
|
|
8593
|
+
clearTimeout(timer);
|
|
8594
|
+
}
|
|
8595
|
+
}
|
|
8596
|
+
/**
|
|
8597
|
+
* Extract the JSON-RPC message matching `id` from an SSE body. Frames are
|
|
8598
|
+
* separated by blank lines; a frame's `data:` lines are concatenated and
|
|
8599
|
+
* parsed as JSON. Returns the id-matching message, else the last parseable
|
|
8600
|
+
* one (servers typically send a single frame carrying the response).
|
|
8601
|
+
*/
|
|
8602
|
+
function parseSseForJsonRpc(text, id) {
|
|
8603
|
+
let last;
|
|
8604
|
+
for (const frame of text.split(/\r?\n\r?\n/)) {
|
|
8605
|
+
const data = frame.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
8606
|
+
if (!data) continue;
|
|
8607
|
+
const parsed = safeJsonParse$1(data);
|
|
8608
|
+
if (parsed === void 0) continue;
|
|
8609
|
+
last = parsed;
|
|
8610
|
+
if (parsed !== null && typeof parsed === "object" && parsed.id === id) return parsed;
|
|
8611
|
+
}
|
|
8612
|
+
return last;
|
|
8613
|
+
}
|
|
8614
|
+
function safeJsonParse$1(text) {
|
|
8615
|
+
try {
|
|
8616
|
+
return JSON.parse(text);
|
|
8617
|
+
} catch {
|
|
8618
|
+
return;
|
|
8619
|
+
}
|
|
8620
|
+
}
|
|
8621
|
+
/**
|
|
8622
|
+
* `fetch()` failures during container boot manifest as a generic
|
|
8623
|
+
* `TypeError: fetch failed` whose `.cause` carries the underlying
|
|
8624
|
+
* `ECONNRESET` / `ECONNREFUSED` / `UND_ERR_SOCKET`; an `AbortError` is the
|
|
8625
|
+
* per-attempt timeout. Treat all of those — plus the synthetic non-2xx retry
|
|
8626
|
+
* — as "not ready, retry".
|
|
8627
|
+
*/
|
|
8628
|
+
function isTransientNetworkError(err) {
|
|
8629
|
+
if (!(err instanceof Error)) return false;
|
|
8630
|
+
if (err.name === "AbortError") return true;
|
|
8631
|
+
if (err.name === "TypeError" && err.message === "fetch failed") return true;
|
|
8632
|
+
if (err.message.startsWith("initialize returned HTTP")) return true;
|
|
8633
|
+
const cause = err.cause;
|
|
8634
|
+
if (cause?.code === "ECONNRESET") return true;
|
|
8635
|
+
if (cause?.code === "ECONNREFUSED") return true;
|
|
8636
|
+
if (cause?.code === "UND_ERR_SOCKET") return true;
|
|
8637
|
+
return false;
|
|
8638
|
+
}
|
|
8639
|
+
|
|
8308
8640
|
//#endregion
|
|
8309
8641
|
//#region src/local/cors-handler.ts
|
|
8310
8642
|
/**
|
|
@@ -17270,9 +17602,10 @@ function createLocalStartApiCommand(opts = {}) {
|
|
|
17270
17602
|
* locally and invoke it once over the AgentCore HTTP contract. Resolves
|
|
17271
17603
|
* the `AWS::BedrockAgentCore::Runtime`, pulls / builds its container,
|
|
17272
17604
|
* starts it on port 8080, waits for `GET /ping`, POSTs the event to
|
|
17273
|
-
* `POST /invocations`, prints the response, and tears down.
|
|
17274
|
-
* container artifact
|
|
17275
|
-
*
|
|
17605
|
+
* `POST /invocations`, prints the response, and tears down. Covers the
|
|
17606
|
+
* container artifact and the CodeConfiguration managed-runtime artifact
|
|
17607
|
+
* (fromCodeAsset, built from source) on the HTTP + MCP protocols; the agent's
|
|
17608
|
+
* calls to real AWS go to real AWS (credentials injected like `cdkl invoke`).
|
|
17276
17609
|
*/
|
|
17277
17610
|
async function localInvokeAgentCoreCommand(target, options, extraStateProviders) {
|
|
17278
17611
|
const logger = getLogger();
|
|
@@ -17329,15 +17662,21 @@ async function localInvokeAgentCoreCommand(target, options, extraStateProviders)
|
|
|
17329
17662
|
onMissing: () => new CdkLocalError(`${getEmbedConfig().cliName} invoke-agentcore requires a <target> (an AgentCore Runtime display path or logical ID). Run \`${getEmbedConfig().cliName} list\` to see them, or run it in a TTY to pick interactively.`, "LOCAL_INVOKE_AGENTCORE_TARGET_REQUIRED")
|
|
17330
17663
|
}), stacks);
|
|
17331
17664
|
logger.info(`Target: ${resolved.stack.stackName}/${resolved.logicalId} (${resolved.protocol})`);
|
|
17665
|
+
const isMcp = resolved.protocol === "MCP";
|
|
17332
17666
|
const sessionId = options.sessionId ?? randomUUID();
|
|
17333
17667
|
const event = await readEvent(options);
|
|
17334
|
-
const
|
|
17668
|
+
const mcpRequest = isMcp ? buildMcpRequest(event) : void 0;
|
|
17669
|
+
let authorization;
|
|
17670
|
+
if (isMcp) {
|
|
17671
|
+
if (resolved.jwtAuthorizer || options.bearerToken) logger.info(`MCP runtime: invoking the local container's ${MCP_PATH} directly (vanilla MCP). An inbound JWT / --bearer-token is an AgentCore managed-plane concern and is not applied locally.`);
|
|
17672
|
+
} else authorization = await resolveInboundAuthorization(resolved, options);
|
|
17335
17673
|
const image = await resolveAgentCoreImage(resolved, options);
|
|
17336
17674
|
const dockerEnv = await buildContainerEnv(resolved, options, profileCredentials, profileCredsFile, extraStateProviders);
|
|
17337
17675
|
const hostPort = await pickFreePort();
|
|
17338
17676
|
const containerHost = options.containerHost;
|
|
17339
17677
|
const containerName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
17340
|
-
|
|
17678
|
+
const containerPortLabel = isMcp ? `${MCP_CONTAINER_PORT}${MCP_PATH}` : "8080";
|
|
17679
|
+
logger.info(`Starting agent container (image=${image}, port=${hostPort} -> ${containerPortLabel})...`);
|
|
17341
17680
|
containerId = await runDetached({
|
|
17342
17681
|
image,
|
|
17343
17682
|
mounts: [],
|
|
@@ -17346,22 +17685,30 @@ async function localInvokeAgentCoreCommand(target, options, extraStateProviders)
|
|
|
17346
17685
|
hostPort,
|
|
17347
17686
|
host: containerHost,
|
|
17348
17687
|
platform: options.platform,
|
|
17349
|
-
name: containerName
|
|
17688
|
+
name: containerName,
|
|
17689
|
+
...isMcp && { containerPort: 8e3 }
|
|
17350
17690
|
});
|
|
17351
17691
|
stopLogs = streamLogs(containerId);
|
|
17352
17692
|
sigintHandler = () => {
|
|
17353
17693
|
cleanup().then(() => process.exit(130));
|
|
17354
17694
|
};
|
|
17355
17695
|
process.on("SIGINT", sigintHandler);
|
|
17356
|
-
|
|
17357
|
-
|
|
17358
|
-
|
|
17359
|
-
|
|
17360
|
-
|
|
17361
|
-
|
|
17362
|
-
|
|
17363
|
-
|
|
17364
|
-
|
|
17696
|
+
if (isMcp && mcpRequest) {
|
|
17697
|
+
logger.info(`MCP request: ${mcpRequest.method}`);
|
|
17698
|
+
const mcp = await mcpInvokeOnce(containerHost, hostPort, mcpRequest);
|
|
17699
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
17700
|
+
emitMcpResult(mcp);
|
|
17701
|
+
} else {
|
|
17702
|
+
await waitForAgentCorePing(containerHost, hostPort);
|
|
17703
|
+
const result = await invokeAgentCore(containerHost, hostPort, event, {
|
|
17704
|
+
sessionId,
|
|
17705
|
+
timeoutMs: 12e4,
|
|
17706
|
+
onChunk: (text) => process.stdout.write(text),
|
|
17707
|
+
...authorization && { authorization }
|
|
17708
|
+
});
|
|
17709
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
17710
|
+
emitResult(result);
|
|
17711
|
+
}
|
|
17365
17712
|
} finally {
|
|
17366
17713
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
17367
17714
|
await cleanup();
|
|
@@ -17401,36 +17748,74 @@ async function resolveInboundAuthorization(resolved, options) {
|
|
|
17401
17748
|
return header;
|
|
17402
17749
|
}
|
|
17403
17750
|
/**
|
|
17404
|
-
* Acquire the agent
|
|
17405
|
-
*
|
|
17406
|
-
*
|
|
17751
|
+
* Acquire the agent image. A CODE artifact (managed runtime) is built from
|
|
17752
|
+
* source (generated Dockerfile over the bundle's cdk.out asset). A CONTAINER
|
|
17753
|
+
* artifact mirrors the container-Lambda path: build from a local cdk.out asset
|
|
17754
|
+
* when the URI matches one, else pull from ECR, else pull a plain registry image.
|
|
17407
17755
|
*/
|
|
17408
17756
|
async function resolveAgentCoreImage(resolved, options) {
|
|
17409
17757
|
const logger = getLogger();
|
|
17410
17758
|
const architecture = platformToArchitecture(options.platform);
|
|
17759
|
+
if (resolved.codeArtifact) return resolveAgentCoreCodeImage(resolved, resolved.codeArtifact, options, architecture);
|
|
17760
|
+
const containerUri = resolved.containerUri;
|
|
17761
|
+
if (containerUri === void 0) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' has neither a container image nor a code artifact to run.`, "LOCAL_INVOKE_AGENTCORE_NO_ARTIFACT");
|
|
17411
17762
|
const manifestPath = resolved.stack.assetManifestPath;
|
|
17412
17763
|
if (manifestPath) {
|
|
17413
17764
|
const cdkOutDir = dirname(manifestPath);
|
|
17414
17765
|
const manifest = await new AssetManifestLoader().loadManifest(cdkOutDir, resolved.stack.stackName);
|
|
17415
17766
|
if (manifest) {
|
|
17416
|
-
const entry = getDockerImageBySourceHash(manifest,
|
|
17767
|
+
const entry = getDockerImageBySourceHash(manifest, containerUri);
|
|
17417
17768
|
if (entry) return buildContainerImage(entry.asset, cdkOutDir, {
|
|
17418
17769
|
architecture,
|
|
17419
17770
|
noBuild: options.build === false
|
|
17420
17771
|
});
|
|
17421
17772
|
}
|
|
17422
17773
|
}
|
|
17423
|
-
if (parseEcrUri(
|
|
17424
|
-
logger.info(`Pulling agent image from ECR: ${
|
|
17425
|
-
return pullEcrImage(
|
|
17774
|
+
if (parseEcrUri(containerUri)) {
|
|
17775
|
+
logger.info(`Pulling agent image from ECR: ${containerUri}`);
|
|
17776
|
+
return pullEcrImage(containerUri, {
|
|
17426
17777
|
skipPull: options.pull === false,
|
|
17427
17778
|
...options.region !== void 0 && { region: options.region },
|
|
17428
17779
|
...options.ecrRoleArn !== void 0 && { ecrRoleArn: options.ecrRoleArn },
|
|
17429
17780
|
...options.profile !== void 0 && { profile: options.profile }
|
|
17430
17781
|
});
|
|
17431
17782
|
}
|
|
17432
|
-
await pullImage(
|
|
17433
|
-
return
|
|
17783
|
+
await pullImage(containerUri, options.pull === false);
|
|
17784
|
+
return containerUri;
|
|
17785
|
+
}
|
|
17786
|
+
/**
|
|
17787
|
+
* Build a local image from a `CodeConfiguration` (managed-runtime) bundle:
|
|
17788
|
+
* locate the fromCodeAsset source dir in cdk.out via its asset hash, then run
|
|
17789
|
+
* the from-source build (generated Dockerfile → install deps → run EntryPoint).
|
|
17790
|
+
* A bundle with no local asset (fromS3) hard-errors — not supported yet.
|
|
17791
|
+
*/
|
|
17792
|
+
async function resolveAgentCoreCodeImage(resolved, code, options, architecture) {
|
|
17793
|
+
const manifestPath = resolved.stack.assetManifestPath;
|
|
17794
|
+
if (!manifestPath) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' uses a code artifact, but its stack has no asset manifest in cdk.out to read the bundle source from.`, "LOCAL_INVOKE_AGENTCORE_CODE_NO_MANIFEST");
|
|
17795
|
+
const cdkOutDir = dirname(manifestPath);
|
|
17796
|
+
const loader = new AssetManifestLoader();
|
|
17797
|
+
const manifest = await loader.loadManifest(cdkOutDir, resolved.stack.stackName);
|
|
17798
|
+
const fileAssets = manifest ? loader.getFileAssets(manifest) : void 0;
|
|
17799
|
+
const asset = fileAssets ? fileAssets.get(code.codeAssetHash) ?? findFileAssetByObjectKey(fileAssets, code.codeAssetHash) : void 0;
|
|
17800
|
+
if (!asset) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' code bundle (asset ${code.codeAssetHash}) was not found in the cdk.out asset manifest. ${getEmbedConfig().cliName} invoke-agentcore runs a local from-source build of a fromCodeAsset bundle; a fromS3 bundle (a pre-existing S3 object) is not supported yet.`, "LOCAL_INVOKE_AGENTCORE_CODE_ASSET_NOT_FOUND");
|
|
17801
|
+
const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset);
|
|
17802
|
+
if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) throw new CdkLocalError(`AgentCore Runtime '${resolved.logicalId}' code bundle source '${sourceDir}' does not exist or is not a directory. Re-synthesize the app and retry.`, "LOCAL_INVOKE_AGENTCORE_CODE_SOURCE_MISSING");
|
|
17803
|
+
return buildAgentCoreCodeImage({
|
|
17804
|
+
sourceDir,
|
|
17805
|
+
runtime: code.runtime,
|
|
17806
|
+
entryPoint: code.entryPoint,
|
|
17807
|
+
architecture,
|
|
17808
|
+
noBuild: options.build === false
|
|
17809
|
+
});
|
|
17810
|
+
}
|
|
17811
|
+
/**
|
|
17812
|
+
* Find the file asset whose destination objectKey is `<hash>.zip` (matching the
|
|
17813
|
+
* `Code.S3.Prefix`'s hash) when the source-hash-keyed lookup misses — covers a
|
|
17814
|
+
* synthesizer whose source hash differs from the destination objectKey.
|
|
17815
|
+
*/
|
|
17816
|
+
function findFileAssetByObjectKey(fileAssets, hash) {
|
|
17817
|
+
const zip = `${hash}.zip`;
|
|
17818
|
+
for (const asset of fileAssets.values()) if (Object.values(asset.destinations).some((d) => d.objectKey === zip || d.objectKey.endsWith(`/${zip}`))) return asset;
|
|
17434
17819
|
}
|
|
17435
17820
|
/**
|
|
17436
17821
|
* Build the container env: resolved template env vars (+ `--env-vars`
|
|
@@ -17540,6 +17925,39 @@ function emitResult(result) {
|
|
|
17540
17925
|
}
|
|
17541
17926
|
process.stdout.write(`${result.raw}\n`);
|
|
17542
17927
|
}
|
|
17928
|
+
/**
|
|
17929
|
+
* Build the JSON-RPC request to send to an MCP runtime from `--event`:
|
|
17930
|
+
* - no `--event` (empty object) → `tools/list` (discover the server's tools),
|
|
17931
|
+
* - an object with a string `method` → that method + its `params`,
|
|
17932
|
+
* - anything else → a fail-fast error.
|
|
17933
|
+
*
|
|
17934
|
+
* Exported for unit testing.
|
|
17935
|
+
*/
|
|
17936
|
+
function buildMcpRequest(event) {
|
|
17937
|
+
if (event === void 0 || event === null) return {
|
|
17938
|
+
method: "tools/list",
|
|
17939
|
+
params: {}
|
|
17940
|
+
};
|
|
17941
|
+
if (typeof event !== "object" || Array.isArray(event)) throw new CdkLocalError("MCP --event must be a JSON object describing a JSON-RPC request (e.g. {\"method\":\"tools/call\",\"params\":{\"name\":\"...\",\"arguments\":{...}}}).", "LOCAL_INVOKE_AGENTCORE_MCP_EVENT_INVALID");
|
|
17942
|
+
const obj = event;
|
|
17943
|
+
if (Object.keys(obj).length === 0) return {
|
|
17944
|
+
method: "tools/list",
|
|
17945
|
+
params: {}
|
|
17946
|
+
};
|
|
17947
|
+
if (typeof obj["method"] !== "string") throw new CdkLocalError(`MCP --event must include a string "method" (a JSON-RPC method such as "tools/list" or "tools/call"). Got keys: ${Object.keys(obj).join(", ")}.`, "LOCAL_INVOKE_AGENTCORE_MCP_EVENT_INVALID");
|
|
17948
|
+
return {
|
|
17949
|
+
method: obj["method"],
|
|
17950
|
+
...obj["params"] !== void 0 && { params: obj["params"] }
|
|
17951
|
+
};
|
|
17952
|
+
}
|
|
17953
|
+
/** Print the MCP JSON-RPC response; exit 1 when it carried a JSON-RPC error. */
|
|
17954
|
+
function emitMcpResult(result) {
|
|
17955
|
+
if (!result.ok) {
|
|
17956
|
+
getLogger().warn("MCP server returned a JSON-RPC error.");
|
|
17957
|
+
process.exitCode = 1;
|
|
17958
|
+
}
|
|
17959
|
+
process.stdout.write(`${result.raw}\n`);
|
|
17960
|
+
}
|
|
17543
17961
|
/** Map a `--platform` value to the architecture `buildContainerImage` expects. */
|
|
17544
17962
|
function platformToArchitecture(platform) {
|
|
17545
17963
|
return platform === "linux/amd64" ? "x86_64" : "arm64";
|
|
@@ -17620,7 +18038,7 @@ function readEnvOverridesFile$2(filePath) {
|
|
|
17620
18038
|
}
|
|
17621
18039
|
function createLocalInvokeAgentCoreCommand(opts = {}) {
|
|
17622
18040
|
setEmbedConfig(opts.embedConfig);
|
|
17623
|
-
const cmd = new Command("invoke-agentcore").description("Run a Bedrock AgentCore Runtime container locally and invoke it once over
|
|
18041
|
+
const cmd = new Command("invoke-agentcore").description("Run a Bedrock AgentCore Runtime container locally and invoke it once over its protocol contract: HTTP (POST /invocations + GET /ping on 8080) or MCP (POST /mcp Streamable HTTP on 8000). Resolves the AWS::BedrockAgentCore::Runtime, pulls/builds its container, injects env vars + AWS credentials, and prints the response. For an MCP runtime, runs the session handshake then sends one JSON-RPC request (tools/list by default, or the method/params from --event). Target accepts a CDK display path (MyStack/MyAgent) or stack-qualified logical ID (MyStack:MyAgentRuntime1234). Single-stack apps may omit the stack prefix. Omit <target> in an interactive terminal to pick from a list. Supports the container artifact and the CodeConfiguration managed-runtime artifact (fromCodeAsset, built from source) on the HTTP + MCP protocols; the agent calls real AWS for managed services.").argument("[target]", "CDK display path or stack-qualified logical ID of the AgentCore Runtime to invoke (omit to pick interactively in a TTY)").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--session-id <id>", "AgentCore runtime session id header value (default: a random UUID)")).addOption(new Option("--bearer-token <jwt>", "Bearer JWT to present when the runtime declares a customJwtAuthorizer. Verified against the runtime OIDC discovery URL (signature / issuer / expiry / audience) before the container starts, then forwarded to /invocations as Authorization: Bearer <jwt>.")).addOption(new Option("--no-verify-auth", "Skip inbound JWT verification even when the runtime declares a customJwtAuthorizer (local-dev escape hatch). A --bearer-token, if given, is still forwarded.")).addOption(new Option("--platform <platform>", "docker --platform for the agent container (linux/amd64 or linux/arm64)").choices(["linux/amd64", "linux/arm64"]).default("linux/arm64")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for the local-build path")).addOption(new Option("--no-build", "Skip docker build on the local-asset path (use the previously-built tag). No-op for the ECR / registry pull paths.")).addOption(new Option("--container-host <host>", "Host to bind the agent port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the runtime's execution role and forward STS-issued temp credentials to the container so the agent runs with the deployed role. Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) uses the runtime's RoleArn when it is a literal ARN; (3) `--no-assume-role` opts out. Off by default — the developer's shell credentials are forwarded unchanged.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Bare form uses the resolved stack name; pass an explicit value when the CFn stack name differs.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).action(withErrorHandling(async (target, options) => {
|
|
17624
18042
|
await localInvokeAgentCoreCommand(target, options, opts.extraStateProviders);
|
|
17625
18043
|
}));
|
|
17626
18044
|
[
|
|
@@ -21354,5 +21772,5 @@ function createLocalListCommand(opts = {}) {
|
|
|
21354
21772
|
}
|
|
21355
21773
|
|
|
21356
21774
|
//#endregion
|
|
21357
|
-
export { createJwksCache as $, invokeTokenAuthorizer as A,
|
|
21358
|
-
//# sourceMappingURL=local-list-
|
|
21775
|
+
export { createJwksCache as $, resolveLambdaArnIntrinsic as $t, invokeTokenAuthorizer as A, substituteAgainstStateAsync as At, VtlEvaluationError as B, resolveCfnRegion as Bt, resolveSelectionExpression as C, architectureToPlatform as Ct, computeRequestIdentityHash as D, resolveRuntimeImage as Dt, buildMethodArn as E, resolveRuntimeFileExtension as Et, buildRestV1Event as F, LocalStateSourceError as Ft, buildMgmtEndpointEnvUrl as G, resolveWatchConfig as Gt, probeHostGatewaySupport as H, CfnLocalStateProvider as Ht, evaluateResponseParameters as I, createLocalStateProvider as It, buildConnectEvent as J, discoverWebSocketApis as Jt, handleConnectionsRequest as K, countTargets as Kt, pickResponseTemplate as L, isCfnFlagPresent as Lt, translateLambdaResponse as M, substituteEnvVarsFromStateAsync as Mt, applyAuthorizerOverlay as N, resolveEnvVars as Nt, evaluateCachedLambdaPolicy as O, EcsTaskResolutionError as Ot, buildHttpApiV2Event as P, materializeLayerFromArn as Pt, buildJwksUrlFromIssuer as Q, pickRefLogicalId as Qt, selectIntegrationResponse as R, rejectExplicitCfnStackWithMultipleStacks as Rt, startApiServer as S, createLocalInvokeCommand as St, defaultCredentialsLoader as T, resolveRuntimeCodeMountPath as Tt, bufferToBody as U, collectSsmParameterRefs as Ut, HOST_GATEWAY_MIN_VERSION as V, resolveCfnStackName as Vt, ConnectionRegistry as W, resolveSsmParameters as Wt, buildMessageEvent as X, parseSelectionExpressionPath as Xt, buildDisconnectEvent as Y, discoverWebSocketApisOrThrow as Yt, buildCognitoJwksUrl as Z, discoverRoutes as Zt, availableApiIdentifiers as _, SUPPORTED_CODE_RUNTIMES as _t, buildCloudMapIndex as a, derivePseudoParametersFromRegion as an, buildCorsConfigByApiId as at, groupRoutesByServer as b, renderCodeDockerfile as bt, createLocalRunTaskCommand as c, LocalInvokeBuildError as cn, matchPreflight as ct, createWatchPredicates as d, MCP_PROTOCOL_VERSION as dt, AGENTCORE_HTTP_PROTOCOL as en, verifyCognitoJwt as et, resolveApiTargetSubset as f, mcpInvokeOnce as ft, buildStageMap as g, waitForAgentCorePing as gt, attachStageContext as h, invokeAgentCore as ht, createLocalStartServiceCommand as i, resolveAgentCoreTarget as in, applyCorsResponseHeaders as it, matchRoute as j, substituteEnvVarsFromState as jt, invokeRequestAuthorizer as k, substituteAgainstState as kt, createLocalInvokeAgentCoreCommand as l, MCP_CONTAINER_PORT as lt, createFileWatcher as m, AGENTCORE_SESSION_ID_HEADER as mt, formatTargetListing as n, AGENTCORE_RUNTIME_TYPE as nn, verifyJwtViaDiscovery as nt, CloudMapRegistry as o, substituteImagePlaceholders as on, buildCorsConfigFromCloudFrontChain as ot, createAuthorizerCache as p, parseSseForJsonRpc as pt, parseConnectionsPath as q, listTargets as qt, createLocalStartAlbCommand as r, AgentCoreResolutionError as rn, attachAuthorizers as rt, getContainerNetworkIp as s, tryResolveImageFnJoin as sn, isFunctionUrlOacFronted as st, createLocalListCommand as t, AGENTCORE_MCP_PROTOCOL as tn, verifyJwtAuthorizer as tt, createLocalStartApiCommand as u, MCP_PATH as ut, filterRoutesByApiIdentifier as v, buildAgentCoreCodeImage as vt, resolveServiceIntegrationParameters as w, buildContainerImage as wt, readMtlsMaterialsFromDisk as x, toCmdArgv as xt, filterRoutesByApiIdentifiers as y, computeCodeImageTag as yt, tryParseStatus as z, resolveCfnFallbackRegion as zt };
|
|
21776
|
+
//# sourceMappingURL=local-list-bwZnI2pV.js.map
|