brewpage-mcp 1.6.0 → 1.7.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.
Files changed (2) hide show
  1. package/dist/index.js +105 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { z } from "zod";
5
5
  const BASE_URL = process.env.BREWPAGE_URL || "https://brewpage.app";
6
6
  const server = new McpServer({
7
7
  name: "brewpage-mcp",
8
- version: "1.6.0",
8
+ version: "1.7.0",
9
9
  });
10
10
  const PUBLIC_NAMESPACE = "public";
11
11
  /**
@@ -408,6 +408,110 @@ server.tool("publish_site", "Publish an HTML site to BrewPage. Single-page: pass
408
408
  content: [{ type: "text", text: lines.join("\n") }],
409
409
  };
410
410
  });
411
+ server.tool("republish_site", "Republish (full-replace) an existing multi-file BrewPage site in place at the SAME URL/ID. Requires owner token. The uploaded bundle becomes the complete new file set — files absent from it are removed, new files added, matching files overwritten. Single-page: pass `entryContent`. Multi-page: pass `files` (array of `{path, content}`). Optional `entry` overrides the default `index.html`.", {
412
+ namespace: z.string().describe("Site namespace"),
413
+ id: z.string().describe("Site ID"),
414
+ ownerToken: z
415
+ .string()
416
+ .describe("Owner token received when the site was created (required for republish)"),
417
+ entryContent: z
418
+ .string()
419
+ .optional()
420
+ .describe("HTML content for a single-page site (entry file). Mutually exclusive with `files`."),
421
+ files: z
422
+ .array(z.object({ path: z.string(), content: z.string() }))
423
+ .optional()
424
+ .describe("Multi-file site: array of `{path, content}`. Mutually exclusive with `entryContent`."),
425
+ entry: z
426
+ .string()
427
+ .optional()
428
+ .describe("Entry file path, default index.html"),
429
+ ttlDays: z
430
+ .number()
431
+ .min(1)
432
+ .max(30)
433
+ .optional()
434
+ .describe("Time to live in days (1-30, default: 15)"),
435
+ tags: z
436
+ .array(z.string())
437
+ .optional()
438
+ .describe("Optional tags (replaces the site's existing tags)"),
439
+ }, async ({ namespace, id, ownerToken, entryContent, files, entry, ttlDays, tags }) => {
440
+ const hasEntry = typeof entryContent === "string";
441
+ const hasFiles = Array.isArray(files) && files.length > 0;
442
+ if (hasEntry === hasFiles) {
443
+ return {
444
+ content: [
445
+ {
446
+ type: "text",
447
+ text: "Provide exactly one of `entryContent` (single-page) or `files` (multi-page).",
448
+ },
449
+ ],
450
+ isError: true,
451
+ };
452
+ }
453
+ const formData = new FormData();
454
+ if (hasFiles) {
455
+ for (const f of files) {
456
+ formData.append("files", new Blob([f.content], { type: "text/html" }), f.path);
457
+ formData.append("paths", f.path);
458
+ }
459
+ }
460
+ else {
461
+ const entryBlob = new Blob([entryContent], { type: "text/html" });
462
+ const entryName = entry || "index.html";
463
+ formData.append("files", entryBlob, entryName);
464
+ formData.append("paths", entryName);
465
+ }
466
+ const headers = { "X-Owner-Token": ownerToken };
467
+ const qsParams = [];
468
+ if (entry)
469
+ qsParams.push(`entry=${encodeURIComponent(entry)}`);
470
+ if (ttlDays)
471
+ qsParams.push(`ttl=${encodeURIComponent(String(ttlDays))}`);
472
+ if (tags && tags.length)
473
+ qsParams.push(`tags=${encodeURIComponent(tags.join(","))}`);
474
+ const qs = qsParams.length ? `?${qsParams.join("&")}` : "";
475
+ const url = `${BASE_URL}/api/sites/${encodeURIComponent(namespace)}/${encodeURIComponent(id)}${qs}`;
476
+ const res = await fetch(url, { method: "PUT", headers, body: formData });
477
+ const data = await res.json().catch(() => null);
478
+ if (!res.ok) {
479
+ return {
480
+ content: [
481
+ {
482
+ type: "text",
483
+ text: `Failed to republish site (${res.status}): ${JSON.stringify(data)}`,
484
+ },
485
+ ],
486
+ isError: true,
487
+ };
488
+ }
489
+ const site = data;
490
+ const lines = [
491
+ `Site republished successfully (full replace)!`,
492
+ ``,
493
+ `URL: ${site.link || "N/A"}`,
494
+ `Namespace: ${site.namespace || namespace}`,
495
+ `ID: ${site.id || id}`,
496
+ `Entry file: ${site.entryFile || "index.html"}`,
497
+ `Files: ${site.fileCount || 1}`,
498
+ ];
499
+ if (site.totalSizeBytes !== undefined)
500
+ lines.push(`Size: ${site.totalSizeBytes} bytes`);
501
+ if (site.ownerLink)
502
+ lines.push(`Owner link: ${site.ownerLink}`);
503
+ if (Array.isArray(site.tags) && site.tags.length)
504
+ lines.push(`Tags: ${site.tags.join(", ")}`);
505
+ if (site.expiresAt)
506
+ lines.push(`Expires: ${site.expiresAt}`);
507
+ if (site.result)
508
+ lines.push(`Result: ${site.result}`);
509
+ if (site.visibilityNotice)
510
+ lines.push(``, String(site.visibilityNotice));
511
+ return {
512
+ content: [{ type: "text", text: lines.join("\n") }],
513
+ };
514
+ });
411
515
  server.tool("update_html", "Update an existing BrewPage HTML/Markdown page. Requires owner token. Returns the updated page metadata.", {
412
516
  namespace: z.string().describe("Page namespace"),
413
517
  id: z.string().describe("Page ID"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brewpage-mcp",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "mcpName": "io.github.kochetkov-ma/brewpage-mcp",
5
5
  "description": "MCP server for BrewPage — publish and manage HTML, KV, JSON, and file content from AI assistants",
6
6
  "type": "module",