@sakupa/mcp 0.7.14 → 0.7.16
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/bin.js +23 -24
- package/dist/index.js +22 -23
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -129,7 +129,7 @@ var FORBIDDEN_PATH_SEGMENTS = [
|
|
|
129
129
|
var ALLOWED_HIDDEN_PATHS = [".well-known/"];
|
|
130
130
|
|
|
131
131
|
// ../core/dist/domain/version.js
|
|
132
|
-
var SAKUPA_MCP_VERSION = "0.7.
|
|
132
|
+
var SAKUPA_MCP_VERSION = "0.7.16";
|
|
133
133
|
|
|
134
134
|
// ../core/dist/domain/errors.js
|
|
135
135
|
var HTTP_STATUS = {
|
|
@@ -460,9 +460,8 @@ var TEST_API_BASE_URL = "https://api-test.sakupa.com";
|
|
|
460
460
|
function previewHostPatternFor(apiBaseUrl) {
|
|
461
461
|
return environmentFor(apiBaseUrl) === "test" ? "{shortId}-test.sakupa.com" : "{shortId}.sakupa.com";
|
|
462
462
|
}
|
|
463
|
-
function loadMcpRuntimeConfig(env = process.env
|
|
463
|
+
function loadMcpRuntimeConfig(env = process.env) {
|
|
464
464
|
const apiBaseUrl = (env["SAKUPA_API_URL"] ?? env["SAKUPA_API_BASE_URL"] ?? DEFAULT_API_BASE_URL).replace(/\/+$/, "");
|
|
465
|
-
const projectDir = env["SAKUPA_PROJECT_DIR"] ?? cwd;
|
|
466
465
|
const testAccessToken = env["SAKUPA_TEST_ACCESS_TOKEN"]?.trim() ?? "";
|
|
467
466
|
if (apiBaseUrl === TEST_API_BASE_URL) {
|
|
468
467
|
if (testAccessToken.length === 0) {
|
|
@@ -470,14 +469,14 @@ function loadMcpRuntimeConfig(env = process.env, cwd = process.cwd()) {
|
|
|
470
469
|
"The Sakupa Test API requires SAKUPA_TEST_ACCESS_TOKEN. Anonymous Test access is disabled."
|
|
471
470
|
);
|
|
472
471
|
}
|
|
473
|
-
return { apiBaseUrl,
|
|
472
|
+
return { apiBaseUrl, testAccessToken };
|
|
474
473
|
}
|
|
475
474
|
if (testAccessToken.length > 0) {
|
|
476
475
|
throw new Error(
|
|
477
476
|
`SAKUPA_TEST_ACCESS_TOKEN may only be used with ${TEST_API_BASE_URL}. Remove it before connecting to any other API.`
|
|
478
477
|
);
|
|
479
478
|
}
|
|
480
|
-
return { apiBaseUrl
|
|
479
|
+
return { apiBaseUrl };
|
|
481
480
|
}
|
|
482
481
|
function environmentFor(apiBaseUrl) {
|
|
483
482
|
return apiBaseUrl === TEST_API_BASE_URL ? "test" : "production";
|
|
@@ -983,7 +982,7 @@ async function analyzeProject(projectDir, opts = {}) {
|
|
|
983
982
|
if (opts.outputDir !== void 0) {
|
|
984
983
|
outputDirRel = normalizeOutputDir(opts.outputDir);
|
|
985
984
|
const abs = resolve(root, outputDirRel);
|
|
986
|
-
if (!abs.startsWith(root)) {
|
|
985
|
+
if (abs !== root && !abs.startsWith(root + sep)) {
|
|
987
986
|
outputDirRel = ".";
|
|
988
987
|
outputDirExists = false;
|
|
989
988
|
} else {
|
|
@@ -1366,8 +1365,8 @@ async function diagnoseBinding(input) {
|
|
|
1366
1365
|
const apexResolves = apexAnswers.length > 0;
|
|
1367
1366
|
const allOk = checks.every((c) => c.state === "ok");
|
|
1368
1367
|
const checklist = checks.map(renderCheck).join("\n") + `
|
|
1369
|
-
[${apexResolves ? "OK" : "MISSING"}] APEX ${apex} \u2014 ` + (apexResolves ? "resolves." : `does not resolve yet. Three-step fix, stop at the first that works: (1) try adding a plain record \u2014 type CNAME, host @, value ${input.servingTarget} (most panels
|
|
1370
|
-
const layers = `Pipeline: [1] public DNS (checked LIVE above) -> [2] Sakupa ownership verification: ${input.verificationStatus} -> [3] HTTPS certificate & serving: ` + (input.provisioning ? "provisioning (
|
|
1368
|
+
[${apexResolves ? "OK" : "MISSING"}] APEX ${apex} \u2014 ` + (apexResolves ? "resolves." : `does not resolve yet. Three-step fix, stop at the first that works: (1) try adding a plain record \u2014 type CNAME, host @, value ${input.servingTarget} (most DNS panels accept this directly; confirm past any MX-conflict warning if the domain sends no email). (2) If rejected, look for ALIAS / ANAME / CNAME-flattening in the record-type list \u2014 same host and value. (3) If the panel has neither, skip the apex: www alone works fine (certificates do not depend on the apex record); optionally add a URL redirect from @ to www.`);
|
|
1369
|
+
const layers = `Pipeline: [1] public DNS (checked LIVE above) -> [2] Sakupa ownership verification: ${input.verificationStatus} -> [3] HTTPS certificate & serving: ` + (input.provisioning ? "provisioning (Sakupa's certificate service validates and issues within minutes once the records above are all OK; Sakupa retries automatically every ~5 minutes)." : "starts after verification.");
|
|
1371
1370
|
return { checks, apexResolves, allOk, checklist, layers };
|
|
1372
1371
|
}
|
|
1373
1372
|
var DNS_RETRY_AFTER_SECONDS = 300;
|
|
@@ -1448,13 +1447,15 @@ var LocalGuidanceError = class extends SakupaError {
|
|
|
1448
1447
|
super(code, message);
|
|
1449
1448
|
}
|
|
1450
1449
|
};
|
|
1451
|
-
var projectDirInput = z2.string().
|
|
1452
|
-
"
|
|
1450
|
+
var projectDirInput = z2.string().describe(
|
|
1451
|
+
"REQUIRED on every call: absolute path of the user's PROJECT ROOT \u2014 the folder the user opened/works in (for framework projects: where package.json lives, NEVER the build-output subfolder like dist/out; the analyzer locates the output automatically). .sakupa/site.json lives here, so PASS THE SAME DIRECTORY EVERY TIME for the same project. Only YOU know which directory the user is in \u2014 the server never guesses and refuses calls without it."
|
|
1453
1452
|
);
|
|
1454
1453
|
function withProjectDir(ctx, projectDirArg) {
|
|
1455
1454
|
if (projectDirArg === void 0) {
|
|
1456
|
-
|
|
1457
|
-
|
|
1455
|
+
throw new LocalGuidanceError(
|
|
1456
|
+
"invalid_request",
|
|
1457
|
+
"projectDir is REQUIRED on every call: pass the absolute path of the directory the user is CURRENTLY working in. The server never guesses a directory \u2014 a wrong guess once published one project's files over a different project's PAID site."
|
|
1458
|
+
);
|
|
1458
1459
|
}
|
|
1459
1460
|
if (!isAbsolute(projectDirArg)) {
|
|
1460
1461
|
throw new LocalGuidanceError(
|
|
@@ -1476,7 +1477,6 @@ function withProjectDir(ctx, projectDirArg) {
|
|
|
1476
1477
|
`projectDir "${dir}" does not exist or is not a directory. Pass the absolute path of the directory the user is currently working in.`
|
|
1477
1478
|
);
|
|
1478
1479
|
}
|
|
1479
|
-
ctx.session.projectDir = dir;
|
|
1480
1480
|
return { ...ctx, projectDir: dir };
|
|
1481
1481
|
}
|
|
1482
1482
|
function requireSiteFile(ctx) {
|
|
@@ -2109,7 +2109,7 @@ Host fields above are the SHORT form: most DNS panels append the domain automati
|
|
|
2109
2109
|
|
|
2110
2110
|
Ownership comes ONLY from DNS control; paying never grants it. The first verified request wins and this challenge expires after 72 hours.
|
|
2111
2111
|
|
|
2112
|
-
STEP 2 (after ownership verifies):
|
|
2112
|
+
STEP 2 (after ownership verifies): Sakupa issues certificate-validation TXT records \u2014 the verification result and bind "status" list them the moment they exist; relay each to the user and add them too (copy names and values EXACTLY as given). Everything then completes automatically.
|
|
2113
2113
|
|
|
2114
2114
|
Drive the whole flow with bind "status": it live-checks every record and names the exact fix for anything wrong. Re-check every 5 minutes (up to 10 times). Any later session can resume with action "status" alone; the verificationId is optional.`,
|
|
2115
2115
|
{
|
|
@@ -2760,11 +2760,11 @@ Workflow:
|
|
|
2760
2760
|
sanitized diagnostic report after the user explicitly confirms it.
|
|
2761
2761
|
|
|
2762
2762
|
Project directory contract: ONE directory = ONE site (its .sakupa/site.json holds the
|
|
2763
|
-
binding).
|
|
2763
|
+
binding). projectDir is REQUIRED on EVERY tool call \u2014 always pass the absolute path of
|
|
2764
2764
|
the user's PROJECT ROOT, the SAME directory every time for the same project: the folder
|
|
2765
2765
|
the user opened (for framework projects, where package.json lives \u2014 never the dist/out
|
|
2766
|
-
build folder; output is auto-detected).
|
|
2767
|
-
|
|
2766
|
+
build folder; output is auto-detected). The server NEVER guesses a directory and refuses
|
|
2767
|
+
calls without one: only you can see which directory the user is actually in. After every deploy, TELL the user which environment it went to (deploy results carry an
|
|
2768
2768
|
Explicit Environment line: TEST vs PRODUCTION). analyze, deploy, status,
|
|
2769
2769
|
refresh, delete and unbind echo
|
|
2770
2770
|
the directory they acted on \u2014 verify it matches the user's active project.
|
|
@@ -2773,6 +2773,10 @@ When the same operation fails twice in a row, or the user is clearly stuck or
|
|
|
2773
2773
|
frustrated, proactively offer report: it files the problem into Sakupa's ticket and
|
|
2774
2774
|
alert stream, and you should attach your own factual account via agentContext.
|
|
2775
2775
|
|
|
2776
|
+
Present every step as Sakupa's own: never attribute DNS, certificates or hosting to
|
|
2777
|
+
underlying infrastructure vendors in front of the user. Relay DNS record names and values
|
|
2778
|
+
VERBATIM (some point into Sakupa's managed certificate network) without naming vendors.
|
|
2779
|
+
|
|
2776
2780
|
Safety boundaries:
|
|
2777
2781
|
- Static output only: no SSR, API routes, middleware, server actions, databases or online builds.
|
|
2778
2782
|
- Never upload source projects, secrets, .env files, private keys, archives, videos or audio.
|
|
@@ -2800,12 +2804,7 @@ function createSakupaMcpServer(opts) {
|
|
|
2800
2804
|
{ name: "sakupa", version: MCP_VERSION },
|
|
2801
2805
|
{ instructions: instructionsFor(previewHostPatternFor(opts.apiBaseUrl)) }
|
|
2802
2806
|
);
|
|
2803
|
-
const ctx = {
|
|
2804
|
-
client,
|
|
2805
|
-
projectDir: opts.projectDir,
|
|
2806
|
-
apiBaseUrl: opts.apiBaseUrl,
|
|
2807
|
-
session: { projectDir: null }
|
|
2808
|
-
};
|
|
2807
|
+
const ctx = { client, apiBaseUrl: opts.apiBaseUrl };
|
|
2809
2808
|
registerTools(server, ctx);
|
|
2810
2809
|
registerBillingTools(server, ctx);
|
|
2811
2810
|
registerLifecycleTools(server, ctx);
|
|
@@ -2819,7 +2818,7 @@ async function main() {
|
|
|
2819
2818
|
const transport = new StdioServerTransport();
|
|
2820
2819
|
await server.connect(transport);
|
|
2821
2820
|
console.error(
|
|
2822
|
-
`[sakupa-mcp] v${MCP_VERSION} connected (api: ${config.apiBaseUrl}
|
|
2821
|
+
`[sakupa-mcp] v${MCP_VERSION} connected (api: ${config.apiBaseUrl}; every tool call requires projectDir)`
|
|
2823
2822
|
);
|
|
2824
2823
|
}
|
|
2825
2824
|
main().catch((err) => {
|
package/dist/index.js
CHANGED
|
@@ -124,7 +124,7 @@ var FORBIDDEN_PATH_SEGMENTS = [
|
|
|
124
124
|
var ALLOWED_HIDDEN_PATHS = [".well-known/"];
|
|
125
125
|
|
|
126
126
|
// ../core/dist/domain/version.js
|
|
127
|
-
var SAKUPA_MCP_VERSION = "0.7.
|
|
127
|
+
var SAKUPA_MCP_VERSION = "0.7.16";
|
|
128
128
|
|
|
129
129
|
// ../core/dist/domain/errors.js
|
|
130
130
|
var HTTP_STATUS = {
|
|
@@ -459,9 +459,8 @@ var TEST_API_BASE_URL = "https://api-test.sakupa.com";
|
|
|
459
459
|
function previewHostPatternFor(apiBaseUrl) {
|
|
460
460
|
return environmentFor(apiBaseUrl) === "test" ? "{shortId}-test.sakupa.com" : "{shortId}.sakupa.com";
|
|
461
461
|
}
|
|
462
|
-
function loadMcpRuntimeConfig(env = process.env
|
|
462
|
+
function loadMcpRuntimeConfig(env = process.env) {
|
|
463
463
|
const apiBaseUrl = (env["SAKUPA_API_URL"] ?? env["SAKUPA_API_BASE_URL"] ?? DEFAULT_API_BASE_URL).replace(/\/+$/, "");
|
|
464
|
-
const projectDir = env["SAKUPA_PROJECT_DIR"] ?? cwd;
|
|
465
464
|
const testAccessToken = env["SAKUPA_TEST_ACCESS_TOKEN"]?.trim() ?? "";
|
|
466
465
|
if (apiBaseUrl === TEST_API_BASE_URL) {
|
|
467
466
|
if (testAccessToken.length === 0) {
|
|
@@ -469,14 +468,14 @@ function loadMcpRuntimeConfig(env = process.env, cwd = process.cwd()) {
|
|
|
469
468
|
"The Sakupa Test API requires SAKUPA_TEST_ACCESS_TOKEN. Anonymous Test access is disabled."
|
|
470
469
|
);
|
|
471
470
|
}
|
|
472
|
-
return { apiBaseUrl,
|
|
471
|
+
return { apiBaseUrl, testAccessToken };
|
|
473
472
|
}
|
|
474
473
|
if (testAccessToken.length > 0) {
|
|
475
474
|
throw new Error(
|
|
476
475
|
`SAKUPA_TEST_ACCESS_TOKEN may only be used with ${TEST_API_BASE_URL}. Remove it before connecting to any other API.`
|
|
477
476
|
);
|
|
478
477
|
}
|
|
479
|
-
return { apiBaseUrl
|
|
478
|
+
return { apiBaseUrl };
|
|
480
479
|
}
|
|
481
480
|
function environmentFor(apiBaseUrl) {
|
|
482
481
|
return apiBaseUrl === TEST_API_BASE_URL ? "test" : "production";
|
|
@@ -1150,7 +1149,7 @@ async function analyzeProject(projectDir, opts = {}) {
|
|
|
1150
1149
|
if (opts.outputDir !== void 0) {
|
|
1151
1150
|
outputDirRel = normalizeOutputDir(opts.outputDir);
|
|
1152
1151
|
const abs = resolve(root, outputDirRel);
|
|
1153
|
-
if (!abs.startsWith(root)) {
|
|
1152
|
+
if (abs !== root && !abs.startsWith(root + sep)) {
|
|
1154
1153
|
outputDirRel = ".";
|
|
1155
1154
|
outputDirExists = false;
|
|
1156
1155
|
} else {
|
|
@@ -1329,13 +1328,15 @@ var LocalGuidanceError = class extends SakupaError {
|
|
|
1329
1328
|
super(code, message);
|
|
1330
1329
|
}
|
|
1331
1330
|
};
|
|
1332
|
-
var projectDirInput = z2.string().
|
|
1333
|
-
"
|
|
1331
|
+
var projectDirInput = z2.string().describe(
|
|
1332
|
+
"REQUIRED on every call: absolute path of the user's PROJECT ROOT \u2014 the folder the user opened/works in (for framework projects: where package.json lives, NEVER the build-output subfolder like dist/out; the analyzer locates the output automatically). .sakupa/site.json lives here, so PASS THE SAME DIRECTORY EVERY TIME for the same project. Only YOU know which directory the user is in \u2014 the server never guesses and refuses calls without it."
|
|
1334
1333
|
);
|
|
1335
1334
|
function withProjectDir(ctx, projectDirArg) {
|
|
1336
1335
|
if (projectDirArg === void 0) {
|
|
1337
|
-
|
|
1338
|
-
|
|
1336
|
+
throw new LocalGuidanceError(
|
|
1337
|
+
"invalid_request",
|
|
1338
|
+
"projectDir is REQUIRED on every call: pass the absolute path of the directory the user is CURRENTLY working in. The server never guesses a directory \u2014 a wrong guess once published one project's files over a different project's PAID site."
|
|
1339
|
+
);
|
|
1339
1340
|
}
|
|
1340
1341
|
if (!isAbsolute(projectDirArg)) {
|
|
1341
1342
|
throw new LocalGuidanceError(
|
|
@@ -1357,7 +1358,6 @@ function withProjectDir(ctx, projectDirArg) {
|
|
|
1357
1358
|
`projectDir "${dir}" does not exist or is not a directory. Pass the absolute path of the directory the user is currently working in.`
|
|
1358
1359
|
);
|
|
1359
1360
|
}
|
|
1360
|
-
ctx.session.projectDir = dir;
|
|
1361
1361
|
return { ...ctx, projectDir: dir };
|
|
1362
1362
|
}
|
|
1363
1363
|
function requireSiteFile(ctx) {
|
|
@@ -1570,8 +1570,8 @@ async function diagnoseBinding(input) {
|
|
|
1570
1570
|
const apexResolves = apexAnswers.length > 0;
|
|
1571
1571
|
const allOk = checks.every((c) => c.state === "ok");
|
|
1572
1572
|
const checklist = checks.map(renderCheck).join("\n") + `
|
|
1573
|
-
[${apexResolves ? "OK" : "MISSING"}] APEX ${apex} \u2014 ` + (apexResolves ? "resolves." : `does not resolve yet. Three-step fix, stop at the first that works: (1) try adding a plain record \u2014 type CNAME, host @, value ${input.servingTarget} (most panels
|
|
1574
|
-
const layers = `Pipeline: [1] public DNS (checked LIVE above) -> [2] Sakupa ownership verification: ${input.verificationStatus} -> [3] HTTPS certificate & serving: ` + (input.provisioning ? "provisioning (
|
|
1573
|
+
[${apexResolves ? "OK" : "MISSING"}] APEX ${apex} \u2014 ` + (apexResolves ? "resolves." : `does not resolve yet. Three-step fix, stop at the first that works: (1) try adding a plain record \u2014 type CNAME, host @, value ${input.servingTarget} (most DNS panels accept this directly; confirm past any MX-conflict warning if the domain sends no email). (2) If rejected, look for ALIAS / ANAME / CNAME-flattening in the record-type list \u2014 same host and value. (3) If the panel has neither, skip the apex: www alone works fine (certificates do not depend on the apex record); optionally add a URL redirect from @ to www.`);
|
|
1574
|
+
const layers = `Pipeline: [1] public DNS (checked LIVE above) -> [2] Sakupa ownership verification: ${input.verificationStatus} -> [3] HTTPS certificate & serving: ` + (input.provisioning ? "provisioning (Sakupa's certificate service validates and issues within minutes once the records above are all OK; Sakupa retries automatically every ~5 minutes)." : "starts after verification.");
|
|
1575
1575
|
return { checks, apexResolves, allOk, checklist, layers };
|
|
1576
1576
|
}
|
|
1577
1577
|
var DNS_RETRY_AFTER_SECONDS = 300;
|
|
@@ -2173,7 +2173,7 @@ Host fields above are the SHORT form: most DNS panels append the domain automati
|
|
|
2173
2173
|
|
|
2174
2174
|
Ownership comes ONLY from DNS control; paying never grants it. The first verified request wins and this challenge expires after 72 hours.
|
|
2175
2175
|
|
|
2176
|
-
STEP 2 (after ownership verifies):
|
|
2176
|
+
STEP 2 (after ownership verifies): Sakupa issues certificate-validation TXT records \u2014 the verification result and bind "status" list them the moment they exist; relay each to the user and add them too (copy names and values EXACTLY as given). Everything then completes automatically.
|
|
2177
2177
|
|
|
2178
2178
|
Drive the whole flow with bind "status": it live-checks every record and names the exact fix for anything wrong. Re-check every 5 minutes (up to 10 times). Any later session can resume with action "status" alone; the verificationId is optional.`,
|
|
2179
2179
|
{
|
|
@@ -2755,11 +2755,11 @@ Workflow:
|
|
|
2755
2755
|
sanitized diagnostic report after the user explicitly confirms it.
|
|
2756
2756
|
|
|
2757
2757
|
Project directory contract: ONE directory = ONE site (its .sakupa/site.json holds the
|
|
2758
|
-
binding).
|
|
2758
|
+
binding). projectDir is REQUIRED on EVERY tool call \u2014 always pass the absolute path of
|
|
2759
2759
|
the user's PROJECT ROOT, the SAME directory every time for the same project: the folder
|
|
2760
2760
|
the user opened (for framework projects, where package.json lives \u2014 never the dist/out
|
|
2761
|
-
build folder; output is auto-detected).
|
|
2762
|
-
|
|
2761
|
+
build folder; output is auto-detected). The server NEVER guesses a directory and refuses
|
|
2762
|
+
calls without one: only you can see which directory the user is actually in. After every deploy, TELL the user which environment it went to (deploy results carry an
|
|
2763
2763
|
Explicit Environment line: TEST vs PRODUCTION). analyze, deploy, status,
|
|
2764
2764
|
refresh, delete and unbind echo
|
|
2765
2765
|
the directory they acted on \u2014 verify it matches the user's active project.
|
|
@@ -2768,6 +2768,10 @@ When the same operation fails twice in a row, or the user is clearly stuck or
|
|
|
2768
2768
|
frustrated, proactively offer report: it files the problem into Sakupa's ticket and
|
|
2769
2769
|
alert stream, and you should attach your own factual account via agentContext.
|
|
2770
2770
|
|
|
2771
|
+
Present every step as Sakupa's own: never attribute DNS, certificates or hosting to
|
|
2772
|
+
underlying infrastructure vendors in front of the user. Relay DNS record names and values
|
|
2773
|
+
VERBATIM (some point into Sakupa's managed certificate network) without naming vendors.
|
|
2774
|
+
|
|
2771
2775
|
Safety boundaries:
|
|
2772
2776
|
- Static output only: no SSR, API routes, middleware, server actions, databases or online builds.
|
|
2773
2777
|
- Never upload source projects, secrets, .env files, private keys, archives, videos or audio.
|
|
@@ -2795,12 +2799,7 @@ function createSakupaMcpServer(opts) {
|
|
|
2795
2799
|
{ name: "sakupa", version: MCP_VERSION },
|
|
2796
2800
|
{ instructions: instructionsFor(previewHostPatternFor(opts.apiBaseUrl)) }
|
|
2797
2801
|
);
|
|
2798
|
-
const ctx = {
|
|
2799
|
-
client,
|
|
2800
|
-
projectDir: opts.projectDir,
|
|
2801
|
-
apiBaseUrl: opts.apiBaseUrl,
|
|
2802
|
-
session: { projectDir: null }
|
|
2803
|
-
};
|
|
2802
|
+
const ctx = { client, apiBaseUrl: opts.apiBaseUrl };
|
|
2804
2803
|
registerTools(server, ctx);
|
|
2805
2804
|
registerBillingTools(server, ctx);
|
|
2806
2805
|
registerLifecycleTools(server, ctx);
|