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.
- package/dist/index.js +62 -13
- 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.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
|
|
233
|
-
entryContent: z
|
|
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:
|
|
248
|
-
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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.`);
|