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.
- package/dist/index.js +105 -1
- 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.
|
|
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