brewpage-mcp 1.9.4 → 1.10.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/README.md +21 -12
- package/dist/index.js +10 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ MCP server for [BrewPage](https://brewpage.app) -- publish and manage HTML, KV,
|
|
|
12
12
|
## Quick Start
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
npx -y brewpage-mcp@1.
|
|
15
|
+
npx -y brewpage-mcp@1.10.0
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
Requires Node.js 20 or newer. This starts the stdio server for an MCP client.
|
|
@@ -28,7 +28,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
|
28
28
|
"mcpServers": {
|
|
29
29
|
"brewpage": {
|
|
30
30
|
"command": "npx",
|
|
31
|
-
"args": ["-y", "brewpage-mcp@1.
|
|
31
|
+
"args": ["-y", "brewpage-mcp@1.10.0"]
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -39,7 +39,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
|
39
39
|
Register a project-scoped stdio server:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.
|
|
42
|
+
claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.10.0
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
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).
|
|
@@ -53,7 +53,7 @@ Open **Settings > MCP** and add:
|
|
|
53
53
|
"mcpServers": {
|
|
54
54
|
"brewpage": {
|
|
55
55
|
"command": "npx",
|
|
56
|
-
"args": ["-y", "brewpage-mcp@1.
|
|
56
|
+
"args": ["-y", "brewpage-mcp@1.10.0"]
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -67,7 +67,7 @@ Open the Cline MCP settings panel and add:
|
|
|
67
67
|
{
|
|
68
68
|
"brewpage": {
|
|
69
69
|
"command": "npx",
|
|
70
|
-
"args": ["-y", "brewpage-mcp@1.
|
|
70
|
+
"args": ["-y", "brewpage-mcp@1.10.0"]
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
```
|
|
@@ -75,7 +75,7 @@ Open the Cline MCP settings panel and add:
|
|
|
75
75
|
### Global Install
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
npm install -g brewpage-mcp@1.
|
|
78
|
+
npm install -g brewpage-mcp@1.10.0
|
|
79
79
|
brewpage-mcp
|
|
80
80
|
```
|
|
81
81
|
|
|
@@ -106,21 +106,21 @@ All five publish tools accept optional `deliveryMode: "path" | "subdomain"`, sen
|
|
|
106
106
|
|
|
107
107
|
The MCP server wraps a subset of the backend-generated public [REST contract](../openapi/openapi.json). Its inputs retain their published behavior: TTL inputs accept 1–30 days (default 15), while backend owner-specific caps and eligible automatic extensions can affect actual expiry. Use returned `expiresAt`. `get_page` reads stored rendered HTML or format-specific code/text from the API; it does not call the owner-only original-source endpoint or a browser viewer. `publish_file` uploads from a source URL and does not expose password or TTL inputs.
|
|
108
108
|
|
|
109
|
-
For a fresh publication, sites and
|
|
109
|
+
For a fresh publication, sites and HTML pages of format `HTML` outside `public` (including the generated unlisted namespace) always use Dedicated subdomain (`subdomain`); omit `deliveryMode` there. An explicit `path` there is not rejected: the publication is created on its subdomain and the result reports `deliveryModeMatched: false`, `deliveryModeReason: "policy:subdomain-required"` and a `Delivery notice:` line. Everything else in any namespace (Markdown, code/text, files, JSON, KV, public HTML) defaults to Promotion (`path`) and may choose Dedicated subdomain, including password-protected content. Promotion uses a BrewPage path with stricter active-document restrictions. Each NEW site gets one publication-specific host for its complete bundle, preserving relative/root-relative paths; missing assets return 404, with no SPA fallback. Third-party widgets may still need provider configuration.
|
|
110
110
|
|
|
111
111
|
Responses use the server's primary `link` verbatim, including the root slash, and expose `routingCohort`, actual/requested/effective modes, `deliveryModeMatched`, `modeLocked`, `hostingVersion`, access and the trusted-apex `managementLink` when present. Never reconstruct a host from namespace or ID. NEW password links are clean URLs: do not add `?p=`; browser readers unlock through the trusted gateway. Owner tokens and API calls stay on the configured apex API origin.
|
|
112
112
|
|
|
113
113
|
When `deliveryMode` is omitted for a `public` HTML or Markdown page, the server may publish it on its own subdomain if the page needs browser features a BrewPage link does not provide. `publish_html` then prints `deliveryModeReason: "auto:<codes>"` and a `Delivery notice:` line with the server's explanation; pass `deliveryMode: "path"` to keep a BrewPage link, or change it later with `update_hosting`. `advisory:<codes>` (from `publish_html` or `update_html`) means the address was not changed but such features were found. Codes are comma-separated, drawn from `storage`, `cookies`, `history`, `downloads`, `top-navigation`, `media-capture`, `pointer-lock`, `embeds`, `fullscreen`. Neither line appears when nothing applies.
|
|
114
114
|
|
|
115
|
-
When creation returns an existing publication, it keeps its actual URL, cohort and mode, even when another mode was requested. OLD publications display **Existing link**, with null mode metadata; their legacy links and behavior remain unchanged. Ordinary content updates and site republishing preserve hosting. An eligible NEW
|
|
115
|
+
When creation returns an existing publication, it keeps its actual URL, cohort and mode, even when another mode was requested. OLD publications display **Existing link**, with null mode metadata; their legacy links and behavior remain unchanged. Ordinary content updates and site republishing preserve hosting. An eligible NEW publication (not a site, not a non-public `html`-format HTML page) may change mode separately with `update_hosting`.
|
|
116
116
|
|
|
117
117
|
### Hosting
|
|
118
118
|
|
|
119
119
|
### `update_hosting`
|
|
120
120
|
|
|
121
|
-
Change an eligible NEW
|
|
121
|
+
Change an eligible NEW publication (not a site, not a non-public `html`-format HTML page) between Promotion and Dedicated subdomain. Parameters: `type` (`html` | `file` | `json` | `kv`), `namespace`, `id`, `ownerToken`, `deliveryMode` (`path` | `subdomain`), and optional `expectedVersion` (current server `hostingVersion`, integer ≥1; pass it when known).
|
|
122
122
|
|
|
123
|
-
The tool sends owner-only `PATCH /api/{html|files|json|kv}/{namespace}/{id}/hosting` with `{deliveryMode
|
|
123
|
+
The tool sends owner-only `PATCH /api/{html|files|json|kv}/{namespace}/{id}/hosting` with `{deliveryMode}` plus `expectedVersion` when given, and `X-Owner-Token`. Mismatches are reported, not rejected: a stale or omitted version still applies the choice (a stale one adds `deliveryModeReason: "policy:version-stale"`); sites and non-public `html`-format HTML pages stay on their subdomain (`policy:mode-locked`) and OLD links stay unchanged (`policy:classic-link`), each answered 200 with `deliveryModeMatched: false` and a `Delivery notice:` line. Wrong owner returns 403, a malformed version (not a positive integer) 400, and a paused real change 503. Selecting the current mode returns 200 without incrementing the version. Always use the returned primary link; the publication host identity is retained through both directions.
|
|
124
124
|
|
|
125
125
|
```text
|
|
126
126
|
update_hosting(type="html", namespace="public", id="aBcDeFgHiJ", ownerToken="tok_...", deliveryMode="subdomain", expectedVersion=1)
|
|
@@ -130,7 +130,7 @@ update_hosting(type="html", namespace="public", id="aBcDeFgHiJ", ownerToken="tok
|
|
|
130
130
|
|
|
131
131
|
### `publish_html`
|
|
132
132
|
|
|
133
|
-
Publish HTML or Markdown content to BrewPage. Returns a public URL and owner token.
|
|
133
|
+
Publish HTML or Markdown content to BrewPage. Returns a public URL and owner token. Markdown raw HTML is rendered through an allowlist: scripts, styles, event handlers, forms, frames/embeds and `meta`/`base`/`link` are removed; links allow `http`, `https`, `mailto`, `tel` and relative URLs, images also `data:`; a lone unknown tag such as `<author>` shows as text.
|
|
134
134
|
|
|
135
135
|
Parameters: `content` (string), `format` (`HTML` | `MARKDOWN`, default `HTML`), `namespace` (optional -- **omit for unlisted content**; pass `public` only for gallery/search eligibility, except when password-protected), `password` (optional), `ttlDays` (1--30, default 15), `filename` (optional, used as title fallback), `showTopBar` (optional boolean -- adds a toolbar with filename, Download button, and theme toggle), `deliveryMode` (optional).
|
|
136
136
|
|
|
@@ -392,7 +392,7 @@ get_stats()
|
|
|
392
392
|
| "Upload this image / PDF / video" | `publish_file` | Fetches from URL; inline preview on short URL |
|
|
393
393
|
| "Deploy this static site" | `publish_site` | Pass `files` array or `entryContent` |
|
|
394
394
|
| "Replace the site I published" | `republish_site` | Owner token; complete bundle replaces all existing files |
|
|
395
|
-
| "Give my public page a dedicated address" | `update_hosting` | NEW
|
|
395
|
+
| "Give my public page a dedicated address" | `update_hosting` | NEW publication, not a site or non-public HTML-format page; owner token + current hosting version |
|
|
396
396
|
| "Fix a typo in the page I shared -- same link" | `update_html` | Requires `ownerToken`; URL stays the same |
|
|
397
397
|
| "Remove the page I published" | `delete_resource` | Requires `ownerToken` from creation |
|
|
398
398
|
| "Read back the page I published" | `get_page` | Returns raw content for editing |
|
|
@@ -433,6 +433,15 @@ Every publish response includes an **owner token** -- the only credential that a
|
|
|
433
433
|
|
|
434
434
|
## Changelog
|
|
435
435
|
|
|
436
|
+
## 1.10.0 -- 2026-10-21
|
|
437
|
+
|
|
438
|
+
- `update_hosting`: `expectedVersion` is optional; a stale version, a locked mode or an OLD link answers with a successful result plus `deliveryModeReason` and a `Delivery notice:` line instead of an error. A publish request for `path` where `subdomain` is required succeeds on the subdomain and prints the notice. Same sixteen tools.
|
|
439
|
+
|
|
440
|
+
## 1.9.5 -- 2026-10-21
|
|
441
|
+
|
|
442
|
+
- `deliveryMode`, `publish_html` and `update_hosting` describe the backend 1.117.0 delivery rule: sites and non-public HTML-format pages require `subdomain`; Markdown, code/text, files, JSON and KV default to `path` in any namespace. `publish_html` notes that Markdown raw HTML is allowlist-sanitized.
|
|
443
|
+
- Keep sixteen tools, all input defaults and HTTP behavior unchanged. Independently versioned from backend API contract 1.117.0.
|
|
444
|
+
|
|
436
445
|
## 1.9.4 -- 2026-10-20
|
|
437
446
|
|
|
438
447
|
- Tool descriptions and responses describe a create that returns an existing publication in plain terms; its link and delivery mode are kept as returned.
|
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.10.0",
|
|
9
9
|
});
|
|
10
10
|
const PUBLIC_NAMESPACE = "public";
|
|
11
11
|
/**
|
|
@@ -37,8 +37,8 @@ const NAMESPACE_DESCRIPTION = "Optional. Omit to generate an unlisted namespace:
|
|
|
37
37
|
"or pass `public` ONLY when the user explicitly wants gallery/search eligibility. " +
|
|
38
38
|
"Password-protected content stays excluded; unlisted, protected or otherwise ineligible native responses carry noindex; eligible public HTML, sites and files may permit indexing.";
|
|
39
39
|
const DELIVERY_MODE_SCHEMA = z.enum(["path", "subdomain"]).optional().describe("Optional delivery choice for a fresh publication, independent of password/namespace. " +
|
|
40
|
-
"
|
|
41
|
-
"
|
|
40
|
+
"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. " +
|
|
41
|
+
"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. " +
|
|
42
42
|
"When creation returns an existing publication, it keeps its actual mode and URL.");
|
|
43
43
|
function hostingResponseLines(data) {
|
|
44
44
|
const fields = ["routingCohort", "deliveryMode", "requestedDeliveryMode", "effectiveDeliveryMode",
|
|
@@ -47,11 +47,11 @@ function hostingResponseLines(data) {
|
|
|
47
47
|
if (data.routingCohort === "old")
|
|
48
48
|
lines.push("Hosting: Existing link");
|
|
49
49
|
if (data.deliveryModeMatched === false)
|
|
50
|
-
lines.push("Requested hosting differs from the
|
|
50
|
+
lines.push("Requested hosting differs from the effective mode; use the returned URL.");
|
|
51
51
|
lines.push(...deliveryReasonLines(data));
|
|
52
52
|
return lines;
|
|
53
53
|
}
|
|
54
|
-
/** Optional HTML
|
|
54
|
+
/** Optional delivery fields on HTML and hosting responses: why the mode was chosen, flagged or not applied. */
|
|
55
55
|
function deliveryReasonLines(data) {
|
|
56
56
|
const { deliveryModeReason: reason, deliveryNotice: notice } = data;
|
|
57
57
|
const lines = [];
|
|
@@ -111,7 +111,7 @@ function formatUpdateResponse(data) {
|
|
|
111
111
|
}
|
|
112
112
|
server.tool("publish_html", "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). " +
|
|
113
113
|
"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; " +
|
|
114
|
-
"the result then includes deliveryModeReason and a delivery notice. Always share the returned URL.", {
|
|
114
|
+
"the result then includes deliveryModeReason and a delivery notice. Markdown raw HTML is allowlisted: scripts, styles, event handlers, forms and embeds are removed. Always share the returned URL.", {
|
|
115
115
|
content: z.string().describe("HTML or Markdown content to publish"),
|
|
116
116
|
deliveryMode: DELIVERY_MODE_SCHEMA,
|
|
117
117
|
format: z
|
|
@@ -229,13 +229,13 @@ server.tool("publish_file", "Upload a file to BrewPage via URL. Repository defau
|
|
|
229
229
|
],
|
|
230
230
|
};
|
|
231
231
|
});
|
|
232
|
-
server.tool("update_hosting", "Change delivery mode for a NEW
|
|
233
|
-
type: z.enum(["html", "file", "json", "kv"]).describe("Resource type; sites
|
|
234
|
-
namespace: z.string().describe("Resource namespace;
|
|
232
|
+
server.tool("update_hosting", "Change delivery mode for a NEW publication (not a site, not a non-public HTML-format page). Requires owner token; pass the current hostingVersion when known. 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.", {
|
|
233
|
+
type: z.enum(["html", "file", "json", "kv"]).describe("Resource type; sites always stay on their own subdomain"),
|
|
234
|
+
namespace: z.string().describe("Resource namespace; non-public HTML-format pages stay on subdomain"),
|
|
235
235
|
id: z.string().describe("Resource ID"),
|
|
236
236
|
ownerToken: z.string().describe("Original resource owner token"),
|
|
237
237
|
deliveryMode: z.enum(["path", "subdomain"]).describe("path = Promotion; subdomain = Dedicated subdomain"),
|
|
238
|
-
expectedVersion: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER).describe("
|
|
238
|
+
expectedVersion: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER).optional().describe("Optional current hostingVersion from the server; a stale value still applies and the result says so"),
|
|
239
239
|
}, async ({ type, namespace, id, ownerToken, deliveryMode, expectedVersion }) => {
|
|
240
240
|
const segment = type === "file" ? "files" : type;
|
|
241
241
|
const path = `/api/${segment}/${encodeURIComponent(namespace)}/${encodeURIComponent(id)}/hosting`;
|
package/package.json
CHANGED