@sakupa/mcp 0.7.0 → 0.7.1

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 CHANGED
@@ -7,6 +7,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
7
7
  var SERVICE_DOMAIN = "sakupa.com";
8
8
  var DEFAULT_API_BASE_URL = "https://api.sakupa.com";
9
9
  var FREE_SITE_URL_SUFFIX = `.${SERVICE_DOMAIN}`;
10
+ var TEST_ACCESS_HEADER = "x-sakupa-test-token";
10
11
  var FREE_SITE_TTL_HOURS = 24;
11
12
  var FREE_SITE_MAX_TOTAL_BYTES = 10 * 1024 * 1024;
12
13
  var PAID_SITE_MAX_TOTAL_BYTES = 2 * 1024 * 1024 * 1024;
@@ -127,7 +128,7 @@ var FORBIDDEN_PATH_SEGMENTS = [
127
128
  var ALLOWED_HIDDEN_PATHS = [".well-known/"];
128
129
 
129
130
  // ../core/dist/domain/version.js
130
- var SAKUPA_MCP_VERSION = "0.7.0";
131
+ var SAKUPA_MCP_VERSION = "0.7.1";
131
132
 
132
133
  // ../core/dist/domain/errors.js
133
134
  var HTTP_STATUS = {
@@ -464,6 +465,28 @@ var utf8Encoder = new TextEncoder();
464
465
  // ../core/dist/services/subscriptions.js
465
466
  var WEBHOOK_PROCESSING_LEASE_MS = 5 * 60 * 1e3;
466
467
 
468
+ // src/config.ts
469
+ var TEST_API_BASE_URL = "https://api-test.sakupa.com";
470
+ function loadMcpRuntimeConfig(env = process.env, cwd = process.cwd()) {
471
+ const apiBaseUrl = (env["SAKUPA_API_URL"] ?? DEFAULT_API_BASE_URL).replace(/\/+$/, "");
472
+ const projectDir = env["SAKUPA_PROJECT_DIR"] ?? cwd;
473
+ const testAccessToken = env["SAKUPA_TEST_ACCESS_TOKEN"]?.trim() ?? "";
474
+ if (apiBaseUrl === TEST_API_BASE_URL) {
475
+ if (testAccessToken.length === 0) {
476
+ throw new Error(
477
+ "The Sakupa Test API requires SAKUPA_TEST_ACCESS_TOKEN. Anonymous Test access is disabled."
478
+ );
479
+ }
480
+ return { apiBaseUrl, projectDir, testAccessToken };
481
+ }
482
+ if (testAccessToken.length > 0) {
483
+ throw new Error(
484
+ `SAKUPA_TEST_ACCESS_TOKEN may only be used with ${TEST_API_BASE_URL}. Remove it before connecting to any other API.`
485
+ );
486
+ }
487
+ return { apiBaseUrl, projectDir };
488
+ }
489
+
467
490
  // src/server.ts
468
491
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
469
492
 
@@ -2195,8 +2218,23 @@ function registerLifecycleTools(server, ctx) {
2195
2218
  // src/transport.ts
2196
2219
  var FetchTransport = class {
2197
2220
  baseUrl;
2198
- constructor(baseUrl) {
2221
+ testAccessToken;
2222
+ constructor(baseUrl, options = {}) {
2199
2223
  this.baseUrl = baseUrl.replace(/\/+$/, "");
2224
+ if (options.testAccessToken && this.baseUrl !== TEST_API_BASE_URL) {
2225
+ throw new Error(`Test access credentials may only be sent to ${TEST_API_BASE_URL}.`);
2226
+ }
2227
+ this.testAccessToken = options.testAccessToken;
2228
+ }
2229
+ testAccessHeadersFor(_url) {
2230
+ if (!this.testAccessToken) return {};
2231
+ let target;
2232
+ try {
2233
+ target = new URL(_url);
2234
+ } catch {
2235
+ return {};
2236
+ }
2237
+ return target.origin === TEST_API_BASE_URL ? { [TEST_ACCESS_HEADER]: this.testAccessToken } : {};
2200
2238
  }
2201
2239
  async request(req) {
2202
2240
  let url = `${this.baseUrl}${req.path}`;
@@ -2207,7 +2245,8 @@ var FetchTransport = class {
2207
2245
  accept: "application/json",
2208
2246
  [MCP_VERSION_HEADER]: MCP_VERSION,
2209
2247
  ...req.body !== void 0 ? { "content-type": "application/json" } : {},
2210
- ...req.headers
2248
+ ...req.headers,
2249
+ ...this.testAccessHeadersFor(url)
2211
2250
  };
2212
2251
  const res = await fetch(url, {
2213
2252
  method: req.method,
@@ -2233,7 +2272,10 @@ var FetchTransport = class {
2233
2272
  }
2234
2273
  const res = await fetch(target.url, {
2235
2274
  method: target.method,
2236
- headers: target.headers,
2275
+ headers: {
2276
+ ...target.headers,
2277
+ ...this.testAccessHeadersFor(target.url)
2278
+ },
2237
2279
  body
2238
2280
  });
2239
2281
  if (!res.ok) {
@@ -2279,7 +2321,9 @@ Safety boundaries:
2279
2321
  Stripe's public no-code portal login, where the customer verifies the checkout email with a
2280
2322
  Stripe one-time passcode; it never restores site authority.`;
2281
2323
  function createSakupaMcpServer(opts) {
2282
- const client = opts.client ?? new HttpApiClient(new FetchTransport(opts.apiBaseUrl));
2324
+ const client = opts.client ?? new HttpApiClient(
2325
+ new FetchTransport(opts.apiBaseUrl, { testAccessToken: opts.testAccessToken })
2326
+ );
2283
2327
  const server = new McpServer(
2284
2328
  { name: "sakupa", version: MCP_VERSION },
2285
2329
  { instructions: INSTRUCTIONS }
@@ -2304,13 +2348,12 @@ function createSakupaMcpServer(opts) {
2304
2348
 
2305
2349
  // src/bin.ts
2306
2350
  async function main() {
2307
- const apiBaseUrl = process.env["SAKUPA_API_URL"] ?? DEFAULT_API_BASE_URL;
2308
- const projectDir = process.env["SAKUPA_PROJECT_DIR"] ?? process.cwd();
2309
- const server = createSakupaMcpServer({ apiBaseUrl, projectDir });
2351
+ const config = loadMcpRuntimeConfig();
2352
+ const server = createSakupaMcpServer(config);
2310
2353
  const transport = new StdioServerTransport();
2311
2354
  await server.connect(transport);
2312
2355
  console.error(
2313
- `[sakupa-mcp] v${MCP_VERSION} connected (api: ${apiBaseUrl}, project: ${projectDir})`
2356
+ `[sakupa-mcp] v${MCP_VERSION} connected (api: ${config.apiBaseUrl}, project: ${config.projectDir})`
2314
2357
  );
2315
2358
  }
2316
2359
  main().catch((err) => {
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // ../core/dist/domain/constants.js
2
2
  var SERVICE_DOMAIN = "sakupa.com";
3
+ var DEFAULT_API_BASE_URL = "https://api.sakupa.com";
3
4
  var FREE_SITE_URL_SUFFIX = `.${SERVICE_DOMAIN}`;
5
+ var TEST_ACCESS_HEADER = "x-sakupa-test-token";
4
6
  var FREE_SITE_TTL_HOURS = 24;
5
7
  var FREE_SITE_MAX_TOTAL_BYTES = 10 * 1024 * 1024;
6
8
  var PAID_SITE_MAX_TOTAL_BYTES = 2 * 1024 * 1024 * 1024;
@@ -121,7 +123,7 @@ var FORBIDDEN_PATH_SEGMENTS = [
121
123
  var ALLOWED_HIDDEN_PATHS = [".well-known/"];
122
124
 
123
125
  // ../core/dist/domain/version.js
124
- var SAKUPA_MCP_VERSION = "0.7.0";
126
+ var SAKUPA_MCP_VERSION = "0.7.1";
125
127
 
126
128
  // ../core/dist/domain/errors.js
127
129
  var HTTP_STATUS = {
@@ -462,11 +464,48 @@ var WEBHOOK_PROCESSING_LEASE_MS = 5 * 60 * 1e3;
462
464
  var MCP_VERSION = SAKUPA_MCP_VERSION;
463
465
  var CLIENT_TYPE = "sakupa-mcp";
464
466
 
467
+ // src/config.ts
468
+ var TEST_API_BASE_URL = "https://api-test.sakupa.com";
469
+ function loadMcpRuntimeConfig(env = process.env, cwd = process.cwd()) {
470
+ const apiBaseUrl = (env["SAKUPA_API_URL"] ?? DEFAULT_API_BASE_URL).replace(/\/+$/, "");
471
+ const projectDir = env["SAKUPA_PROJECT_DIR"] ?? cwd;
472
+ const testAccessToken = env["SAKUPA_TEST_ACCESS_TOKEN"]?.trim() ?? "";
473
+ if (apiBaseUrl === TEST_API_BASE_URL) {
474
+ if (testAccessToken.length === 0) {
475
+ throw new Error(
476
+ "The Sakupa Test API requires SAKUPA_TEST_ACCESS_TOKEN. Anonymous Test access is disabled."
477
+ );
478
+ }
479
+ return { apiBaseUrl, projectDir, testAccessToken };
480
+ }
481
+ if (testAccessToken.length > 0) {
482
+ throw new Error(
483
+ `SAKUPA_TEST_ACCESS_TOKEN may only be used with ${TEST_API_BASE_URL}. Remove it before connecting to any other API.`
484
+ );
485
+ }
486
+ return { apiBaseUrl, projectDir };
487
+ }
488
+
465
489
  // src/transport.ts
466
490
  var FetchTransport = class {
467
491
  baseUrl;
468
- constructor(baseUrl) {
492
+ testAccessToken;
493
+ constructor(baseUrl, options = {}) {
469
494
  this.baseUrl = baseUrl.replace(/\/+$/, "");
495
+ if (options.testAccessToken && this.baseUrl !== TEST_API_BASE_URL) {
496
+ throw new Error(`Test access credentials may only be sent to ${TEST_API_BASE_URL}.`);
497
+ }
498
+ this.testAccessToken = options.testAccessToken;
499
+ }
500
+ testAccessHeadersFor(_url) {
501
+ if (!this.testAccessToken) return {};
502
+ let target;
503
+ try {
504
+ target = new URL(_url);
505
+ } catch {
506
+ return {};
507
+ }
508
+ return target.origin === TEST_API_BASE_URL ? { [TEST_ACCESS_HEADER]: this.testAccessToken } : {};
470
509
  }
471
510
  async request(req) {
472
511
  let url = `${this.baseUrl}${req.path}`;
@@ -477,7 +516,8 @@ var FetchTransport = class {
477
516
  accept: "application/json",
478
517
  [MCP_VERSION_HEADER]: MCP_VERSION,
479
518
  ...req.body !== void 0 ? { "content-type": "application/json" } : {},
480
- ...req.headers
519
+ ...req.headers,
520
+ ...this.testAccessHeadersFor(url)
481
521
  };
482
522
  const res = await fetch(url, {
483
523
  method: req.method,
@@ -503,7 +543,10 @@ var FetchTransport = class {
503
543
  }
504
544
  const res = await fetch(target.url, {
505
545
  method: target.method,
506
- headers: target.headers,
546
+ headers: {
547
+ ...target.headers,
548
+ ...this.testAccessHeadersFor(target.url)
549
+ },
507
550
  body
508
551
  });
509
552
  if (!res.ok) {
@@ -2271,7 +2314,9 @@ Safety boundaries:
2271
2314
  Stripe's public no-code portal login, where the customer verifies the checkout email with a
2272
2315
  Stripe one-time passcode; it never restores site authority.`;
2273
2316
  function createSakupaMcpServer(opts) {
2274
- const client = opts.client ?? new HttpApiClient(new FetchTransport(opts.apiBaseUrl));
2317
+ const client = opts.client ?? new HttpApiClient(
2318
+ new FetchTransport(opts.apiBaseUrl, { testAccessToken: opts.testAccessToken })
2319
+ );
2275
2320
  const server = new McpServer(
2276
2321
  { name: "sakupa", version: MCP_VERSION },
2277
2322
  { instructions: INSTRUCTIONS }
@@ -2298,9 +2343,11 @@ export {
2298
2343
  FetchTransport,
2299
2344
  HttpApiClient,
2300
2345
  MCP_VERSION,
2346
+ TEST_API_BASE_URL,
2301
2347
  analyzeProject,
2302
2348
  createSakupaMcpServer,
2303
2349
  deleteSiteFile,
2350
+ loadMcpRuntimeConfig,
2304
2351
  readSiteFile,
2305
2352
  registerLifecycleTools,
2306
2353
  registerTools,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sakupa/mcp",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Sakupa MCP server: publish AI-made static sites from your AI tool. AI-made pages, live in seconds.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",