@sakupa/mcp 0.4.0 → 0.5.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 +61 -15
- package/dist/index.js +64 -18
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -126,6 +126,9 @@ var FORBIDDEN_PATH_SEGMENTS = [
|
|
|
126
126
|
];
|
|
127
127
|
var ALLOWED_HIDDEN_PATHS = [".well-known/"];
|
|
128
128
|
|
|
129
|
+
// ../core/dist/domain/version.js
|
|
130
|
+
var SAKUPA_MCP_VERSION = "0.5.0";
|
|
131
|
+
|
|
129
132
|
// ../core/dist/domain/errors.js
|
|
130
133
|
var HTTP_STATUS = {
|
|
131
134
|
invalid_request: 400,
|
|
@@ -461,6 +464,12 @@ var MCP_VERSION_HEADER = "x-sakupa-mcp-version";
|
|
|
461
464
|
var utf8Decoder = new TextDecoder("utf-8", { fatal: false });
|
|
462
465
|
var utf8Encoder = new TextEncoder();
|
|
463
466
|
|
|
467
|
+
// ../core/dist/services/subscriptions.js
|
|
468
|
+
var WEBHOOK_PROCESSING_LEASE_MS = 5 * 60 * 1e3;
|
|
469
|
+
|
|
470
|
+
// ../core/dist/services/billing-cancellation.js
|
|
471
|
+
var encoder = new TextEncoder();
|
|
472
|
+
|
|
464
473
|
// src/server.ts
|
|
465
474
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
466
475
|
|
|
@@ -609,6 +618,13 @@ var HttpApiClient = class {
|
|
|
609
618
|
{ credential, body: req }
|
|
610
619
|
);
|
|
611
620
|
}
|
|
621
|
+
async requestBillingCancellation(req) {
|
|
622
|
+
return this.call(
|
|
623
|
+
"POST",
|
|
624
|
+
"/v1/billing/cancellation-requests",
|
|
625
|
+
{ body: req }
|
|
626
|
+
);
|
|
627
|
+
}
|
|
612
628
|
async createTicket(credential, req) {
|
|
613
629
|
return this.call("POST", "/v1/support/tickets", {
|
|
614
630
|
credential,
|
|
@@ -1115,7 +1131,7 @@ function credentialGitReminder(projectDir) {
|
|
|
1115
1131
|
}
|
|
1116
1132
|
|
|
1117
1133
|
// src/version.ts
|
|
1118
|
-
var MCP_VERSION =
|
|
1134
|
+
var MCP_VERSION = SAKUPA_MCP_VERSION;
|
|
1119
1135
|
var CLIENT_TYPE = "sakupa-mcp";
|
|
1120
1136
|
|
|
1121
1137
|
// src/tools/context.ts
|
|
@@ -1283,6 +1299,9 @@ Next action: ${analysis.suggestedNextAction}`,
|
|
|
1283
1299
|
outputDir: z.string().optional().describe("Output directory relative to the project root (overrides detection)."),
|
|
1284
1300
|
spaFallback: z.boolean().optional().describe("Enable SPA fallback (requires spaFallbackConfirmed: true)."),
|
|
1285
1301
|
spaFallbackConfirmed: z.boolean().optional().describe("User explicitly confirmed the SPA fallback 404-behavior change."),
|
|
1302
|
+
publicConfirmed: z.boolean().optional().describe(
|
|
1303
|
+
"Required only for the first deployment: user explicitly confirmed creation of a public 24-hour URL."
|
|
1304
|
+
),
|
|
1286
1305
|
lang: z.string().optional().describe("Site language override (en | ja | zh-CN); defaults to the html lang.")
|
|
1287
1306
|
}
|
|
1288
1307
|
},
|
|
@@ -1303,6 +1322,11 @@ Next action: ${analysis.suggestedNextAction}`,
|
|
|
1303
1322
|
const outputAbs = resolve2(ctx.projectDir, analysis.recommendedOutputDir ?? ".");
|
|
1304
1323
|
const manifest = await buildHashedManifest(files, outputAbs);
|
|
1305
1324
|
const existing = readSiteFile(ctx.projectDir);
|
|
1325
|
+
if (!existing && args.publicConfirmed !== true) {
|
|
1326
|
+
return text(
|
|
1327
|
+
`First deployment creates a public URL that anyone with the link can open. The free preview stays live for ${FREE_SITE_TTL_HOURS} hours. Explain this to the user and obtain explicit confirmation before retrying deploy_site with publicConfirmed: true.`
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1306
1330
|
ensureUploadSizeWithinLimits(manifest, !existing);
|
|
1307
1331
|
if (!existing) {
|
|
1308
1332
|
const created = await ctx.client.createSite({
|
|
@@ -1543,18 +1567,40 @@ Full status:`, res);
|
|
|
1543
1567
|
server.registerTool(
|
|
1544
1568
|
"manage_billing",
|
|
1545
1569
|
{
|
|
1546
|
-
description: "Open the Stripe-hosted billing portal for this site: update the payment method, view invoices, or cancel the subscription. All billing operations happen on the Stripe-hosted page \u2014 never inside the AI tool.
|
|
1547
|
-
inputSchema: {
|
|
1570
|
+
description: "Open the Stripe-hosted billing portal for this site: update the payment method, view invoices, or cancel the subscription. All billing operations happen on the Stripe-hosted page \u2014 never inside the AI tool. With .sakupa/site.json, this opens the site-specific portal. If the local credential was lost, pass the remembered Sakupa site URL: Sakupa returns the same generic response for every URL and, only when an eligible subscription exists, emails its Stripe billing address a one-time period-end cancellation confirmation link. This never restores site authority.",
|
|
1571
|
+
inputSchema: {
|
|
1572
|
+
siteUrl: z.string().optional().describe(
|
|
1573
|
+
"Without .sakupa/site.json only: the remembered https://{shortId}.sakupa.com URL."
|
|
1574
|
+
)
|
|
1575
|
+
}
|
|
1548
1576
|
},
|
|
1549
|
-
async () => {
|
|
1577
|
+
async (args) => {
|
|
1550
1578
|
try {
|
|
1551
|
-
const site =
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1579
|
+
const site = readSiteFile(ctx.projectDir);
|
|
1580
|
+
if (site) {
|
|
1581
|
+
if (args.siteUrl !== void 0) {
|
|
1582
|
+
return text(
|
|
1583
|
+
"A local Sakupa credential is present, so manage_billing uses the site-specific Stripe portal. Remove siteUrl and call manage_billing again; the public lost-key path is deliberately unavailable while site authority is present."
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
const res2 = await ctx.client.createBillingPortal(site.siteId, site.credential);
|
|
1587
|
+
return text(
|
|
1588
|
+
`Stripe billing portal for this site:
|
|
1589
|
+
${res2.portalUrl}
|
|
1556
1590
|
|
|
1557
1591
|
Open this link in a browser to update the payment method, view invoices, or manage the subscription. The link is temporary \u2014 create a fresh one when needed.`
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
if (args.siteUrl === void 0) {
|
|
1595
|
+
return text(
|
|
1596
|
+
"No .sakupa/site.json was found. Provide the remembered Sakupa site URL to request a cancellation email. Sakupa will not reveal whether the URL, site, billing email, or subscription exists. If eligible, the original Stripe billing email receives a short-lived one-time link. Opening it only shows the consequences; the user must press the confirmation button to stop the next renewal. This does not recover a key or grant deployment, download, or content access."
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
const res = await ctx.client.requestBillingCancellation({ siteUrl: args.siteUrl });
|
|
1600
|
+
return text(
|
|
1601
|
+
`${res.message}
|
|
1602
|
+
|
|
1603
|
+
For privacy, this response is identical whether or not the URL, site, customer, email, or subscription exists. Check the original Stripe billing inbox. The email link expires quickly and can only schedule cancellation at the current paid period end; it cannot restore the Sakupa credential or access the site.`
|
|
1558
1604
|
);
|
|
1559
1605
|
} catch (e) {
|
|
1560
1606
|
return toolError(e);
|
|
@@ -1618,13 +1664,12 @@ ${res.archiveUrl}`
|
|
|
1618
1664
|
server.registerTool(
|
|
1619
1665
|
"set_billing_plan",
|
|
1620
1666
|
{
|
|
1621
|
-
description: `Change this site's hosting plan
|
|
1667
|
+
description: `Change this site's hosting plan or cancel/re-enable renewal. Plans: ${planCatalog()}. Plan changes go through the Stripe subscription; renewals bill the new plan. Auto-upgrade (one plan up when usage exceeds the current plan, capped at Business) is built in and not configurable. cancelRenewal: true ends the subscription at the period end; the paid service remains available until then. The site reverts to a free 24h site only after the signed Stripe final-cancellation webhook arrives. The API returns the consequences first; explicit owner confirmation (confirm: true) is required before anything is applied.`,
|
|
1622
1668
|
inputSchema: {
|
|
1623
1669
|
plan: planEnum.optional().describe("Target monthly plan."),
|
|
1624
1670
|
cancelRenewal: z.boolean().optional().describe(
|
|
1625
1671
|
"true: cancel renewal (the site stays permanent to the end of the paid month, then reverts to free). false: re-enable renewal."
|
|
1626
1672
|
),
|
|
1627
|
-
cancelNow: z.boolean().optional().describe("End the subscription immediately; the site reverts to free right away."),
|
|
1628
1673
|
confirm: z.boolean().optional().describe("User saw the consequences and explicitly confirmed.")
|
|
1629
1674
|
}
|
|
1630
1675
|
},
|
|
@@ -1634,7 +1679,6 @@ ${res.archiveUrl}`
|
|
|
1634
1679
|
const req = {
|
|
1635
1680
|
...args.plan !== void 0 ? { plan: args.plan } : {},
|
|
1636
1681
|
...args.cancelRenewal !== void 0 ? { cancelRenewal: args.cancelRenewal } : {},
|
|
1637
|
-
...args.cancelNow !== void 0 ? { cancelNow: args.cancelNow } : {},
|
|
1638
1682
|
confirm: args.confirm === true
|
|
1639
1683
|
};
|
|
1640
1684
|
try {
|
|
@@ -1815,8 +1859,8 @@ Workflow:
|
|
|
1815
1859
|
serving surface alongside the permanent URL. Ownership is proven only by DNS control; the
|
|
1816
1860
|
first verified request wins; unverified requests expire after 72 hours. billing_status,
|
|
1817
1861
|
set_billing_plan, manage_billing and recover_domain_site manage the paid lifecycle.
|
|
1818
|
-
|
|
1819
|
-
|
|
1862
|
+
An immediate Stripe cancellation reverts the site to a free 24h site and removes all paid
|
|
1863
|
+
data when Sakupa receives the signed cancellation webhook.
|
|
1820
1864
|
|
|
1821
1865
|
Safety boundaries:
|
|
1822
1866
|
- Static output only: no SSR, API routes, middleware, server actions, databases or online builds.
|
|
@@ -1824,7 +1868,9 @@ Safety boundaries:
|
|
|
1824
1868
|
- Payment card data is entered only on Stripe-hosted pages \u2014 never through the AI tool.
|
|
1825
1869
|
- A subscription never grants domain ownership; only DNS verification does.
|
|
1826
1870
|
- The management credential lives only in .sakupa/site.json; never share or upload it. Without
|
|
1827
|
-
a bound custom domain, a lost credential is unrecoverable by design
|
|
1871
|
+
a bound custom domain, a lost credential is unrecoverable by design. manage_billing can accept
|
|
1872
|
+
the remembered Sakupa URL and request a one-time cancellation link sent only to the exact
|
|
1873
|
+
subscription's Stripe billing email; it never restores site authority.`;
|
|
1828
1874
|
function createSakupaMcpServer(opts) {
|
|
1829
1875
|
const client = opts.client ?? new HttpApiClient(new FetchTransport(opts.apiBaseUrl));
|
|
1830
1876
|
const server = new McpServer(
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
// src/version.ts
|
|
2
|
-
var MCP_VERSION = "0.4.0";
|
|
3
|
-
var CLIENT_TYPE = "sakupa-mcp";
|
|
4
|
-
|
|
5
1
|
// ../core/dist/domain/constants.js
|
|
6
2
|
var SERVICE_DOMAIN = "sakupa.com";
|
|
7
3
|
var FREE_SITE_URL_SUFFIX = `.${SERVICE_DOMAIN}`;
|
|
@@ -124,6 +120,9 @@ var FORBIDDEN_PATH_SEGMENTS = [
|
|
|
124
120
|
];
|
|
125
121
|
var ALLOWED_HIDDEN_PATHS = [".well-known/"];
|
|
126
122
|
|
|
123
|
+
// ../core/dist/domain/version.js
|
|
124
|
+
var SAKUPA_MCP_VERSION = "0.5.0";
|
|
125
|
+
|
|
127
126
|
// ../core/dist/domain/errors.js
|
|
128
127
|
var HTTP_STATUS = {
|
|
129
128
|
invalid_request: 400,
|
|
@@ -459,6 +458,16 @@ var MCP_VERSION_HEADER = "x-sakupa-mcp-version";
|
|
|
459
458
|
var utf8Decoder = new TextDecoder("utf-8", { fatal: false });
|
|
460
459
|
var utf8Encoder = new TextEncoder();
|
|
461
460
|
|
|
461
|
+
// ../core/dist/services/subscriptions.js
|
|
462
|
+
var WEBHOOK_PROCESSING_LEASE_MS = 5 * 60 * 1e3;
|
|
463
|
+
|
|
464
|
+
// ../core/dist/services/billing-cancellation.js
|
|
465
|
+
var encoder = new TextEncoder();
|
|
466
|
+
|
|
467
|
+
// src/version.ts
|
|
468
|
+
var MCP_VERSION = SAKUPA_MCP_VERSION;
|
|
469
|
+
var CLIENT_TYPE = "sakupa-mcp";
|
|
470
|
+
|
|
462
471
|
// src/transport.ts
|
|
463
472
|
var FetchTransport = class {
|
|
464
473
|
baseUrl;
|
|
@@ -657,6 +666,13 @@ var HttpApiClient = class {
|
|
|
657
666
|
{ credential, body: req }
|
|
658
667
|
);
|
|
659
668
|
}
|
|
669
|
+
async requestBillingCancellation(req) {
|
|
670
|
+
return this.call(
|
|
671
|
+
"POST",
|
|
672
|
+
"/v1/billing/cancellation-requests",
|
|
673
|
+
{ body: req }
|
|
674
|
+
);
|
|
675
|
+
}
|
|
660
676
|
async createTicket(credential, req) {
|
|
661
677
|
return this.call("POST", "/v1/support/tickets", {
|
|
662
678
|
credential,
|
|
@@ -1331,6 +1347,9 @@ Next action: ${analysis.suggestedNextAction}`,
|
|
|
1331
1347
|
outputDir: z.string().optional().describe("Output directory relative to the project root (overrides detection)."),
|
|
1332
1348
|
spaFallback: z.boolean().optional().describe("Enable SPA fallback (requires spaFallbackConfirmed: true)."),
|
|
1333
1349
|
spaFallbackConfirmed: z.boolean().optional().describe("User explicitly confirmed the SPA fallback 404-behavior change."),
|
|
1350
|
+
publicConfirmed: z.boolean().optional().describe(
|
|
1351
|
+
"Required only for the first deployment: user explicitly confirmed creation of a public 24-hour URL."
|
|
1352
|
+
),
|
|
1334
1353
|
lang: z.string().optional().describe("Site language override (en | ja | zh-CN); defaults to the html lang.")
|
|
1335
1354
|
}
|
|
1336
1355
|
},
|
|
@@ -1351,6 +1370,11 @@ Next action: ${analysis.suggestedNextAction}`,
|
|
|
1351
1370
|
const outputAbs = resolve2(ctx.projectDir, analysis.recommendedOutputDir ?? ".");
|
|
1352
1371
|
const manifest = await buildHashedManifest(files, outputAbs);
|
|
1353
1372
|
const existing = readSiteFile(ctx.projectDir);
|
|
1373
|
+
if (!existing && args.publicConfirmed !== true) {
|
|
1374
|
+
return text(
|
|
1375
|
+
`First deployment creates a public URL that anyone with the link can open. The free preview stays live for ${FREE_SITE_TTL_HOURS} hours. Explain this to the user and obtain explicit confirmation before retrying deploy_site with publicConfirmed: true.`
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1354
1378
|
ensureUploadSizeWithinLimits(manifest, !existing);
|
|
1355
1379
|
if (!existing) {
|
|
1356
1380
|
const created = await ctx.client.createSite({
|
|
@@ -1591,18 +1615,40 @@ Full status:`, res);
|
|
|
1591
1615
|
server.registerTool(
|
|
1592
1616
|
"manage_billing",
|
|
1593
1617
|
{
|
|
1594
|
-
description: "Open the Stripe-hosted billing portal for this site: update the payment method, view invoices, or cancel the subscription. All billing operations happen on the Stripe-hosted page \u2014 never inside the AI tool.
|
|
1595
|
-
inputSchema: {
|
|
1618
|
+
description: "Open the Stripe-hosted billing portal for this site: update the payment method, view invoices, or cancel the subscription. All billing operations happen on the Stripe-hosted page \u2014 never inside the AI tool. With .sakupa/site.json, this opens the site-specific portal. If the local credential was lost, pass the remembered Sakupa site URL: Sakupa returns the same generic response for every URL and, only when an eligible subscription exists, emails its Stripe billing address a one-time period-end cancellation confirmation link. This never restores site authority.",
|
|
1619
|
+
inputSchema: {
|
|
1620
|
+
siteUrl: z.string().optional().describe(
|
|
1621
|
+
"Without .sakupa/site.json only: the remembered https://{shortId}.sakupa.com URL."
|
|
1622
|
+
)
|
|
1623
|
+
}
|
|
1596
1624
|
},
|
|
1597
|
-
async () => {
|
|
1625
|
+
async (args) => {
|
|
1598
1626
|
try {
|
|
1599
|
-
const site =
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1627
|
+
const site = readSiteFile(ctx.projectDir);
|
|
1628
|
+
if (site) {
|
|
1629
|
+
if (args.siteUrl !== void 0) {
|
|
1630
|
+
return text(
|
|
1631
|
+
"A local Sakupa credential is present, so manage_billing uses the site-specific Stripe portal. Remove siteUrl and call manage_billing again; the public lost-key path is deliberately unavailable while site authority is present."
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
const res2 = await ctx.client.createBillingPortal(site.siteId, site.credential);
|
|
1635
|
+
return text(
|
|
1636
|
+
`Stripe billing portal for this site:
|
|
1637
|
+
${res2.portalUrl}
|
|
1604
1638
|
|
|
1605
1639
|
Open this link in a browser to update the payment method, view invoices, or manage the subscription. The link is temporary \u2014 create a fresh one when needed.`
|
|
1640
|
+
);
|
|
1641
|
+
}
|
|
1642
|
+
if (args.siteUrl === void 0) {
|
|
1643
|
+
return text(
|
|
1644
|
+
"No .sakupa/site.json was found. Provide the remembered Sakupa site URL to request a cancellation email. Sakupa will not reveal whether the URL, site, billing email, or subscription exists. If eligible, the original Stripe billing email receives a short-lived one-time link. Opening it only shows the consequences; the user must press the confirmation button to stop the next renewal. This does not recover a key or grant deployment, download, or content access."
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
const res = await ctx.client.requestBillingCancellation({ siteUrl: args.siteUrl });
|
|
1648
|
+
return text(
|
|
1649
|
+
`${res.message}
|
|
1650
|
+
|
|
1651
|
+
For privacy, this response is identical whether or not the URL, site, customer, email, or subscription exists. Check the original Stripe billing inbox. The email link expires quickly and can only schedule cancellation at the current paid period end; it cannot restore the Sakupa credential or access the site.`
|
|
1606
1652
|
);
|
|
1607
1653
|
} catch (e) {
|
|
1608
1654
|
return toolError(e);
|
|
@@ -1666,13 +1712,12 @@ ${res.archiveUrl}`
|
|
|
1666
1712
|
server.registerTool(
|
|
1667
1713
|
"set_billing_plan",
|
|
1668
1714
|
{
|
|
1669
|
-
description: `Change this site's hosting plan
|
|
1715
|
+
description: `Change this site's hosting plan or cancel/re-enable renewal. Plans: ${planCatalog()}. Plan changes go through the Stripe subscription; renewals bill the new plan. Auto-upgrade (one plan up when usage exceeds the current plan, capped at Business) is built in and not configurable. cancelRenewal: true ends the subscription at the period end; the paid service remains available until then. The site reverts to a free 24h site only after the signed Stripe final-cancellation webhook arrives. The API returns the consequences first; explicit owner confirmation (confirm: true) is required before anything is applied.`,
|
|
1670
1716
|
inputSchema: {
|
|
1671
1717
|
plan: planEnum.optional().describe("Target monthly plan."),
|
|
1672
1718
|
cancelRenewal: z.boolean().optional().describe(
|
|
1673
1719
|
"true: cancel renewal (the site stays permanent to the end of the paid month, then reverts to free). false: re-enable renewal."
|
|
1674
1720
|
),
|
|
1675
|
-
cancelNow: z.boolean().optional().describe("End the subscription immediately; the site reverts to free right away."),
|
|
1676
1721
|
confirm: z.boolean().optional().describe("User saw the consequences and explicitly confirmed.")
|
|
1677
1722
|
}
|
|
1678
1723
|
},
|
|
@@ -1682,7 +1727,6 @@ ${res.archiveUrl}`
|
|
|
1682
1727
|
const req = {
|
|
1683
1728
|
...args.plan !== void 0 ? { plan: args.plan } : {},
|
|
1684
1729
|
...args.cancelRenewal !== void 0 ? { cancelRenewal: args.cancelRenewal } : {},
|
|
1685
|
-
...args.cancelNow !== void 0 ? { cancelNow: args.cancelNow } : {},
|
|
1686
1730
|
confirm: args.confirm === true
|
|
1687
1731
|
};
|
|
1688
1732
|
try {
|
|
@@ -1811,8 +1855,8 @@ Workflow:
|
|
|
1811
1855
|
serving surface alongside the permanent URL. Ownership is proven only by DNS control; the
|
|
1812
1856
|
first verified request wins; unverified requests expire after 72 hours. billing_status,
|
|
1813
1857
|
set_billing_plan, manage_billing and recover_domain_site manage the paid lifecycle.
|
|
1814
|
-
|
|
1815
|
-
|
|
1858
|
+
An immediate Stripe cancellation reverts the site to a free 24h site and removes all paid
|
|
1859
|
+
data when Sakupa receives the signed cancellation webhook.
|
|
1816
1860
|
|
|
1817
1861
|
Safety boundaries:
|
|
1818
1862
|
- Static output only: no SSR, API routes, middleware, server actions, databases or online builds.
|
|
@@ -1820,7 +1864,9 @@ Safety boundaries:
|
|
|
1820
1864
|
- Payment card data is entered only on Stripe-hosted pages \u2014 never through the AI tool.
|
|
1821
1865
|
- A subscription never grants domain ownership; only DNS verification does.
|
|
1822
1866
|
- The management credential lives only in .sakupa/site.json; never share or upload it. Without
|
|
1823
|
-
a bound custom domain, a lost credential is unrecoverable by design
|
|
1867
|
+
a bound custom domain, a lost credential is unrecoverable by design. manage_billing can accept
|
|
1868
|
+
the remembered Sakupa URL and request a one-time cancellation link sent only to the exact
|
|
1869
|
+
subscription's Stripe billing email; it never restores site authority.`;
|
|
1824
1870
|
function createSakupaMcpServer(opts) {
|
|
1825
1871
|
const client = opts.client ?? new HttpApiClient(new FetchTransport(opts.apiBaseUrl));
|
|
1826
1872
|
const server = new McpServer(
|