@cubis/foundry 0.3.61 → 0.3.63
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/CHANGELOG.md +7 -0
- package/README.md +1 -0
- package/dist/cli/commands/register.js +1 -0
- package/dist/cli/commands/register.js.map +1 -1
- package/dist/cli/core.js +2 -0
- package/dist/cli/core.js.map +1 -1
- package/dist/cli/init/execute.js +2 -1
- package/dist/cli/init/execute.js.map +1 -1
- package/mcp/README.md +49 -3
- package/mcp/dist/index.js +238 -30
- package/mcp/src/tools/index.ts +14 -0
- package/mcp/src/tools/registry.test.ts +7 -5
- package/mcp/src/tools/registry.ts +38 -0
- package/mcp/src/tools/skillGetReference.ts +77 -0
- package/mcp/src/tools/skillTools.test.ts +156 -0
- package/mcp/src/tools/skillValidate.ts +101 -0
- package/mcp/src/vault/manifest.test.ts +101 -0
- package/mcp/src/vault/manifest.ts +90 -0
- package/package.json +1 -1
- package/src/cli/commands/register.ts +1 -0
- package/src/cli/core.ts +2 -0
- package/src/cli/init/execute.ts +3 -0
- package/workflows/workflows/agent-environment-setup/platforms/antigravity/rules/GEMINI.md +57 -78
- package/workflows/workflows/agent-environment-setup/platforms/codex/rules/AGENTS.md +58 -79
- package/workflows/workflows/agent-environment-setup/platforms/copilot/rules/AGENTS.md +57 -78
- package/workflows/workflows/agent-environment-setup/platforms/copilot/rules/copilot-instructions.md +57 -78
package/dist/cli/init/execute.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
function hasMcpSelection(selectedMcps, mcpId) {
|
|
2
2
|
return selectedMcps.includes(mcpId);
|
|
3
3
|
}
|
|
4
|
-
export function buildInitExecutionPlan({ selections, dryRun, target, }) {
|
|
4
|
+
export function buildInitExecutionPlan({ selections, dryRun, overwrite, target, }) {
|
|
5
5
|
const planItems = [];
|
|
6
6
|
const wantsPostman = hasMcpSelection(selections.selectedMcps, "postman");
|
|
7
7
|
const wantsStitch = hasMcpSelection(selections.selectedMcps, "stitch");
|
|
@@ -20,6 +20,7 @@ export function buildInitExecutionPlan({ selections, dryRun, target, }) {
|
|
|
20
20
|
skillProfile: selections.skillProfile,
|
|
21
21
|
allSkills: selections.skillProfile === "full",
|
|
22
22
|
dryRun,
|
|
23
|
+
overwrite,
|
|
23
24
|
yes: true,
|
|
24
25
|
target,
|
|
25
26
|
postman: wantsPostman,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../src/cli/init/execute.ts"],"names":[],"mappings":"AAOA,SAAS,eAAe,CAAC,YAAyB,EAAE,KAAgB;IAClE,OAAO,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,EACrC,UAAU,EACV,MAAM,EACN,MAAM,
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../../src/cli/init/execute.ts"],"names":[],"mappings":"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"}
|
package/mcp/README.md
CHANGED
|
@@ -24,13 +24,13 @@ This server exposes built-in tools plus dynamic passthrough tools discovered fro
|
|
|
24
24
|
|
|
25
25
|
| Domain | Tools | Purpose |
|
|
26
26
|
| ----------- | ----- | ------------------------------------------------------------- |
|
|
27
|
-
| **Skills** |
|
|
27
|
+
| **Skills** | 7 | Browse/search/validate/get + targeted reference loading for skills |
|
|
28
28
|
| **Postman config** | 3 | Read/write Postman MCP mode in `cbx_config.json` |
|
|
29
29
|
| **Stitch config** | 3 | Read/write Stitch active profile in `cbx_config.json` |
|
|
30
30
|
| **Postman passthrough** | dynamic | `postman.<tool_name>` for all discovered upstream Postman tools |
|
|
31
31
|
| **Stitch passthrough** | dynamic | `stitch.<tool_name>` for all discovered upstream Stitch tools |
|
|
32
32
|
|
|
33
|
-
The skill vault uses a **lazy content model**: startup only scans metadata (category/name/path).
|
|
33
|
+
The skill vault uses a **lazy content model**: startup only scans metadata (category/name/path). Exact skill selection is validated via `skill_validate`, `skill_get` loads the core `SKILL.md`, and sidecar markdown is loaded only when needed via `skill_get_reference`.
|
|
34
34
|
|
|
35
35
|
## Architecture
|
|
36
36
|
|
|
@@ -67,7 +67,9 @@ mcp/
|
|
|
67
67
|
│ │ ├── skillListCategories.ts
|
|
68
68
|
│ │ ├── skillBrowseCategory.ts
|
|
69
69
|
│ │ ├── skillSearch.ts
|
|
70
|
+
│ │ ├── skillValidate.ts
|
|
70
71
|
│ │ ├── skillGet.ts
|
|
72
|
+
│ │ ├── skillGetReference.ts
|
|
71
73
|
│ │ ├── skillBudgetReport.ts
|
|
72
74
|
│ │ ├── postmanModes.ts # Mode ↔ URL mapping
|
|
73
75
|
│ │ ├── postmanGetMode.ts
|
|
@@ -269,6 +271,32 @@ Search skills by keyword.
|
|
|
269
271
|
}
|
|
270
272
|
```
|
|
271
273
|
|
|
274
|
+
#### `skill_validate`
|
|
275
|
+
|
|
276
|
+
Validate an exact skill ID before loading it.
|
|
277
|
+
|
|
278
|
+
**Input**:
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{ "id": "flutter-expert" }
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Output**:
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"id": "flutter-expert",
|
|
289
|
+
"exists": true,
|
|
290
|
+
"canonicalId": "flutter-expert",
|
|
291
|
+
"category": "mobile",
|
|
292
|
+
"description": "Flutter app architecture...",
|
|
293
|
+
"isWrapper": false,
|
|
294
|
+
"isAlias": false,
|
|
295
|
+
"replacementId": null,
|
|
296
|
+
"availableReferences": ["references/project-structure.md"]
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
272
300
|
#### `skill_get`
|
|
273
301
|
|
|
274
302
|
Get the full SKILL.md content for a specific skill.
|
|
@@ -276,12 +304,30 @@ Get the full SKILL.md content for a specific skill.
|
|
|
276
304
|
**Input**:
|
|
277
305
|
|
|
278
306
|
```json
|
|
279
|
-
{ "id": "nestjs-expert" }
|
|
307
|
+
{ "id": "nestjs-expert", "includeReferences": false }
|
|
280
308
|
```
|
|
281
309
|
|
|
282
310
|
**Output**: Full markdown content of the skill file (as `type: "text"` content block).
|
|
283
311
|
`structuredContent.metrics` includes `loadedSkillEstimatedTokens` and estimated savings vs full catalog.
|
|
284
312
|
|
|
313
|
+
By policy, agents should call `includeReferences: false` by default and fetch sidecar docs only when needed.
|
|
314
|
+
|
|
315
|
+
#### `skill_get_reference`
|
|
316
|
+
|
|
317
|
+
Get one validated markdown sidecar file for a skill.
|
|
318
|
+
|
|
319
|
+
**Input**:
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{
|
|
323
|
+
"id": "flutter-expert",
|
|
324
|
+
"path": "references/project-structure.md"
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**Output**: Raw markdown content of the requested reference file (as `type: "text"` content block).
|
|
329
|
+
`structuredContent` includes the resolved relative path and token metrics.
|
|
330
|
+
|
|
285
331
|
#### `skill_budget_report`
|
|
286
332
|
|
|
287
333
|
Consolidated Skill Log + Context Budget report for selected/loaded skill IDs.
|
package/mcp/dist/index.js
CHANGED
|
@@ -433,6 +433,36 @@ async function readFullSkillContent(skillPath) {
|
|
|
433
433
|
}
|
|
434
434
|
var MARKDOWN_LINK_RE = /\[[^\]]+\]\(([^)]+)\)/g;
|
|
435
435
|
var MAX_REFERENCED_FILES = 25;
|
|
436
|
+
function parseFrontmatter2(content) {
|
|
437
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
|
|
438
|
+
if (!match) return { raw: "", body: content };
|
|
439
|
+
return { raw: match[1], body: content.slice(match[0].length) };
|
|
440
|
+
}
|
|
441
|
+
function parseMetadataFromFrontmatter(frontmatter) {
|
|
442
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
443
|
+
const metadata = {};
|
|
444
|
+
let inMetadata = false;
|
|
445
|
+
for (const line of lines) {
|
|
446
|
+
if (/^metadata\s*:\s*$/.test(line)) {
|
|
447
|
+
inMetadata = true;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (!inMetadata) continue;
|
|
451
|
+
if (!line.trim()) continue;
|
|
452
|
+
if (!/^\s+/.test(line)) break;
|
|
453
|
+
const kv = line.match(/^\s+([A-Za-z0-9_-]+)\s*:\s*(.+)\s*$/);
|
|
454
|
+
if (!kv) continue;
|
|
455
|
+
metadata[kv[1]] = kv[2].trim().replace(/^['"]|['"]$/g, "");
|
|
456
|
+
}
|
|
457
|
+
return metadata;
|
|
458
|
+
}
|
|
459
|
+
function parseSkillFrontmatter(content) {
|
|
460
|
+
const { raw } = parseFrontmatter2(content);
|
|
461
|
+
return {
|
|
462
|
+
description: parseDescriptionFromFrontmatter(content, Number.MAX_SAFE_INTEGER),
|
|
463
|
+
metadata: parseMetadataFromFrontmatter(raw)
|
|
464
|
+
};
|
|
465
|
+
}
|
|
436
466
|
function normalizeLinkTarget(rawTarget) {
|
|
437
467
|
let target = String(rawTarget || "").trim();
|
|
438
468
|
if (!target) return null;
|
|
@@ -519,6 +549,36 @@ async function collectSiblingMarkdownTargets(skillDir) {
|
|
|
519
549
|
targets.sort((a, b) => a.localeCompare(b));
|
|
520
550
|
return targets;
|
|
521
551
|
}
|
|
552
|
+
async function listReferencedMarkdownPaths(skillPath, skillContent) {
|
|
553
|
+
const source = skillContent ?? await readFullSkillContent(skillPath);
|
|
554
|
+
const skillDir = path3.dirname(skillPath);
|
|
555
|
+
const explicitTargets = collectReferencedMarkdownTargets(source);
|
|
556
|
+
const siblingTargets = await collectSiblingMarkdownTargets(skillDir);
|
|
557
|
+
const merged = /* @__PURE__ */ new Set([...explicitTargets, ...siblingTargets]);
|
|
558
|
+
return [...merged].sort((a, b) => a.localeCompare(b)).slice(0, MAX_REFERENCED_FILES);
|
|
559
|
+
}
|
|
560
|
+
async function readSkillReferenceFile(skillPath, relativePath) {
|
|
561
|
+
const normalized = String(relativePath || "").trim();
|
|
562
|
+
if (!normalized || !normalized.toLowerCase().endsWith(".md")) {
|
|
563
|
+
throw new Error("Reference path must be a non-empty relative markdown file.");
|
|
564
|
+
}
|
|
565
|
+
const available = await listReferencedMarkdownPaths(skillPath);
|
|
566
|
+
if (!available.includes(normalized)) {
|
|
567
|
+
throw new Error(
|
|
568
|
+
`Reference path "${normalized}" is not available for this skill.`
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
const skillDir = path3.dirname(skillPath);
|
|
572
|
+
const resolved = path3.resolve(skillDir, normalized);
|
|
573
|
+
const relative = path3.relative(skillDir, resolved);
|
|
574
|
+
if (relative.startsWith("..") || path3.isAbsolute(relative)) {
|
|
575
|
+
throw new Error(`Reference path "${normalized}" escapes the skill directory.`);
|
|
576
|
+
}
|
|
577
|
+
return {
|
|
578
|
+
relativePath: relative.split(path3.sep).join("/"),
|
|
579
|
+
content: await readFile(resolved, "utf8")
|
|
580
|
+
};
|
|
581
|
+
}
|
|
522
582
|
async function readSkillContentWithReferences(skillPath, includeReferences = true) {
|
|
523
583
|
const skillContent = await readFullSkillContent(skillPath);
|
|
524
584
|
if (!includeReferences) {
|
|
@@ -527,6 +587,10 @@ async function readSkillContentWithReferences(skillPath, includeReferences = tru
|
|
|
527
587
|
const references = await readReferencedMarkdownFiles(skillPath, skillContent);
|
|
528
588
|
return { skillContent, references };
|
|
529
589
|
}
|
|
590
|
+
async function readSkillFrontmatter(skillPath) {
|
|
591
|
+
const content = await readFullSkillContent(skillPath);
|
|
592
|
+
return parseSkillFrontmatter(content);
|
|
593
|
+
}
|
|
530
594
|
async function enrichWithDescriptions(skills, maxLength) {
|
|
531
595
|
return Promise.all(
|
|
532
596
|
skills.map(async (skill) => {
|
|
@@ -539,7 +603,7 @@ async function enrichWithDescriptions(skills, maxLength) {
|
|
|
539
603
|
|
|
540
604
|
// src/server.ts
|
|
541
605
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
542
|
-
import { z as
|
|
606
|
+
import { z as z15 } from "zod";
|
|
543
607
|
|
|
544
608
|
// src/tools/skillListCategories.ts
|
|
545
609
|
import { z as z2 } from "zod";
|
|
@@ -709,13 +773,85 @@ async function handleSkillSearch(args, manifest, summaryMaxLength, charsPerToken
|
|
|
709
773
|
};
|
|
710
774
|
}
|
|
711
775
|
|
|
712
|
-
// src/tools/
|
|
776
|
+
// src/tools/skillValidate.ts
|
|
713
777
|
import { z as z5 } from "zod";
|
|
778
|
+
var skillValidateName = "skill_validate";
|
|
779
|
+
var skillValidateDescription = "Validate an exact skill ID before loading it. Returns alias metadata and discoverable reference markdown paths.";
|
|
780
|
+
var skillValidateSchema = z5.object({
|
|
781
|
+
id: z5.string().describe("The exact skill ID (directory name) to validate")
|
|
782
|
+
});
|
|
783
|
+
function assertConcreteSkillId(id) {
|
|
784
|
+
if (id.startsWith("workflow-") || id.startsWith("agent-")) {
|
|
785
|
+
invalidInput(
|
|
786
|
+
`Skill id "${id}" appears to be a wrapper id. Use workflow/agent routing (for example $workflow-implement-track or $agent-backend-specialist) and call skill_get only for concrete skill ids.`
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
async function handleSkillValidate(args, manifest, charsPerToken) {
|
|
791
|
+
const { id } = args;
|
|
792
|
+
assertConcreteSkillId(id);
|
|
793
|
+
const skill = manifest.skills.find((entry) => entry.id === id);
|
|
794
|
+
if (!skill) {
|
|
795
|
+
const payload2 = {
|
|
796
|
+
id,
|
|
797
|
+
exists: false,
|
|
798
|
+
canonicalId: null,
|
|
799
|
+
category: null,
|
|
800
|
+
description: null,
|
|
801
|
+
isWrapper: false,
|
|
802
|
+
isAlias: false,
|
|
803
|
+
replacementId: null,
|
|
804
|
+
availableReferences: []
|
|
805
|
+
};
|
|
806
|
+
const text2 = JSON.stringify(payload2, null, 2);
|
|
807
|
+
const metrics2 = buildSkillToolMetrics({
|
|
808
|
+
charsPerToken,
|
|
809
|
+
fullCatalogEstimatedTokens: manifest.fullCatalogEstimatedTokens,
|
|
810
|
+
responseEstimatedTokens: estimateTokensFromText(text2, charsPerToken),
|
|
811
|
+
responseCharacterCount: text2.length
|
|
812
|
+
});
|
|
813
|
+
return {
|
|
814
|
+
content: [{ type: "text", text: text2 }],
|
|
815
|
+
structuredContent: { ...payload2, metrics: metrics2 },
|
|
816
|
+
_meta: { metrics: metrics2 }
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
const frontmatter = await readSkillFrontmatter(skill.path);
|
|
820
|
+
const replacementId = frontmatter.metadata.replaced_by || frontmatter.metadata.alias_of || null;
|
|
821
|
+
const isAlias = Boolean(replacementId || frontmatter.metadata.deprecated);
|
|
822
|
+
const availableReferences = await listReferencedMarkdownPaths(skill.path);
|
|
823
|
+
const payload = {
|
|
824
|
+
id,
|
|
825
|
+
exists: true,
|
|
826
|
+
canonicalId: replacementId || skill.id,
|
|
827
|
+
category: skill.category,
|
|
828
|
+
description: frontmatter.description || skill.description || null,
|
|
829
|
+
isWrapper: false,
|
|
830
|
+
isAlias,
|
|
831
|
+
replacementId,
|
|
832
|
+
availableReferences
|
|
833
|
+
};
|
|
834
|
+
const text = JSON.stringify(payload, null, 2);
|
|
835
|
+
const metrics = buildSkillToolMetrics({
|
|
836
|
+
charsPerToken,
|
|
837
|
+
fullCatalogEstimatedTokens: manifest.fullCatalogEstimatedTokens,
|
|
838
|
+
responseEstimatedTokens: estimateTokensFromText(text, charsPerToken),
|
|
839
|
+
responseCharacterCount: text.length
|
|
840
|
+
});
|
|
841
|
+
return {
|
|
842
|
+
content: [{ type: "text", text }],
|
|
843
|
+
structuredContent: { ...payload, metrics },
|
|
844
|
+
_meta: { metrics }
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// src/tools/skillGet.ts
|
|
849
|
+
import { z as z6 } from "zod";
|
|
714
850
|
var skillGetName = "skill_get";
|
|
715
851
|
var skillGetDescription = "Get full content of a specific skill by ID. Returns SKILL.md content and optionally direct referenced markdown files.";
|
|
716
|
-
var skillGetSchema =
|
|
717
|
-
id:
|
|
718
|
-
includeReferences:
|
|
852
|
+
var skillGetSchema = z6.object({
|
|
853
|
+
id: z6.string().describe("The skill ID (directory name) to retrieve"),
|
|
854
|
+
includeReferences: z6.boolean().optional().describe(
|
|
719
855
|
"Whether to include direct local markdown references from SKILL.md (default: true)"
|
|
720
856
|
)
|
|
721
857
|
});
|
|
@@ -780,13 +916,63 @@ async function handleSkillGet(args, manifest, charsPerToken) {
|
|
|
780
916
|
};
|
|
781
917
|
}
|
|
782
918
|
|
|
919
|
+
// src/tools/skillGetReference.ts
|
|
920
|
+
import { z as z7 } from "zod";
|
|
921
|
+
var skillGetReferenceName = "skill_get_reference";
|
|
922
|
+
var skillGetReferenceDescription = "Get one validated markdown reference file for a skill by exact relative path.";
|
|
923
|
+
var skillGetReferenceSchema = z7.object({
|
|
924
|
+
id: z7.string().describe("The exact skill ID (directory name)"),
|
|
925
|
+
path: z7.string().describe("Exact relative markdown reference path exposed by skill_validate")
|
|
926
|
+
});
|
|
927
|
+
function assertConcreteSkillId2(id) {
|
|
928
|
+
if (id.startsWith("workflow-") || id.startsWith("agent-")) {
|
|
929
|
+
invalidInput(
|
|
930
|
+
`Skill id "${id}" appears to be a wrapper id. Use workflow/agent routing (for example $workflow-implement-track or $agent-backend-specialist) and call skill_get only for concrete skill ids.`
|
|
931
|
+
);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
async function handleSkillGetReference(args, manifest, charsPerToken) {
|
|
935
|
+
const { id, path: path8 } = args;
|
|
936
|
+
assertConcreteSkillId2(id);
|
|
937
|
+
const skill = manifest.skills.find((entry) => entry.id === id);
|
|
938
|
+
if (!skill) {
|
|
939
|
+
notFound("Skill", id);
|
|
940
|
+
}
|
|
941
|
+
let reference;
|
|
942
|
+
try {
|
|
943
|
+
reference = await readSkillReferenceFile(skill.path, path8);
|
|
944
|
+
} catch (error) {
|
|
945
|
+
invalidInput(error instanceof Error ? error.message : String(error));
|
|
946
|
+
}
|
|
947
|
+
const metrics = buildSkillToolMetrics({
|
|
948
|
+
charsPerToken,
|
|
949
|
+
fullCatalogEstimatedTokens: manifest.fullCatalogEstimatedTokens,
|
|
950
|
+
responseEstimatedTokens: estimateTokensFromText(
|
|
951
|
+
reference.content,
|
|
952
|
+
charsPerToken
|
|
953
|
+
),
|
|
954
|
+
responseCharacterCount: reference.content.length
|
|
955
|
+
});
|
|
956
|
+
return {
|
|
957
|
+
content: [{ type: "text", text: reference.content }],
|
|
958
|
+
structuredContent: {
|
|
959
|
+
skillId: id,
|
|
960
|
+
path: reference.relativePath,
|
|
961
|
+
metrics
|
|
962
|
+
},
|
|
963
|
+
_meta: {
|
|
964
|
+
metrics
|
|
965
|
+
}
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
|
|
783
969
|
// src/tools/skillBudgetReport.ts
|
|
784
|
-
import { z as
|
|
970
|
+
import { z as z8 } from "zod";
|
|
785
971
|
var skillBudgetReportName = "skill_budget_report";
|
|
786
972
|
var skillBudgetReportDescription = "Report estimated context/token budget for selected and loaded skills compared to the full skill catalog.";
|
|
787
|
-
var skillBudgetReportSchema =
|
|
788
|
-
selectedSkillIds:
|
|
789
|
-
loadedSkillIds:
|
|
973
|
+
var skillBudgetReportSchema = z8.object({
|
|
974
|
+
selectedSkillIds: z8.array(z8.string()).default([]).describe("Skill IDs selected after search/browse."),
|
|
975
|
+
loadedSkillIds: z8.array(z8.string()).default([]).describe("Skill IDs loaded via skill_get.")
|
|
790
976
|
});
|
|
791
977
|
function uniqueStrings(values) {
|
|
792
978
|
return [...new Set(values.map((value) => String(value)))];
|
|
@@ -876,7 +1062,7 @@ function handleSkillBudgetReport(args, manifest, charsPerToken) {
|
|
|
876
1062
|
}
|
|
877
1063
|
|
|
878
1064
|
// src/tools/postmanGetMode.ts
|
|
879
|
-
import { z as
|
|
1065
|
+
import { z as z9 } from "zod";
|
|
880
1066
|
|
|
881
1067
|
// src/cbxConfig/paths.ts
|
|
882
1068
|
import path4 from "path";
|
|
@@ -1136,8 +1322,8 @@ function isValidMode(mode) {
|
|
|
1136
1322
|
// src/tools/postmanGetMode.ts
|
|
1137
1323
|
var postmanGetModeName = "postman_get_mode";
|
|
1138
1324
|
var postmanGetModeDescription = "Get the current Postman MCP mode from cbx_config.json. Returns the friendly mode name and URL.";
|
|
1139
|
-
var postmanGetModeSchema =
|
|
1140
|
-
scope:
|
|
1325
|
+
var postmanGetModeSchema = z9.object({
|
|
1326
|
+
scope: z9.enum(["global", "project", "auto"]).optional().describe(
|
|
1141
1327
|
"Config scope to read. Default: auto (project if exists, else global)"
|
|
1142
1328
|
)
|
|
1143
1329
|
});
|
|
@@ -1192,12 +1378,12 @@ function handlePostmanGetMode(args) {
|
|
|
1192
1378
|
}
|
|
1193
1379
|
|
|
1194
1380
|
// src/tools/postmanSetMode.ts
|
|
1195
|
-
import { z as
|
|
1381
|
+
import { z as z10 } from "zod";
|
|
1196
1382
|
var postmanSetModeName = "postman_set_mode";
|
|
1197
1383
|
var postmanSetModeDescription = "Set the Postman MCP mode in cbx_config.json. Modes: minimal, code, full.";
|
|
1198
|
-
var postmanSetModeSchema =
|
|
1199
|
-
mode:
|
|
1200
|
-
scope:
|
|
1384
|
+
var postmanSetModeSchema = z10.object({
|
|
1385
|
+
mode: z10.enum(["minimal", "code", "full"]).describe("Postman MCP mode to set: minimal, code, or full"),
|
|
1386
|
+
scope: z10.enum(["global", "project", "auto"]).optional().describe(
|
|
1201
1387
|
"Config scope to write. Default: auto (project if exists, else global)"
|
|
1202
1388
|
)
|
|
1203
1389
|
});
|
|
@@ -1236,11 +1422,11 @@ function handlePostmanSetMode(args) {
|
|
|
1236
1422
|
}
|
|
1237
1423
|
|
|
1238
1424
|
// src/tools/postmanGetStatus.ts
|
|
1239
|
-
import { z as
|
|
1425
|
+
import { z as z11 } from "zod";
|
|
1240
1426
|
var postmanGetStatusName = "postman_get_status";
|
|
1241
1427
|
var postmanGetStatusDescription = "Get full Postman configuration status including mode, URL, and workspace ID.";
|
|
1242
|
-
var postmanGetStatusSchema =
|
|
1243
|
-
scope:
|
|
1428
|
+
var postmanGetStatusSchema = z11.object({
|
|
1429
|
+
scope: z11.enum(["global", "project", "auto"]).optional().describe(
|
|
1244
1430
|
"Config scope to read. Default: auto (project if exists, else global)"
|
|
1245
1431
|
)
|
|
1246
1432
|
});
|
|
@@ -1280,11 +1466,11 @@ function handlePostmanGetStatus(args) {
|
|
|
1280
1466
|
}
|
|
1281
1467
|
|
|
1282
1468
|
// src/tools/stitchGetMode.ts
|
|
1283
|
-
import { z as
|
|
1469
|
+
import { z as z12 } from "zod";
|
|
1284
1470
|
var stitchGetModeName = "stitch_get_mode";
|
|
1285
1471
|
var stitchGetModeDescription = "Get the active Stitch profile name and URL from cbx_config.json. Never exposes API keys.";
|
|
1286
|
-
var stitchGetModeSchema =
|
|
1287
|
-
scope:
|
|
1472
|
+
var stitchGetModeSchema = z12.object({
|
|
1473
|
+
scope: z12.enum(["global", "project", "auto"]).optional().describe(
|
|
1288
1474
|
"Config scope to read. Default: auto (project if exists, else global)"
|
|
1289
1475
|
)
|
|
1290
1476
|
});
|
|
@@ -1319,12 +1505,12 @@ function handleStitchGetMode(args) {
|
|
|
1319
1505
|
}
|
|
1320
1506
|
|
|
1321
1507
|
// src/tools/stitchSetProfile.ts
|
|
1322
|
-
import { z as
|
|
1508
|
+
import { z as z13 } from "zod";
|
|
1323
1509
|
var stitchSetProfileName = "stitch_set_profile";
|
|
1324
1510
|
var stitchSetProfileDescription = "Set the active Stitch profile in cbx_config.json. The profile must already exist in the config.";
|
|
1325
|
-
var stitchSetProfileSchema =
|
|
1326
|
-
profileName:
|
|
1327
|
-
scope:
|
|
1511
|
+
var stitchSetProfileSchema = z13.object({
|
|
1512
|
+
profileName: z13.string().min(1).describe("Name of the Stitch profile to activate"),
|
|
1513
|
+
scope: z13.enum(["global", "project", "auto"]).optional().describe(
|
|
1328
1514
|
"Config scope to write. Default: auto (project if exists, else global)"
|
|
1329
1515
|
)
|
|
1330
1516
|
});
|
|
@@ -1369,11 +1555,11 @@ function handleStitchSetProfile(args) {
|
|
|
1369
1555
|
}
|
|
1370
1556
|
|
|
1371
1557
|
// src/tools/stitchGetStatus.ts
|
|
1372
|
-
import { z as
|
|
1558
|
+
import { z as z14 } from "zod";
|
|
1373
1559
|
var stitchGetStatusName = "stitch_get_status";
|
|
1374
1560
|
var stitchGetStatusDescription = "Get full Stitch configuration status including active profile, all profile names, and URLs. Never exposes API keys.";
|
|
1375
|
-
var stitchGetStatusSchema =
|
|
1376
|
-
scope:
|
|
1561
|
+
var stitchGetStatusSchema = z14.object({
|
|
1562
|
+
scope: z14.enum(["global", "project", "auto"]).optional().describe(
|
|
1377
1563
|
"Config scope to read. Default: auto (project if exists, else global)"
|
|
1378
1564
|
)
|
|
1379
1565
|
});
|
|
@@ -1458,6 +1644,17 @@ var TOOL_REGISTRY = [
|
|
|
1458
1644
|
ctx.charsPerToken
|
|
1459
1645
|
)
|
|
1460
1646
|
},
|
|
1647
|
+
{
|
|
1648
|
+
name: skillValidateName,
|
|
1649
|
+
description: skillValidateDescription,
|
|
1650
|
+
schema: skillValidateSchema,
|
|
1651
|
+
category: "skill",
|
|
1652
|
+
createHandler: (ctx) => async (args) => handleSkillValidate(
|
|
1653
|
+
args,
|
|
1654
|
+
ctx.manifest,
|
|
1655
|
+
ctx.charsPerToken
|
|
1656
|
+
)
|
|
1657
|
+
},
|
|
1461
1658
|
{
|
|
1462
1659
|
name: skillGetName,
|
|
1463
1660
|
description: skillGetDescription,
|
|
@@ -1469,6 +1666,17 @@ var TOOL_REGISTRY = [
|
|
|
1469
1666
|
ctx.charsPerToken
|
|
1470
1667
|
)
|
|
1471
1668
|
},
|
|
1669
|
+
{
|
|
1670
|
+
name: skillGetReferenceName,
|
|
1671
|
+
description: skillGetReferenceDescription,
|
|
1672
|
+
schema: skillGetReferenceSchema,
|
|
1673
|
+
category: "skill",
|
|
1674
|
+
createHandler: (ctx) => async (args) => handleSkillGetReference(
|
|
1675
|
+
args,
|
|
1676
|
+
ctx.manifest,
|
|
1677
|
+
ctx.charsPerToken
|
|
1678
|
+
)
|
|
1679
|
+
},
|
|
1472
1680
|
{
|
|
1473
1681
|
name: skillBudgetReportName,
|
|
1474
1682
|
description: skillBudgetReportDescription,
|
|
@@ -1850,7 +2058,7 @@ async function createServer({
|
|
|
1850
2058
|
`Registered ${TOOL_REGISTRY.length} built-in tools from registry`
|
|
1851
2059
|
);
|
|
1852
2060
|
const upstreamCatalogs = await discoverUpstreamCatalogs(defaultConfigScope);
|
|
1853
|
-
const dynamicSchema =
|
|
2061
|
+
const dynamicSchema = z15.object({}).passthrough();
|
|
1854
2062
|
const registeredDynamicToolNames = /* @__PURE__ */ new Set();
|
|
1855
2063
|
for (const catalog of [upstreamCatalogs.postman, upstreamCatalogs.stitch]) {
|
|
1856
2064
|
for (const tool of catalog.tools) {
|
package/mcp/src/tools/index.ts
CHANGED
|
@@ -40,6 +40,13 @@ export {
|
|
|
40
40
|
handleSkillSearch,
|
|
41
41
|
} from "./skillSearch.js";
|
|
42
42
|
|
|
43
|
+
export {
|
|
44
|
+
skillValidateName,
|
|
45
|
+
skillValidateDescription,
|
|
46
|
+
skillValidateSchema,
|
|
47
|
+
handleSkillValidate,
|
|
48
|
+
} from "./skillValidate.js";
|
|
49
|
+
|
|
43
50
|
export {
|
|
44
51
|
skillGetName,
|
|
45
52
|
skillGetDescription,
|
|
@@ -47,6 +54,13 @@ export {
|
|
|
47
54
|
handleSkillGet,
|
|
48
55
|
} from "./skillGet.js";
|
|
49
56
|
|
|
57
|
+
export {
|
|
58
|
+
skillGetReferenceName,
|
|
59
|
+
skillGetReferenceDescription,
|
|
60
|
+
skillGetReferenceSchema,
|
|
61
|
+
handleSkillGetReference,
|
|
62
|
+
} from "./skillGetReference.js";
|
|
63
|
+
|
|
50
64
|
export {
|
|
51
65
|
skillBudgetReportName,
|
|
52
66
|
skillBudgetReportDescription,
|
|
@@ -38,7 +38,9 @@ describe("tool registry", () => {
|
|
|
38
38
|
expect(names).toContain("skill_list_categories");
|
|
39
39
|
expect(names).toContain("skill_browse_category");
|
|
40
40
|
expect(names).toContain("skill_search");
|
|
41
|
+
expect(names).toContain("skill_validate");
|
|
41
42
|
expect(names).toContain("skill_get");
|
|
43
|
+
expect(names).toContain("skill_get_reference");
|
|
42
44
|
expect(names).toContain("skill_budget_report");
|
|
43
45
|
expect(names).toContain("postman_get_mode");
|
|
44
46
|
expect(names).toContain("postman_set_mode");
|
|
@@ -48,8 +50,8 @@ describe("tool registry", () => {
|
|
|
48
50
|
expect(names).toContain("stitch_get_status");
|
|
49
51
|
});
|
|
50
52
|
|
|
51
|
-
it("has exactly
|
|
52
|
-
expect(TOOL_REGISTRY).toHaveLength(
|
|
53
|
+
it("has exactly 13 built-in tools", () => {
|
|
54
|
+
expect(TOOL_REGISTRY).toHaveLength(13);
|
|
53
55
|
});
|
|
54
56
|
|
|
55
57
|
it("has no duplicate tool names", () => {
|
|
@@ -70,7 +72,7 @@ describe("tool registry", () => {
|
|
|
70
72
|
|
|
71
73
|
it("filters by category", () => {
|
|
72
74
|
const skillTools = getToolsByCategory("skill");
|
|
73
|
-
expect(skillTools).toHaveLength(
|
|
75
|
+
expect(skillTools).toHaveLength(7);
|
|
74
76
|
expect(skillTools.every((t) => t.category === "skill")).toBe(true);
|
|
75
77
|
|
|
76
78
|
const postmanTools = getToolsByCategory("postman");
|
|
@@ -103,11 +105,11 @@ describe("tool registry", () => {
|
|
|
103
105
|
|
|
104
106
|
it("buildRegistrySummary produces correct structure", () => {
|
|
105
107
|
const summary = buildRegistrySummary();
|
|
106
|
-
expect(summary.totalTools).toBe(
|
|
108
|
+
expect(summary.totalTools).toBe(13);
|
|
107
109
|
expect(summary.categories).toHaveProperty("skill");
|
|
108
110
|
expect(summary.categories).toHaveProperty("postman");
|
|
109
111
|
expect(summary.categories).toHaveProperty("stitch");
|
|
110
|
-
expect(summary.categories.skill.tools).toHaveLength(
|
|
112
|
+
expect(summary.categories.skill.tools).toHaveLength(7);
|
|
111
113
|
expect(summary.categories.postman.tools).toHaveLength(3);
|
|
112
114
|
expect(summary.categories.stitch.tools).toHaveLength(3);
|
|
113
115
|
});
|
|
@@ -67,6 +67,13 @@ import {
|
|
|
67
67
|
handleSkillSearch,
|
|
68
68
|
} from "./skillSearch.js";
|
|
69
69
|
|
|
70
|
+
import {
|
|
71
|
+
skillValidateName,
|
|
72
|
+
skillValidateDescription,
|
|
73
|
+
skillValidateSchema,
|
|
74
|
+
handleSkillValidate,
|
|
75
|
+
} from "./skillValidate.js";
|
|
76
|
+
|
|
70
77
|
import {
|
|
71
78
|
skillGetName,
|
|
72
79
|
skillGetDescription,
|
|
@@ -74,6 +81,13 @@ import {
|
|
|
74
81
|
handleSkillGet,
|
|
75
82
|
} from "./skillGet.js";
|
|
76
83
|
|
|
84
|
+
import {
|
|
85
|
+
skillGetReferenceName,
|
|
86
|
+
skillGetReferenceDescription,
|
|
87
|
+
skillGetReferenceSchema,
|
|
88
|
+
handleSkillGetReference,
|
|
89
|
+
} from "./skillGetReference.js";
|
|
90
|
+
|
|
77
91
|
import {
|
|
78
92
|
skillBudgetReportName,
|
|
79
93
|
skillBudgetReportDescription,
|
|
@@ -175,6 +189,18 @@ export const TOOL_REGISTRY: readonly ToolRegistryEntry[] = [
|
|
|
175
189
|
ctx.charsPerToken,
|
|
176
190
|
),
|
|
177
191
|
},
|
|
192
|
+
{
|
|
193
|
+
name: skillValidateName,
|
|
194
|
+
description: skillValidateDescription,
|
|
195
|
+
schema: skillValidateSchema,
|
|
196
|
+
category: "skill",
|
|
197
|
+
createHandler: (ctx) => async (args) =>
|
|
198
|
+
handleSkillValidate(
|
|
199
|
+
args as z.infer<typeof skillValidateSchema>,
|
|
200
|
+
ctx.manifest,
|
|
201
|
+
ctx.charsPerToken,
|
|
202
|
+
),
|
|
203
|
+
},
|
|
178
204
|
{
|
|
179
205
|
name: skillGetName,
|
|
180
206
|
description: skillGetDescription,
|
|
@@ -187,6 +213,18 @@ export const TOOL_REGISTRY: readonly ToolRegistryEntry[] = [
|
|
|
187
213
|
ctx.charsPerToken,
|
|
188
214
|
),
|
|
189
215
|
},
|
|
216
|
+
{
|
|
217
|
+
name: skillGetReferenceName,
|
|
218
|
+
description: skillGetReferenceDescription,
|
|
219
|
+
schema: skillGetReferenceSchema,
|
|
220
|
+
category: "skill",
|
|
221
|
+
createHandler: (ctx) => async (args) =>
|
|
222
|
+
handleSkillGetReference(
|
|
223
|
+
args as z.infer<typeof skillGetReferenceSchema>,
|
|
224
|
+
ctx.manifest,
|
|
225
|
+
ctx.charsPerToken,
|
|
226
|
+
),
|
|
227
|
+
},
|
|
190
228
|
{
|
|
191
229
|
name: skillBudgetReportName,
|
|
192
230
|
description: skillBudgetReportDescription,
|