brewpage-mcp 1.1.0 → 1.2.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 +62 -13
  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.0.0",
8
+ version: "1.1.1",
9
9
  });
10
10
  async function apiRequest(method, path, body, headers) {
11
11
  const url = `${BASE_URL}${path}`;
@@ -229,8 +229,19 @@ server.tool("get_stats", "Get BrewPage platform-wide usage statistics including
229
229
  ],
230
230
  };
231
231
  });
232
- server.tool("publish_site", "Publish a multi-file HTML site to BrewPage. Provide HTML content as the entry file (index.html). Returns a public URL and owner token.", {
233
- entryContent: z.string().describe("HTML content for the site entry file (index.html)"),
232
+ server.tool("publish_site", "Publish an HTML site to BrewPage. Single-page: pass `entryContent`. Multi-page: pass `files` (array of `{path, content}`). Optional `entry` overrides default `index.html`.", {
233
+ entryContent: z
234
+ .string()
235
+ .optional()
236
+ .describe("HTML content for a single-page site (entry file). Mutually exclusive with `files`."),
237
+ files: z
238
+ .array(z.object({ path: z.string(), content: z.string() }))
239
+ .optional()
240
+ .describe("Multi-file site: array of `{path, content}`. Mutually exclusive with `entryContent`."),
241
+ entry: z
242
+ .string()
243
+ .optional()
244
+ .describe("Entry file path, default index.html"),
234
245
  namespace: z
235
246
  .string()
236
247
  .optional()
@@ -244,20 +255,52 @@ server.tool("publish_site", "Publish a multi-file HTML site to BrewPage. Provide
244
255
  .min(1)
245
256
  .max(30)
246
257
  .optional()
247
- .describe("Time to live in days (1-30, default: 5)"),
248
- }, async ({ entryContent, namespace, password, ttlDays }) => {
258
+ .describe("Time to live in days (1-30, default: 15)"),
259
+ ownerToken: z
260
+ .string()
261
+ .optional()
262
+ .describe("Existing owner token to group this site under the same owner as previous content"),
263
+ }, async ({ entryContent, files, entry, namespace, password, ttlDays, ownerToken }) => {
264
+ const hasEntry = typeof entryContent === "string";
265
+ const hasFiles = Array.isArray(files) && files.length > 0;
266
+ if (hasEntry === hasFiles) {
267
+ return {
268
+ content: [
269
+ {
270
+ type: "text",
271
+ text: "Provide exactly one of `entryContent` (single-page) or `files` (multi-page).",
272
+ },
273
+ ],
274
+ isError: true,
275
+ };
276
+ }
249
277
  const formData = new FormData();
250
- const entryBlob = new Blob([entryContent], { type: "text/html" });
251
- formData.append("files", entryBlob, "index.html");
252
- formData.append("paths", "index.html");
253
- if (namespace)
254
- formData.append("ns", namespace);
255
- if (ttlDays)
256
- formData.append("ttl", String(ttlDays));
278
+ if (hasFiles) {
279
+ for (const f of files) {
280
+ formData.append("files", new Blob([f.content], { type: "text/html" }), f.path);
281
+ formData.append("paths", f.path);
282
+ }
283
+ }
284
+ else {
285
+ const entryBlob = new Blob([entryContent], { type: "text/html" });
286
+ const entryName = entry || "index.html";
287
+ formData.append("files", entryBlob, entryName);
288
+ formData.append("paths", entryName);
289
+ }
257
290
  const headers = {};
258
291
  if (password)
259
292
  headers["X-Password"] = password;
260
- const url = `${BASE_URL}/api/sites`;
293
+ if (ownerToken)
294
+ headers["X-Owner-Token"] = ownerToken;
295
+ const qsParams = [];
296
+ if (entry)
297
+ qsParams.push(`entry=${encodeURIComponent(entry)}`);
298
+ if (namespace)
299
+ qsParams.push(`ns=${encodeURIComponent(namespace)}`);
300
+ if (ttlDays)
301
+ qsParams.push(`ttl=${encodeURIComponent(String(ttlDays))}`);
302
+ const qs = qsParams.length ? `?${qsParams.join("&")}` : "";
303
+ const url = `${BASE_URL}/api/sites${qs}`;
261
304
  const res = await fetch(url, { method: "POST", headers, body: formData });
262
305
  const data = await res.json().catch(() => null);
263
306
  if (!res.ok) {
@@ -281,6 +324,12 @@ server.tool("publish_site", "Publish a multi-file HTML site to BrewPage. Provide
281
324
  `Entry file: ${site.entryFile || "index.html"}`,
282
325
  `Files: ${site.fileCount || 1}`,
283
326
  ];
327
+ if (site.totalSizeBytes !== undefined)
328
+ lines.push(`Size: ${site.totalSizeBytes} bytes`);
329
+ if (site.ownerLink)
330
+ lines.push(`Owner link: ${site.ownerLink}`);
331
+ if (Array.isArray(site.tags) && site.tags.length)
332
+ lines.push(`Tags: ${site.tags.join(", ")}`);
284
333
  if (site.expiresAt)
285
334
  lines.push(`Expires: ${site.expiresAt}`);
286
335
  lines.push(``, `===================================`, `OWNER TOKEN: ${site.ownerToken}`, `===================================`, ``, `IMPORTANT: Save your owner token! You need it to view info or delete this site. It cannot be recovered if lost.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brewpage-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "MCP server for BrewPage — publish and manage HTML, KV, JSON, and file content from AI assistants",
5
5
  "type": "module",
6
6
  "bin": {