@stackwright-pro/mcp 0.2.0-alpha.112 → 0.2.0-alpha.113
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 +7 -7
package/dist/server.mjs
CHANGED
|
@@ -556,7 +556,7 @@ function registerDashboardTools(server2) {
|
|
|
556
556
|
yamlLines.push(` header: Status`);
|
|
557
557
|
yamlLines.push(` type: badge`);
|
|
558
558
|
}
|
|
559
|
-
const
|
|
559
|
+
const yaml4 = yamlLines.join("\n");
|
|
560
560
|
return {
|
|
561
561
|
content: [
|
|
562
562
|
{
|
|
@@ -566,7 +566,7 @@ function registerDashboardTools(server2) {
|
|
|
566
566
|
Layout: ${layout}
|
|
567
567
|
|
|
568
568
|
\`\`\`yaml
|
|
569
|
-
${
|
|
569
|
+
${yaml4}
|
|
570
570
|
\`\`\`
|
|
571
571
|
|
|
572
572
|
\u{1F4A1} Add this to pages/dashboard/content.yml and validate with stackwright_validate_pages.`
|
|
@@ -647,7 +647,7 @@ ${yaml3}
|
|
|
647
647
|
yamlLines.push(" action: navigate");
|
|
648
648
|
yamlLines.push(` href: "/${entity}"`);
|
|
649
649
|
yamlLines.push(" style: secondary");
|
|
650
|
-
const
|
|
650
|
+
const yaml4 = yamlLines.join("\n");
|
|
651
651
|
return {
|
|
652
652
|
content: [
|
|
653
653
|
{
|
|
@@ -655,7 +655,7 @@ ${yaml3}
|
|
|
655
655
|
text: `\u{1F4C4} Generated Detail Page for: ${entity}
|
|
656
656
|
|
|
657
657
|
\`\`\`yaml
|
|
658
|
-
${
|
|
658
|
+
${yaml4}
|
|
659
659
|
\`\`\`
|
|
660
660
|
|
|
661
661
|
\u{1F4A1} Add this to pages/${entity}/[${slugField}]/content.yml and validate.`
|
|
@@ -2826,21 +2826,40 @@ function readAllManifestPages(artifactsDir) {
|
|
|
2826
2826
|
return allPages;
|
|
2827
2827
|
}
|
|
2828
2828
|
for (const entry of entries) {
|
|
2829
|
-
if (
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2829
|
+
if (entry.endsWith("-manifest.json")) {
|
|
2830
|
+
const filePath = join5(artifactsDir, entry);
|
|
2831
|
+
try {
|
|
2832
|
+
const raw = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
2833
|
+
if (!Array.isArray(raw.pages)) continue;
|
|
2834
|
+
for (const page of raw.pages) {
|
|
2835
|
+
if (typeof page.slug !== "string" || !page.slug) continue;
|
|
2836
|
+
allPages.push({
|
|
2837
|
+
slug: page.slug,
|
|
2838
|
+
authRequired: page.authRequired,
|
|
2839
|
+
requiredRoles: page.requiredRoles,
|
|
2840
|
+
source: entry
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
} catch {
|
|
2844
|
+
}
|
|
2845
|
+
continue;
|
|
2846
|
+
}
|
|
2847
|
+
if (entry === "workflow-config.json" || entry.startsWith("workflow-config-")) {
|
|
2848
|
+
const filePath = join5(artifactsDir, entry);
|
|
2849
|
+
try {
|
|
2850
|
+
const raw = JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
2851
|
+
const route = raw.workflow?.route;
|
|
2852
|
+
if (typeof route !== "string" || !route) continue;
|
|
2853
|
+
const slug = route.startsWith("/") ? route.slice(1) : route;
|
|
2836
2854
|
allPages.push({
|
|
2837
|
-
slug
|
|
2838
|
-
authRequired:
|
|
2839
|
-
requiredRoles:
|
|
2855
|
+
slug,
|
|
2856
|
+
authRequired: true,
|
|
2857
|
+
requiredRoles: void 0,
|
|
2840
2858
|
source: entry
|
|
2841
2859
|
});
|
|
2860
|
+
} catch {
|
|
2842
2861
|
}
|
|
2843
|
-
|
|
2862
|
+
continue;
|
|
2844
2863
|
}
|
|
2845
2864
|
}
|
|
2846
2865
|
return allPages;
|
|
@@ -2848,7 +2867,7 @@ function readAllManifestPages(artifactsDir) {
|
|
|
2848
2867
|
function registerReadAuthManifestsTool(server2) {
|
|
2849
2868
|
server2.tool(
|
|
2850
2869
|
"stackwright_pro_read_auth_manifests",
|
|
2851
|
-
"Read ALL
|
|
2870
|
+
"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).",
|
|
2852
2871
|
{
|
|
2853
2872
|
rbacRoles: z11.array(z11.string()).describe(
|
|
2854
2873
|
'RBAC role hierarchy in descending privilege order, e.g. ["ESF8_COORDINATOR", "HOSPITAL_EM", "OBSERVER"]. Used to compute the lowestRole for each authRequired page.'
|
|
@@ -2994,7 +3013,7 @@ function enrichErrors(issues, synonyms) {
|
|
|
2994
3013
|
});
|
|
2995
3014
|
}
|
|
2996
3015
|
function handleValidateYamlFragment(input) {
|
|
2997
|
-
const { schemaName, yaml:
|
|
3016
|
+
const { schemaName, yaml: yaml4 } = input;
|
|
2998
3017
|
if (!SUPPORTED_SCHEMA_NAMES.includes(schemaName)) {
|
|
2999
3018
|
return {
|
|
3000
3019
|
valid: false,
|
|
@@ -3004,7 +3023,7 @@ function handleValidateYamlFragment(input) {
|
|
|
3004
3023
|
const name = schemaName;
|
|
3005
3024
|
let parsed;
|
|
3006
3025
|
try {
|
|
3007
|
-
parsed = yamlLoad2(
|
|
3026
|
+
parsed = yamlLoad2(yaml4);
|
|
3008
3027
|
} catch (err) {
|
|
3009
3028
|
return {
|
|
3010
3029
|
valid: false,
|
|
@@ -3030,8 +3049,8 @@ function registerValidateYamlFragmentTool(server2) {
|
|
|
3030
3049
|
),
|
|
3031
3050
|
yaml: z12.string().describe("YAML string to validate (can also be JSON \u2014 js-yaml parses both)")
|
|
3032
3051
|
},
|
|
3033
|
-
async ({ schemaName, yaml:
|
|
3034
|
-
const result = handleValidateYamlFragment({ schemaName, yaml:
|
|
3052
|
+
async ({ schemaName, yaml: yaml4 }) => {
|
|
3053
|
+
const result = handleValidateYamlFragment({ schemaName, yaml: yaml4 });
|
|
3035
3054
|
return {
|
|
3036
3055
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
3037
3056
|
};
|
|
@@ -3244,6 +3263,9 @@ var MANIFEST_NAME_TO_PHASE = {
|
|
|
3244
3263
|
"stackwright-pro-dashboard-otter": "dashboard",
|
|
3245
3264
|
"stackwright-pro-form-wizard-otter": "workflow",
|
|
3246
3265
|
"stackwright-pro-geo-otter": "geo",
|
|
3266
|
+
// auth-reconcile runs AFTER pages+dashboard+geo+workflow (wave 6)
|
|
3267
|
+
// Reads auth-config.json + all four manifests; emits auth-reconcile-config.json + stackwright.auth.yml
|
|
3268
|
+
"stackwright-pro-auth-reconcile-otter": "auth-reconcile",
|
|
3247
3269
|
"stackwright-pro-polish-otter": "polish",
|
|
3248
3270
|
"stackwright-services-otter": "services",
|
|
3249
3271
|
"stackwright-pro-qa-otter": "qa"
|
|
@@ -3459,13 +3481,19 @@ var PHASE_ORDER = [
|
|
|
3459
3481
|
"api",
|
|
3460
3482
|
"data",
|
|
3461
3483
|
"auth",
|
|
3462
|
-
// moved earlier
|
|
3484
|
+
// wave 2 — moved earlier, only depends on design-language.json (designer)
|
|
3463
3485
|
"geo",
|
|
3464
3486
|
"workflow",
|
|
3465
3487
|
"services",
|
|
3466
3488
|
"pages",
|
|
3467
3489
|
"dashboard",
|
|
3490
|
+
// auth-reconcile: wave 6 — post-manifest reconciliation
|
|
3491
|
+
// Reads auth-config.json + pages/dashboard/geo/workflow manifests;
|
|
3492
|
+
// emits auth-reconcile-config.json + stackwright.auth.yml (Gate 4 single source of truth)
|
|
3493
|
+
"auth-reconcile",
|
|
3494
|
+
// wave 7: polish — depends on auth-reconcile (replaces auth-config.json direct dep)
|
|
3468
3495
|
"polish",
|
|
3496
|
+
// wave 8: qa — terminal QA gate, depends on polish + transitively everything
|
|
3469
3497
|
"qa"
|
|
3470
3498
|
];
|
|
3471
3499
|
var PHASE_ARTIFACT = {
|
|
@@ -3479,6 +3507,7 @@ var PHASE_ARTIFACT = {
|
|
|
3479
3507
|
dashboard: "dashboard-manifest.json",
|
|
3480
3508
|
workflow: "workflow-config.json",
|
|
3481
3509
|
services: "services-config.json",
|
|
3510
|
+
"auth-reconcile": "auth-reconcile-config.json",
|
|
3482
3511
|
polish: "polish-manifest.json",
|
|
3483
3512
|
geo: "geo-manifest.json",
|
|
3484
3513
|
qa: "qa-findings.json"
|
|
@@ -3493,11 +3522,13 @@ var PHASE_TO_OTTER2 = {
|
|
|
3493
3522
|
pages: "stackwright-pro-page-otter",
|
|
3494
3523
|
dashboard: "stackwright-pro-dashboard-otter",
|
|
3495
3524
|
workflow: "stackwright-pro-form-wizard-otter",
|
|
3525
|
+
"auth-reconcile": "stackwright-pro-auth-reconcile-otter",
|
|
3496
3526
|
services: "stackwright-services-otter",
|
|
3497
3527
|
polish: "stackwright-pro-polish-otter",
|
|
3498
3528
|
geo: "stackwright-pro-geo-otter",
|
|
3499
3529
|
qa: "stackwright-pro-qa-otter"
|
|
3500
3530
|
};
|
|
3531
|
+
var AUTO_EXECUTE_PHASES = /* @__PURE__ */ new Set(["auth-reconcile"]);
|
|
3501
3532
|
var PhaseStatusSchema = z14.object({
|
|
3502
3533
|
status: z14.enum(["pending", "running", "completed", "skipped", "failed"]).default("pending"),
|
|
3503
3534
|
questionsCollected: z14.boolean().default(false),
|
|
@@ -3754,6 +3785,9 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3754
3785
|
isError: true
|
|
3755
3786
|
};
|
|
3756
3787
|
}
|
|
3788
|
+
if (AUTO_EXECUTE_PHASES.has(phase)) {
|
|
3789
|
+
return { text: JSON.stringify({ ready: true, phase, autoExecute: true }), isError: false };
|
|
3790
|
+
}
|
|
3757
3791
|
const answerFile = join7(cwd, ".stackwright", "answers", `${phase}.json`);
|
|
3758
3792
|
if (!existsSync7(answerFile)) {
|
|
3759
3793
|
return {
|
|
@@ -3783,6 +3817,10 @@ function handleCheckExecutionReady(_cwd, phase) {
|
|
|
3783
3817
|
const answeredPhases = [];
|
|
3784
3818
|
const missingPhases = [];
|
|
3785
3819
|
for (const phase2 of PHASE_ORDER) {
|
|
3820
|
+
if (AUTO_EXECUTE_PHASES.has(phase2)) {
|
|
3821
|
+
answeredPhases.push(phase2);
|
|
3822
|
+
continue;
|
|
3823
|
+
}
|
|
3786
3824
|
const answerFile = join7(answersDir, `${phase2}.json`);
|
|
3787
3825
|
if (existsSync7(answerFile)) {
|
|
3788
3826
|
try {
|
|
@@ -4111,6 +4149,8 @@ var PHASE_REQUIRED_KEYS = {
|
|
|
4111
4149
|
dashboard: ["version", "generatedBy"],
|
|
4112
4150
|
workflow: ["version", "generatedBy"],
|
|
4113
4151
|
services: ["version", "generatedBy", "flows"],
|
|
4152
|
+
// auth-reconcile: must have version, generatedBy, and merged protectedRoutes
|
|
4153
|
+
"auth-reconcile": ["version", "generatedBy", "protectedRoutes"],
|
|
4114
4154
|
polish: ["version", "generatedBy"],
|
|
4115
4155
|
geo: ["version", "generatedBy", "geoCollections"],
|
|
4116
4156
|
// qa: skipped=true path only needs version+generatedBy+skipped;
|
|
@@ -4390,6 +4430,28 @@ var PHASE_ARTIFACT_SCHEMA = {
|
|
|
4390
4430
|
null,
|
|
4391
4431
|
2
|
|
4392
4432
|
),
|
|
4433
|
+
"auth-reconcile": JSON.stringify(
|
|
4434
|
+
{
|
|
4435
|
+
version: "1.0",
|
|
4436
|
+
generatedBy: "stackwright-pro-auth-reconcile-otter",
|
|
4437
|
+
authType: "<oidc|saml|cac|oauth2>",
|
|
4438
|
+
rbacRoles: ["<HIGHEST_ROLE>", "<MID_ROLE>", "<LOWEST_ROLE>"],
|
|
4439
|
+
rbacDefaultRole: "<LOWEST_ROLE>",
|
|
4440
|
+
protectedRoutes: [
|
|
4441
|
+
{ pattern: "/dashboard", requiredRole: "<role>" },
|
|
4442
|
+
{ pattern: "/dashboard/:path*", requiredRole: "<role>" }
|
|
4443
|
+
],
|
|
4444
|
+
uncoveredSlugs: [],
|
|
4445
|
+
manifestsRead: ["pages-manifest.json", "dashboard-manifest.json", "geo-manifest.json"],
|
|
4446
|
+
summary: {
|
|
4447
|
+
totalRoutes: 2,
|
|
4448
|
+
manifestDerived: 2,
|
|
4449
|
+
crossCutting: 0
|
|
4450
|
+
}
|
|
4451
|
+
},
|
|
4452
|
+
null,
|
|
4453
|
+
2
|
|
4454
|
+
),
|
|
4393
4455
|
polish: JSON.stringify(
|
|
4394
4456
|
{
|
|
4395
4457
|
version: "1.0",
|
|
@@ -5141,6 +5203,20 @@ var OTTER_WRITE_ALLOWLISTS = {
|
|
|
5141
5203
|
description: "Stackwright config (themeName + customTheme writeback)"
|
|
5142
5204
|
}
|
|
5143
5205
|
],
|
|
5206
|
+
// swp-hng0: auth-reconcile otter — reads all manifests, re-emits auth-reconcile-config.json
|
|
5207
|
+
// AND overwrites stackwright.auth.yml (Gate 4: single source of truth after reconciliation)
|
|
5208
|
+
"stackwright-pro-auth-reconcile-otter": [
|
|
5209
|
+
{
|
|
5210
|
+
prefix: ".stackwright/artifacts/",
|
|
5211
|
+
suffix: ".json",
|
|
5212
|
+
description: "Auth reconcile artifact (auth-reconcile-config.json)"
|
|
5213
|
+
},
|
|
5214
|
+
{
|
|
5215
|
+
prefix: "stackwright.auth.",
|
|
5216
|
+
suffix: ".yml",
|
|
5217
|
+
description: "Canonical auth sidecar (stackwright.auth.yml \u2014 Gate 4 rewrite)"
|
|
5218
|
+
}
|
|
5219
|
+
],
|
|
5144
5220
|
"stackwright-pro-auth-otter": [
|
|
5145
5221
|
{ prefix: ".stackwright/artifacts/", suffix: ".json", description: "Auth config artifact" },
|
|
5146
5222
|
{
|
|
@@ -6564,7 +6640,11 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6564
6640
|
],
|
|
6565
6641
|
[
|
|
6566
6642
|
"stackwright-pro-auth-otter.json",
|
|
6567
|
-
"
|
|
6643
|
+
"c9302132ac4180c70dd452814eb4aa4fb9a1d6a9683f6e01ffce509a6001c6cf"
|
|
6644
|
+
],
|
|
6645
|
+
[
|
|
6646
|
+
"stackwright-pro-auth-reconcile-otter.json",
|
|
6647
|
+
"7ffd67d51568c7b2a1eb2ea0ac2b2a8041eb9ddd9c1eb5b95744e55ba049b385"
|
|
6568
6648
|
],
|
|
6569
6649
|
[
|
|
6570
6650
|
"stackwright-pro-dashboard-otter.json",
|
|
@@ -6572,7 +6652,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6572
6652
|
],
|
|
6573
6653
|
[
|
|
6574
6654
|
"stackwright-pro-data-otter.json",
|
|
6575
|
-
"
|
|
6655
|
+
"7d1de846488d84eebf8798aef4d75f4f4d250c1ae070ce1200ead3bb8a545eaf"
|
|
6576
6656
|
],
|
|
6577
6657
|
[
|
|
6578
6658
|
"stackwright-pro-designer-otter.json",
|
|
@@ -6600,7 +6680,7 @@ var _checksums = /* @__PURE__ */ new Map([
|
|
|
6600
6680
|
],
|
|
6601
6681
|
[
|
|
6602
6682
|
"stackwright-pro-polish-otter.json",
|
|
6603
|
-
"
|
|
6683
|
+
"3ae1252a60727ebca67e33a807d5061cafb88de52f3b98a9d4641f9db725a6ed"
|
|
6604
6684
|
],
|
|
6605
6685
|
[
|
|
6606
6686
|
"stackwright-pro-qa-otter.json",
|
|
@@ -8514,12 +8594,181 @@ function registerListSpecsTool(server2) {
|
|
|
8514
8594
|
);
|
|
8515
8595
|
}
|
|
8516
8596
|
|
|
8517
|
-
// src/tools/
|
|
8597
|
+
// src/tools/describe-endpoint.ts
|
|
8518
8598
|
import { z as z25 } from "zod";
|
|
8519
|
-
import {
|
|
8599
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
8600
|
+
import { extname as extname2 } from "path";
|
|
8601
|
+
import yaml2 from "js-yaml";
|
|
8602
|
+
function resolveRef(root, ref) {
|
|
8603
|
+
if (!ref.startsWith("#/")) {
|
|
8604
|
+
throw new Error(`[describe_endpoint] External $refs not supported: "${ref}"`);
|
|
8605
|
+
}
|
|
8606
|
+
const parts = ref.slice(2).split("/");
|
|
8607
|
+
let current = root;
|
|
8608
|
+
for (const part of parts) {
|
|
8609
|
+
const key = part.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
8610
|
+
if (current == null || typeof current !== "object") {
|
|
8611
|
+
throw new Error(
|
|
8612
|
+
`[describe_endpoint] $ref "${ref}" cannot be resolved \u2014 segment "${key}" not found`
|
|
8613
|
+
);
|
|
8614
|
+
}
|
|
8615
|
+
current = current[key];
|
|
8616
|
+
}
|
|
8617
|
+
if (current == null || typeof current !== "object") {
|
|
8618
|
+
throw new Error(`[describe_endpoint] $ref "${ref}" resolved to a non-object value`);
|
|
8619
|
+
}
|
|
8620
|
+
return current;
|
|
8621
|
+
}
|
|
8622
|
+
function deref(root, obj) {
|
|
8623
|
+
if (obj == null || typeof obj !== "object") return {};
|
|
8624
|
+
const o = obj;
|
|
8625
|
+
if (typeof o["$ref"] === "string") {
|
|
8626
|
+
const resolved = resolveRef(root, o["$ref"]);
|
|
8627
|
+
return deref(root, resolved);
|
|
8628
|
+
}
|
|
8629
|
+
return o;
|
|
8630
|
+
}
|
|
8631
|
+
function loadSpec(specPath) {
|
|
8632
|
+
const raw = readFileSync16(specPath, "utf-8");
|
|
8633
|
+
const ext = extname2(specPath).toLowerCase();
|
|
8634
|
+
if (ext === ".json") {
|
|
8635
|
+
return JSON.parse(raw);
|
|
8636
|
+
}
|
|
8637
|
+
const parsed = yaml2.load(raw);
|
|
8638
|
+
if (parsed == null || typeof parsed !== "object") {
|
|
8639
|
+
throw new Error(`[describe_endpoint] Spec file "${specPath}" did not parse to an object`);
|
|
8640
|
+
}
|
|
8641
|
+
return parsed;
|
|
8642
|
+
}
|
|
8643
|
+
function extractParams(root, pathItem, operation) {
|
|
8644
|
+
const pathLevelRaw = Array.isArray(pathItem["parameters"]) ? pathItem["parameters"] : [];
|
|
8645
|
+
const opLevelRaw = Array.isArray(operation["parameters"]) ? operation["parameters"] : [];
|
|
8646
|
+
const merged = /* @__PURE__ */ new Map();
|
|
8647
|
+
for (const raw of [...pathLevelRaw, ...opLevelRaw]) {
|
|
8648
|
+
const param = deref(root, raw);
|
|
8649
|
+
const name = String(param["name"] ?? "");
|
|
8650
|
+
const inVal = String(param["in"] ?? "");
|
|
8651
|
+
if (name && inVal) {
|
|
8652
|
+
merged.set(`${name}:${inVal}`, param);
|
|
8653
|
+
}
|
|
8654
|
+
}
|
|
8655
|
+
const pathParams = [];
|
|
8656
|
+
const queryParams = [];
|
|
8657
|
+
for (const param of merged.values()) {
|
|
8658
|
+
const inVal = String(param["in"] ?? "");
|
|
8659
|
+
const name = String(param["name"] ?? "");
|
|
8660
|
+
const schema = deref(root, param["schema"] ?? {});
|
|
8661
|
+
if (inVal === "path") {
|
|
8662
|
+
pathParams.push({ name, in: "path", required: true, schema });
|
|
8663
|
+
} else if (inVal === "query") {
|
|
8664
|
+
const required = param["required"] === true;
|
|
8665
|
+
queryParams.push({ name, in: "query", required, schema });
|
|
8666
|
+
}
|
|
8667
|
+
}
|
|
8668
|
+
return { pathParams, queryParams };
|
|
8669
|
+
}
|
|
8670
|
+
function extractSecurity(root, globalSecurity, operationSecurity) {
|
|
8671
|
+
const securityRequirements = operationSecurity !== void 0 ? operationSecurity : globalSecurity;
|
|
8672
|
+
if (securityRequirements.length === 0) return [];
|
|
8673
|
+
const schemes = root["components"]?.["securitySchemes"];
|
|
8674
|
+
if (!schemes || typeof schemes !== "object") return [];
|
|
8675
|
+
const result = [];
|
|
8676
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8677
|
+
for (const req of securityRequirements) {
|
|
8678
|
+
if (!req || typeof req !== "object") continue;
|
|
8679
|
+
for (const schemeName of Object.keys(req)) {
|
|
8680
|
+
if (seen.has(schemeName)) continue;
|
|
8681
|
+
seen.add(schemeName);
|
|
8682
|
+
const schemeRaw = schemes[schemeName];
|
|
8683
|
+
if (!schemeRaw) continue;
|
|
8684
|
+
const scheme = deref(root, schemeRaw);
|
|
8685
|
+
const type = String(scheme["type"] ?? "unknown");
|
|
8686
|
+
if (type === "apiKey") {
|
|
8687
|
+
const inVal = String(scheme["in"] ?? "header");
|
|
8688
|
+
const paramName = String(scheme["name"] ?? schemeName);
|
|
8689
|
+
result.push({ name: schemeName, type, in: inVal, paramName });
|
|
8690
|
+
} else if (type === "http") {
|
|
8691
|
+
const httpScheme = String(scheme["scheme"] ?? "bearer").toLowerCase();
|
|
8692
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8693
|
+
void httpScheme;
|
|
8694
|
+
} else if (type === "oauth2") {
|
|
8695
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8696
|
+
} else if (type === "openIdConnect") {
|
|
8697
|
+
result.push({ name: schemeName, type, in: "bearer", paramName: "Authorization" });
|
|
8698
|
+
} else {
|
|
8699
|
+
result.push({ name: schemeName, type, in: "unknown", paramName: schemeName });
|
|
8700
|
+
}
|
|
8701
|
+
}
|
|
8702
|
+
}
|
|
8703
|
+
return result;
|
|
8704
|
+
}
|
|
8705
|
+
function handleDescribeEndpoint(input) {
|
|
8706
|
+
const { specPath, path: path5, method } = input;
|
|
8707
|
+
const normalizedMethod = method.toLowerCase();
|
|
8708
|
+
const root = loadSpec(specPath);
|
|
8709
|
+
const globalSecurity = Array.isArray(root["security"]) ? root["security"] : [];
|
|
8710
|
+
const paths = root["paths"];
|
|
8711
|
+
if (!paths) {
|
|
8712
|
+
return { pathParams: [], queryParams: [], security: [] };
|
|
8713
|
+
}
|
|
8714
|
+
const pathItem = deref(root, paths[path5]);
|
|
8715
|
+
if (!pathItem || Object.keys(pathItem).length === 0) {
|
|
8716
|
+
throw new Error(`[describe_endpoint] Path "${path5}" not found in spec "${specPath}"`);
|
|
8717
|
+
}
|
|
8718
|
+
const operation = deref(root, pathItem[normalizedMethod]);
|
|
8719
|
+
if (!operation || Object.keys(operation).length === 0) {
|
|
8720
|
+
throw new Error(
|
|
8721
|
+
`[describe_endpoint] Method "${method.toUpperCase()}" not found at path "${path5}" in spec "${specPath}"`
|
|
8722
|
+
);
|
|
8723
|
+
}
|
|
8724
|
+
const { pathParams, queryParams } = extractParams(root, pathItem, operation);
|
|
8725
|
+
const operationSecurity = Array.isArray(operation["security"]) ? operation["security"] : void 0;
|
|
8726
|
+
const security = extractSecurity(root, globalSecurity, operationSecurity);
|
|
8727
|
+
return { pathParams, queryParams, security };
|
|
8728
|
+
}
|
|
8729
|
+
function registerDescribeEndpointTool(server2) {
|
|
8730
|
+
server2.tool(
|
|
8731
|
+
"stackwright_pro_describe_endpoint",
|
|
8732
|
+
[
|
|
8733
|
+
"Inspect an OpenAPI 3.0/3.1 spec and return resolved parameter and security details for a",
|
|
8734
|
+
"specific operation. Deterministic \u2014 no LLM. Handles $ref resolution within the spec document.",
|
|
8735
|
+
"",
|
|
8736
|
+
"Returns:",
|
|
8737
|
+
" pathParams \u2014 path parameters [{name, in, required, schema}]",
|
|
8738
|
+
" queryParams \u2014 query parameters [{name, in, required, schema}]",
|
|
8739
|
+
" security \u2014 applicable security schemes [{name, type, in, paramName}]",
|
|
8740
|
+
"",
|
|
8741
|
+
"Used by the data-otter to populate params: entries in stackwright.collections.yml",
|
|
8742
|
+
"from required query params and resolved auth query params (swp-8r8i + swp-1l6l)."
|
|
8743
|
+
].join("\n"),
|
|
8744
|
+
{
|
|
8745
|
+
specPath: z25.string().describe("Absolute path to the OpenAPI spec file (.yaml, .yml, or .json)"),
|
|
8746
|
+
path: z25.string().describe('The API path to inspect (e.g. "/stations/{stationId}/observations")'),
|
|
8747
|
+
method: z25.string().describe('HTTP method (case-insensitive: "get", "post", "put", etc.)')
|
|
8748
|
+
},
|
|
8749
|
+
async ({ specPath, path: path5, method }) => {
|
|
8750
|
+
try {
|
|
8751
|
+
const result = handleDescribeEndpoint({ specPath, path: path5, method });
|
|
8752
|
+
return {
|
|
8753
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
8754
|
+
};
|
|
8755
|
+
} catch (err) {
|
|
8756
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
8757
|
+
return {
|
|
8758
|
+
content: [{ type: "text", text: JSON.stringify({ error: message }) }],
|
|
8759
|
+
isError: true
|
|
8760
|
+
};
|
|
8761
|
+
}
|
|
8762
|
+
}
|
|
8763
|
+
);
|
|
8764
|
+
}
|
|
8765
|
+
|
|
8766
|
+
// src/tools/consolidate-integrations.ts
|
|
8767
|
+
import { z as z26 } from "zod";
|
|
8768
|
+
import { readdirSync as readdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync10, existsSync as existsSync16, mkdirSync as mkdirSync9 } from "fs";
|
|
8520
8769
|
import { lockSync as lockSync2 } from "proper-lockfile";
|
|
8521
8770
|
import { join as join17, basename as basename3 } from "path";
|
|
8522
|
-
import
|
|
8771
|
+
import yaml3 from "js-yaml";
|
|
8523
8772
|
var BASE_MOCK_PORT = 4011;
|
|
8524
8773
|
var STRIP_SUFFIXES2 = ["-openapi", "-asyncapi", "-api", "-spec"];
|
|
8525
8774
|
function deriveNameFromFilename(filename) {
|
|
@@ -8552,7 +8801,7 @@ function handleConsolidateIntegrations(input) {
|
|
|
8552
8801
|
entries.forEach((filename, index) => {
|
|
8553
8802
|
let artifact = {};
|
|
8554
8803
|
try {
|
|
8555
|
-
const raw =
|
|
8804
|
+
const raw = readFileSync17(join17(artifactsDir, filename), "utf-8");
|
|
8556
8805
|
artifact = JSON.parse(raw);
|
|
8557
8806
|
} catch {
|
|
8558
8807
|
}
|
|
@@ -8589,8 +8838,8 @@ function handleConsolidateIntegrations(input) {
|
|
|
8589
8838
|
let existing = {};
|
|
8590
8839
|
if (existsSync16(integrationsYmlPath)) {
|
|
8591
8840
|
try {
|
|
8592
|
-
const raw =
|
|
8593
|
-
existing =
|
|
8841
|
+
const raw = readFileSync17(integrationsYmlPath, "utf-8");
|
|
8842
|
+
existing = yaml3.load(raw) ?? {};
|
|
8594
8843
|
} catch {
|
|
8595
8844
|
existing = {};
|
|
8596
8845
|
}
|
|
@@ -8607,7 +8856,7 @@ function handleConsolidateIntegrations(input) {
|
|
|
8607
8856
|
try {
|
|
8608
8857
|
const header = `# stackwright.integrations.yml \u2014 Auto-generated by stackwright_pro_consolidate_integrations
|
|
8609
8858
|
`;
|
|
8610
|
-
const body =
|
|
8859
|
+
const body = yaml3.dump(merged, { lineWidth: 120, noRefs: true });
|
|
8611
8860
|
writeFileSync10(integrationsYmlPath, header + body, "utf-8");
|
|
8612
8861
|
} finally {
|
|
8613
8862
|
release();
|
|
@@ -8636,7 +8885,7 @@ function registerConsolidateIntegrationsTool(server2) {
|
|
|
8636
8885
|
"no per-spec artifacts are found."
|
|
8637
8886
|
].join(" "),
|
|
8638
8887
|
{
|
|
8639
|
-
projectRoot:
|
|
8888
|
+
projectRoot: z26.string().describe("Absolute path to the project root directory")
|
|
8640
8889
|
},
|
|
8641
8890
|
async ({ projectRoot }) => {
|
|
8642
8891
|
try {
|
|
@@ -8694,7 +8943,7 @@ var package_default = {
|
|
|
8694
8943
|
"test:coverage": "vitest run --coverage"
|
|
8695
8944
|
},
|
|
8696
8945
|
name: "@stackwright-pro/mcp",
|
|
8697
|
-
version: "0.2.0-alpha.
|
|
8946
|
+
version: "0.2.0-alpha.113",
|
|
8698
8947
|
description: "MCP tools for Stackwright Pro - Data Explorer, Security, ISR, and Dashboard generation",
|
|
8699
8948
|
license: "SEE LICENSE IN LICENSE",
|
|
8700
8949
|
main: "./dist/server.js",
|
|
@@ -8777,6 +9026,7 @@ registerCompileTools(server);
|
|
|
8777
9026
|
registerStripLegacyIntegrationsTool(server);
|
|
8778
9027
|
registerEmitterTools(server);
|
|
8779
9028
|
registerListSpecsTool(server);
|
|
9029
|
+
registerDescribeEndpointTool(server);
|
|
8780
9030
|
registerConsolidateIntegrationsTool(server);
|
|
8781
9031
|
registerContentTypeTools(server);
|
|
8782
9032
|
registerPageTools(server);
|