@cubis/foundry 0.3.35 → 0.3.37
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 +22 -1
- package/bin/cubis.js +466 -10
- package/package.json +1 -1
- package/workflows/skills/postman/SKILL.md +17 -0
- package/workflows/skills/skills_index.json +20 -0
- package/workflows/skills/stitch/SKILL.md +17 -0
- package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/postman/SKILL.md +17 -0
- package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/SKILL.md +17 -0
- package/workflows/workflows/agent-environment-setup/platforms/cursor/skills/postman/SKILL.md +17 -0
- package/workflows/workflows/agent-environment-setup/platforms/cursor/skills/stitch/SKILL.md +17 -0
- package/workflows/workflows/agent-environment-setup/platforms/windsurf/skills/postman/SKILL.md +17 -0
- package/workflows/workflows/agent-environment-setup/platforms/windsurf/skills/stitch/SKILL.md +17 -0
package/README.md
CHANGED
|
@@ -273,9 +273,14 @@ cbx workflows install --platform codex --bundle agent-environment-setup --postma
|
|
|
273
273
|
--mcp-runtime docker \
|
|
274
274
|
--mcp-fallback local \
|
|
275
275
|
--mcp-image ghcr.io/cubis/foundry-mcp:0.1.0 \
|
|
276
|
-
--mcp-update-policy pinned
|
|
276
|
+
--mcp-update-policy pinned \
|
|
277
|
+
--mcp-build-local # optional: build image locally instead of docker pull
|
|
277
278
|
```
|
|
278
279
|
|
|
280
|
+
When `--mcp-runtime docker` is selected and Docker is available, install now prepares the image automatically:
|
|
281
|
+
- Pulls the image by default (`docker pull`)
|
|
282
|
+
- Or builds locally when `--mcp-build-local` is set
|
|
283
|
+
|
|
279
284
|
MCP tool catalog commands:
|
|
280
285
|
|
|
281
286
|
```bash
|
|
@@ -284,6 +289,22 @@ cbx mcp tools list --service postman --scope global
|
|
|
284
289
|
cbx mcp tools list --service stitch --scope global
|
|
285
290
|
```
|
|
286
291
|
|
|
292
|
+
MCP Docker runtime commands:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
# Inspect runtime/container state
|
|
296
|
+
cbx mcp runtime status --scope global --name cbx-mcp
|
|
297
|
+
|
|
298
|
+
# Start runtime container (pull/build image first as needed)
|
|
299
|
+
cbx mcp runtime up --scope global --name cbx-mcp --port 3310
|
|
300
|
+
|
|
301
|
+
# Recreate existing container
|
|
302
|
+
cbx mcp runtime up --scope global --name cbx-mcp --replace
|
|
303
|
+
|
|
304
|
+
# Stop/remove runtime container
|
|
305
|
+
cbx mcp runtime down --name cbx-mcp
|
|
306
|
+
```
|
|
307
|
+
|
|
287
308
|
Docker E2E MCP check (single command):
|
|
288
309
|
|
|
289
310
|
```bash
|
package/bin/cubis.js
CHANGED
|
@@ -151,6 +151,7 @@ const POSTMAN_API_KEY_ENV_VAR = "POSTMAN_API_KEY";
|
|
|
151
151
|
const POSTMAN_MCP_URL = "https://mcp.postman.com/minimal";
|
|
152
152
|
const POSTMAN_API_BASE_URL = "https://api.getpostman.com";
|
|
153
153
|
const POSTMAN_SKILL_ID = "postman";
|
|
154
|
+
const STITCH_SKILL_ID = "stitch";
|
|
154
155
|
const STITCH_MCP_SERVER_ID = "StitchMCP";
|
|
155
156
|
const STITCH_API_KEY_ENV_VAR = "STITCH_API_KEY";
|
|
156
157
|
const STITCH_MCP_URL = "https://stitch.googleapis.com/mcp";
|
|
@@ -167,6 +168,9 @@ const DEFAULT_MCP_RUNTIME = "docker";
|
|
|
167
168
|
const DEFAULT_MCP_FALLBACK = "local";
|
|
168
169
|
const DEFAULT_MCP_UPDATE_POLICY = "pinned";
|
|
169
170
|
const DEFAULT_MCP_DOCKER_IMAGE = "ghcr.io/cubis/foundry-mcp:0.1.0";
|
|
171
|
+
const DEFAULT_MCP_DOCKER_CONTAINER_NAME = "cbx-mcp";
|
|
172
|
+
const DEFAULT_MCP_DOCKER_HOST_PORT = 3310;
|
|
173
|
+
const MCP_DOCKER_CONTAINER_PORT = 3100;
|
|
170
174
|
const TECH_SCAN_MAX_FILES = 5000;
|
|
171
175
|
const TECH_SCAN_IGNORED_DIRS = new Set([
|
|
172
176
|
".git",
|
|
@@ -404,6 +408,139 @@ function normalizeMcpUpdatePolicy(value, fallback = DEFAULT_MCP_UPDATE_POLICY) {
|
|
|
404
408
|
return normalized;
|
|
405
409
|
}
|
|
406
410
|
|
|
411
|
+
function normalizePortNumber(value, fallback = DEFAULT_MCP_DOCKER_HOST_PORT) {
|
|
412
|
+
if (value === undefined || value === null || value === "") return fallback;
|
|
413
|
+
const parsed = Number.parseInt(String(value).trim(), 10);
|
|
414
|
+
if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`Invalid port '${value}'. Use an integer between 1 and 65535.`,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
return parsed;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function parseDockerPsRow(line) {
|
|
423
|
+
const [id, image, status, names] = String(line || "").split("\t");
|
|
424
|
+
if (!id || !names) return null;
|
|
425
|
+
return {
|
|
426
|
+
id: id.trim(),
|
|
427
|
+
image: String(image || "").trim(),
|
|
428
|
+
status: String(status || "").trim(),
|
|
429
|
+
name: String(names || "").trim(),
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async function checkDockerAvailable({ cwd = process.cwd() } = {}) {
|
|
434
|
+
try {
|
|
435
|
+
await execFile("docker", ["ps"], { cwd });
|
|
436
|
+
return true;
|
|
437
|
+
} catch {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function checkDockerImageExists({ image, cwd = process.cwd() }) {
|
|
443
|
+
try {
|
|
444
|
+
await execFile("docker", ["image", "inspect", image], { cwd });
|
|
445
|
+
return true;
|
|
446
|
+
} catch {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async function pullMcpDockerImage({ image, cwd = process.cwd() }) {
|
|
452
|
+
await execFile("docker", ["pull", image], { cwd });
|
|
453
|
+
return {
|
|
454
|
+
action: "pulled",
|
|
455
|
+
image,
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async function buildMcpDockerImageLocal({ image, cwd = process.cwd() }) {
|
|
460
|
+
const dockerContext = path.join(packageRoot(), "mcp");
|
|
461
|
+
if (!(await pathExists(path.join(dockerContext, "Dockerfile")))) {
|
|
462
|
+
throw new Error(`MCP Dockerfile is missing at ${dockerContext}.`);
|
|
463
|
+
}
|
|
464
|
+
await execFile("docker", ["build", "-t", image, dockerContext], { cwd });
|
|
465
|
+
return {
|
|
466
|
+
action: "built-local",
|
|
467
|
+
image,
|
|
468
|
+
context: dockerContext,
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function ensureMcpDockerImage({
|
|
473
|
+
image,
|
|
474
|
+
updatePolicy,
|
|
475
|
+
buildLocal = false,
|
|
476
|
+
cwd = process.cwd(),
|
|
477
|
+
}) {
|
|
478
|
+
if (buildLocal) {
|
|
479
|
+
return buildMcpDockerImageLocal({ image, cwd });
|
|
480
|
+
}
|
|
481
|
+
if (updatePolicy === "pinned") {
|
|
482
|
+
const exists = await checkDockerImageExists({ image, cwd });
|
|
483
|
+
if (exists) {
|
|
484
|
+
return {
|
|
485
|
+
action: "already-present",
|
|
486
|
+
image,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return pullMcpDockerImage({ image, cwd });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async function inspectDockerContainerByName({
|
|
494
|
+
name,
|
|
495
|
+
cwd = process.cwd(),
|
|
496
|
+
}) {
|
|
497
|
+
try {
|
|
498
|
+
const { stdout } = await execFile(
|
|
499
|
+
"docker",
|
|
500
|
+
[
|
|
501
|
+
"ps",
|
|
502
|
+
"-a",
|
|
503
|
+
"--filter",
|
|
504
|
+
`name=^/${name}$`,
|
|
505
|
+
"--format",
|
|
506
|
+
"{{.ID}}\t{{.Image}}\t{{.Status}}\t{{.Names}}",
|
|
507
|
+
],
|
|
508
|
+
{ cwd },
|
|
509
|
+
);
|
|
510
|
+
const row = String(stdout || "")
|
|
511
|
+
.trim()
|
|
512
|
+
.split(/\r?\n/)
|
|
513
|
+
.map((line) => parseDockerPsRow(line))
|
|
514
|
+
.find(Boolean);
|
|
515
|
+
if (!row || row.name !== name) return null;
|
|
516
|
+
return row;
|
|
517
|
+
} catch {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async function resolveDockerContainerHostPort({
|
|
523
|
+
name,
|
|
524
|
+
containerPort = MCP_DOCKER_CONTAINER_PORT,
|
|
525
|
+
cwd = process.cwd(),
|
|
526
|
+
}) {
|
|
527
|
+
try {
|
|
528
|
+
const { stdout } = await execFile(
|
|
529
|
+
"docker",
|
|
530
|
+
["port", name, `${containerPort}/tcp`],
|
|
531
|
+
{ cwd },
|
|
532
|
+
);
|
|
533
|
+
const line = String(stdout || "")
|
|
534
|
+
.trim()
|
|
535
|
+
.split(/\r?\n/)[0];
|
|
536
|
+
if (!line) return null;
|
|
537
|
+
const match = line.match(/:(\d+)\s*$/);
|
|
538
|
+
return match ? Number.parseInt(match[1], 10) : null;
|
|
539
|
+
} catch {
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
407
544
|
function isPathInsideRoot(targetPath, rootPath) {
|
|
408
545
|
const target = path.resolve(targetPath);
|
|
409
546
|
const root = path.resolve(rootPath);
|
|
@@ -3803,7 +3940,8 @@ async function resolvePostmanInstallSelection({
|
|
|
3803
3940
|
);
|
|
3804
3941
|
}
|
|
3805
3942
|
|
|
3806
|
-
const
|
|
3943
|
+
const stitchRequested = Boolean(options.stitch);
|
|
3944
|
+
const enabled = Boolean(options.postman) || hasWorkspaceOption || stitchRequested;
|
|
3807
3945
|
if (!enabled) return { enabled: false };
|
|
3808
3946
|
|
|
3809
3947
|
const envApiKey = normalizePostmanApiKey(process.env[POSTMAN_API_KEY_ENV_VAR]);
|
|
@@ -3816,7 +3954,7 @@ async function resolvePostmanInstallSelection({
|
|
|
3816
3954
|
: null;
|
|
3817
3955
|
let mcpScope = requestedMcpScope || normalizeMcpScope(scope, "project");
|
|
3818
3956
|
const warnings = [];
|
|
3819
|
-
const stitchEnabled = platform === "antigravity";
|
|
3957
|
+
const stitchEnabled = platform === "antigravity" || stitchRequested;
|
|
3820
3958
|
const envStitchApiKey = normalizePostmanApiKey(
|
|
3821
3959
|
process.env[STITCH_API_KEY_ENV_VAR],
|
|
3822
3960
|
);
|
|
@@ -3834,6 +3972,7 @@ async function resolvePostmanInstallSelection({
|
|
|
3834
3972
|
options.mcpUpdatePolicy,
|
|
3835
3973
|
DEFAULT_MCP_UPDATE_POLICY,
|
|
3836
3974
|
);
|
|
3975
|
+
const mcpBuildLocal = Boolean(options.mcpBuildLocal);
|
|
3837
3976
|
const mcpToolSync = options.mcpToolSync !== false;
|
|
3838
3977
|
|
|
3839
3978
|
const canPrompt = !options.yes && process.stdin.isTTY;
|
|
@@ -3925,14 +4064,9 @@ async function resolvePostmanInstallSelection({
|
|
|
3925
4064
|
|
|
3926
4065
|
let effectiveRuntime = requestedRuntime;
|
|
3927
4066
|
let runtimeSkipped = false;
|
|
4067
|
+
let dockerImageAction = "not-requested";
|
|
3928
4068
|
if (requestedRuntime === "docker") {
|
|
3929
|
-
|
|
3930
|
-
try {
|
|
3931
|
-
await execFile("docker", ["ps"], { cwd });
|
|
3932
|
-
dockerAvailable = true;
|
|
3933
|
-
} catch {
|
|
3934
|
-
dockerAvailable = false;
|
|
3935
|
-
}
|
|
4069
|
+
const dockerAvailable = await checkDockerAvailable({ cwd });
|
|
3936
4070
|
if (!dockerAvailable) {
|
|
3937
4071
|
if (requestedFallback === "fail") {
|
|
3938
4072
|
throw new Error(
|
|
@@ -3950,6 +4084,35 @@ async function resolvePostmanInstallSelection({
|
|
|
3950
4084
|
"Docker runtime unavailable; falling back to local runtime (--mcp-fallback=local).",
|
|
3951
4085
|
);
|
|
3952
4086
|
}
|
|
4087
|
+
} else {
|
|
4088
|
+
try {
|
|
4089
|
+
const ensured = await ensureMcpDockerImage({
|
|
4090
|
+
image: requestedImage,
|
|
4091
|
+
updatePolicy: requestedUpdatePolicy,
|
|
4092
|
+
buildLocal: mcpBuildLocal,
|
|
4093
|
+
cwd,
|
|
4094
|
+
});
|
|
4095
|
+
dockerImageAction = ensured.action;
|
|
4096
|
+
} catch (error) {
|
|
4097
|
+
if (requestedFallback === "fail") {
|
|
4098
|
+
throw new Error(
|
|
4099
|
+
`Docker runtime requested but MCP image preparation failed (${error.message}).`,
|
|
4100
|
+
);
|
|
4101
|
+
}
|
|
4102
|
+
if (requestedFallback === "skip") {
|
|
4103
|
+
runtimeSkipped = true;
|
|
4104
|
+
dockerImageAction = "failed";
|
|
4105
|
+
warnings.push(
|
|
4106
|
+
`MCP Docker image preparation failed; skipping MCP runtime patch because --mcp-fallback=skip. (${error.message})`,
|
|
4107
|
+
);
|
|
4108
|
+
} else {
|
|
4109
|
+
effectiveRuntime = "local";
|
|
4110
|
+
dockerImageAction = "failed";
|
|
4111
|
+
warnings.push(
|
|
4112
|
+
`MCP Docker image preparation failed; falling back to local runtime (--mcp-fallback=local). (${error.message})`,
|
|
4113
|
+
);
|
|
4114
|
+
}
|
|
4115
|
+
}
|
|
3953
4116
|
}
|
|
3954
4117
|
}
|
|
3955
4118
|
|
|
@@ -3991,6 +4154,7 @@ async function resolvePostmanInstallSelection({
|
|
|
3991
4154
|
docker: {
|
|
3992
4155
|
image: requestedImage,
|
|
3993
4156
|
updatePolicy: requestedUpdatePolicy,
|
|
4157
|
+
buildLocal: mcpBuildLocal,
|
|
3994
4158
|
},
|
|
3995
4159
|
catalog: {
|
|
3996
4160
|
toolSync: mcpToolSync,
|
|
@@ -4034,6 +4198,8 @@ async function resolvePostmanInstallSelection({
|
|
|
4034
4198
|
mcpFallback: requestedFallback,
|
|
4035
4199
|
mcpImage: requestedImage,
|
|
4036
4200
|
mcpUpdatePolicy: requestedUpdatePolicy,
|
|
4201
|
+
mcpBuildLocal,
|
|
4202
|
+
dockerImageAction,
|
|
4037
4203
|
mcpToolSync,
|
|
4038
4204
|
runtimeSkipped,
|
|
4039
4205
|
defaultWorkspaceId: defaultWorkspaceId ?? null,
|
|
@@ -4299,6 +4465,8 @@ async function configurePostmanInstallArtifacts({
|
|
|
4299
4465
|
mcpFallback: postmanSelection.mcpFallback,
|
|
4300
4466
|
mcpImage: postmanSelection.mcpImage,
|
|
4301
4467
|
mcpUpdatePolicy: postmanSelection.mcpUpdatePolicy,
|
|
4468
|
+
mcpBuildLocal: postmanSelection.mcpBuildLocal,
|
|
4469
|
+
dockerImageAction: postmanSelection.dockerImageAction,
|
|
4302
4470
|
mcpToolSync: postmanSelection.mcpToolSync,
|
|
4303
4471
|
apiKeySource: effectiveApiKeySource,
|
|
4304
4472
|
stitchApiKeySource: effectiveStitchApiKeySource,
|
|
@@ -5252,6 +5420,8 @@ function printPostmanSetupSummary({ postmanSetup }) {
|
|
|
5252
5420
|
console.log(`- MCP fallback: ${postmanSetup.mcpFallback}`);
|
|
5253
5421
|
console.log(`- MCP image: ${postmanSetup.mcpImage}`);
|
|
5254
5422
|
console.log(`- MCP update policy: ${postmanSetup.mcpUpdatePolicy}`);
|
|
5423
|
+
console.log(`- MCP build local: ${postmanSetup.mcpBuildLocal ? "yes" : "no"}`);
|
|
5424
|
+
console.log(`- MCP image prepare: ${postmanSetup.dockerImageAction}`);
|
|
5255
5425
|
console.log(`- MCP tool sync: ${postmanSetup.mcpToolSync ? "enabled" : "disabled"}`);
|
|
5256
5426
|
console.log(`- Postman API key source: ${postmanSetup.apiKeySource}`);
|
|
5257
5427
|
if (postmanSetup.stitchApiKeySource) {
|
|
@@ -5721,6 +5891,10 @@ function withInstallOptions(command) {
|
|
|
5721
5891
|
"--postman",
|
|
5722
5892
|
"optional: install Postman skill and generate cbx_config.json",
|
|
5723
5893
|
)
|
|
5894
|
+
.option(
|
|
5895
|
+
"--stitch",
|
|
5896
|
+
"optional: include Stitch MCP profile/config alongside Postman",
|
|
5897
|
+
)
|
|
5724
5898
|
.option(
|
|
5725
5899
|
"--postman-api-key <key>",
|
|
5726
5900
|
"deprecated: inline key mode is disabled. Use env vars + profiles.",
|
|
@@ -5757,6 +5931,14 @@ function withInstallOptions(command) {
|
|
|
5757
5931
|
"MCP image update policy: pinned|latest",
|
|
5758
5932
|
DEFAULT_MCP_UPDATE_POLICY,
|
|
5759
5933
|
)
|
|
5934
|
+
.option(
|
|
5935
|
+
"--mcp-build-local",
|
|
5936
|
+
"build MCP Docker image from local package mcp/ directory instead of pulling",
|
|
5937
|
+
)
|
|
5938
|
+
.option(
|
|
5939
|
+
"--mcp-tool-sync",
|
|
5940
|
+
"enable automatic MCP tool catalog sync (default: enabled)",
|
|
5941
|
+
)
|
|
5760
5942
|
.option("--no-mcp-tool-sync", "disable automatic MCP tool catalog sync")
|
|
5761
5943
|
.option(
|
|
5762
5944
|
"--terminal-integration",
|
|
@@ -6079,7 +6261,11 @@ async function runWorkflowInstall(options) {
|
|
|
6079
6261
|
scope,
|
|
6080
6262
|
overwrite: Boolean(options.overwrite),
|
|
6081
6263
|
profilePathsOverride: artifactProfilePaths,
|
|
6082
|
-
extraSkillIds: postmanSelection.enabled
|
|
6264
|
+
extraSkillIds: postmanSelection.enabled
|
|
6265
|
+
? postmanSelection.stitchEnabled
|
|
6266
|
+
? [POSTMAN_SKILL_ID, STITCH_SKILL_ID]
|
|
6267
|
+
: [POSTMAN_SKILL_ID]
|
|
6268
|
+
: [],
|
|
6083
6269
|
skillProfile: skillInstallOptions.skillProfile,
|
|
6084
6270
|
terminalVerifierSelection,
|
|
6085
6271
|
dryRun,
|
|
@@ -7715,6 +7901,220 @@ async function runMcpToolsList(options) {
|
|
|
7715
7901
|
}
|
|
7716
7902
|
}
|
|
7717
7903
|
|
|
7904
|
+
function resolveCbxRootPath({ scope, cwd = process.cwd() }) {
|
|
7905
|
+
if (scope === "global") {
|
|
7906
|
+
return path.join(os.homedir(), ".cbx");
|
|
7907
|
+
}
|
|
7908
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
7909
|
+
return path.join(workspaceRoot, ".cbx");
|
|
7910
|
+
}
|
|
7911
|
+
|
|
7912
|
+
function resolveMcpRuntimeDefaultsFromConfig(configValue) {
|
|
7913
|
+
const mcp =
|
|
7914
|
+
configValue && typeof configValue.mcp === "object" && !Array.isArray(configValue.mcp)
|
|
7915
|
+
? configValue.mcp
|
|
7916
|
+
: {};
|
|
7917
|
+
const docker =
|
|
7918
|
+
mcp && typeof mcp.docker === "object" && !Array.isArray(mcp.docker)
|
|
7919
|
+
? mcp.docker
|
|
7920
|
+
: {};
|
|
7921
|
+
|
|
7922
|
+
const runtime = normalizeMcpRuntime(mcp.runtime, DEFAULT_MCP_RUNTIME);
|
|
7923
|
+
const fallback = normalizeMcpFallback(mcp.fallback, DEFAULT_MCP_FALLBACK);
|
|
7924
|
+
const image =
|
|
7925
|
+
normalizePostmanApiKey(docker.image) || DEFAULT_MCP_DOCKER_IMAGE;
|
|
7926
|
+
const updatePolicy = normalizeMcpUpdatePolicy(
|
|
7927
|
+
docker.updatePolicy,
|
|
7928
|
+
DEFAULT_MCP_UPDATE_POLICY,
|
|
7929
|
+
);
|
|
7930
|
+
const buildLocal = Boolean(docker.buildLocal);
|
|
7931
|
+
|
|
7932
|
+
return {
|
|
7933
|
+
runtime,
|
|
7934
|
+
fallback,
|
|
7935
|
+
image,
|
|
7936
|
+
updatePolicy,
|
|
7937
|
+
buildLocal,
|
|
7938
|
+
};
|
|
7939
|
+
}
|
|
7940
|
+
|
|
7941
|
+
async function loadMcpRuntimeDefaults({ scope, cwd = process.cwd() }) {
|
|
7942
|
+
const configPath = resolveCbxConfigPath({ scope, cwd });
|
|
7943
|
+
const existing = await readJsonFileIfExists(configPath);
|
|
7944
|
+
const configValue =
|
|
7945
|
+
existing.value &&
|
|
7946
|
+
typeof existing.value === "object" &&
|
|
7947
|
+
!Array.isArray(existing.value)
|
|
7948
|
+
? existing.value
|
|
7949
|
+
: {};
|
|
7950
|
+
return {
|
|
7951
|
+
configPath,
|
|
7952
|
+
defaults: resolveMcpRuntimeDefaultsFromConfig(configValue),
|
|
7953
|
+
hasConfig: existing.exists,
|
|
7954
|
+
};
|
|
7955
|
+
}
|
|
7956
|
+
|
|
7957
|
+
async function runMcpRuntimeStatus(options) {
|
|
7958
|
+
try {
|
|
7959
|
+
const opts = resolveActionOptions(options);
|
|
7960
|
+
const cwd = process.cwd();
|
|
7961
|
+
const scope = normalizeMcpScope(opts.scope, "global");
|
|
7962
|
+
const defaults = await loadMcpRuntimeDefaults({ scope, cwd });
|
|
7963
|
+
const containerName =
|
|
7964
|
+
normalizePostmanApiKey(opts.name) || DEFAULT_MCP_DOCKER_CONTAINER_NAME;
|
|
7965
|
+
const dockerAvailable = await checkDockerAvailable({ cwd });
|
|
7966
|
+
const container = dockerAvailable
|
|
7967
|
+
? await inspectDockerContainerByName({ name: containerName, cwd })
|
|
7968
|
+
: null;
|
|
7969
|
+
|
|
7970
|
+
console.log(`Scope: ${scope}`);
|
|
7971
|
+
console.log(`Config file: ${defaults.configPath}`);
|
|
7972
|
+
console.log(`Config present: ${defaults.hasConfig ? "yes" : "no"}`);
|
|
7973
|
+
console.log(`Configured runtime: ${defaults.defaults.runtime}`);
|
|
7974
|
+
console.log(`Configured fallback: ${defaults.defaults.fallback}`);
|
|
7975
|
+
console.log(`Configured image: ${defaults.defaults.image}`);
|
|
7976
|
+
console.log(`Configured update policy: ${defaults.defaults.updatePolicy}`);
|
|
7977
|
+
console.log(`Configured build local: ${defaults.defaults.buildLocal ? "yes" : "no"}`);
|
|
7978
|
+
console.log(`Docker available: ${dockerAvailable ? "yes" : "no"}`);
|
|
7979
|
+
console.log(`Container name: ${containerName}`);
|
|
7980
|
+
if (!dockerAvailable) {
|
|
7981
|
+
console.log("Container status: unavailable (docker not reachable)");
|
|
7982
|
+
return;
|
|
7983
|
+
}
|
|
7984
|
+
if (!container) {
|
|
7985
|
+
console.log("Container status: not found");
|
|
7986
|
+
return;
|
|
7987
|
+
}
|
|
7988
|
+
const isRunning = container.status.toLowerCase().startsWith("up ");
|
|
7989
|
+
console.log(`Container status: ${container.status}`);
|
|
7990
|
+
console.log(`Container image: ${container.image}`);
|
|
7991
|
+
if (isRunning) {
|
|
7992
|
+
const hostPort =
|
|
7993
|
+
(await resolveDockerContainerHostPort({
|
|
7994
|
+
name: containerName,
|
|
7995
|
+
containerPort: MCP_DOCKER_CONTAINER_PORT,
|
|
7996
|
+
cwd,
|
|
7997
|
+
})) || DEFAULT_MCP_DOCKER_HOST_PORT;
|
|
7998
|
+
console.log(
|
|
7999
|
+
`Endpoint: http://127.0.0.1:${hostPort}/mcp`,
|
|
8000
|
+
);
|
|
8001
|
+
}
|
|
8002
|
+
} catch (error) {
|
|
8003
|
+
console.error(`\nError: ${error.message}`);
|
|
8004
|
+
process.exit(1);
|
|
8005
|
+
}
|
|
8006
|
+
}
|
|
8007
|
+
|
|
8008
|
+
async function runMcpRuntimeUp(options) {
|
|
8009
|
+
try {
|
|
8010
|
+
const opts = resolveActionOptions(options);
|
|
8011
|
+
const cwd = process.cwd();
|
|
8012
|
+
const scope = normalizeMcpScope(opts.scope, "global");
|
|
8013
|
+
const defaults = await loadMcpRuntimeDefaults({ scope, cwd });
|
|
8014
|
+
const containerName =
|
|
8015
|
+
normalizePostmanApiKey(opts.name) || DEFAULT_MCP_DOCKER_CONTAINER_NAME;
|
|
8016
|
+
const image =
|
|
8017
|
+
normalizePostmanApiKey(opts.image) || defaults.defaults.image;
|
|
8018
|
+
const updatePolicy = normalizeMcpUpdatePolicy(
|
|
8019
|
+
opts.updatePolicy,
|
|
8020
|
+
defaults.defaults.updatePolicy,
|
|
8021
|
+
);
|
|
8022
|
+
const buildLocal = hasCliFlag("--build-local")
|
|
8023
|
+
? true
|
|
8024
|
+
: defaults.defaults.buildLocal;
|
|
8025
|
+
const hostPort = normalizePortNumber(opts.port, DEFAULT_MCP_DOCKER_HOST_PORT);
|
|
8026
|
+
const replace = Boolean(opts.replace);
|
|
8027
|
+
const dockerAvailable = await checkDockerAvailable({ cwd });
|
|
8028
|
+
if (!dockerAvailable) {
|
|
8029
|
+
throw new Error("Docker is unavailable. Start OrbStack/Docker and retry.");
|
|
8030
|
+
}
|
|
8031
|
+
|
|
8032
|
+
const prepared = await ensureMcpDockerImage({
|
|
8033
|
+
image,
|
|
8034
|
+
updatePolicy,
|
|
8035
|
+
buildLocal,
|
|
8036
|
+
cwd,
|
|
8037
|
+
});
|
|
8038
|
+
|
|
8039
|
+
const existing = await inspectDockerContainerByName({
|
|
8040
|
+
name: containerName,
|
|
8041
|
+
cwd,
|
|
8042
|
+
});
|
|
8043
|
+
if (existing && !replace) {
|
|
8044
|
+
throw new Error(
|
|
8045
|
+
`Container '${containerName}' already exists (${existing.status}). Use --replace to recreate it.`,
|
|
8046
|
+
);
|
|
8047
|
+
}
|
|
8048
|
+
if (existing && replace) {
|
|
8049
|
+
await execFile("docker", ["rm", "-f", containerName], { cwd });
|
|
8050
|
+
}
|
|
8051
|
+
|
|
8052
|
+
const cbxRoot = resolveCbxRootPath({ scope, cwd });
|
|
8053
|
+
await mkdir(cbxRoot, { recursive: true });
|
|
8054
|
+
await execFile(
|
|
8055
|
+
"docker",
|
|
8056
|
+
[
|
|
8057
|
+
"run",
|
|
8058
|
+
"-d",
|
|
8059
|
+
"--name",
|
|
8060
|
+
containerName,
|
|
8061
|
+
"-p",
|
|
8062
|
+
`${hostPort}:${MCP_DOCKER_CONTAINER_PORT}`,
|
|
8063
|
+
"-v",
|
|
8064
|
+
`${cbxRoot}:/root/.cbx`,
|
|
8065
|
+
"-e",
|
|
8066
|
+
"CBX_MCP_TRANSPORT=streamable-http",
|
|
8067
|
+
image,
|
|
8068
|
+
],
|
|
8069
|
+
{ cwd },
|
|
8070
|
+
);
|
|
8071
|
+
|
|
8072
|
+
const running = await inspectDockerContainerByName({
|
|
8073
|
+
name: containerName,
|
|
8074
|
+
cwd,
|
|
8075
|
+
});
|
|
8076
|
+
console.log(`Scope: ${scope}`);
|
|
8077
|
+
console.log(`Container: ${containerName}`);
|
|
8078
|
+
console.log(`Image: ${image}`);
|
|
8079
|
+
console.log(`Image prepare: ${prepared.action}`);
|
|
8080
|
+
console.log(`Update policy: ${updatePolicy}`);
|
|
8081
|
+
console.log(`Build local: ${buildLocal ? "yes" : "no"}`);
|
|
8082
|
+
console.log(`Mount: ${cbxRoot} -> /root/.cbx`);
|
|
8083
|
+
console.log(`Port: ${hostPort}:${MCP_DOCKER_CONTAINER_PORT}`);
|
|
8084
|
+
console.log(`Status: ${running ? running.status : "started"}`);
|
|
8085
|
+
console.log(`Endpoint: http://127.0.0.1:${hostPort}/mcp`);
|
|
8086
|
+
} catch (error) {
|
|
8087
|
+
console.error(`\nError: ${error.message}`);
|
|
8088
|
+
process.exit(1);
|
|
8089
|
+
}
|
|
8090
|
+
}
|
|
8091
|
+
|
|
8092
|
+
async function runMcpRuntimeDown(options) {
|
|
8093
|
+
try {
|
|
8094
|
+
const opts = resolveActionOptions(options);
|
|
8095
|
+
const cwd = process.cwd();
|
|
8096
|
+
const containerName =
|
|
8097
|
+
normalizePostmanApiKey(opts.name) || DEFAULT_MCP_DOCKER_CONTAINER_NAME;
|
|
8098
|
+
const dockerAvailable = await checkDockerAvailable({ cwd });
|
|
8099
|
+
if (!dockerAvailable) {
|
|
8100
|
+
throw new Error("Docker is unavailable. Start OrbStack/Docker and retry.");
|
|
8101
|
+
}
|
|
8102
|
+
const existing = await inspectDockerContainerByName({
|
|
8103
|
+
name: containerName,
|
|
8104
|
+
cwd,
|
|
8105
|
+
});
|
|
8106
|
+
if (!existing) {
|
|
8107
|
+
console.log(`Container '${containerName}' is not present.`);
|
|
8108
|
+
return;
|
|
8109
|
+
}
|
|
8110
|
+
await execFile("docker", ["rm", "-f", containerName], { cwd });
|
|
8111
|
+
console.log(`Removed container '${containerName}'.`);
|
|
8112
|
+
} catch (error) {
|
|
8113
|
+
console.error(`\nError: ${error.message}`);
|
|
8114
|
+
process.exit(1);
|
|
8115
|
+
}
|
|
8116
|
+
}
|
|
8117
|
+
|
|
7718
8118
|
function printRulesInitSummary({
|
|
7719
8119
|
platform,
|
|
7720
8120
|
scope,
|
|
@@ -8158,6 +8558,62 @@ mcpToolsCommand
|
|
|
8158
8558
|
)
|
|
8159
8559
|
.action(runMcpToolsList);
|
|
8160
8560
|
|
|
8561
|
+
const mcpRuntimeCommand = mcpCommand
|
|
8562
|
+
.command("runtime")
|
|
8563
|
+
.description("Manage local Docker runtime container for Cubis MCP gateway");
|
|
8564
|
+
|
|
8565
|
+
mcpRuntimeCommand
|
|
8566
|
+
.command("status")
|
|
8567
|
+
.description("Show Docker runtime status and configured MCP runtime defaults")
|
|
8568
|
+
.option(
|
|
8569
|
+
"--scope <scope>",
|
|
8570
|
+
"config scope: project|workspace|global|user",
|
|
8571
|
+
"global",
|
|
8572
|
+
)
|
|
8573
|
+
.option(
|
|
8574
|
+
"--name <name>",
|
|
8575
|
+
"container name",
|
|
8576
|
+
DEFAULT_MCP_DOCKER_CONTAINER_NAME,
|
|
8577
|
+
)
|
|
8578
|
+
.action(runMcpRuntimeStatus);
|
|
8579
|
+
|
|
8580
|
+
mcpRuntimeCommand
|
|
8581
|
+
.command("up")
|
|
8582
|
+
.description("Start Docker runtime container for Cubis MCP gateway")
|
|
8583
|
+
.option(
|
|
8584
|
+
"--scope <scope>",
|
|
8585
|
+
"config scope: project|workspace|global|user",
|
|
8586
|
+
"global",
|
|
8587
|
+
)
|
|
8588
|
+
.option(
|
|
8589
|
+
"--name <name>",
|
|
8590
|
+
"container name",
|
|
8591
|
+
DEFAULT_MCP_DOCKER_CONTAINER_NAME,
|
|
8592
|
+
)
|
|
8593
|
+
.option("--image <image:tag>", "docker image to run")
|
|
8594
|
+
.option("--update-policy <policy>", "pinned|latest")
|
|
8595
|
+
.option(
|
|
8596
|
+
"--build-local",
|
|
8597
|
+
"build MCP Docker image from local package mcp/ directory instead of pulling",
|
|
8598
|
+
)
|
|
8599
|
+
.option("--port <port>", "host port to map to container :3100")
|
|
8600
|
+
.option("--replace", "remove existing container with same name before start")
|
|
8601
|
+
.action(runMcpRuntimeUp);
|
|
8602
|
+
|
|
8603
|
+
mcpRuntimeCommand
|
|
8604
|
+
.command("down")
|
|
8605
|
+
.description("Stop and remove Docker runtime container")
|
|
8606
|
+
.option(
|
|
8607
|
+
"--name <name>",
|
|
8608
|
+
"container name",
|
|
8609
|
+
DEFAULT_MCP_DOCKER_CONTAINER_NAME,
|
|
8610
|
+
)
|
|
8611
|
+
.action(runMcpRuntimeDown);
|
|
8612
|
+
|
|
8613
|
+
mcpRuntimeCommand.action(() => {
|
|
8614
|
+
mcpRuntimeCommand.help();
|
|
8615
|
+
});
|
|
8616
|
+
|
|
8161
8617
|
mcpCommand.action(() => {
|
|
8162
8618
|
mcpCommand.help();
|
|
8163
8619
|
});
|
package/package.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: postman
|
|
3
|
+
description: Automate API testing and collection management with Postman MCP. Use for workspace, collection, environment, and mock operations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Postman MCP
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to work with Postman through MCP tools.
|
|
9
|
+
|
|
10
|
+
## Required Environment Variables
|
|
11
|
+
|
|
12
|
+
- `POSTMAN_API_KEY_<PROFILE>` for authenticated Postman access.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
- Use environment variables for secrets. Do not inline API keys.
|
|
17
|
+
- Prefer tool discovery (`getEnabledTools`) before making assumptions about available tool sets.
|
|
@@ -741,6 +741,16 @@
|
|
|
741
741
|
"visual testing"
|
|
742
742
|
]
|
|
743
743
|
},
|
|
744
|
+
{
|
|
745
|
+
"id": "postman",
|
|
746
|
+
"name": "postman",
|
|
747
|
+
"canonical_id": "postman",
|
|
748
|
+
"deprecated": false,
|
|
749
|
+
"replaced_by": null,
|
|
750
|
+
"path": ".agents/skills/postman/SKILL.md",
|
|
751
|
+
"description": "Automate API testing and collection management with Postman MCP. Use for workspace, collection, environment, and mock operations.",
|
|
752
|
+
"triggers": []
|
|
753
|
+
},
|
|
744
754
|
{
|
|
745
755
|
"id": "powershell-windows",
|
|
746
756
|
"name": "powershell-windows",
|
|
@@ -980,6 +990,16 @@
|
|
|
980
990
|
"description": "Runs CodeQL-based static security analysis (database build, query pack selection, and SARIF results) for vulnerability discovery and audits. Not for custom QL authoring or CI/CD setup.",
|
|
981
991
|
"triggers": []
|
|
982
992
|
},
|
|
993
|
+
{
|
|
994
|
+
"id": "stitch",
|
|
995
|
+
"name": "stitch",
|
|
996
|
+
"canonical_id": "stitch",
|
|
997
|
+
"deprecated": false,
|
|
998
|
+
"replaced_by": null,
|
|
999
|
+
"path": ".agents/skills/stitch/SKILL.md",
|
|
1000
|
+
"description": "Automate Stitch workflows through Stitch MCP using mcp-remote transport.",
|
|
1001
|
+
"triggers": []
|
|
1002
|
+
},
|
|
983
1003
|
{
|
|
984
1004
|
"id": "stripe-best-practices",
|
|
985
1005
|
"name": "stripe-best-practices",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stitch
|
|
3
|
+
description: Automate Stitch workflows through Stitch MCP using mcp-remote transport.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stitch MCP
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to call Stitch tools through MCP passthrough.
|
|
9
|
+
|
|
10
|
+
## Required Environment Variables
|
|
11
|
+
|
|
12
|
+
- `STITCH_API_KEY_<PROFILE>` for Stitch API-key mode.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
- Stitch transport uses `mcp-remote` to `https://stitch.googleapis.com/mcp`.
|
|
17
|
+
- Inject `X-Goog-Api-Key` from environment at runtime. Do not store raw keys in config files.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: postman
|
|
3
|
+
description: Automate API testing and collection management with Postman MCP. Use for workspace, collection, environment, and mock operations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Postman MCP
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to work with Postman through MCP tools.
|
|
9
|
+
|
|
10
|
+
## Required Environment Variables
|
|
11
|
+
|
|
12
|
+
- `POSTMAN_API_KEY_<PROFILE>` for authenticated Postman access.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
- Use environment variables for secrets. Do not inline API keys.
|
|
17
|
+
- Prefer tool discovery (`getEnabledTools`) before making assumptions about available tool sets.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stitch
|
|
3
|
+
description: Automate Stitch workflows through Stitch MCP using mcp-remote transport.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stitch MCP
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to call Stitch tools through MCP passthrough.
|
|
9
|
+
|
|
10
|
+
## Required Environment Variables
|
|
11
|
+
|
|
12
|
+
- `STITCH_API_KEY_<PROFILE>` for Stitch API-key mode.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
- Stitch transport uses `mcp-remote` to `https://stitch.googleapis.com/mcp`.
|
|
17
|
+
- Inject `X-Goog-Api-Key` from environment at runtime. Do not store raw keys in config files.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: postman
|
|
3
|
+
description: Automate API testing and collection management with Postman MCP. Use for workspace, collection, environment, and mock operations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Postman MCP
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to work with Postman through MCP tools.
|
|
9
|
+
|
|
10
|
+
## Required Environment Variables
|
|
11
|
+
|
|
12
|
+
- `POSTMAN_API_KEY_<PROFILE>` for authenticated Postman access.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
- Use environment variables for secrets. Do not inline API keys.
|
|
17
|
+
- Prefer tool discovery (`getEnabledTools`) before making assumptions about available tool sets.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stitch
|
|
3
|
+
description: Automate Stitch workflows through Stitch MCP using mcp-remote transport.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stitch MCP
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to call Stitch tools through MCP passthrough.
|
|
9
|
+
|
|
10
|
+
## Required Environment Variables
|
|
11
|
+
|
|
12
|
+
- `STITCH_API_KEY_<PROFILE>` for Stitch API-key mode.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
- Stitch transport uses `mcp-remote` to `https://stitch.googleapis.com/mcp`.
|
|
17
|
+
- Inject `X-Goog-Api-Key` from environment at runtime. Do not store raw keys in config files.
|
package/workflows/workflows/agent-environment-setup/platforms/windsurf/skills/postman/SKILL.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: postman
|
|
3
|
+
description: Automate API testing and collection management with Postman MCP. Use for workspace, collection, environment, and mock operations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Postman MCP
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to work with Postman through MCP tools.
|
|
9
|
+
|
|
10
|
+
## Required Environment Variables
|
|
11
|
+
|
|
12
|
+
- `POSTMAN_API_KEY_<PROFILE>` for authenticated Postman access.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
- Use environment variables for secrets. Do not inline API keys.
|
|
17
|
+
- Prefer tool discovery (`getEnabledTools`) before making assumptions about available tool sets.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stitch
|
|
3
|
+
description: Automate Stitch workflows through Stitch MCP using mcp-remote transport.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Stitch MCP
|
|
7
|
+
|
|
8
|
+
Use this skill when you need to call Stitch tools through MCP passthrough.
|
|
9
|
+
|
|
10
|
+
## Required Environment Variables
|
|
11
|
+
|
|
12
|
+
- `STITCH_API_KEY_<PROFILE>` for Stitch API-key mode.
|
|
13
|
+
|
|
14
|
+
## Notes
|
|
15
|
+
|
|
16
|
+
- Stitch transport uses `mcp-remote` to `https://stitch.googleapis.com/mcp`.
|
|
17
|
+
- Inject `X-Goog-Api-Key` from environment at runtime. Do not store raw keys in config files.
|