@stackwright-pro/mcp 0.2.0-alpha.112 → 0.2.0-alpha.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/integrity.js +7 -3
- package/dist/integrity.js.map +1 -1
- package/dist/integrity.mjs +7 -3
- package/dist/integrity.mjs.map +1 -1
- package/dist/pipeline-constants.d.mts +5 -3
- package/dist/pipeline-constants.d.ts +5 -3
- package/dist/pipeline-constants.js +30 -1
- package/dist/pipeline-constants.js.map +1 -1
- package/dist/pipeline-constants.mjs +30 -1
- package/dist/pipeline-constants.mjs.map +1 -1
- package/dist/server.js +292 -42
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +283 -33
- package/dist/server.mjs.map +1 -1
- package/package.json +6 -6
package/dist/server.js
CHANGED
|
@@ -580,7 +580,7 @@ function registerDashboardTools(server2) {
|
|
|
580
580
|
yamlLines.push(` header: Status`);
|
|
581
581
|
yamlLines.push(` type: badge`);
|
|
582
582
|
}
|
|
583
|
-
const
|
|
583
|
+
const yaml4 = yamlLines.join("\n");
|
|
584
584
|
return {
|
|
585
585
|
content: [
|
|
586
586
|
{
|
|
@@ -590,7 +590,7 @@ function registerDashboardTools(server2) {
|
|
|
590
590
|
Layout: ${layout}
|
|
591
591
|
|
|
592
592
|
\`\`\`yaml
|
|
593
|
-
${
|
|
593
|
+
${yaml4}
|
|
594
594
|
\`\`\`
|
|
595
595
|
|
|
596
596
|
\u{1F4A1} Add this to pages/dashboard/content.yml and validate with stackwright_validate_pages.`
|
|
@@ -671,7 +671,7 @@ ${yaml3}
|
|
|
671
671
|
yamlLines.push(" action: navigate");
|
|
672
672
|
yamlLines.push(` href: "/${entity}"`);
|
|
673
673
|
yamlLines.push(" style: secondary");
|
|
674
|
-
const
|
|
674
|
+
const yaml4 = yamlLines.join("\n");
|
|
675
675
|
return {
|
|
676
676
|
content: [
|
|
677
677
|
{
|
|
@@ -679,7 +679,7 @@ ${yaml3}
|
|
|
679
679
|
text: `\u{1F4C4} Generated Detail Page for: ${entity}
|
|
680
680
|
|
|
681
681
|
\`\`\`yaml
|
|
682
|
-
${
|
|
682
|
+
${yaml4}
|
|
683
683
|
\`\`\`
|
|
684
684
|
|
|
685
685
|
\u{1F4A1} Add this to pages/${entity}/[${slugField}]/content.yml and validate.`
|
|
@@ -2834,21 +2834,40 @@ function readAllManifestPages(artifactsDir) {
|
|
|
2834
2834
|
return allPages;
|
|
2835
2835
|
}
|
|
2836
2836
|
for (const entry of entries) {
|
|
2837
|
-
if (
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2837
|
+
if (entry.endsWith("-manifest.json")) {
|
|
2838
|
+
const filePath = (0, import_path6.join)(artifactsDir, entry);
|
|
2839
|
+
try {
|
|
2840
|
+
const raw = JSON.parse((0, import_fs6.readFileSync)(filePath, "utf-8"));
|
|
2841
|
+
if (!Array.isArray(raw.pages)) continue;
|
|
2842
|
+
for (const page of raw.pages) {
|
|
2843
|
+
if (typeof page.slug !== "string" || !page.slug) continue;
|
|
2844
|
+
allPages.push({
|
|
2845
|
+
slug: page.slug,
|
|
2846
|
+
authRequired: page.authRequired,
|
|
2847
|
+
requiredRoles: page.requiredRoles,
|
|
2848
|
+
source: entry
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
} catch {
|
|
2852
|
+
}
|
|
2853
|
+
continue;
|
|
2854
|
+
}
|
|
2855
|
+
if (entry === "workflow-config.json" || entry.startsWith("workflow-config-")) {
|
|
2856
|
+
const filePath = (0, import_path6.join)(artifactsDir, entry);
|
|
2857
|
+
try {
|
|
2858
|
+
const raw = JSON.parse((0, import_fs6.readFileSync)(filePath, "utf-8"));
|
|
2859
|
+
const route = raw.workflow?.route;
|
|
2860
|
+
if (typeof route !== "string" || !route) continue;
|
|
2861
|
+
const slug = route.startsWith("/") ? route.slice(1) : route;
|
|
2844
2862
|
allPages.push({
|
|
2845
|
-
slug
|
|
2846
|
-
authRequired:
|
|
2847
|
-
requiredRoles:
|
|
2863
|
+
slug,
|
|
2864
|
+
authRequired: true,
|
|
2865
|
+
requiredRoles: void 0,
|
|
2848
2866
|
source: entry
|
|
2849
2867
|
});
|
|
2868
|
+
} catch {
|
|
2850
2869
|
}
|
|
2851
|
-
|
|
2870
|
+
continue;
|
|
2852
2871
|
}
|
|
2853
2872
|
}
|
|
2854
2873
|
return allPages;
|
|
@@ -2856,7 +2875,7 @@ function readAllManifestPages(artifactsDir) {
|
|
|
2856
2875
|
function registerReadAuthManifestsTool(server2) {
|
|
2857
2876
|
server2.tool(
|
|
2858
2877
|
"stackwright_pro_read_auth_manifests",
|
|
2859
|
-
"Read ALL
|
|
2878
|
+
"Read ALL route-producing artifacts from the pipeline artifacts directory: *-manifest.json files (pages, dashboard, geo, form-wizard) AND workflow-config.json / workflow-config-*.json (different shape: workflow.route). Deterministically derives protectedRoutes and uncoveredSlugs. Uses the both-forms invariant (swp-n7kk): every authRequired route registers BOTH /{slug} (exact) AND /{slug}/:path* (sub-path). Workflow routes appear in uncoveredSlugs (no requiredRoles declared) for auth-reconcile to resolve via cross-cutting rules from auth-config.json. Extended by swp-hng0 to cover all four manifest types (was: pages+dashboard only).",
|
|
2860
2879
|
{
|
|
2861
2880
|
rbacRoles: import_zod11.z.array(import_zod11.z.string()).describe(
|
|
2862
2881
|
'RBAC role hierarchy in descending privilege order, e.g. ["ESF8_COORDINATOR", "HOSPITAL_EM", "OBSERVER"]. Used to compute the lowestRole for each authRequired page.'
|
|
@@ -2990,7 +3009,7 @@ function enrichErrors(issues, synonyms) {
|
|
|
2990
3009
|
});
|
|
2991
3010
|
}
|
|
2992
3011
|
function handleValidateYamlFragment(input) {
|
|
2993
|
-
const { schemaName, yaml:
|
|
3012
|
+
const { schemaName, yaml: yaml4 } = input;
|
|
2994
3013
|
if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
|
|
2995
3014
|
return {
|
|
2996
3015
|
valid: false,
|
|
@@ -3000,7 +3019,7 @@ function handleValidateYamlFragment(input) {
|
|
|
3000
3019
|
const name = schemaName;
|
|
3001
3020
|
let parsed;
|
|
3002
3021
|
try {
|
|
3003
|
-
parsed = (0, import_js_yaml2.load)(
|
|
3022
|
+
parsed = (0, import_js_yaml2.load)(yaml4);
|
|
3004
3023
|
} catch (err) {
|
|
3005
3024
|
return {
|
|
3006
3025
|
valid: false,
|
|
@@ -3026,8 +3045,8 @@ function registerValidateYamlFragmentTool(server2) {
|
|
|
3026
3045
|
),
|
|
3027
3046
|
yaml: import_zod12.z.string().describe("YAML string to validate (can also be JSON \u2014 js-yaml parses both)")
|
|
3028
3047
|
},
|
|
3029
|
-
async ({ schemaName, yaml:
|
|
3030
|
-
const result = handleValidateYamlFragment({ schemaName, yaml:
|
|
3048
|
+
async ({ schemaName, yaml: yaml4 }) => {
|
|
3049
|
+
const result = handleValidateYamlFragment({ schemaName, yaml: yaml4 });
|
|
3031
3050
|
return {
|
|
3032
3051
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
3033
3052
|
};
|
|
@@ -3240,6 +3259,9 @@ var MANIFEST_NAME_TO_PHASE = {
|
|
|
3240
3259
|
"stackwright-pro-dashboard-otter": "dashboard",
|
|
3241
3260
|
"stackwright-pro-form-wizard-otter": "workflow",
|
|
3242
3261
|
"stackwright-pro-geo-otter": "geo",
|
|
3262
|
+
// auth-reconcile runs AFTER pages+dashboard+geo+workflow (wave 6)
|
|
3263
|
+
// Reads auth-config.json + all four manifests; emits auth-reconcile-config.json + stackwright.auth.yml
|
|
3264
|
+
"stackwright-pro-auth-reconcile-otter": "auth-reconcile",
|
|
3243
3265
|
"stackwright-pro-polish-otter": "polish",
|
|
3244
3266
|
"stackwright-services-otter": "services",
|
|
3245
3267
|
"stackwright-pro-qa-otter": "qa"
|
|
@@ -3455,13 +3477,19 @@ var PHASE_ORDER = [
|
|
|
3455
3477
|
"api",
|
|
3456
3478
|
"data",
|
|
3457
3479
|
"auth",
|
|
3458
|
-
// moved earlier
|
|
3480
|
+
// wave 2 — moved earlier, only depends on design-language.json (designer)
|
|
3459
3481
|
"geo",
|
|
3460
3482
|
"workflow",
|
|
3461
3483
|
"services",
|
|
3462
3484
|
"pages",
|
|
3463
3485
|
"dashboard",
|
|
3486
|
+
// auth-reconcile: wave 6 — post-manifest reconciliation
|
|
3487
|
+
// Reads auth-config.json + pages/dashboard/geo/workflow manifests;
|
|
3488
|
+
// emits auth-reconcile-config.json + stackwright.auth.yml (Gate 4 single source of truth)
|
|
3489
|
+
"auth-reconcile",
|
|
3490
|
+
// wave 7: polish — depends on auth-reconcile (replaces auth-config.json direct dep)
|
|
3464
3491
|
"polish",
|
|
3492
|
+
// wave 8: qa — terminal QA gate, depends on polish + transitively everything
|
|
3465
3493
|
"qa"
|
|
3466
3494
|
];
|
|
3467
3495
|
var PHASE_ARTIFACT = {
|
|
@@ -3475,6 +3503,7 @@ var PHASE_ARTIFACT = {
|
|
|
3475
3503
|
dashboard: "dashboard-manifest.json",
|
|
3476
3504
|
workflow: "workflow-config.json",
|
|
3477
3505
|
services: "services-config.json",
|
|
3506
|
+
"auth-reconcile": "auth-reconcile-config.json",
|
|
3478
3507
|
polish: "polish-manifest.json",
|
|
3479
3508
|
geo: "geo-manifest.json",
|
|
3480
3509
|
qa: "qa-findings.json"
|
|
@@ -3489,11 +3518,13 @@ var PHASE_TO_OTTER2 = {
|
|
|
3489
3518
|
pages: "stackwright-pro-page-otter",
|
|
3490
3519
|
dashboard: "stackwright-pro-dashboard-otter",
|
|
3491
3520
|
workflow: "stackwright-pro-form-wizard-otter",
|
|
3521
|
+
"auth-reconcile": "stackwright-pro-auth-reconcile-otter",
|
|
3492
3522
|
services: "stackwright-services-otter",
|
|
3493
3523
|
polish: "stackwright-pro-polish-otter",
|
|
3494
3524
|
geo: "stackwright-pro-geo-otter",
|
|
3495
3525
|
qa: "stackwright-pro-qa-otter"
|
|
3496
3526
|
};
|
|
3527
|
+
var AUTO_EXECUTE_PHASES = /* @__PURE__ */ new Set(["auth-reconcile"]);
|
|
3497
3528
|
var PhaseStatusSchema = import_zod14.z.object({
|
|
3498
3529
|
status: import_zod14.z.enum(["pending", "running", "completed", "skipped", "failed"]).default("pending"),
|
|
3499
3530
|
questionsCollected: import_zod14.z.boolean().default(false),
|
|
@@ -3750,6 +3781,9 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3750
3781
|
isError: true
|
|
3751
3782
|
};
|
|
3752
3783
|
}
|
|
3784
|
+
if (AUTO_EXECUTE_PHASES.has(phase)) {
|
|
3785
|
+
return { text: JSON.stringify({ ready: true, phase, autoExecute: true }), isError: false };
|
|
3786
|
+
}
|
|
3753
3787
|
const answerFile = (0, import_path8.join)(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3754
3788
|
if (!(0, import_fs8.existsSync)(answerFile)) {
|
|
3755
3789
|
return {
|
|
@@ -3779,6 +3813,10 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3779
3813
|
const answeredPhases = [];
|
|
3780
3814
|
const missingPhases = [];
|
|
3781
3815
|
for (const phase2 of PHASE_ORDER) {
|
|
3816
|
+
if (AUTO_EXECUTE_PHASES.has(phase2)) {
|
|
3817
|
+
answeredPhases.push(phase2);
|
|
3818
|
+
continue;
|
|
3819
|
+
}
|
|
3782
3820
|
const answerFile = (0, import_path8.join)(answersDir, `${phase2}.json`);
|
|
3783
3821
|
if ((0, import_fs8.existsSync)(answerFile)) {
|
|
3784
3822
|
try {
|
|
@@ -4107,6 +4145,8 @@ var PHASE_REQUIRED_KEYS = {
|
|
|
4107
4145
|
dashboard: ["version", "generatedBy"],
|
|
4108
4146
|
workflow: ["version", "generatedBy"],
|
|
4109
4147
|
services: ["version", "generatedBy", "flows"],
|
|
4148
|
+
// auth-reconcile: must have version, generatedBy, and merged protectedRoutes
|
|
4149
|
+
"auth-reconcile": ["version", "generatedBy", "protectedRoutes"],
|
|
4110
4150
|
polish: ["version", "generatedBy"],
|
|
4111
4151
|
geo: ["version", "generatedBy", "geoCollections"],
|
|
4112
4152
|
// qa: skipped=true path only needs version+generatedBy+skipped;
|
|
@@ -4386,6 +4426,28 @@ var PHASE_ARTIFACT_SCHEMA = {
|
|
|
4386
4426
|
null,
|
|
4387
4427
|
2
|
|
4388
4428
|
),
|
|
4429
|
+
"auth-reconcile": JSON.stringify(
|
|
4430
|
+
{
|
|
4431
|
+
version: "1.0",
|
|
4432
|
+
generatedBy: "stackwright-pro-auth-reconcile-otter",
|
|
4433
|
+
authType: "<oidc|saml|cac|oauth2>",
|
|
4434
|
+
rbacRoles: ["<HIGHEST_ROLE>", "<MID_ROLE>", "<LOWEST_ROLE>"],
|
|
4435
|
+
rbacDefaultRole: "<LOWEST_ROLE>",
|
|
4436
|
+
protectedRoutes: [
|
|
4437
|
+
{ pattern: "/dashboard", requiredRole: "<role>" },
|
|
4438
|
+
{ pattern: "/dashboard/:path*", requiredRole: "<role>" }
|
|
4439
|
+
],
|
|
4440
|
+
uncoveredSlugs: [],
|
|
4441
|
+
manifestsRead: ["pages-manifest.json", "dashboard-manifest.json", "geo-manifest.json"],
|
|
4442
|
+
summary: {
|
|
4443
|
+
totalRoutes: 2,
|
|
4444
|
+
manifestDerived: 2,
|
|
4445
|
+
crossCutting: 0
|
|
4446
|
+
}
|
|
4447
|
+
},
|
|
4448
|
+
null,
|
|
4449
|
+
2
|
|
4450
|
+
),
|
|
4389
4451
|
polish: JSON.stringify(
|
|
4390
4452
|
{
|
|
4391
4453
|
version: "1.0",
|
|
@@ -5137,6 +5199,20 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
5137
5199
|
description: "Stackwright config (themeName + customTheme writeback)"
|
|
5138
5200
|
}
|
|
5139
5201
|
],
|
|
5202
|
+
// swp-hng0: auth-reconcile otter — reads all manifests, re-emits auth-reconcile-config.json
|
|
5203
|
+
// AND overwrites stackwright.auth.yml (Gate 4: single source of truth after reconciliation)
|
|
5204
|
+
"stackwright-pro-auth-reconcile-otter": [
|
|
5205
|
+
{
|
|
5206
|
+
prefix: ".stackwright/artifacts/",
|
|
5207
|
+
suffix: ".json",
|
|
5208
|
+
description: "Auth reconcile artifact (auth-reconcile-config.json)"
|
|
5209
|
+
},
|
|
5210
|
+
{
|
|
5211
|
+
prefix: "stackwright.auth.",
|
|
5212
|
+
suffix: ".yml",
|
|
5213
|
+
description: "Canonical auth sidecar (stackwright.auth.yml \u2014 Gate 4 rewrite)"
|
|
5214
|
+
}
|
|
5215
|
+
],
|
|
5140
5216
|
"stackwright-pro-auth-otter": [
|
|
5141
5217
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Auth config artifact" },
|
|
5142
5218
|
{
|
|
@@ -6560,7 +6636,11 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6560
6636
|
],
|
|
6561
6637
|
[
|
|
6562
6638
|
"stackwright-pro-auth-otter.json",
|
|
6563
|
-
"
|
|
6639
|
+
"c9302132ac4180c70dd452814eb4aa4fb9a1d6a9683f6e01ffce509a6001c6cf"
|
|
6640
|
+
],
|
|
6641
|
+
[
|
|
6642
|
+
"stackwright-pro-auth-reconcile-otter.json",
|
|
6643
|
+
"7ffd67d51568c7b2a1eb2ea0ac2b2a8041eb9ddd9c1eb5b95744e55ba049b385"
|
|
6564
6644
|
],
|
|
6565
6645
|
[
|
|
6566
6646
|
"stackwright-pro-dashboard-otter.json",
|
|
@@ -6568,7 +6648,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6568
6648
|
],
|
|
6569
6649
|
[
|
|
6570
6650
|
"stackwright-pro-data-otter.json",
|
|
6571
|
-
"
|
|
6651
|
+
"7d1de846488d84eebf8798aef4d75f4f4d250c1ae070ce1200ead3bb8a545eaf"
|
|
6572
6652
|
],
|
|
6573
6653
|
[
|
|
6574
6654
|
"stackwright-pro-designer-otter.json",
|
|
@@ -6596,7 +6676,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6596
6676
|
],
|
|
6597
6677
|
[
|
|
6598
6678
|
"stackwright-pro-polish-otter.json",
|
|
6599
|
-
"
|
|
6679
|
+
"3ae1252a60727ebca67e33a807d5061cafb88de52f3b98a9d4641f9db725a6ed"
|
|
6600
6680
|
],
|
|
6601
6681
|
[
|
|
6602
6682
|
"stackwright-pro-qa-otter.json",
|
|
@@ -8501,16 +8581,185 @@ function registerListSpecsTool(server2) {
|
|
|
8501
8581
|
);
|
|
8502
8582
|
}
|
|
8503
8583
|
|
|
8504
|
-
// src/tools/
|
|
8584
|
+
// src/tools/describe-endpoint.ts
|
|
8505
8585
|
var import_zod25 = require("zod");
|
|
8506
8586
|
var import_fs18 = require("fs");
|
|
8507
|
-
var import_proper_lockfile2 = require("proper-lockfile");
|
|
8508
8587
|
var import_path18 = require("path");
|
|
8509
8588
|
var import_js_yaml5 = __toESM(require("js-yaml"));
|
|
8589
|
+
function resolveRef(root, ref) {
|
|
8590
|
+
if (!ref.startsWith("#/")) {
|
|
8591
|
+
throw new Error(`[describe_endpoint] External $refs not supported: "${ref}"`);
|
|
8592
|
+
}
|
|
8593
|
+
const parts = ref.slice(2).split("/");
|
|
8594
|
+
let current = root;
|
|
8595
|
+
for (const part of parts) {
|
|
8596
|
+
const key = part.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
8597
|
+
if (current == null || typeof current !== "object") {
|
|
8598
|
+
throw new Error(
|
|
8599
|
+
`[describe_endpoint] $ref "${ref}" cannot be resolved \u2014 segment "${key}" not found`
|
|
8600
|
+
);
|
|
8601
|
+
}
|
|
8602
|
+
current = current[key];
|
|
8603
|
+
}
|
|
8604
|
+
if (current == null || typeof current !== "object") {
|
|
8605
|
+
throw new Error(`[describe_endpoint] $ref "${ref}" resolved to a non-object value`);
|
|
8606
|
+
}
|
|
8607
|
+
return current;
|
|
8608
|
+
}
|
|
8609
|
+
function deref(root, obj) {
|
|
8610
|
+
if (obj == null || typeof obj !== "object") return {};
|
|
8611
|
+
const o = obj;
|
|
8612
|
+
if (typeof o["$ref"] === "string") {
|
|
8613
|
+
const resolved = resolveRef(root, o["$ref"]);
|
|
8614
|
+
return deref(root, resolved);
|
|
8615
|
+
}
|
|
8616
|
+
return o;
|
|
8617
|
+
}
|
|
8618
|
+
function loadSpec(specPath) {
|
|
8619
|
+
const raw = (0, import_fs18.readFileSync)(specPath, "utf-8");
|
|
8620
|
+
const ext = (0, import_path18.extname)(specPath).toLowerCase();
|
|
8621
|
+
if (ext === ".json") {
|
|
8622
|
+
return JSON.parse(raw);
|
|
8623
|
+
}
|
|
8624
|
+
const parsed = import_js_yaml5.default.load(raw);
|
|
8625
|
+
if (parsed == null || typeof parsed !== "object") {
|
|
8626
|
+
throw new Error(`[describe_endpoint] Spec file "${specPath}" did not parse to an object`);
|
|
8627
|
+
}
|
|
8628
|
+
return parsed;
|
|
8629
|
+
}
|
|
8630
|
+
function extractParams(root, pathItem, operation) {
|
|
8631
|
+
const pathLevelRaw = Array.isArray(pathItem["parameters"]) ? pathItem["parameters"] : [];
|
|
8632
|
+
const opLevelRaw = Array.isArray(operation["parameters"]) ? operation["parameters"] : [];
|
|
8633
|
+
const merged = /* @__PURE__ */ new Map();
|
|
8634
|
+
for (const raw of [...pathLevelRaw, ...opLevelRaw]) {
|
|
8635
|
+
const param = deref(root, raw);
|
|
8636
|
+
const name = String(param["name"] ?? "");
|
|
8637
|
+
const inVal = String(param["in"] ?? "");
|
|
8638
|
+
if (name && inVal) {
|
|
8639
|
+
merged.set(`${name}:${inVal}`, param);
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
const pathParams = [];
|
|
8643
|
+
const queryParams = [];
|
|
8644
|
+
for (const param of merged.values()) {
|
|
8645
|
+
const inVal = String(param["in"] ?? "");
|
|
8646
|
+
const name = String(param["name"] ?? "");
|
|
8647
|
+
const schema = deref(root, param["schema"] ?? {});
|
|
8648
|
+
if (inVal === "path") {
|
|
8649
|
+
pathParams.push({ name, in: "path", required: true, schema });
|
|
8650
|
+
} else if (inVal === "query") {
|
|
8651
|
+
const required = param["required"] === true;
|
|
8652
|
+
queryParams.push({ name, in: "query", required, schema });
|
|
8653
|
+
}
|
|
8654
|
+
}
|
|
8655
|
+
return { pathParams, queryParams };
|
|
8656
|
+
}
|
|
8657
|
+
function extractSecurity(root, globalSecurity, operationSecurity) {
|
|
8658
|
+
const securityRequirements = operationSecurity !== void 0 ? operationSecurity : globalSecurity;
|
|
8659
|
+
if (securityRequirements.length === 0) return [];
|
|
8660
|
+
const schemes = root["components"]?.["securitySchemes"];
|
|
8661
|
+
if (!schemes || typeof schemes !== "object") return [];
|
|
8662
|
+
const result = [];
|
|
8663
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8664
|
+
for (const req of securityRequirements) {
|
|
8665
|
+
if (!req || typeof req !== "object") continue;
|
|
8666
|
+
for (const schemeName of Object.keys(req)) {
|
|
8667
|
+
if (seen.has(schemeName)) continue;
|
|
8668
|
+
seen.add(schemeName);
|
|
8669
|
+
const schemeRaw = schemes[schemeName];
|
|
8670
|
+
if (!schemeRaw) continue;
|
|
8671
|
+
const scheme = deref(root, schemeRaw);
|
|
8672
|
+
const type = String(scheme["type"] ?? "unknown");
|
|
8673
|
+
if (type === "apiKey") {
|
|
8674
|
+
const inVal = String(scheme["in"] ?? "header");
|
|
8675
|
+
const paramName = String(scheme["name"] ?? schemeName);
|
|
8676
|
+
result.push({ name: schemeName, type, in: inVal, paramName });
|
|
8677
|
+
} else if (type === "http") {
|
|
8678
|
+
const httpScheme = String(scheme["scheme"] ?? "bearer").toLowerCase();
|
|
8679
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8680
|
+
void httpScheme;
|
|
8681
|
+
} else if (type === "oauth2") {
|
|
8682
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8683
|
+
} else if (type === "openIdConnect") {
|
|
8684
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8685
|
+
} else {
|
|
8686
|
+
result.push({ name: schemeName, type, in: "unknown", paramName: schemeName });
|
|
8687
|
+
}
|
|
8688
|
+
}
|
|
8689
|
+
}
|
|
8690
|
+
return result;
|
|
8691
|
+
}
|
|
8692
|
+
function handleDescribeEndpoint(input) {
|
|
8693
|
+
const { specPath, path: path5, method } = input;
|
|
8694
|
+
const normalizedMethod = method.toLowerCase();
|
|
8695
|
+
const root = loadSpec(specPath);
|
|
8696
|
+
const globalSecurity = Array.isArray(root["security"]) ? root["security"] : [];
|
|
8697
|
+
const paths = root["paths"];
|
|
8698
|
+
if (!paths) {
|
|
8699
|
+
return { pathParams: [], queryParams: [], security: [] };
|
|
8700
|
+
}
|
|
8701
|
+
const pathItem = deref(root, paths[path5]);
|
|
8702
|
+
if (!pathItem || Object.keys(pathItem).length === 0) {
|
|
8703
|
+
throw new Error(`[describe_endpoint] Path "${path5}" not found in spec "${specPath}"`);
|
|
8704
|
+
}
|
|
8705
|
+
const operation = deref(root, pathItem[normalizedMethod]);
|
|
8706
|
+
if (!operation || Object.keys(operation).length === 0) {
|
|
8707
|
+
throw new Error(
|
|
8708
|
+
`[describe_endpoint] Method "${method.toUpperCase()}" not found at path "${path5}" in spec "${specPath}"`
|
|
8709
|
+
);
|
|
8710
|
+
}
|
|
8711
|
+
const { pathParams, queryParams } = extractParams(root, pathItem, operation);
|
|
8712
|
+
const operationSecurity = Array.isArray(operation["security"]) ? operation["security"] : void 0;
|
|
8713
|
+
const security = extractSecurity(root, globalSecurity, operationSecurity);
|
|
8714
|
+
return { pathParams, queryParams, security };
|
|
8715
|
+
}
|
|
8716
|
+
function registerDescribeEndpointTool(server2) {
|
|
8717
|
+
server2.tool(
|
|
8718
|
+
"stackwright_pro_describe_endpoint",
|
|
8719
|
+
[
|
|
8720
|
+
"Inspect an OpenAPI 3.0/3.1 spec and return resolved parameter and security details for a",
|
|
8721
|
+
"specific operation. Deterministic \u2014 no LLM. Handles $ref resolution within the spec document.",
|
|
8722
|
+
"",
|
|
8723
|
+
"Returns:",
|
|
8724
|
+
" pathParams \u2014 path parameters [{name, in, required, schema}]",
|
|
8725
|
+
" queryParams \u2014 query parameters [{name, in, required, schema}]",
|
|
8726
|
+
" security \u2014 applicable security schemes [{name, type, in, paramName}]",
|
|
8727
|
+
"",
|
|
8728
|
+
"Used by the data-otter to populate params: entries in stackwright.collections.yml",
|
|
8729
|
+
"from required query params and resolved auth query params (swp-8r8i + swp-1l6l)."
|
|
8730
|
+
].join("\n"),
|
|
8731
|
+
{
|
|
8732
|
+
specPath: import_zod25.z.string().describe("Absolute path to the OpenAPI spec file (.yaml, .yml, or .json)"),
|
|
8733
|
+
path: import_zod25.z.string().describe('The API path to inspect (e.g. "/stations/{stationId}/observations")'),
|
|
8734
|
+
method: import_zod25.z.string().describe('HTTP method (case-insensitive: "get", "post", "put", etc.)')
|
|
8735
|
+
},
|
|
8736
|
+
async ({ specPath, path: path5, method }) => {
|
|
8737
|
+
try {
|
|
8738
|
+
const result = handleDescribeEndpoint({ specPath, path: path5, method });
|
|
8739
|
+
return {
|
|
8740
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
8741
|
+
};
|
|
8742
|
+
} catch (err) {
|
|
8743
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8744
|
+
return {
|
|
8745
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
8746
|
+
isError: true
|
|
8747
|
+
};
|
|
8748
|
+
}
|
|
8749
|
+
}
|
|
8750
|
+
);
|
|
8751
|
+
}
|
|
8752
|
+
|
|
8753
|
+
// src/tools/consolidate-integrations.ts
|
|
8754
|
+
var import_zod26 = require("zod");
|
|
8755
|
+
var import_fs19 = require("fs");
|
|
8756
|
+
var import_proper_lockfile2 = require("proper-lockfile");
|
|
8757
|
+
var import_path19 = require("path");
|
|
8758
|
+
var import_js_yaml6 = __toESM(require("js-yaml"));
|
|
8510
8759
|
var BASE_MOCK_PORT = 4011;
|
|
8511
8760
|
var STRIP_SUFFIXES2 = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
8512
8761
|
function deriveNameFromFilename(filename) {
|
|
8513
|
-
const base = (0,
|
|
8762
|
+
const base = (0, import_path19.basename)(filename, ".json").replace(/^api-config-/, "");
|
|
8514
8763
|
for (const suffix of STRIP_SUFFIXES2) {
|
|
8515
8764
|
if (base.endsWith(suffix)) return base.slice(0, -suffix.length);
|
|
8516
8765
|
}
|
|
@@ -8518,13 +8767,13 @@ function deriveNameFromFilename(filename) {
|
|
|
8518
8767
|
}
|
|
8519
8768
|
function handleConsolidateIntegrations(input) {
|
|
8520
8769
|
const { projectRoot } = input;
|
|
8521
|
-
const artifactsDir = (0,
|
|
8522
|
-
if (!(0,
|
|
8770
|
+
const artifactsDir = (0, import_path19.join)(projectRoot, ".stackwright", "artifacts");
|
|
8771
|
+
if (!(0, import_fs19.existsSync)(artifactsDir)) {
|
|
8523
8772
|
return { written: false, integrationCount: 0, reason: "no per-spec artifacts found" };
|
|
8524
8773
|
}
|
|
8525
8774
|
let entries;
|
|
8526
8775
|
try {
|
|
8527
|
-
entries = (0,
|
|
8776
|
+
entries = (0, import_fs19.readdirSync)(artifactsDir).filter(
|
|
8528
8777
|
(f) => f.startsWith("api-config-") && f.endsWith(".json") && f !== "api-config.json"
|
|
8529
8778
|
);
|
|
8530
8779
|
} catch {
|
|
@@ -8539,7 +8788,7 @@ function handleConsolidateIntegrations(input) {
|
|
|
8539
8788
|
entries.forEach((filename, index) => {
|
|
8540
8789
|
let artifact = {};
|
|
8541
8790
|
try {
|
|
8542
|
-
const raw = (0,
|
|
8791
|
+
const raw = (0, import_fs19.readFileSync)((0, import_path19.join)(artifactsDir, filename), "utf-8");
|
|
8543
8792
|
artifact = JSON.parse(raw);
|
|
8544
8793
|
} catch {
|
|
8545
8794
|
}
|
|
@@ -8572,20 +8821,20 @@ function handleConsolidateIntegrations(input) {
|
|
|
8572
8821
|
});
|
|
8573
8822
|
const integrations = Array.from(integrationsByName.values());
|
|
8574
8823
|
const dedupedCount = entries.length - skippedIntegrationsList.length - integrations.length;
|
|
8575
|
-
const integrationsYmlPath = (0,
|
|
8824
|
+
const integrationsYmlPath = (0, import_path19.join)(projectRoot, "stackwright.integrations.yml");
|
|
8576
8825
|
let existing = {};
|
|
8577
|
-
if ((0,
|
|
8826
|
+
if ((0, import_fs19.existsSync)(integrationsYmlPath)) {
|
|
8578
8827
|
try {
|
|
8579
|
-
const raw = (0,
|
|
8580
|
-
existing =
|
|
8828
|
+
const raw = (0, import_fs19.readFileSync)(integrationsYmlPath, "utf-8");
|
|
8829
|
+
existing = import_js_yaml6.default.load(raw) ?? {};
|
|
8581
8830
|
} catch {
|
|
8582
8831
|
existing = {};
|
|
8583
8832
|
}
|
|
8584
8833
|
}
|
|
8585
8834
|
const merged = { ...existing, integrations };
|
|
8586
|
-
(0,
|
|
8587
|
-
if (!(0,
|
|
8588
|
-
(0,
|
|
8835
|
+
(0, import_fs19.mkdirSync)((0, import_path19.join)(projectRoot), { recursive: true });
|
|
8836
|
+
if (!(0, import_fs19.existsSync)(integrationsYmlPath)) {
|
|
8837
|
+
(0, import_fs19.writeFileSync)(integrationsYmlPath, "", "utf-8");
|
|
8589
8838
|
}
|
|
8590
8839
|
const release = (0, import_proper_lockfile2.lockSync)(integrationsYmlPath, {
|
|
8591
8840
|
realpath: false,
|
|
@@ -8594,8 +8843,8 @@ function handleConsolidateIntegrations(input) {
|
|
|
8594
8843
|
try {
|
|
8595
8844
|
const header = `# stackwright.integrations.yml \u2014 Auto-generated by stackwright_pro_consolidate_integrations
|
|
8596
8845
|
`;
|
|
8597
|
-
const body =
|
|
8598
|
-
(0,
|
|
8846
|
+
const body = import_js_yaml6.default.dump(merged, { lineWidth: 120, noRefs: true });
|
|
8847
|
+
(0, import_fs19.writeFileSync)(integrationsYmlPath, header + body, "utf-8");
|
|
8599
8848
|
} finally {
|
|
8600
8849
|
release();
|
|
8601
8850
|
}
|
|
@@ -8623,7 +8872,7 @@ function registerConsolidateIntegrationsTool(server2) {
|
|
|
8623
8872
|
"no per-spec artifacts are found."
|
|
8624
8873
|
].join(" "),
|
|
8625
8874
|
{
|
|
8626
|
-
projectRoot:
|
|
8875
|
+
projectRoot: import_zod26.z.string().describe("Absolute path to the project root directory")
|
|
8627
8876
|
},
|
|
8628
8877
|
async ({ projectRoot }) => {
|
|
8629
8878
|
try {
|
|
@@ -8681,7 +8930,7 @@ var package_default = {
|
|
|
8681
8930
|
"test:coverage": "vitest run --coverage"
|
|
8682
8931
|
},
|
|
8683
8932
|
name: "@stackwright-pro/mcp",
|
|
8684
|
-
version: "0.2.0-alpha.
|
|
8933
|
+
version: "0.2.0-alpha.114",
|
|
8685
8934
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
8686
8935
|
license: "SEE LICENSE IN LICENSE",
|
|
8687
8936
|
main: "./dist/server.js",
|
|
@@ -8751,6 +9000,7 @@ registerCompileTools(server);
|
|
|
8751
9000
|
registerStripLegacyIntegrationsTool(server);
|
|
8752
9001
|
registerEmitterTools(server);
|
|
8753
9002
|
registerListSpecsTool(server);
|
|
9003
|
+
registerDescribeEndpointTool(server);
|
|
8754
9004
|
registerConsolidateIntegrationsTool(server);
|
|
8755
9005
|
(0, import_register.registerContentTypeTools)(server);
|
|
8756
9006
|
(0, import_register.registerPageTools)(server);
|