brewpage-mcp 1.9.2 → 1.9.4

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 (3) hide show
  1. package/README.md +22 -10
  2. package/dist/index.js +22 -21
  3. package/package.json +2 -2
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.2
15
+ npx -y brewpage-mcp@1.9.4
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.2"]
31
+ "args": ["-y", "brewpage-mcp@1.9.4"]
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.2
42
+ claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.9.4
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.2"]
56
+ "args": ["-y", "brewpage-mcp@1.9.4"]
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.2"]
70
+ "args": ["-y", "brewpage-mcp@1.9.4"]
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.2
78
+ npm install -g brewpage-mcp@1.9.4
79
79
  brewpage-mcp
80
80
  ```
81
81
 
@@ -102,7 +102,9 @@ Sixteen tools are available. Creation returns an owner token; keep it to modify
102
102
  | `search_gallery` | Browse public content or list owned resources |
103
103
  | `get_stats` | Read platform-wide statistics |
104
104
 
105
- All five publish tools accept optional `deliveryMode: "path" | "subdomain"`, sent only as `X-Delivery-Mode`; authored JSON and multipart bodies keep their existing format. Visibility and password protection are separate choices. Omitted namespace still generates an unlisted `priv-<random>` namespace; anyone with an unpassworded unlisted link can read it. `public` grants gallery/search eligibility only; password-protected content remains excluded.
105
+ All five publish tools accept optional `deliveryMode: "path" | "subdomain"`, sent only as `X-Delivery-Mode`; authored JSON and multipart bodies keep their existing format. Visibility and password protection are separate choices. Omitted namespace still generates an unlisted `priv-<random>` namespace; anyone with an unpassworded unlisted link can read it. `public` grants gallery/search eligibility only; password-protected content remains excluded and unlisted, protected or otherwise ineligible native responses carry `noindex`; eligible public HTML, sites and files may permit indexing.
106
+
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.
106
108
 
107
109
  For a fresh publication, sites and non-public namespaces require Dedicated subdomain (`subdomain`); explicit `path` is rejected. Other public publications default 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.
108
110
 
@@ -110,7 +112,7 @@ Responses use the server's primary `link` verbatim, including the root slash, an
110
112
 
111
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.
112
114
 
113
- An existing deduplication winner 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 public non-site may change mode separately with `update_hosting`.
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 public non-site may change mode separately with `update_hosting`.
114
116
 
115
117
  ### Hosting
116
118
 
@@ -220,7 +222,7 @@ publish_site(files=[{"path": "index.html", "content": "..."}, {"path": "style.cs
220
222
 
221
223
  Replace an owned site's complete file set at the same ID and primary link. Files absent from the new bundle are removed; matching files are overwritten and new files added. Hosting stays unchanged.
222
224
 
223
- Parameters: `namespace`, `id`, `ownerToken`, exactly one of `entryContent` or a non-empty `files` array of `{path, content}`, optional `entry` (default `index.html`), `ttlDays` (1--30, default 15), and `tags`.
225
+ Parameters: `namespace`, `id`, `ownerToken`, exactly one of `entryContent` or a non-empty `files` array of `{path, content}`, optional `entry` (default `index.html`), `ttlDays` (compatibility input 1--30; existing expiry is preserved), and `tags`.
224
226
 
225
227
  ```text
226
228
  republish_site(namespace="public", id="aBcDeFgHiJ", ownerToken="tok_...", files=[{"path":"index.html","content":"<h1>Updated site</h1>"}])
@@ -350,7 +352,7 @@ search_gallery(mine=true, ownerToken="tok_...", sort="date")
350
352
 
351
353
  ### `delete_resource`
352
354
 
353
- Delete a BrewPage resource (HTML page, KV store, JSON collection, or file) using the owner token received at creation.
355
+ Delete a BrewPage resource (HTML page, KV store, JSON document, or file) using the owner token received at creation.
354
356
 
355
357
  Parameters: `type` (`html` | `kv` | `json` | `file`), `namespace` (string), `id` (string), `ownerToken` (string).
356
358
 
@@ -431,6 +433,16 @@ Every publish response includes an **owner token** -- the only credential that a
431
433
 
432
434
  ## Changelog
433
435
 
436
+ ## 1.9.4 -- 2026-10-20
437
+
438
+ - Tool descriptions and responses describe a create that returns an existing publication in plain terms; its link and delivery mode are kept as returned.
439
+ - Keep sixteen tools, all input defaults and HTTP behavior unchanged. Independently versioned from backend API contract 1.113.0.
440
+
441
+ ## 1.9.3 -- 2026-10-11
442
+
443
+ - Correct tool/discovery descriptions for unlisted access, conditional native indexing, exact share links, current limits, owner/extension TTL exceptions, retained site-republish expiry and API content representations.
444
+ - Keep sixteen tools, all input defaults and HTTP behavior unchanged. This metadata package patch is independently versioned from backend API contract 1.107.2.
445
+
434
446
  ## 1.9.2 -- 2026-10-06
435
447
 
436
448
  - Dependencies are declared as exact versions, so an installed server uses the same SDK and `zod` versions it is built and tested with. No functional change.
package/dist/index.js CHANGED
@@ -5,13 +5,13 @@ 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.2",
8
+ version: "1.9.4",
9
9
  });
10
10
  const PUBLIC_NAMESPACE = "public";
11
11
  /**
12
12
  * Generate a short, unlisted private namespace slug used when the caller omits
13
13
  * `namespace`. Sending an explicit non-public ns prevents the backend from
14
- * applying its `public` default, which would gallery-list + index the content.
14
+ * applying its `public` default and making the content discovery-eligible.
15
15
  * Format: `priv-<6 lowercase-alphanumeric chars>`.
16
16
  */
17
17
  function defaultPrivateNamespace() {
@@ -32,13 +32,14 @@ function resolveNamespace(namespace) {
32
32
  const UNLISTED_NOTICE = "Unlisted link — anyone who has it can open it, but it's not in the gallery or search. " +
33
33
  "Use namespace `public` for gallery/search eligibility; password-protected content stays excluded.";
34
34
  const PROTECTED_UNLISTED_NOTICE = "Password-protected unlisted link — readers need the password; excluded from gallery and search.";
35
- const NAMESPACE_DESCRIPTION = "Optional. Omit to keep the content PRIVATE/unlisted (reachable only by its link — " +
36
- "not listed in the gallery and not indexed by search engines). Pass a custom namespace " +
37
- "to group private content, or pass `public` ONLY when the user explicitly wants gallery/search " +
38
- "eligibility. Password-protected content stays excluded from gallery/search.";
35
+ const NAMESPACE_DESCRIPTION = "Optional. Omit to generate an unlisted namespace: anyone with its unpassworded link can read it, " +
36
+ "but it is excluded from gallery/search. Pass a custom namespace to group unlisted content, " +
37
+ "or pass `public` ONLY when the user explicitly wants gallery/search eligibility. " +
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
- "Public non-sites default to path (Promotion); fresh sites and non-public content require subdomain. " +
41
- "An existing dedup winner keeps its actual mode and URL.");
40
+ "Public non-sites normally default to path (Promotion); public HTML may automatically choose subdomain for browser features. " +
41
+ "Fresh sites and non-public content require subdomain. " +
42
+ "When creation returns an existing publication, it keeps its actual mode and URL.");
42
43
  function hostingResponseLines(data) {
43
44
  const fields = ["routingCohort", "deliveryMode", "requestedDeliveryMode", "effectiveDeliveryMode",
44
45
  "deliveryModeMatched", "modeLocked", "hostingVersion", "access", "managementLink"];
@@ -46,7 +47,7 @@ function hostingResponseLines(data) {
46
47
  if (data.routingCohort === "old")
47
48
  lines.push("Hosting: Existing link");
48
49
  if (data.deliveryModeMatched === false)
49
- lines.push("Requested hosting differs from the existing winner; its URL and mode were preserved.");
50
+ lines.push("Requested hosting differs from the existing publication; its URL and mode were preserved.");
50
51
  lines.push(...deliveryReasonLines(data));
51
52
  return lines;
52
53
  }
@@ -108,7 +109,7 @@ function formatUpdateResponse(data) {
108
109
  lines.push(...hostingResponseLines(data));
109
110
  return lines.join("\n");
110
111
  }
111
- server.tool("publish_html", "Publish HTML or Markdown content to BrewPage. Returns a public URL and owner token. Supports password protection, custom TTL, optional filename, and an opt-in top toolbar (showTopBar). " +
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). " +
112
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; " +
113
114
  "the result then includes deliveryModeReason and a delivery notice. Always share the returned URL.", {
114
115
  content: z.string().describe("HTML or Markdown content to publish"),
@@ -131,7 +132,7 @@ server.tool("publish_html", "Publish HTML or Markdown content to BrewPage. Retur
131
132
  .min(1)
132
133
  .max(30)
133
134
  .optional()
134
- .describe("Time to live in days (1-30, default: 15)"),
135
+ .describe("Requested TTL in days (1-30, default 15). Backend owner caps and eligible automatic extensions can affect expiry; use returned expiresAt."),
135
136
  filename: z
136
137
  .string()
137
138
  .max(200)
@@ -177,7 +178,7 @@ server.tool("publish_html", "Publish HTML or Markdown content to BrewPage. Retur
177
178
  ],
178
179
  };
179
180
  });
180
- server.tool("publish_file", "Upload a file to BrewPage via URL. Returns a public URL and owner token.", {
181
+ server.tool("publish_file", "Upload a file to BrewPage via URL. Repository defaults: 5 MiB for files/audio, 20 MiB for video. Returns its primary share link and owner token. Does not expose password or TTL inputs.", {
181
182
  url: z.string().url().describe("URL of the file to upload"),
182
183
  deliveryMode: DELIVERY_MODE_SCHEMA,
183
184
  namespace: z
@@ -286,7 +287,7 @@ server.tool("delete_resource", "Delete a BrewPage resource (HTML page, KV store,
286
287
  ],
287
288
  };
288
289
  });
289
- server.tool("get_page", "Fetch a published BrewPage HTML page content by namespace and ID.", {
290
+ server.tool("get_page", "Fetch stored rendered HTML or format-specific code/text by namespace and ID. This reads API content, not the owner-only original-source endpoint or browser viewer. Protected reads use the password header.", {
290
291
  namespace: z.string().describe("Page namespace"),
291
292
  id: z.string().describe("Page ID"),
292
293
  password: z
@@ -359,7 +360,7 @@ server.tool("get_stats", "Get BrewPage platform-wide usage statistics including
359
360
  ],
360
361
  };
361
362
  });
362
- 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`.", {
363
+ 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`. Repository defaults: 20 MiB total, 100 files, 5 MiB per file. Use its returned primary link and retain the owner token.", {
363
364
  deliveryMode: DELIVERY_MODE_SCHEMA,
364
365
  entryContent: z
365
366
  .string()
@@ -386,7 +387,7 @@ server.tool("publish_site", "Publish an HTML site to BrewPage. Single-page: pass
386
387
  .min(1)
387
388
  .max(30)
388
389
  .optional()
389
- .describe("Time to live in days (1-30, default: 15)"),
390
+ .describe("Requested TTL in days (1-30, default 15). Backend owner caps and eligible automatic extensions can affect expiry; use returned expiresAt."),
390
391
  ownerToken: z
391
392
  .string()
392
393
  .optional()
@@ -475,7 +476,7 @@ server.tool("publish_site", "Publish an HTML site to BrewPage. Single-page: pass
475
476
  content: [{ type: "text", text: lines.join("\n") }],
476
477
  };
477
478
  });
478
- server.tool("republish_site", "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: pass `entryContent`. Multi-page: pass `files` (array of `{path, content}`). Optional `entry` overrides the default `index.html`.", {
479
+ server.tool("republish_site", "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: pass `entryContent`. Multi-page: pass `files` (array of `{path, content}`). Optional `entry` overrides the default `index.html`. Site bundles use repository defaults of 20 MiB total, 100 files, 5 MiB per file; republishing preserves existing expiry.", {
479
480
  namespace: z.string().describe("Site namespace"),
480
481
  id: z.string().describe("Site ID"),
481
482
  ownerToken: z
@@ -498,7 +499,7 @@ server.tool("republish_site", "Republish (full-replace) an existing multi-file B
498
499
  .min(1)
499
500
  .max(30)
500
501
  .optional()
501
- .describe("Time to live in days (1-30, default: 15)"),
502
+ .describe("Compatibility input (1-30); site republishing preserves existing expiry. Use returned expiresAt."),
502
503
  tags: z
503
504
  .array(z.string())
504
505
  .optional()
@@ -620,7 +621,7 @@ server.tool("update_html", "Update an existing BrewPage HTML/Markdown page. Requ
620
621
  ],
621
622
  };
622
623
  });
623
- server.tool("publish_json", "Publish a JSON document to BrewPage. Accepts a JSON object, array, or a JSON-encoded string. Returns a public URL and owner token.", {
624
+ server.tool("publish_json", "Publish a JSON document to BrewPage (repository default limit 1 MiB). Accepts a JSON object, array, or a JSON-encoded string containing any valid JSON, including scalars/null. Returns its primary share link and owner token.", {
624
625
  deliveryMode: DELIVERY_MODE_SCHEMA,
625
626
  json: z
626
627
  .union([z.string(), z.record(z.unknown()), z.array(z.unknown())])
@@ -639,7 +640,7 @@ server.tool("publish_json", "Publish a JSON document to BrewPage. Accepts a JSON
639
640
  .min(1)
640
641
  .max(30)
641
642
  .optional()
642
- .describe("Time to live in days (1-30, default: 15)"),
643
+ .describe("Requested TTL in days (1-30, default 15). Backend owner caps and eligible automatic extensions can affect expiry; use returned expiresAt."),
643
644
  tags: z
644
645
  .array(z.string())
645
646
  .optional()
@@ -767,7 +768,7 @@ server.tool("update_json", "Update an existing BrewPage JSON document. Requires
767
768
  ],
768
769
  };
769
770
  });
770
- server.tool("publish_kv", "Publish a key/value entry to BrewPage. Creates a KV store; the entry is addressed by (namespace, id, key). Returns a public URL and owner token.", {
771
+ server.tool("publish_kv", "Publish a key/value entry to BrewPage. Creates a KV store; the entry is addressed by (namespace, id, key). Repository defaults: 1 MiB per value, 1,000 keys per store. Returns its primary share link and owner token.", {
771
772
  deliveryMode: DELIVERY_MODE_SCHEMA,
772
773
  key: z.string().describe("KV key (first part of the (namespace, id, key) addressing tuple)"),
773
774
  value: z.string().describe("KV value"),
@@ -785,7 +786,7 @@ server.tool("publish_kv", "Publish a key/value entry to BrewPage. Creates a KV s
785
786
  .min(1)
786
787
  .max(30)
787
788
  .optional()
788
- .describe("Time to live in days (1-30, default: 15)"),
789
+ .describe("Requested TTL in days (1-30, default 15). Backend owner caps and eligible automatic extensions can affect expiry; use returned expiresAt."),
789
790
  tags: z
790
791
  .array(z.string())
791
792
  .optional()
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "brewpage-mcp",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "mcpName": "io.github.kochetkov-ma/brewpage-mcp",
5
- "description": "MCP server for BrewPage — publish and manage HTML, KV, JSON, and file content from AI assistants",
5
+ "description": "MCP server for BrewPage — publish and manage HTML, sites, KV, JSON and files from AI assistants",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "brewpage-mcp": "./dist/index.js"