brewpage-mcp 1.14.1 → 1.14.3
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/README.md +14 -6
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +22 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Choosing a host? See how BrewPage compares with GitHub Pages, Netlify Drop, Tiin
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npx -y brewpage-mcp@1.14.
|
|
20
|
+
npx -y brewpage-mcp@1.14.3
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Requires Node.js 20 or newer. This starts the stdio server for an MCP client.
|
|
@@ -33,7 +33,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
|
33
33
|
"mcpServers": {
|
|
34
34
|
"brewpage": {
|
|
35
35
|
"command": "npx",
|
|
36
|
-
"args": ["-y", "brewpage-mcp@1.14.
|
|
36
|
+
"args": ["-y", "brewpage-mcp@1.14.3"]
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -44,7 +44,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
|
44
44
|
Register a project-scoped stdio server:
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
|
-
claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.14.
|
|
47
|
+
claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.14.3
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
This writes `.mcp.json` in the project root. Use `--scope user` for user-scoped registration. See the [official Claude Code MCP guide](https://code.claude.com/docs/en/mcp).
|
|
@@ -58,7 +58,7 @@ Open **Settings > MCP** and add:
|
|
|
58
58
|
"mcpServers": {
|
|
59
59
|
"brewpage": {
|
|
60
60
|
"command": "npx",
|
|
61
|
-
"args": ["-y", "brewpage-mcp@1.14.
|
|
61
|
+
"args": ["-y", "brewpage-mcp@1.14.3"]
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -72,7 +72,7 @@ Open the Cline MCP settings panel and add:
|
|
|
72
72
|
{
|
|
73
73
|
"brewpage": {
|
|
74
74
|
"command": "npx",
|
|
75
|
-
"args": ["-y", "brewpage-mcp@1.14.
|
|
75
|
+
"args": ["-y", "brewpage-mcp@1.14.3"]
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
```
|
|
@@ -80,7 +80,7 @@ Open the Cline MCP settings panel and add:
|
|
|
80
80
|
### Global Install
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
npm install -g brewpage-mcp@1.14.
|
|
83
|
+
npm install -g brewpage-mcp@1.14.3
|
|
84
84
|
brewpage-mcp
|
|
85
85
|
```
|
|
86
86
|
|
|
@@ -509,6 +509,14 @@ Every publish response includes an **owner token** -- the only credential that a
|
|
|
509
509
|
|
|
510
510
|
## Changelog
|
|
511
511
|
|
|
512
|
+
## 1.14.3 -- 2026-10-26
|
|
513
|
+
|
|
514
|
+
- Old publications (made before 5 October 2026): the 409 hint says to POST the content to `createUrl` (sent for every type) with the matching publish tool in that namespace; `republishUrl` is browser-only. Update tool descriptions mention `createUrl`.
|
|
515
|
+
|
|
516
|
+
## 1.14.2 -- 2026-10-26
|
|
517
|
+
|
|
518
|
+
- MCP Registry entry lists the remote endpoint `https://brewpage.app/mcp` (Streamable HTTP, no auth) beside the npm package. Tools are unchanged.
|
|
519
|
+
|
|
512
520
|
## 1.14.1 -- 2026-10-26
|
|
513
521
|
|
|
514
522
|
- Security fix: `namespace`, `id` and `key` are encoded as single path segments; empty, `.` and `..` are refused before any request, so calls stay under `/api/<type>/`. Valid inputs are unchanged.
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
export declare const DEFAULT_BASE_URL = "https://brewpage.app";
|
|
3
|
-
export declare const USER_AGENT = "brewpage-mcp/1.14.
|
|
3
|
+
export declare const USER_AGENT = "brewpage-mcp/1.14.3";
|
|
4
4
|
export declare const OWNER_TOKEN_ENV = "BREWPAGE_OWNER_TOKEN";
|
|
5
5
|
export interface ServerOptions {
|
|
6
6
|
/** BrewPage origin for API calls; default https://brewpage.app. */
|
package/dist/tools.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
export const DEFAULT_BASE_URL = "https://brewpage.app";
|
|
4
|
-
export const USER_AGENT = "brewpage-mcp/1.14.
|
|
4
|
+
export const USER_AGENT = "brewpage-mcp/1.14.3";
|
|
5
5
|
export const OWNER_TOKEN_ENV = "BREWPAGE_OWNER_TOKEN";
|
|
6
6
|
const READ_ONLY = { readOnlyHint: true };
|
|
7
7
|
const DESTRUCTIVE = { readOnlyHint: false, destructiveHint: true };
|
|
@@ -13,10 +13,10 @@ function toolMeta(title, hints) {
|
|
|
13
13
|
const NO_OWNER_TOKEN = `No owner token: pass ownerToken or set ${OWNER_TOKEN_ENV}.`;
|
|
14
14
|
const CREATE_OWNER_TOKEN_DESCRIPTION = `Optional. Default: ${OWNER_TOKEN_ENV}, else a new token per create.`;
|
|
15
15
|
const OWNER_TOKEN_DESCRIPTION = `Owner token from create. Default: ${OWNER_TOKEN_ENV}.`;
|
|
16
|
-
const SECRETS = "Secrets allowed; likely ones are flagged in notices.
|
|
17
|
-
const MANAGE_URL_TEXT = "Returns manageUrl
|
|
16
|
+
const SECRETS = "Secrets allowed; likely ones are flagged in notices. Private namespace + password suit secrets.";
|
|
17
|
+
const MANAGE_URL_TEXT = "Returns manageUrl, a private management link (grants control; not for public sharing).";
|
|
18
18
|
const OLD_READ_ONLY_CODE = "OLD_PUBLICATION_READ_ONLY";
|
|
19
|
-
const OLD_READ_ONLY_TEXT = ` Old publications (made before 5 October 2026) are read-only: 409 ${OLD_READ_ONLY_CODE}.`;
|
|
19
|
+
const OLD_READ_ONLY_TEXT = ` Old publications (made before 5 October 2026) are read-only: 409 ${OLD_READ_ONLY_CODE} with createUrl to publish anew.`;
|
|
20
20
|
/** Notice codes the agent must relay to the user. */
|
|
21
21
|
const WARNING_CODES = new Set(["secret-detected", "manage-link-disabled"]);
|
|
22
22
|
/** MCP type name -> REST path segment. */
|
|
@@ -54,11 +54,13 @@ function ownerFailure(operation, status, data, fromEnv) {
|
|
|
54
54
|
lines.push(`Wrong owner token for this publication. Use ownerToken from create.${fromEnv ? ` Sent: ${OWNER_TOKEN_ENV}.` : ""}`);
|
|
55
55
|
}
|
|
56
56
|
if (status === 409 && code === OLD_READ_ONLY_CODE) {
|
|
57
|
-
const republishUrl = data
|
|
58
|
-
lines.push("Read-only old publication: cannot be updated.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
const { createUrl, republishUrl } = data;
|
|
58
|
+
lines.push("Read-only old publication: cannot be updated. " +
|
|
59
|
+
(typeof createUrl === "string" ? `createUrl (${createUrl}) is the create endpoint for publishing the content anew in that namespace.`
|
|
60
|
+
: "The content can be published anew in the same namespace.") +
|
|
61
|
+
" A new publication gets a new URL and owner token; the old URL keeps its old content until it expires." +
|
|
62
|
+
(typeof republishUrl === "string" ? " republishUrl is a browser-only composer link." : "") +
|
|
63
|
+
(operation === "set KV" ? " A new KV store holds only the keys published to it." : ""));
|
|
62
64
|
}
|
|
63
65
|
return textResult(lines.join("\n"), true);
|
|
64
66
|
}
|
|
@@ -80,8 +82,8 @@ const UNLISTED_NOTICE = "Private: not listed. Anyone with the link can open it."
|
|
|
80
82
|
const LISTABLE_HINT = " Use namespace public to list it in the gallery and sitemap.";
|
|
81
83
|
const PROTECTED_UNLISTED_NOTICE = "Password: link + password. Not listed.";
|
|
82
84
|
const PUBLIC_NAMESPACE_LEAD = "namespace=public: listed in the public gallery and the sitemap, so Google, Bing and AI search can find and index it " +
|
|
83
|
-
"and people can discover it.
|
|
84
|
-
const PUBLIC_NAMESPACE_TAIL = " Anyone can find and read a public page
|
|
85
|
+
"and people can discover it. Suits anything meant to be found or shared: docs, guides, demos, portfolios, reports, landing pages. ";
|
|
86
|
+
const PUBLIC_NAMESPACE_TAIL = " Anyone can find and read a public page, so it is unsuitable for secrets, tokens or personal data. " +
|
|
85
87
|
"Omit (default): private. Unlisted and not indexed; anyone who has the link can still open it. ";
|
|
86
88
|
const NAMESPACE_FORMAT = "Lowercase only: 1-32 of a-z, 0-9, hyphen.";
|
|
87
89
|
/** `namespace` for publish_html and publish_file. */
|
|
@@ -95,16 +97,16 @@ const SITE_NAMESPACE_DESCRIPTION = PUBLIC_NAMESPACE_LEAD + "A site always gets i
|
|
|
95
97
|
const DATA_NAMESPACE_DESCRIPTION = "Omit (default): private. namespace=public: the shared public namespace. JSON and KV are never listed in the gallery " +
|
|
96
98
|
"or indexed, in any namespace; anyone who has the link can open it. " + NAMESPACE_FORMAT;
|
|
97
99
|
const TTL_SCHEMA = z.union([z.number().positive(), z.string().min(1).max(32)]).optional().describe("Requested TTL: days as a number, or a string such as '2w', '36h' or '1 month' (default 15 days). " +
|
|
98
|
-
"The server rounds to whole days and clamps to the allowed range (1-30 by default; owner caps differ), reporting each adjustment in notices;
|
|
100
|
+
"The server rounds to whole days and clamps to the allowed range (1-30 by default; owner caps differ), reporting each adjustment in notices; the returned expiresAt is the actual expiry.");
|
|
99
101
|
const DELIVERY_MODE_SCHEMA = z.enum(["path", "subdomain"]).optional().describe("Optional delivery choice for a fresh publication, independent of password/namespace. " +
|
|
100
102
|
"Fresh sites and HTML-format pages outside public (including the default unlisted namespace) always use subdomain; an explicit path there still publishes, on subdomain, with a delivery notice. " +
|
|
101
103
|
"Everything else in any namespace (Markdown, code/text, files, JSON, KV, public HTML) defaults to path (Promotion); public HTML may automatically choose subdomain for browser features. " +
|
|
102
104
|
"When creation returns an existing publication, it keeps its actual mode and URL.");
|
|
103
105
|
/** Site upload rules shared by publish_site and republish_site; current values come from get_limits. */
|
|
104
|
-
const SITE_LIMITS_TEXT = "
|
|
106
|
+
const SITE_LIMITS_TEXT = "Default limits (current values: GET /api/limits): a request is at most 25 MB; a site at most 200 files, 5 MB per file and 20 MB in total; " +
|
|
105
107
|
"each path at most 380 characters. System files (.DS_Store, __MACOSX/ and similar) are skipped and never count; files with an unsupported extension " +
|
|
106
108
|
"or a too-long path are skipped and listed in notices. Entry: `entry`, else root index.html, else the shallowest HTML file. " +
|
|
107
|
-
"Errors: 400 = a site limit exceeded (the message names it
|
|
109
|
+
"Errors: 400 = a site limit exceeded (the message names it), invalid path or no HTML file; 413 = request over 25 MB, " +
|
|
108
110
|
"too many files and form fields, or a file name/path too long; 422 = no servable file left.";
|
|
109
111
|
function hostingResponseLines(data) {
|
|
110
112
|
const fields = ["routingCohort", "deliveryMode", "requestedDeliveryMode", "effectiveDeliveryMode",
|
|
@@ -180,7 +182,7 @@ export function createServer(options = {}) {
|
|
|
180
182
|
const apiFetch = options.fetch ?? fetch;
|
|
181
183
|
const server = new McpServer({
|
|
182
184
|
name: "brewpage-mcp",
|
|
183
|
-
version: "1.14.
|
|
185
|
+
version: "1.14.3",
|
|
184
186
|
});
|
|
185
187
|
/** Caller argument first, then the configured default; `fromEnv` marks the configured one. */
|
|
186
188
|
function ownerTokenFor(argument) {
|
|
@@ -252,7 +254,7 @@ export function createServer(options = {}) {
|
|
|
252
254
|
...toolMeta("Publish HTML or Markdown page", ADDITIVE),
|
|
253
255
|
description: "Publish HTML or Markdown content to BrewPage (repository default limit 5 MiB). Returns its primary share link and owner token. Supports password protection, custom TTL, optional filename, and an opt-in top toolbar (showTopBar). " +
|
|
254
256
|
"When deliveryMode is omitted for a `public` page, the server may publish it on its own subdomain if the page needs browser features a BrewPage link does not provide; " +
|
|
255
|
-
"the result then includes deliveryModeReason and a delivery notice. Markdown raw HTML is allowlisted: scripts, styles, event handlers, forms and embeds are removed.
|
|
257
|
+
"the result then includes deliveryModeReason and a delivery notice. Markdown raw HTML is allowlisted: scripts, styles, event handlers, forms and embeds are removed. " +
|
|
256
258
|
MANAGE_URL_TEXT + " " + SECRETS,
|
|
257
259
|
inputSchema: {
|
|
258
260
|
content: z.string().describe("HTML or Markdown content to publish"),
|
|
@@ -372,7 +374,7 @@ export function createServer(options = {}) {
|
|
|
372
374
|
});
|
|
373
375
|
server.registerTool("update_hosting", {
|
|
374
376
|
...toolMeta("Change delivery mode", ADDITIVE),
|
|
375
|
-
description: "Change delivery mode for a NEW publication (not a site, not a non-public HTML-format page). Requires owner token;
|
|
377
|
+
description: "Change delivery mode for a NEW publication (not a site, not a non-public HTML-format page). Requires owner token; optional expectedVersion = current hostingVersion. Mismatches are reported, not rejected: a stale version still applies, and OLD links or forced subdomain modes stay unchanged; the result then carries a delivery notice. Content updates never change hosting.",
|
|
376
378
|
inputSchema: {
|
|
377
379
|
type: z.enum(["html", "file", "json", "kv"]).describe("Resource type; sites always stay on their own subdomain"),
|
|
378
380
|
namespace: z.string().describe("Resource namespace; non-public HTML-format pages stay on subdomain"),
|
|
@@ -504,7 +506,7 @@ export function createServer(options = {}) {
|
|
|
504
506
|
});
|
|
505
507
|
server.registerTool("publish_site", {
|
|
506
508
|
...toolMeta("Publish site", ADDITIVE),
|
|
507
|
-
description: "Publish an HTML site to BrewPage. Single-page:
|
|
509
|
+
description: "Publish an HTML site to BrewPage. Single-page: `entryContent`. Multi-page: `files` (array of `{path, content}`). Optional `entry` overrides default `index.html`. Returns its primary share link and owner token. " + SITE_LIMITS_TEXT +
|
|
508
510
|
" " + MANAGE_URL_TEXT + " " + SECRETS,
|
|
509
511
|
inputSchema: {
|
|
510
512
|
deliveryMode: DELIVERY_MODE_SCHEMA,
|
|
@@ -612,7 +614,7 @@ export function createServer(options = {}) {
|
|
|
612
614
|
});
|
|
613
615
|
server.registerTool("republish_site", {
|
|
614
616
|
...toolMeta("Republish site", DESTRUCTIVE),
|
|
615
|
-
description: "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:
|
|
617
|
+
description: "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: `entryContent`. Multi-page: `files` (array of `{path, content}`). Optional `entry` overrides the default `index.html`. Republishing preserves existing expiry." + OLD_READ_ONLY_TEXT + " " + SITE_LIMITS_TEXT,
|
|
616
618
|
inputSchema: {
|
|
617
619
|
namespace: z.string().describe("Site namespace"),
|
|
618
620
|
id: z.string().describe("Site ID"),
|
|
@@ -999,7 +1001,7 @@ export function createServer(options = {}) {
|
|
|
999
1001
|
...toolMeta("Get upload limits", READ_ONLY),
|
|
1000
1002
|
description: "Get the current BrewPage upload limits (GET /api/limits): request size, site file count, per-file and total size, " +
|
|
1001
1003
|
"ZIP expansion, path and name lengths, TTL default/maximum, allowed file extensions and skipped system files. Sizes are bytes. " +
|
|
1002
|
-
`With ${OWNER_TOKEN_ENV} set: also reach (token|profile) and ownerMaxTtlDays
|
|
1004
|
+
`With ${OWNER_TOKEN_ENV} set: also reach (token|profile) and ownerMaxTtlDays.`,
|
|
1003
1005
|
inputSchema: {},
|
|
1004
1006
|
}, async () => {
|
|
1005
1007
|
const headers = defaultOwnerToken ? { "X-Owner-Token": defaultOwnerToken } : {};
|
package/package.json
CHANGED