@sakupa/mcp 0.3.0 → 0.4.0
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 +25 -12
- package/dist/index.js +25 -12
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1057,7 +1057,7 @@ async function analyzeProject(projectDir, opts = {}) {
|
|
|
1057
1057
|
|
|
1058
1058
|
// src/project-file.ts
|
|
1059
1059
|
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
1060
|
-
import { join as join2 } from "node:path";
|
|
1060
|
+
import { dirname, join as join2 } from "node:path";
|
|
1061
1061
|
var SITE_DIR = ".sakupa";
|
|
1062
1062
|
var SITE_FILE = "site.json";
|
|
1063
1063
|
function siteFilePath(projectDir) {
|
|
@@ -1100,9 +1100,22 @@ function writeSiteFile(projectDir, file) {
|
|
|
1100
1100
|
} catch {
|
|
1101
1101
|
}
|
|
1102
1102
|
}
|
|
1103
|
+
function isInsideGitRepo(projectDir) {
|
|
1104
|
+
let dir = projectDir;
|
|
1105
|
+
for (; ; ) {
|
|
1106
|
+
if (existsSync(join2(dir, ".git"))) return true;
|
|
1107
|
+
const parent = dirname(dir);
|
|
1108
|
+
if (parent === dir) return false;
|
|
1109
|
+
dir = parent;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
function credentialGitReminder(projectDir) {
|
|
1113
|
+
if (!isInsideGitRepo(projectDir)) return "";
|
|
1114
|
+
return '\nNOTE: this project is inside a git repository. The management credential in .sakupa/site.json is the key to this site \u2014 do NOT commit it to a PUBLIC repository (add ".sakupa/" to .gitignore yourself if you want to keep it out of version control).';
|
|
1115
|
+
}
|
|
1103
1116
|
|
|
1104
1117
|
// src/version.ts
|
|
1105
|
-
var MCP_VERSION = "0.
|
|
1118
|
+
var MCP_VERSION = "0.4.0";
|
|
1106
1119
|
var CLIENT_TYPE = "sakupa-mcp";
|
|
1107
1120
|
|
|
1108
1121
|
// src/tools/context.ts
|
|
@@ -1318,7 +1331,7 @@ Files uploaded: ${uploaded2} (${finalized2.totalBytes} bytes)
|
|
|
1318
1331
|
` + (finalized2.expiresAt ? `Expires at: ${finalized2.expiresAt}
|
|
1319
1332
|
` : "") + `
|
|
1320
1333
|
This is a FREE temporary preview: it stays live for ${FREE_SITE_TTL_HOURS} hours. Deploying again or calling refresh_site extends the validity; subscribing the site (subscribe_site) makes this URL permanent. The management credential was saved to .sakupa/site.json \u2014 keep that file: it is the only way to manage this site.
|
|
1321
|
-
` + (finalized2.warnings.length > 0 ? `
|
|
1334
|
+
` + credentialGitReminder(ctx.projectDir) + (finalized2.warnings.length > 0 ? `
|
|
1322
1335
|
Warnings:
|
|
1323
1336
|
${JSON.stringify(finalized2.warnings, null, 2)}` : "")
|
|
1324
1337
|
);
|
|
@@ -1449,9 +1462,11 @@ Once payment confirms, the site becomes permanent on its current URL. Binding a
|
|
|
1449
1462
|
server.registerTool(
|
|
1450
1463
|
"bind_domain",
|
|
1451
1464
|
{
|
|
1452
|
-
description: "Bind a custom domain to this subscribed site \u2014 an OPTIONAL extra serving surface; the permanent {shortId}.sakupa.com URL keeps working alongside it. Requires an ACTIVE subscription (subscribe_site). Ownership is proven ONLY by DNS control of the
|
|
1465
|
+
description: "Bind a custom domain to this subscribed site \u2014 an OPTIONAL extra serving surface; the permanent {shortId}.sakupa.com URL keeps working alongside it. The binding unit is the APEX domain: binding example.com automatically includes www.example.com (both serve the same content, one apex TXT verification covers both), and one site binds at most ONE apex domain \u2014 a second domain needs a second subscribed site. Requires an ACTIVE subscription (subscribe_site). Ownership is proven ONLY by DNS control of the apex \u2014 payment never grants ownership. A binding request never reserves the domain: whoever proves DNS control first gets it, and unverified requests expire after 72 hours. Call again with verificationId to check progress.",
|
|
1453
1466
|
inputSchema: {
|
|
1454
|
-
hostname: z.string().describe(
|
|
1467
|
+
hostname: z.string().describe(
|
|
1468
|
+
'Domain to bind: the apex ("example.com") or its www form ("www.example.com") \u2014 both mean the same unit. Other subdomains cannot be bound.'
|
|
1469
|
+
),
|
|
1455
1470
|
verificationId: z.string().optional().describe("Check an existing DNS verification instead of starting a new one.")
|
|
1456
1471
|
}
|
|
1457
1472
|
},
|
|
@@ -1466,7 +1481,7 @@ Once payment confirms, the site becomes permanent on its current URL. Binding a
|
|
|
1466
1481
|
return text(
|
|
1467
1482
|
`DNS verification ${res2.verificationId}: ${res2.status}
|
|
1468
1483
|
${res2.message}
|
|
1469
|
-
` + (res2.provisioningJobId ? `Provisioning started (job ${res2.provisioningJobId}). HTTPS
|
|
1484
|
+
` + (res2.provisioningJobId ? `Provisioning started (job ${res2.provisioningJobId}). HTTPS certificates and serving setup are in progress; check again with bind_domain + verificationId later.
|
|
1470
1485
|
` : "") + (res2.pendingDnsRecords.length > 0 ? `
|
|
1471
1486
|
DNS records still required:
|
|
1472
1487
|
${JSON.stringify(res2.pendingDnsRecords, null, 2)}` : "")
|
|
@@ -1477,18 +1492,16 @@ ${JSON.stringify(res2.pendingDnsRecords, null, 2)}` : "")
|
|
|
1477
1492
|
hostname: args.hostname
|
|
1478
1493
|
};
|
|
1479
1494
|
const res = await ctx.client.bindDomain(site.credential, req);
|
|
1480
|
-
const servingHint = res.isApex ? `"${res.hostname}" is an apex domain: your DNS provider must support ALIAS, ANAME, CNAME flattening, or alias records to point it at the assigned CDN domain.` : `"${res.hostname}" is a subdomain: create a CNAME record pointing it at the assigned CDN domain once provisioning completes.`;
|
|
1481
1495
|
return text(
|
|
1482
|
-
`Domain binding started for ${res.
|
|
1496
|
+
`Domain binding started for ${res.apexDomain} (includes: ${res.includedHostnames.join(", ")} \u2014 both will serve this site).
|
|
1483
1497
|
|
|
1484
1498
|
1. Prove control of ${res.apexDomain} by creating this DNS record:
|
|
1485
1499
|
name: ${res.verificationRecord.name}
|
|
1486
1500
|
type: ${res.verificationRecord.type}
|
|
1487
1501
|
value: ${res.verificationRecord.value}
|
|
1488
|
-
Ownership comes ONLY from DNS control; paying never grants it. This request does not reserve the
|
|
1502
|
+
Ownership comes ONLY from DNS control; paying never grants it. This request does not reserve the domain \u2014 the first verified request wins, and this challenge expires after 72 hours.
|
|
1489
1503
|
|
|
1490
|
-
2. Serving DNS (after verification): ${
|
|
1491
|
-
Server guidance: ${res.servingInstructions}
|
|
1504
|
+
2. Serving DNS (after verification): ${res.servingInstructions}
|
|
1492
1505
|
|
|
1493
1506
|
Then run bind_domain again with verificationId: "${res.verificationId}" to check verification and start provisioning.`
|
|
1494
1507
|
);
|
|
@@ -1593,7 +1606,7 @@ Site: ${res.siteId} (hostnames: ${res.boundHostnames.join(", ") || "(none)"})
|
|
|
1593
1606
|
Previous credentials revoked: ${res.revokedPreviousCredentials ? "YES" : "no (preserved on request)"}
|
|
1594
1607
|
|
|
1595
1608
|
A NEW management credential was written to .sakupa/site.json in this project \u2014 this project now manages the site.
|
|
1596
|
-
|
|
1609
|
+
` + credentialGitReminder(ctx.projectDir) + `
|
|
1597
1610
|
Download the current site content (signed URL):
|
|
1598
1611
|
${res.archiveUrl}`
|
|
1599
1612
|
);
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/version.ts
|
|
2
|
-
var MCP_VERSION = "0.
|
|
2
|
+
var MCP_VERSION = "0.4.0";
|
|
3
3
|
var CLIENT_TYPE = "sakupa-mcp";
|
|
4
4
|
|
|
5
5
|
// ../core/dist/domain/constants.js
|
|
@@ -673,7 +673,7 @@ var HttpApiClient = class {
|
|
|
673
673
|
|
|
674
674
|
// src/project-file.ts
|
|
675
675
|
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
676
|
-
import { join } from "node:path";
|
|
676
|
+
import { dirname, join } from "node:path";
|
|
677
677
|
var SITE_DIR = ".sakupa";
|
|
678
678
|
var SITE_FILE = "site.json";
|
|
679
679
|
function siteFilePath(projectDir) {
|
|
@@ -722,6 +722,19 @@ function deleteSiteFile(projectDir) {
|
|
|
722
722
|
rmSync(path, { force: true });
|
|
723
723
|
}
|
|
724
724
|
}
|
|
725
|
+
function isInsideGitRepo(projectDir) {
|
|
726
|
+
let dir = projectDir;
|
|
727
|
+
for (; ; ) {
|
|
728
|
+
if (existsSync(join(dir, ".git"))) return true;
|
|
729
|
+
const parent = dirname(dir);
|
|
730
|
+
if (parent === dir) return false;
|
|
731
|
+
dir = parent;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function credentialGitReminder(projectDir) {
|
|
735
|
+
if (!isInsideGitRepo(projectDir)) return "";
|
|
736
|
+
return '\nNOTE: this project is inside a git repository. The management credential in .sakupa/site.json is the key to this site \u2014 do NOT commit it to a PUBLIC repository (add ".sakupa/" to .gitignore yourself if you want to keep it out of version control).';
|
|
737
|
+
}
|
|
725
738
|
|
|
726
739
|
// src/analyze/analyzer.ts
|
|
727
740
|
import { promises as fs } from "node:fs";
|
|
@@ -1366,7 +1379,7 @@ Files uploaded: ${uploaded2} (${finalized2.totalBytes} bytes)
|
|
|
1366
1379
|
` + (finalized2.expiresAt ? `Expires at: ${finalized2.expiresAt}
|
|
1367
1380
|
` : "") + `
|
|
1368
1381
|
This is a FREE temporary preview: it stays live for ${FREE_SITE_TTL_HOURS} hours. Deploying again or calling refresh_site extends the validity; subscribing the site (subscribe_site) makes this URL permanent. The management credential was saved to .sakupa/site.json \u2014 keep that file: it is the only way to manage this site.
|
|
1369
|
-
` + (finalized2.warnings.length > 0 ? `
|
|
1382
|
+
` + credentialGitReminder(ctx.projectDir) + (finalized2.warnings.length > 0 ? `
|
|
1370
1383
|
Warnings:
|
|
1371
1384
|
${JSON.stringify(finalized2.warnings, null, 2)}` : "")
|
|
1372
1385
|
);
|
|
@@ -1497,9 +1510,11 @@ Once payment confirms, the site becomes permanent on its current URL. Binding a
|
|
|
1497
1510
|
server.registerTool(
|
|
1498
1511
|
"bind_domain",
|
|
1499
1512
|
{
|
|
1500
|
-
description: "Bind a custom domain to this subscribed site \u2014 an OPTIONAL extra serving surface; the permanent {shortId}.sakupa.com URL keeps working alongside it. Requires an ACTIVE subscription (subscribe_site). Ownership is proven ONLY by DNS control of the
|
|
1513
|
+
description: "Bind a custom domain to this subscribed site \u2014 an OPTIONAL extra serving surface; the permanent {shortId}.sakupa.com URL keeps working alongside it. The binding unit is the APEX domain: binding example.com automatically includes www.example.com (both serve the same content, one apex TXT verification covers both), and one site binds at most ONE apex domain \u2014 a second domain needs a second subscribed site. Requires an ACTIVE subscription (subscribe_site). Ownership is proven ONLY by DNS control of the apex \u2014 payment never grants ownership. A binding request never reserves the domain: whoever proves DNS control first gets it, and unverified requests expire after 72 hours. Call again with verificationId to check progress.",
|
|
1501
1514
|
inputSchema: {
|
|
1502
|
-
hostname: z.string().describe(
|
|
1515
|
+
hostname: z.string().describe(
|
|
1516
|
+
'Domain to bind: the apex ("example.com") or its www form ("www.example.com") \u2014 both mean the same unit. Other subdomains cannot be bound.'
|
|
1517
|
+
),
|
|
1503
1518
|
verificationId: z.string().optional().describe("Check an existing DNS verification instead of starting a new one.")
|
|
1504
1519
|
}
|
|
1505
1520
|
},
|
|
@@ -1514,7 +1529,7 @@ Once payment confirms, the site becomes permanent on its current URL. Binding a
|
|
|
1514
1529
|
return text(
|
|
1515
1530
|
`DNS verification ${res2.verificationId}: ${res2.status}
|
|
1516
1531
|
${res2.message}
|
|
1517
|
-
` + (res2.provisioningJobId ? `Provisioning started (job ${res2.provisioningJobId}). HTTPS
|
|
1532
|
+
` + (res2.provisioningJobId ? `Provisioning started (job ${res2.provisioningJobId}). HTTPS certificates and serving setup are in progress; check again with bind_domain + verificationId later.
|
|
1518
1533
|
` : "") + (res2.pendingDnsRecords.length > 0 ? `
|
|
1519
1534
|
DNS records still required:
|
|
1520
1535
|
${JSON.stringify(res2.pendingDnsRecords, null, 2)}` : "")
|
|
@@ -1525,18 +1540,16 @@ ${JSON.stringify(res2.pendingDnsRecords, null, 2)}` : "")
|
|
|
1525
1540
|
hostname: args.hostname
|
|
1526
1541
|
};
|
|
1527
1542
|
const res = await ctx.client.bindDomain(site.credential, req);
|
|
1528
|
-
const servingHint = res.isApex ? `"${res.hostname}" is an apex domain: your DNS provider must support ALIAS, ANAME, CNAME flattening, or alias records to point it at the assigned CDN domain.` : `"${res.hostname}" is a subdomain: create a CNAME record pointing it at the assigned CDN domain once provisioning completes.`;
|
|
1529
1543
|
return text(
|
|
1530
|
-
`Domain binding started for ${res.
|
|
1544
|
+
`Domain binding started for ${res.apexDomain} (includes: ${res.includedHostnames.join(", ")} \u2014 both will serve this site).
|
|
1531
1545
|
|
|
1532
1546
|
1. Prove control of ${res.apexDomain} by creating this DNS record:
|
|
1533
1547
|
name: ${res.verificationRecord.name}
|
|
1534
1548
|
type: ${res.verificationRecord.type}
|
|
1535
1549
|
value: ${res.verificationRecord.value}
|
|
1536
|
-
Ownership comes ONLY from DNS control; paying never grants it. This request does not reserve the
|
|
1550
|
+
Ownership comes ONLY from DNS control; paying never grants it. This request does not reserve the domain \u2014 the first verified request wins, and this challenge expires after 72 hours.
|
|
1537
1551
|
|
|
1538
|
-
2. Serving DNS (after verification): ${
|
|
1539
|
-
Server guidance: ${res.servingInstructions}
|
|
1552
|
+
2. Serving DNS (after verification): ${res.servingInstructions}
|
|
1540
1553
|
|
|
1541
1554
|
Then run bind_domain again with verificationId: "${res.verificationId}" to check verification and start provisioning.`
|
|
1542
1555
|
);
|
|
@@ -1641,7 +1654,7 @@ Site: ${res.siteId} (hostnames: ${res.boundHostnames.join(", ") || "(none)"})
|
|
|
1641
1654
|
Previous credentials revoked: ${res.revokedPreviousCredentials ? "YES" : "no (preserved on request)"}
|
|
1642
1655
|
|
|
1643
1656
|
A NEW management credential was written to .sakupa/site.json in this project \u2014 this project now manages the site.
|
|
1644
|
-
|
|
1657
|
+
` + credentialGitReminder(ctx.projectDir) + `
|
|
1645
1658
|
Download the current site content (signed URL):
|
|
1646
1659
|
${res.archiveUrl}`
|
|
1647
1660
|
);
|