@cubis/foundry 0.3.40 → 0.3.41
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 +64 -0
- package/bin/cubis.js +347 -22
- package/mcp/README.md +72 -8
- package/mcp/config.json +3 -0
- package/mcp/dist/index.js +315 -68
- package/mcp/src/config/index.test.ts +1 -0
- package/mcp/src/config/schema.ts +5 -0
- package/mcp/src/index.ts +40 -9
- package/mcp/src/server.ts +66 -10
- package/mcp/src/telemetry/tokenBudget.ts +114 -0
- package/mcp/src/tools/index.ts +7 -0
- package/mcp/src/tools/skillBrowseCategory.ts +22 -5
- package/mcp/src/tools/skillBudgetReport.ts +128 -0
- package/mcp/src/tools/skillGet.ts +18 -0
- package/mcp/src/tools/skillListCategories.ts +19 -6
- package/mcp/src/tools/skillSearch.ts +22 -5
- package/mcp/src/tools/skillTools.test.ts +61 -9
- package/mcp/src/vault/manifest.test.ts +19 -1
- package/mcp/src/vault/manifest.ts +12 -1
- package/mcp/src/vault/scanner.test.ts +1 -0
- package/mcp/src/vault/scanner.ts +1 -0
- package/mcp/src/vault/types.ts +6 -0
- package/package.json +1 -1
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/rules/GEMINI.md +28 -0
- package/workflows/workflows/agent-environment-setup/platforms/codex/rules/AGENTS.md +31 -2
- package/workflows/workflows/agent-environment-setup/platforms/copilot/rules/AGENTS.md +28 -0
- package/workflows/workflows/agent-environment-setup/platforms/copilot/rules/copilot-instructions.md +28 -0
- package/workflows/workflows/agent-environment-setup/platforms/cursor/rules/.cursorrules +28 -0
- package/workflows/workflows/agent-environment-setup/platforms/windsurf/rules/.windsurfrules +28 -0
package/README.md
CHANGED
|
@@ -193,6 +193,17 @@ cbx workflows install --platform codex --bundle agent-environment-setup --postma
|
|
|
193
193
|
|
|
194
194
|
If active Postman env var (for example `POSTMAN_API_KEY_DEFAULT`) is available and `--yes` is not used, installer can show workspace chooser and save selected `workspaceId` in active Postman profile.
|
|
195
195
|
|
|
196
|
+
`--postman` now installs side-by-side MCP topology by default:
|
|
197
|
+
- direct Postman MCP server (`postman`)
|
|
198
|
+
- direct Stitch MCP server where applicable (`StitchMCP` for Antigravity)
|
|
199
|
+
- local Foundry MCP command server (`cubis-foundry` via `cbx mcp serve --transport stdio --scope auto`)
|
|
200
|
+
|
|
201
|
+
To opt out of Foundry MCP registration during install:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
cbx workflows install --platform codex --bundle agent-environment-setup --postman --no-foundry-mcp
|
|
205
|
+
```
|
|
206
|
+
|
|
196
207
|
### Manual workspace ID
|
|
197
208
|
|
|
198
209
|
```bash
|
|
@@ -245,14 +256,17 @@ Runtime target patching:
|
|
|
245
256
|
Codex:
|
|
246
257
|
- Global MCP runtime target: `~/.codex/config.toml` (via `codex mcp add/remove`)
|
|
247
258
|
- Project MCP runtime target: `<workspace>/.vscode/mcp.json`
|
|
259
|
+
- Foundry side-by-side server id: `cubis-foundry` (command: `cbx mcp serve --transport stdio --scope auto`)
|
|
248
260
|
|
|
249
261
|
Antigravity:
|
|
250
262
|
- Global runtime target: `~/.gemini/settings.json` (`mcpServers`)
|
|
251
263
|
- Project runtime target: `<workspace>/.gemini/settings.json` (`mcpServers`)
|
|
264
|
+
- Foundry side-by-side server id: `cubis-foundry` (command template)
|
|
252
265
|
|
|
253
266
|
Copilot:
|
|
254
267
|
- Global runtime target: `~/.copilot/mcp-config.json` (`servers`)
|
|
255
268
|
- Project runtime target: `<workspace>/.vscode/mcp.json` (`servers`)
|
|
269
|
+
- Foundry side-by-side server id: `cubis-foundry` (stdio command server)
|
|
256
270
|
|
|
257
271
|
## Command Reference
|
|
258
272
|
|
|
@@ -289,6 +303,19 @@ cbx mcp tools list --service postman --scope global
|
|
|
289
303
|
cbx mcp tools list --service stitch --scope global
|
|
290
304
|
```
|
|
291
305
|
|
|
306
|
+
Foundry local serve command (canonical entrypoint for MCP client registration):
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
# stdio (default)
|
|
310
|
+
cbx mcp serve --transport stdio --scope auto
|
|
311
|
+
|
|
312
|
+
# http for local smoke/debug
|
|
313
|
+
cbx mcp serve --transport http --scope auto --host 127.0.0.1 --port 3100
|
|
314
|
+
|
|
315
|
+
# verify vault only
|
|
316
|
+
cbx mcp serve --scan-only
|
|
317
|
+
```
|
|
318
|
+
|
|
292
319
|
MCP Docker runtime commands:
|
|
293
320
|
|
|
294
321
|
```bash
|
|
@@ -317,6 +344,18 @@ Optional strict key mode:
|
|
|
317
344
|
CBX_MCP_REQUIRE_KEYS=1 npm run test:mcp:docker
|
|
318
345
|
```
|
|
319
346
|
|
|
347
|
+
Context budget reporting (from MCP skill tools):
|
|
348
|
+
|
|
349
|
+
- Skill tools now include `structuredContent.metrics` with deterministic estimates.
|
|
350
|
+
- Metrics include:
|
|
351
|
+
- `fullCatalogEstimatedTokens`
|
|
352
|
+
- `responseEstimatedTokens`
|
|
353
|
+
- `selectedSkillsEstimatedTokens` or `loadedSkillEstimatedTokens`
|
|
354
|
+
- `estimatedSavingsVsFullCatalog`
|
|
355
|
+
- `estimatedSavingsVsFullCatalogPercent`
|
|
356
|
+
- New rollup tool: `skill_budget_report` for consolidated Skill Log + Context Budget.
|
|
357
|
+
- All token values are estimates using `ceil(char_count / charsPerToken)` (default `charsPerToken=4`), not provider billing tokens.
|
|
358
|
+
|
|
320
359
|
Install profile flags:
|
|
321
360
|
|
|
322
361
|
```bash
|
|
@@ -398,6 +437,31 @@ If installer says config was skipped:
|
|
|
398
437
|
- Re-run with `--overwrite`, or
|
|
399
438
|
- Use `cbx workflows config` / `cbx workflows config keys ...` to mutate existing config.
|
|
400
439
|
|
|
440
|
+
### Docker runtime starts but skill discovery is zero
|
|
441
|
+
|
|
442
|
+
Cause:
|
|
443
|
+
- Runtime container has no skill mount at `/workflows/skills`.
|
|
444
|
+
|
|
445
|
+
Fix:
|
|
446
|
+
```bash
|
|
447
|
+
# Ensure host skill vault exists
|
|
448
|
+
ls ~/.agents/skills
|
|
449
|
+
|
|
450
|
+
# Recreate runtime
|
|
451
|
+
cbx mcp runtime up --scope global --name cbx-mcp --replace
|
|
452
|
+
|
|
453
|
+
# Check mount hint
|
|
454
|
+
cbx mcp runtime status --scope global --name cbx-mcp
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
If `~/.agents/skills` is missing, runtime still starts but will warn and skill discovery may return zero.
|
|
458
|
+
|
|
459
|
+
### Docker vs stdio behavior
|
|
460
|
+
|
|
461
|
+
- `cbx mcp runtime up` runs HTTP transport in Docker for shared local endpoint (`http://127.0.0.1:<port>/mcp`).
|
|
462
|
+
- `cbx mcp serve --transport stdio` runs local stdio transport for command-based MCP clients.
|
|
463
|
+
- Prefer stdio command server entries (`cubis-foundry`) for direct client integrations; use Docker runtime for explicit HTTP endpoint use cases.
|
|
464
|
+
|
|
401
465
|
### Duplicate skills shown in UI after older installs
|
|
402
466
|
|
|
403
467
|
Installer now auto-cleans nested duplicate skills (for example duplicates under `postman/*`).
|
package/bin/cubis.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
writeFile,
|
|
15
15
|
} from "node:fs/promises";
|
|
16
16
|
import { createRequire } from "node:module";
|
|
17
|
-
import { execFile as execFileCallback } from "node:child_process";
|
|
17
|
+
import { spawn, execFile as execFileCallback } from "node:child_process";
|
|
18
18
|
import os from "node:os";
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import process from "node:process";
|
|
@@ -151,6 +151,16 @@ 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 FOUNDRY_MCP_SERVER_ID = "cubis-foundry";
|
|
155
|
+
const FOUNDRY_MCP_COMMAND = "cbx";
|
|
156
|
+
const FOUNDRY_MCP_DEFAULT_ARGS = [
|
|
157
|
+
"mcp",
|
|
158
|
+
"serve",
|
|
159
|
+
"--transport",
|
|
160
|
+
"stdio",
|
|
161
|
+
"--scope",
|
|
162
|
+
"auto",
|
|
163
|
+
];
|
|
154
164
|
const STITCH_SKILL_ID = "stitch";
|
|
155
165
|
const STITCH_MCP_SERVER_ID = "StitchMCP";
|
|
156
166
|
const STITCH_API_KEY_ENV_VAR = "STITCH_API_KEY";
|
|
@@ -541,6 +551,23 @@ async function resolveDockerContainerHostPort({
|
|
|
541
551
|
}
|
|
542
552
|
}
|
|
543
553
|
|
|
554
|
+
async function inspectDockerContainerMounts({
|
|
555
|
+
name,
|
|
556
|
+
cwd = process.cwd(),
|
|
557
|
+
}) {
|
|
558
|
+
try {
|
|
559
|
+
const { stdout } = await execFile(
|
|
560
|
+
"docker",
|
|
561
|
+
["inspect", "--format", "{{json .Mounts}}", name],
|
|
562
|
+
{ cwd },
|
|
563
|
+
);
|
|
564
|
+
const parsed = JSON.parse(String(stdout || "").trim());
|
|
565
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
566
|
+
} catch {
|
|
567
|
+
return [];
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
544
571
|
function isPathInsideRoot(targetPath, rootPath) {
|
|
545
572
|
const target = path.resolve(targetPath);
|
|
546
573
|
const root = path.resolve(rootPath);
|
|
@@ -2821,8 +2848,11 @@ function buildManagedWorkflowBlock(platformId, workflows) {
|
|
|
2821
2848
|
lines.push("## CBX Workflow Routing (auto-managed)");
|
|
2822
2849
|
if (platformId === "codex") {
|
|
2823
2850
|
lines.push("Use Codex callable wrappers:");
|
|
2824
|
-
lines.push("- Workflows:
|
|
2825
|
-
lines.push("- Agents:
|
|
2851
|
+
lines.push("- Workflows: $workflow-<name>");
|
|
2852
|
+
lines.push("- Agents: $agent-<name>");
|
|
2853
|
+
lines.push(
|
|
2854
|
+
"- Use raw $workflow-* / $agent-* tokens (no backticks) so Codex can render icons.",
|
|
2855
|
+
);
|
|
2826
2856
|
lines.push("");
|
|
2827
2857
|
|
|
2828
2858
|
if (workflows.length === 0) {
|
|
@@ -2832,18 +2862,16 @@ function buildManagedWorkflowBlock(platformId, workflows) {
|
|
|
2832
2862
|
const triggerPreview = workflow.triggers.slice(0, 2).join(", ");
|
|
2833
2863
|
const hint = triggerPreview ? ` (${triggerPreview})` : "";
|
|
2834
2864
|
lines.push(
|
|
2835
|
-
`-
|
|
2865
|
+
`- ${workflow.command} -> $${CODEX_WORKFLOW_SKILL_PREFIX}${workflow.id}${hint}`,
|
|
2836
2866
|
);
|
|
2837
2867
|
}
|
|
2838
2868
|
}
|
|
2839
2869
|
|
|
2840
2870
|
lines.push("");
|
|
2841
2871
|
lines.push("Selection policy:");
|
|
2842
|
-
lines.push("1. If user names a
|
|
2872
|
+
lines.push("1. If user names a $workflow-*, use it directly.");
|
|
2843
2873
|
lines.push("2. Else map intent to one primary workflow.");
|
|
2844
|
-
lines.push(
|
|
2845
|
-
"3. Use `$agent-*` wrappers only when role specialization is needed.",
|
|
2846
|
-
);
|
|
2874
|
+
lines.push("3. Use $agent-* wrappers only when role specialization is needed.");
|
|
2847
2875
|
lines.push("");
|
|
2848
2876
|
lines.push("<!-- cbx:workflows:auto:end -->");
|
|
2849
2877
|
return lines.join("\n");
|
|
@@ -3316,6 +3344,47 @@ function buildVsCodePostmanServer({
|
|
|
3316
3344
|
};
|
|
3317
3345
|
}
|
|
3318
3346
|
|
|
3347
|
+
function buildFoundryServeArgs({ scope = "auto" } = {}) {
|
|
3348
|
+
const normalizedScope =
|
|
3349
|
+
scope === "global" || scope === "project" || scope === "auto"
|
|
3350
|
+
? scope
|
|
3351
|
+
: "auto";
|
|
3352
|
+
return ["mcp", "serve", "--transport", "stdio", "--scope", normalizedScope];
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
function buildVsCodeFoundryServer({ scope = "auto" } = {}) {
|
|
3356
|
+
return {
|
|
3357
|
+
type: "stdio",
|
|
3358
|
+
command: FOUNDRY_MCP_COMMAND,
|
|
3359
|
+
args: buildFoundryServeArgs({ scope }),
|
|
3360
|
+
env: {},
|
|
3361
|
+
};
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
function buildCopilotCliPostmanServer({
|
|
3365
|
+
apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR,
|
|
3366
|
+
mcpUrl = POSTMAN_MCP_URL,
|
|
3367
|
+
}) {
|
|
3368
|
+
return {
|
|
3369
|
+
type: "http",
|
|
3370
|
+
url: mcpUrl,
|
|
3371
|
+
headers: {
|
|
3372
|
+
Authorization: buildPostmanAuthHeader({ apiKeyEnvVar }),
|
|
3373
|
+
},
|
|
3374
|
+
tools: ["*"],
|
|
3375
|
+
};
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
function buildCopilotCliFoundryServer({ scope = "auto" } = {}) {
|
|
3379
|
+
return {
|
|
3380
|
+
type: "stdio",
|
|
3381
|
+
command: FOUNDRY_MCP_COMMAND,
|
|
3382
|
+
args: buildFoundryServeArgs({ scope }),
|
|
3383
|
+
env: {},
|
|
3384
|
+
tools: ["*"],
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3319
3388
|
function buildGeminiPostmanServer({
|
|
3320
3389
|
apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR,
|
|
3321
3390
|
mcpUrl = POSTMAN_MCP_URL,
|
|
@@ -3328,12 +3397,19 @@ function buildGeminiPostmanServer({
|
|
|
3328
3397
|
};
|
|
3329
3398
|
}
|
|
3330
3399
|
|
|
3400
|
+
function buildGeminiFoundryServer({ scope = "auto" } = {}) {
|
|
3401
|
+
return {
|
|
3402
|
+
command: FOUNDRY_MCP_COMMAND,
|
|
3403
|
+
args: buildFoundryServeArgs({ scope }),
|
|
3404
|
+
env: {},
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3331
3408
|
function buildGeminiStitchServer({
|
|
3332
3409
|
apiKeyEnvVar = STITCH_API_KEY_ENV_VAR,
|
|
3333
3410
|
mcpUrl = STITCH_MCP_URL,
|
|
3334
3411
|
}) {
|
|
3335
3412
|
return {
|
|
3336
|
-
$typeName: "exa.cascade_plugins_pb.CascadePluginCommandTemplate",
|
|
3337
3413
|
command: "npx",
|
|
3338
3414
|
args: [
|
|
3339
3415
|
"-y",
|
|
@@ -3752,6 +3828,7 @@ async function applyPostmanMcpForPlatform({
|
|
|
3752
3828
|
stitchApiKeyEnvVar,
|
|
3753
3829
|
stitchMcpUrl,
|
|
3754
3830
|
includeStitchMcp = false,
|
|
3831
|
+
includeFoundryMcp = true,
|
|
3755
3832
|
dryRun = false,
|
|
3756
3833
|
cwd = process.cwd(),
|
|
3757
3834
|
}) {
|
|
@@ -3777,6 +3854,13 @@ async function applyPostmanMcpForPlatform({
|
|
|
3777
3854
|
apiKeyEnvVar,
|
|
3778
3855
|
mcpUrl,
|
|
3779
3856
|
});
|
|
3857
|
+
if (includeFoundryMcp) {
|
|
3858
|
+
mcpServers[FOUNDRY_MCP_SERVER_ID] = buildGeminiFoundryServer({
|
|
3859
|
+
scope: "auto",
|
|
3860
|
+
});
|
|
3861
|
+
} else {
|
|
3862
|
+
delete mcpServers[FOUNDRY_MCP_SERVER_ID];
|
|
3863
|
+
}
|
|
3780
3864
|
if (includeStitchMcp) {
|
|
3781
3865
|
mcpServers[STITCH_MCP_SERVER_ID] = buildGeminiStitchServer({
|
|
3782
3866
|
apiKeyEnvVar: stitchApiKeyEnvVar,
|
|
@@ -3806,6 +3890,28 @@ async function applyPostmanMcpForPlatform({
|
|
|
3806
3890
|
targetPath: configPath,
|
|
3807
3891
|
updater: (existing) => {
|
|
3808
3892
|
const next = { ...existing };
|
|
3893
|
+
if (mcpScope === "global") {
|
|
3894
|
+
const mcpServers =
|
|
3895
|
+
next.mcpServers &&
|
|
3896
|
+
typeof next.mcpServers === "object" &&
|
|
3897
|
+
!Array.isArray(next.mcpServers)
|
|
3898
|
+
? { ...next.mcpServers }
|
|
3899
|
+
: {};
|
|
3900
|
+
mcpServers[POSTMAN_SKILL_ID] = buildCopilotCliPostmanServer({
|
|
3901
|
+
apiKeyEnvVar,
|
|
3902
|
+
mcpUrl,
|
|
3903
|
+
});
|
|
3904
|
+
if (includeFoundryMcp) {
|
|
3905
|
+
mcpServers[FOUNDRY_MCP_SERVER_ID] = buildCopilotCliFoundryServer({
|
|
3906
|
+
scope: "auto",
|
|
3907
|
+
});
|
|
3908
|
+
} else {
|
|
3909
|
+
delete mcpServers[FOUNDRY_MCP_SERVER_ID];
|
|
3910
|
+
}
|
|
3911
|
+
next.mcpServers = mcpServers;
|
|
3912
|
+
return next;
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3809
3915
|
const servers =
|
|
3810
3916
|
next.servers &&
|
|
3811
3917
|
typeof next.servers === "object" &&
|
|
@@ -3816,6 +3922,13 @@ async function applyPostmanMcpForPlatform({
|
|
|
3816
3922
|
apiKeyEnvVar,
|
|
3817
3923
|
mcpUrl,
|
|
3818
3924
|
});
|
|
3925
|
+
if (includeFoundryMcp) {
|
|
3926
|
+
servers[FOUNDRY_MCP_SERVER_ID] = buildVsCodeFoundryServer({
|
|
3927
|
+
scope: "auto",
|
|
3928
|
+
});
|
|
3929
|
+
} else {
|
|
3930
|
+
delete servers[FOUNDRY_MCP_SERVER_ID];
|
|
3931
|
+
}
|
|
3819
3932
|
next.servers = servers;
|
|
3820
3933
|
return next;
|
|
3821
3934
|
},
|
|
@@ -3847,6 +3960,13 @@ async function applyPostmanMcpForPlatform({
|
|
|
3847
3960
|
apiKeyEnvVar,
|
|
3848
3961
|
mcpUrl,
|
|
3849
3962
|
});
|
|
3963
|
+
if (includeFoundryMcp) {
|
|
3964
|
+
servers[FOUNDRY_MCP_SERVER_ID] = buildVsCodeFoundryServer({
|
|
3965
|
+
scope: "auto",
|
|
3966
|
+
});
|
|
3967
|
+
} else {
|
|
3968
|
+
delete servers[FOUNDRY_MCP_SERVER_ID];
|
|
3969
|
+
}
|
|
3850
3970
|
next.servers = servers;
|
|
3851
3971
|
return next;
|
|
3852
3972
|
},
|
|
@@ -3877,6 +3997,13 @@ async function applyPostmanMcpForPlatform({
|
|
|
3877
3997
|
} catch {
|
|
3878
3998
|
// Best effort. Add will still run and becomes source of truth.
|
|
3879
3999
|
}
|
|
4000
|
+
try {
|
|
4001
|
+
await execFile("codex", ["mcp", "remove", FOUNDRY_MCP_SERVER_ID], {
|
|
4002
|
+
cwd,
|
|
4003
|
+
});
|
|
4004
|
+
} catch {
|
|
4005
|
+
// Best effort. Add will still run and becomes source of truth.
|
|
4006
|
+
}
|
|
3880
4007
|
|
|
3881
4008
|
try {
|
|
3882
4009
|
await execFile(
|
|
@@ -3905,6 +4032,27 @@ async function applyPostmanMcpForPlatform({
|
|
|
3905
4032
|
};
|
|
3906
4033
|
}
|
|
3907
4034
|
|
|
4035
|
+
if (includeFoundryMcp) {
|
|
4036
|
+
try {
|
|
4037
|
+
await execFile(
|
|
4038
|
+
"codex",
|
|
4039
|
+
[
|
|
4040
|
+
"mcp",
|
|
4041
|
+
"add",
|
|
4042
|
+
FOUNDRY_MCP_SERVER_ID,
|
|
4043
|
+
"--",
|
|
4044
|
+
FOUNDRY_MCP_COMMAND,
|
|
4045
|
+
...FOUNDRY_MCP_DEFAULT_ARGS,
|
|
4046
|
+
],
|
|
4047
|
+
{ cwd },
|
|
4048
|
+
);
|
|
4049
|
+
} catch (error) {
|
|
4050
|
+
warnings.push(
|
|
4051
|
+
`Failed to register ${FOUNDRY_MCP_SERVER_ID} MCP via Codex CLI. Ensure 'cbx' and 'codex' are installed and rerun. (${error.message})`,
|
|
4052
|
+
);
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
|
|
3908
4056
|
return {
|
|
3909
4057
|
kind: "codex-cli",
|
|
3910
4058
|
scope: mcpScope,
|
|
@@ -4001,6 +4149,7 @@ async function resolvePostmanInstallSelection({
|
|
|
4001
4149
|
);
|
|
4002
4150
|
const mcpBuildLocal = Boolean(options.mcpBuildLocal);
|
|
4003
4151
|
const mcpToolSync = options.mcpToolSync !== false;
|
|
4152
|
+
const foundryMcpEnabled = options.foundryMcp !== false;
|
|
4004
4153
|
|
|
4005
4154
|
const canPrompt = !options.yes && process.stdin.isTTY;
|
|
4006
4155
|
if (canPrompt && !hasWorkspaceOption) {
|
|
@@ -4228,6 +4377,7 @@ async function resolvePostmanInstallSelection({
|
|
|
4228
4377
|
mcpBuildLocal,
|
|
4229
4378
|
dockerImageAction,
|
|
4230
4379
|
mcpToolSync,
|
|
4380
|
+
foundryMcpEnabled,
|
|
4231
4381
|
runtimeSkipped,
|
|
4232
4382
|
defaultWorkspaceId: defaultWorkspaceId ?? null,
|
|
4233
4383
|
workspaceSelectionSource,
|
|
@@ -4457,6 +4607,7 @@ async function configurePostmanInstallArtifacts({
|
|
|
4457
4607
|
stitchApiKeyEnvVar: effectiveStitchApiKeyEnvVar,
|
|
4458
4608
|
stitchMcpUrl: effectiveStitchMcpUrl,
|
|
4459
4609
|
includeStitchMcp: shouldInstallStitch,
|
|
4610
|
+
includeFoundryMcp: postmanSelection.foundryMcpEnabled,
|
|
4460
4611
|
dryRun,
|
|
4461
4612
|
cwd,
|
|
4462
4613
|
});
|
|
@@ -4495,6 +4646,7 @@ async function configurePostmanInstallArtifacts({
|
|
|
4495
4646
|
mcpBuildLocal: postmanSelection.mcpBuildLocal,
|
|
4496
4647
|
dockerImageAction: postmanSelection.dockerImageAction,
|
|
4497
4648
|
mcpToolSync: postmanSelection.mcpToolSync,
|
|
4649
|
+
foundryMcpEnabled: postmanSelection.foundryMcpEnabled,
|
|
4498
4650
|
apiKeySource: effectiveApiKeySource,
|
|
4499
4651
|
stitchApiKeySource: effectiveStitchApiKeySource,
|
|
4500
4652
|
defaultWorkspaceId: effectiveDefaultWorkspaceId,
|
|
@@ -5454,6 +5606,9 @@ function printPostmanSetupSummary({ postmanSetup }) {
|
|
|
5454
5606
|
console.log(`- MCP build local: ${postmanSetup.mcpBuildLocal ? "yes" : "no"}`);
|
|
5455
5607
|
console.log(`- MCP image prepare: ${postmanSetup.dockerImageAction}`);
|
|
5456
5608
|
console.log(`- MCP tool sync: ${postmanSetup.mcpToolSync ? "enabled" : "disabled"}`);
|
|
5609
|
+
console.log(
|
|
5610
|
+
`- Foundry MCP side-by-side: ${postmanSetup.foundryMcpEnabled ? "enabled" : "disabled"}`,
|
|
5611
|
+
);
|
|
5457
5612
|
console.log(`- Postman API key source: ${postmanSetup.apiKeySource}`);
|
|
5458
5613
|
if (postmanSetup.stitchApiKeySource) {
|
|
5459
5614
|
console.log(`- Stitch API key source: ${postmanSetup.stitchApiKeySource}`);
|
|
@@ -5971,6 +6126,10 @@ function withInstallOptions(command) {
|
|
|
5971
6126
|
"enable automatic MCP tool catalog sync (default: enabled)",
|
|
5972
6127
|
)
|
|
5973
6128
|
.option("--no-mcp-tool-sync", "disable automatic MCP tool catalog sync")
|
|
6129
|
+
.option(
|
|
6130
|
+
"--no-foundry-mcp",
|
|
6131
|
+
"disable side-by-side cubis-foundry MCP registration during --postman setup",
|
|
6132
|
+
)
|
|
5974
6133
|
.option(
|
|
5975
6134
|
"--terminal-integration",
|
|
5976
6135
|
"Antigravity only: enable terminal verification integration (prompts for verifier when interactive)",
|
|
@@ -7933,6 +8092,111 @@ async function runMcpToolsList(options) {
|
|
|
7933
8092
|
}
|
|
7934
8093
|
}
|
|
7935
8094
|
|
|
8095
|
+
function normalizeMcpServeTransport(value, fallback = "stdio") {
|
|
8096
|
+
const normalized = String(value || fallback)
|
|
8097
|
+
.trim()
|
|
8098
|
+
.toLowerCase();
|
|
8099
|
+
if (normalized === "stdio") return "stdio";
|
|
8100
|
+
if (normalized === "http" || normalized === "streamable-http") return "http";
|
|
8101
|
+
throw new Error(
|
|
8102
|
+
`Unknown MCP transport '${value}'. Use stdio|http.`,
|
|
8103
|
+
);
|
|
8104
|
+
}
|
|
8105
|
+
|
|
8106
|
+
function normalizeMcpServeScope(value, fallback = "auto") {
|
|
8107
|
+
const normalized = String(value || fallback)
|
|
8108
|
+
.trim()
|
|
8109
|
+
.toLowerCase();
|
|
8110
|
+
if (
|
|
8111
|
+
normalized === "auto" ||
|
|
8112
|
+
normalized === "global" ||
|
|
8113
|
+
normalized === "project"
|
|
8114
|
+
) {
|
|
8115
|
+
return normalized;
|
|
8116
|
+
}
|
|
8117
|
+
throw new Error(
|
|
8118
|
+
`Unknown MCP scope '${value}'. Use auto|global|project.`,
|
|
8119
|
+
);
|
|
8120
|
+
}
|
|
8121
|
+
|
|
8122
|
+
function resolveBundledMcpEntryPath() {
|
|
8123
|
+
return path.join(packageRoot(), "mcp", "dist", "index.js");
|
|
8124
|
+
}
|
|
8125
|
+
|
|
8126
|
+
async function runMcpServe(options) {
|
|
8127
|
+
try {
|
|
8128
|
+
const opts = resolveActionOptions(options);
|
|
8129
|
+
const cwd = process.cwd();
|
|
8130
|
+
const entryPath = resolveBundledMcpEntryPath();
|
|
8131
|
+
if (!(await pathExists(entryPath))) {
|
|
8132
|
+
throw new Error(
|
|
8133
|
+
`Bundled MCP server not found at ${entryPath}. Install @cubis/foundry with bundled mcp/dist assets.`,
|
|
8134
|
+
);
|
|
8135
|
+
}
|
|
8136
|
+
|
|
8137
|
+
const transport = normalizeMcpServeTransport(opts.transport, "stdio");
|
|
8138
|
+
const scope = normalizeMcpServeScope(opts.scope, "auto");
|
|
8139
|
+
const args = [entryPath, "--transport", transport, "--scope", scope];
|
|
8140
|
+
|
|
8141
|
+
if (opts.host) {
|
|
8142
|
+
args.push("--host", String(opts.host));
|
|
8143
|
+
}
|
|
8144
|
+
if (opts.port !== undefined && opts.port !== null && opts.port !== "") {
|
|
8145
|
+
args.push("--port", String(normalizePortNumber(opts.port, 3100)));
|
|
8146
|
+
}
|
|
8147
|
+
if (opts.scanOnly) {
|
|
8148
|
+
args.push("--scan-only");
|
|
8149
|
+
}
|
|
8150
|
+
if (opts.config) {
|
|
8151
|
+
args.push("--config", expandPath(String(opts.config), cwd));
|
|
8152
|
+
}
|
|
8153
|
+
if (opts.debug) {
|
|
8154
|
+
args.push("--debug");
|
|
8155
|
+
}
|
|
8156
|
+
|
|
8157
|
+
await new Promise((resolve, reject) => {
|
|
8158
|
+
const child = spawn(process.execPath, args, {
|
|
8159
|
+
cwd,
|
|
8160
|
+
stdio: "inherit",
|
|
8161
|
+
env: process.env,
|
|
8162
|
+
});
|
|
8163
|
+
|
|
8164
|
+
const forwardSignal = (signal) => {
|
|
8165
|
+
if (!child.killed) {
|
|
8166
|
+
child.kill(signal);
|
|
8167
|
+
}
|
|
8168
|
+
};
|
|
8169
|
+
const onSigInt = () => forwardSignal("SIGINT");
|
|
8170
|
+
const onSigTerm = () => forwardSignal("SIGTERM");
|
|
8171
|
+
process.on("SIGINT", onSigInt);
|
|
8172
|
+
process.on("SIGTERM", onSigTerm);
|
|
8173
|
+
|
|
8174
|
+
child.once("error", (error) => {
|
|
8175
|
+
process.off("SIGINT", onSigInt);
|
|
8176
|
+
process.off("SIGTERM", onSigTerm);
|
|
8177
|
+
reject(error);
|
|
8178
|
+
});
|
|
8179
|
+
|
|
8180
|
+
child.once("exit", (code, signal) => {
|
|
8181
|
+
process.off("SIGINT", onSigInt);
|
|
8182
|
+
process.off("SIGTERM", onSigTerm);
|
|
8183
|
+
if (signal) {
|
|
8184
|
+
reject(new Error(`MCP server terminated by signal ${signal}.`));
|
|
8185
|
+
return;
|
|
8186
|
+
}
|
|
8187
|
+
if (code && code !== 0) {
|
|
8188
|
+
reject(new Error(`MCP server exited with status ${code}.`));
|
|
8189
|
+
return;
|
|
8190
|
+
}
|
|
8191
|
+
resolve(null);
|
|
8192
|
+
});
|
|
8193
|
+
});
|
|
8194
|
+
} catch (error) {
|
|
8195
|
+
console.error(`\nError: ${error.message}`);
|
|
8196
|
+
process.exit(1);
|
|
8197
|
+
}
|
|
8198
|
+
}
|
|
8199
|
+
|
|
7936
8200
|
function resolveCbxRootPath({ scope, cwd = process.cwd() }) {
|
|
7937
8201
|
if (scope === "global") {
|
|
7938
8202
|
return path.join(os.homedir(), ".cbx");
|
|
@@ -7998,6 +8262,8 @@ async function runMcpRuntimeStatus(options) {
|
|
|
7998
8262
|
const container = dockerAvailable
|
|
7999
8263
|
? await inspectDockerContainerByName({ name: containerName, cwd })
|
|
8000
8264
|
: null;
|
|
8265
|
+
const skillsRoot = path.join(os.homedir(), ".agents", "skills");
|
|
8266
|
+
const skillsRootExists = await pathExists(skillsRoot);
|
|
8001
8267
|
|
|
8002
8268
|
console.log(`Scope: ${scope}`);
|
|
8003
8269
|
console.log(`Config file: ${defaults.configPath}`);
|
|
@@ -8007,6 +8273,9 @@ async function runMcpRuntimeStatus(options) {
|
|
|
8007
8273
|
console.log(`Configured image: ${defaults.defaults.image}`);
|
|
8008
8274
|
console.log(`Configured update policy: ${defaults.defaults.updatePolicy}`);
|
|
8009
8275
|
console.log(`Configured build local: ${defaults.defaults.buildLocal ? "yes" : "no"}`);
|
|
8276
|
+
console.log(
|
|
8277
|
+
`Host skills root: ${skillsRoot} (${skillsRootExists ? "present" : "missing"})`,
|
|
8278
|
+
);
|
|
8010
8279
|
console.log(`Docker available: ${dockerAvailable ? "yes" : "no"}`);
|
|
8011
8280
|
console.log(`Container name: ${containerName}`);
|
|
8012
8281
|
if (!dockerAvailable) {
|
|
@@ -8015,11 +8284,34 @@ async function runMcpRuntimeStatus(options) {
|
|
|
8015
8284
|
}
|
|
8016
8285
|
if (!container) {
|
|
8017
8286
|
console.log("Container status: not found");
|
|
8287
|
+
if (!skillsRootExists) {
|
|
8288
|
+
console.log(
|
|
8289
|
+
"Hint: ~/.agents/skills is missing. Create/populate it before starting runtime to enable vault discovery.",
|
|
8290
|
+
);
|
|
8291
|
+
}
|
|
8018
8292
|
return;
|
|
8019
8293
|
}
|
|
8020
8294
|
const isRunning = container.status.toLowerCase().startsWith("up ");
|
|
8021
8295
|
console.log(`Container status: ${container.status}`);
|
|
8022
8296
|
console.log(`Container image: ${container.image}`);
|
|
8297
|
+
const mounts = await inspectDockerContainerMounts({ name: containerName, cwd });
|
|
8298
|
+
const skillsMount = mounts.find(
|
|
8299
|
+
(mount) => String(mount.Destination || "") === "/workflows/skills",
|
|
8300
|
+
);
|
|
8301
|
+
if (skillsMount) {
|
|
8302
|
+
const source = String(skillsMount.Source || "(unknown)");
|
|
8303
|
+
console.log(`Skills mount: ${source} -> /workflows/skills`);
|
|
8304
|
+
if (!(await pathExists(source))) {
|
|
8305
|
+
console.log(
|
|
8306
|
+
"Hint: mounted skill source path is missing on host; vault discovery may return zero skills.",
|
|
8307
|
+
);
|
|
8308
|
+
}
|
|
8309
|
+
} else {
|
|
8310
|
+
console.log("Skills mount: missing");
|
|
8311
|
+
console.log(
|
|
8312
|
+
"Hint: recreate runtime with a skills mount (host ~/.agents/skills -> /workflows/skills).",
|
|
8313
|
+
);
|
|
8314
|
+
}
|
|
8023
8315
|
if (isRunning) {
|
|
8024
8316
|
const hostPort =
|
|
8025
8317
|
(await resolveDockerContainerHostPort({
|
|
@@ -8082,22 +8374,32 @@ async function runMcpRuntimeUp(options) {
|
|
|
8082
8374
|
}
|
|
8083
8375
|
|
|
8084
8376
|
const cbxRoot = resolveCbxRootPath({ scope, cwd });
|
|
8377
|
+
const skillsRoot = path.join(os.homedir(), ".agents", "skills");
|
|
8378
|
+
const skillsRootExists = await pathExists(skillsRoot);
|
|
8379
|
+
const runtimeWarnings = [];
|
|
8380
|
+
if (!skillsRootExists) {
|
|
8381
|
+
runtimeWarnings.push(
|
|
8382
|
+
`Skill mount source is missing: ${skillsRoot}. Runtime will start without /workflows/skills and vault discovery can return zero skills.`,
|
|
8383
|
+
);
|
|
8384
|
+
}
|
|
8085
8385
|
await mkdir(cbxRoot, { recursive: true });
|
|
8386
|
+
const dockerArgs = [
|
|
8387
|
+
"run",
|
|
8388
|
+
"-d",
|
|
8389
|
+
"--name",
|
|
8390
|
+
containerName,
|
|
8391
|
+
"-p",
|
|
8392
|
+
`${hostPort}:${MCP_DOCKER_CONTAINER_PORT}`,
|
|
8393
|
+
"-v",
|
|
8394
|
+
`${cbxRoot}:/root/.cbx`,
|
|
8395
|
+
];
|
|
8396
|
+
if (skillsRootExists) {
|
|
8397
|
+
dockerArgs.push("-v", `${skillsRoot}:/workflows/skills:ro`);
|
|
8398
|
+
}
|
|
8399
|
+
dockerArgs.push("-e", "CBX_MCP_TRANSPORT=streamable-http", image);
|
|
8086
8400
|
await execFile(
|
|
8087
8401
|
"docker",
|
|
8088
|
-
|
|
8089
|
-
"run",
|
|
8090
|
-
"-d",
|
|
8091
|
-
"--name",
|
|
8092
|
-
containerName,
|
|
8093
|
-
"-p",
|
|
8094
|
-
`${hostPort}:${MCP_DOCKER_CONTAINER_PORT}`,
|
|
8095
|
-
"-v",
|
|
8096
|
-
`${cbxRoot}:/root/.cbx`,
|
|
8097
|
-
"-e",
|
|
8098
|
-
"CBX_MCP_TRANSPORT=streamable-http",
|
|
8099
|
-
image,
|
|
8100
|
-
],
|
|
8402
|
+
dockerArgs,
|
|
8101
8403
|
{ cwd },
|
|
8102
8404
|
);
|
|
8103
8405
|
|
|
@@ -8112,9 +8414,20 @@ async function runMcpRuntimeUp(options) {
|
|
|
8112
8414
|
console.log(`Update policy: ${updatePolicy}`);
|
|
8113
8415
|
console.log(`Build local: ${buildLocal ? "yes" : "no"}`);
|
|
8114
8416
|
console.log(`Mount: ${cbxRoot} -> /root/.cbx`);
|
|
8417
|
+
if (skillsRootExists) {
|
|
8418
|
+
console.log(`Mount: ${skillsRoot} -> /workflows/skills (ro)`);
|
|
8419
|
+
} else {
|
|
8420
|
+
console.log("Mount: /workflows/skills (not mounted - source missing)");
|
|
8421
|
+
}
|
|
8115
8422
|
console.log(`Port: ${hostPort}:${MCP_DOCKER_CONTAINER_PORT}`);
|
|
8116
8423
|
console.log(`Status: ${running ? running.status : "started"}`);
|
|
8117
8424
|
console.log(`Endpoint: http://127.0.0.1:${hostPort}/mcp`);
|
|
8425
|
+
if (runtimeWarnings.length > 0) {
|
|
8426
|
+
console.log("Warnings:");
|
|
8427
|
+
for (const warning of runtimeWarnings) {
|
|
8428
|
+
console.log(`- ${warning}`);
|
|
8429
|
+
}
|
|
8430
|
+
}
|
|
8118
8431
|
} catch (error) {
|
|
8119
8432
|
console.error(`\nError: ${error.message}`);
|
|
8120
8433
|
process.exit(1);
|
|
@@ -8563,6 +8876,18 @@ const mcpCommand = program
|
|
|
8563
8876
|
.command("mcp")
|
|
8564
8877
|
.description("Manage Cubis MCP runtime catalogs and tool discovery");
|
|
8565
8878
|
|
|
8879
|
+
mcpCommand
|
|
8880
|
+
.command("serve")
|
|
8881
|
+
.description("Launch bundled Cubis Foundry MCP server (canonical local entrypoint)")
|
|
8882
|
+
.option("--transport <transport>", "stdio|http", "stdio")
|
|
8883
|
+
.option("--scope <scope>", "auto|global|project", "auto")
|
|
8884
|
+
.option("--port <port>", "HTTP port override")
|
|
8885
|
+
.option("--host <host>", "HTTP host override")
|
|
8886
|
+
.option("--scan-only", "scan vault and exit")
|
|
8887
|
+
.option("--config <path>", "explicit MCP server config file path")
|
|
8888
|
+
.option("--debug", "enable debug logging in MCP server")
|
|
8889
|
+
.action(runMcpServe);
|
|
8890
|
+
|
|
8566
8891
|
const mcpToolsCommand = mcpCommand
|
|
8567
8892
|
.command("tools")
|
|
8568
8893
|
.description("Discover and inspect upstream MCP tool catalogs");
|