brewpage-mcp 1.9.5 → 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 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.9.5
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.9.5"]
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.9.5
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.9.5"]
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.9.5"]
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.9.5
78
+ npm install -g brewpage-mcp@1.10.0
79
79
  brewpage-mcp
80
80
  ```
81
81
 
@@ -106,7 +106,7 @@ 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 HTML pages of format `HTML` outside `public` (including the generated unlisted namespace) require Dedicated subdomain (`subdomain`); explicit `path` is rejected. 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.
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
 
@@ -118,9 +118,9 @@ When creation returns an existing publication, it keeps its actual URL, cohort a
118
118
 
119
119
  ### `update_hosting`
120
120
 
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 `expectedVersion` (current server `hostingVersion`, integer ≥1).
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, expectedVersion}` and `X-Owner-Token`. OLD links, sites and non-public `html`-format HTML pages are locked. The server returns 403 for invalid owner authority, 400 for locked hosting, and 409 for a stale version; reload current metadata before retrying. A paused real change returns 503. Selecting the current mode returns 200 without incrementing the version. Use the returned primary link after a successful change; the publication host identity is retained through both directions.
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)
@@ -433,6 +433,10 @@ 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
+
436
440
  ## 1.9.5 -- 2026-10-21
437
441
 
438
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.
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.9.5",
8
+ version: "1.10.0",
9
9
  });
10
10
  const PUBLIC_NAMESPACE = "public";
11
11
  /**
@@ -37,7 +37,7 @@ 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
- "Fresh sites and HTML-format pages outside public (including the default unlisted namespace) require subdomain; explicit path there is rejected. " +
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
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) {
@@ -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 existing publication; its URL and mode were preserved.");
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 create/update fields: why the delivery mode was chosen or flagged. */
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 = [];
@@ -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 publication (not a site, not a non-public HTML-format page). Requires owner token and current hostingVersion. OLD links and forced subdomain modes are locked; reload after a 409 conflict. Content updates never change hosting.", {
233
- type: z.enum(["html", "file", "json", "kv"]).describe("Resource type; sites have no hosting-choice endpoint"),
234
- namespace: z.string().describe("Resource namespace; non-public HTML-format pages are locked"),
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("Current hostingVersion from the server; required for concurrency control"),
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brewpage-mcp",
3
- "version": "1.9.5",
3
+ "version": "1.10.0",
4
4
  "mcpName": "io.github.kochetkov-ma/brewpage-mcp",
5
5
  "description": "MCP server for BrewPage — publish and manage HTML, sites, KV, JSON and files from AI assistants",
6
6
  "type": "module",