brewpage-mcp 1.13.0 → 1.13.2

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 +24 -15
  2. package/dist/index.js +44 -32
  3. 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.13.0
20
+ npx -y brewpage-mcp@1.13.2
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.13.0"]
36
+ "args": ["-y", "brewpage-mcp@1.13.2"]
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.13.0
47
+ claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.13.2
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.13.0"]
61
+ "args": ["-y", "brewpage-mcp@1.13.2"]
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.13.0"]
75
+ "args": ["-y", "brewpage-mcp@1.13.2"]
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.13.0
83
+ npm install -g brewpage-mcp@1.13.2
84
84
  brewpage-mcp
85
85
  ```
86
86
 
@@ -104,14 +104,14 @@ Twenty tools are available. Creation returns an owner token; keep it to modify o
104
104
  | `get_kv` | Read a KV value |
105
105
  | `update_hosting` | Change eligible delivery mode with owner token and hosting version |
106
106
  | `delete_resource` | Delete an HTML page, site, JSON document, KV store or file |
107
- | `set_visibility` | Hide or show a public publication in gallery & search; link unchanged |
107
+ | `set_visibility` | Hide or show a public publication in the gallery and sitemap; link unchanged |
108
108
  | `bind_token` | Add an owner token to your profile (admin token) |
109
109
  | `mint_token` | Mint a profile token, shown once (admin token) |
110
110
  | `search_gallery` | Browse public content or list owned resources |
111
111
  | `get_stats` | Read platform-wide statistics |
112
112
  | `get_limits` | Read the current upload limits (`GET /api/limits`) |
113
113
 
114
- 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.
114
+ 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 is private (a generated `priv-<random>` namespace): unlisted and not indexed; anyone with the link can still open it. `public` lists HTML, sites and files in the gallery and sitemap, so Google, Bing and AI search can find and index them; public pages are served on the brewpage.app domain for the strongest reach (a site always gets its own subdomain). Never put secrets in a public publication. Password-protected publications, JSON and KV are never listed.
115
115
 
116
116
  The MCP server wraps a subset of the backend-generated public [REST contract](../openapi/openapi.json). TTL inputs take days or a string such as `2w`/`36h` (default 15); the server rounds to whole days and clamps to 1–30 (owner-specific caps and eligible automatic extensions can differ). Use returned `expiresAt`. Every tolerated-input adjustment (TTL clamp, format alias, skipped site member, normalised namespace) comes back in the response `notices` array and is printed as `Notice (code): message`; `secret-detected` and `manage-link-disabled` print as `WARNING (code): message`: relay them to the user. A minted `manageUrl` prints as `MANAGE LINK (private): …`: give it to the user privately, never publish it. A listed public result prints `VISIBILITY: public, listed. Hide: set_visibility listed=false`. `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.
117
117
 
@@ -141,7 +141,7 @@ update_hosting(type="html", namespace="public", id="aBcDeFgHiJ", ownerToken="tok
141
141
 
142
142
  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.
143
143
 
144
- Parameters: `content` (string), `format` (default `HTML`; `HTML`, `MARKDOWN` or an alias the server maps, e.g. `md`), `namespace` (optional -- **omit for unlisted content**; pass `public` only for gallery/search eligibility, except when password-protected), `password` (optional), `ttlDays` (days or a string such as `2w`; default 15; the server clamps to 1--30), `filename` (optional, used as title fallback), `showTopBar` (optional boolean -- adds a toolbar with filename, Download button, and theme toggle), `deliveryMode` (optional).
144
+ Parameters: `content` (string), `format` (default `HTML`; `HTML`, `MARKDOWN` or an alias the server maps, e.g. `md`), `namespace` (optional -- **omit for private, unlisted content**; `public` lists the page in the gallery and sitemap for Google, Bing and AI search, served on the brewpage.app domain; never put secrets there), `password` (optional), `ttlDays` (days or a string such as `2w`; default 15; the server clamps to 1--30), `filename` (optional, used as title fallback), `showTopBar` (optional boolean -- adds a toolbar with filename, Download button, and theme toggle), `deliveryMode` (optional).
145
145
 
146
146
  The result includes `deliveryModeReason` and a `Delivery notice:` line when the server returns them (see above).
147
147
 
@@ -197,7 +197,7 @@ get_page(namespace="public", id="aBcDeFgHiJ")
197
197
 
198
198
  Upload a file to BrewPage by fetching it from a URL. Returns a public URL and owner token. Supports images, PDFs, video, audio, code files, and archives.
199
199
 
200
- Parameters: `url` (string, the source URL to fetch), `namespace` (optional -- omit to keep the file private/unlisted: reachable only by its link, not in the gallery, not search-indexed; pass `public` only for gallery/search eligibility), `filename` (optional custom filename), `deliveryMode` (optional). This tool does not expose base64, password, or TTL inputs.
200
+ Parameters: `url` (string, the source URL to fetch), `namespace` (optional -- omit to keep the file private: unlisted and not indexed, anyone with the link can open it; `public` lists the file in the gallery and sitemap for Google, Bing and AI search), `filename` (optional custom filename), `deliveryMode` (optional). This tool does not expose base64, password, or TTL inputs.
201
201
 
202
202
  Example prompt:
203
203
 
@@ -217,7 +217,7 @@ Publish a single-page or multi-file HTML site. Pass `entryContent` for a single
217
217
 
218
218
  Limits (current values from `get_limits`): a request is at most 25 MB; a site at most 200 files, 5 MB per file and 20 MB in total; each path at most 380 characters. System files such as `.DS_Store` and `__MACOSX/` are skipped and never count; files with an unsupported extension or a too-long path are skipped and reported in notices. Entry: `entry`, else root `index.html`, else the shallowest HTML file. A site limit hit answers 400 with a message naming the limit; 413 means the request is over 25 MB, has too many files and form fields, or a file name/path is too long; 422 means no servable file is left.
219
219
 
220
- Parameters: exactly one of `entryContent` (string) or non-empty `files` (array of `{path, content}`), `entry` (optional entry file path, default `index.html`), `namespace` (optional -- omit for unlisted content; `public` grants gallery eligibility except when password-protected), `password` (optional), `ttlDays` (days or a string such as `2w`; default 15; the server clamps to 1--30), `ownerToken` (optional, groups site under an existing owner), `deliveryMode` (optional; fresh sites require `subdomain`).
220
+ Parameters: exactly one of `entryContent` (string) or non-empty `files` (array of `{path, content}`), `entry` (optional entry file path, default `index.html`), `namespace` (optional -- omit for private, unlisted content; `public` lists the site in the gallery and sitemap for Google, Bing and AI search; a site always gets its own subdomain), `password` (optional), `ttlDays` (days or a string such as `2w`; default 15; the server clamps to 1--30), `ownerToken` (optional, groups site under an existing owner), `deliveryMode` (optional; fresh sites require `subdomain`).
221
221
 
222
222
  Example prompt:
223
223
 
@@ -247,7 +247,7 @@ republish_site(namespace="public", id="aBcDeFgHiJ", ownerToken="tok_...", files=
247
247
 
248
248
  Publish a JSON document to BrewPage. Returns a public URL and owner token. The body may be passed as a JSON string or as a structured object.
249
249
 
250
- Parameters: `json` (string, object, or array), `namespace` (optional -- omit for unlisted content; `public` grants gallery/search eligibility, except when password-protected), `password` (optional), `ttlDays` (days or a string such as `2w`; default 15; the server clamps to 1--30), `tags` (optional `string[]`), `ownerToken` (optional existing owner), `deliveryMode` (optional).
250
+ Parameters: `json` (string, object, or array), `namespace` (optional -- omit for private content; `public` is the shared public namespace; JSON is never listed or indexed, anyone with the link can open it), `password` (optional), `ttlDays` (days or a string such as `2w`; default 15; the server clamps to 1--30), `tags` (optional `string[]`), `ownerToken` (optional existing owner), `deliveryMode` (optional).
251
251
 
252
252
  Example prompt:
253
253
 
@@ -297,7 +297,7 @@ update_json(namespace="public", id="aBcDeFgHiJ", json={"mode": "dark", "version"
297
297
 
298
298
  Create a new KV (key/value) entry. Returns a public URL and owner token. The KV entry is addressed by `(namespace, id, key)`; this tool creates the entry and emits the generated `id` along with the owner token.
299
299
 
300
- Parameters: `key` (string), `value` (string), `namespace` (optional -- omit for unlisted content; `public` grants gallery/search eligibility, except when password-protected), `password` (optional), `ttlDays` (days or a string such as `2w`; default 15; the server clamps to 1--30), `tags` (optional `string[]`), `ownerToken` (optional existing owner), `deliveryMode` (optional).
300
+ Parameters: `key` (string), `value` (string), `namespace` (optional -- omit for private content; `public` is the shared public namespace; KV is never listed or indexed, anyone with the link can open it), `password` (optional), `ttlDays` (days or a string such as `2w`; default 15; the server clamps to 1--30), `tags` (optional `string[]`), `ownerToken` (optional existing owner), `deliveryMode` (optional).
301
301
 
302
302
  Example prompt:
303
303
 
@@ -379,7 +379,7 @@ delete_resource(type="html", namespace="public", id="aBcDeFgHiJ", ownerToken="to
379
379
 
380
380
  ### `set_visibility`
381
381
 
382
- Hide or show a public publication in gallery & search; the link stays the same. Only public, password-free HTML, sites and files can be listed; others answer with a `visibility-not-listable` notice.
382
+ Hide or show a public publication in the gallery and sitemap; the link stays the same. Only public, password-free HTML, sites and files can be listed; others answer with a `visibility-not-listable` notice.
383
383
 
384
384
  Parameters: `type` (`html` | `site` | `file` | `json` | `kv`), `namespace`, `id`, `listed` (boolean), `ownerToken` (optional).
385
385
 
@@ -443,7 +443,7 @@ get_limits()
443
443
  |--------------------|------|-------|
444
444
  | "Publish this HTML so I can share it" | `publish_html` | `format=HTML`; save `ownerToken` |
445
445
  | "Share these meeting notes as a link" | `publish_html` | `format=MARKDOWN`; readable rendered output |
446
- | "Host this AI-generated artifact" | `publish_html` | `namespace` optional; **private/unlisted by default**; `public` grants gallery/search eligibility except when protected |
446
+ | "Host this AI-generated artifact" | `publish_html` | `namespace` optional; **private/unlisted by default**; `public` lists it in the gallery and sitemap |
447
447
  | "Upload this image / PDF / video" | `publish_file` | Fetches from URL; inline preview on short URL |
448
448
  | "Deploy this static site" | `publish_site` | Pass `files` array or `entryContent` |
449
449
  | "Replace the site I published" | `republish_site` | Owner token; complete bundle replaces all existing files |
@@ -491,6 +491,15 @@ Every publish response includes an **owner token** -- the only credential that a
491
491
 
492
492
  ## Changelog
493
493
 
494
+ ## 1.13.2 -- 2026-10-25
495
+
496
+ - `namespace` guidance matches BrewPage 2.1.5: `public` lists HTML, sites and files in the gallery and sitemap for Google, Bing and AI search (pages on the brewpage.app domain, a site on its own subdomain); omitted stays private; JSON and KV are never listed. Private `publish_json`/`publish_kv` results no longer suggest `public` for listing.
497
+ - Old publications (made before 5 October 2026): the 409 hint mentions `republishUrl` only when the server sends it (HTML pages).
498
+
499
+ ## 1.13.1 -- 2026-10-25
500
+
501
+ - Old publications (made before 5 October 2026): update tools say they answer `409 OLD_PUBLICATION_READ_ONLY`; that failure adds a hint to publish the content anew and give the user the new URL and owner token.
502
+
494
503
  ## 1.13.0 -- 2026-10-25
495
504
 
496
505
  - Identifying `User-Agent: brewpage-mcp/1.13.0` on every BrewPage call.
package/dist/index.js CHANGED
@@ -3,10 +3,10 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
5
5
  const BASE_URL = process.env.BREWPAGE_URL || "https://brewpage.app";
6
- const USER_AGENT = "brewpage-mcp/1.13.0";
6
+ const USER_AGENT = "brewpage-mcp/1.13.2";
7
7
  const server = new McpServer({
8
8
  name: "brewpage-mcp",
9
- version: "1.13.0",
9
+ version: "1.13.2",
10
10
  });
11
11
  const OWNER_TOKEN_SHAPE = /^[A-Za-z0-9]{32}$/;
12
12
  const OWNER_TOKEN_ENV = "BREWPAGE_OWNER_TOKEN";
@@ -25,6 +25,8 @@ const CREATE_OWNER_TOKEN_DESCRIPTION = `Optional. Default: ${OWNER_TOKEN_ENV}, e
25
25
  const OWNER_TOKEN_DESCRIPTION = `Owner token from create. Default: ${OWNER_TOKEN_ENV}.`;
26
26
  const SECRETS = "Secrets allowed; likely ones are flagged in notices. For a secret: private namespace + password.";
27
27
  const MANAGE_URL_TEXT = "Returns manageUrl (private): give it to the user privately; never publish it.";
28
+ const OLD_READ_ONLY_CODE = "OLD_PUBLICATION_READ_ONLY";
29
+ const OLD_READ_ONLY_TEXT = ` Old publications (made before 5 October 2026) are read-only: 409 ${OLD_READ_ONLY_CODE}.`;
28
30
  /** Notice codes the agent must relay to the user. */
29
31
  const WARNING_CODES = new Set(["secret-detected", "manage-link-disabled"]);
30
32
  /** MCP type name -> REST path segment. */
@@ -42,13 +44,20 @@ function maskToken(token) {
42
44
  function textResult(text, isError = false) {
43
45
  return isError ? { content: [{ type: "text", text }], isError: true } : { content: [{ type: "text", text }] };
44
46
  }
45
- /** Owner-tool failure: the server answer verbatim plus a hint for a plain wrong-token 403. */
47
+ /** Owner-tool failure: the server answer verbatim plus a hint for a plain wrong-token 403 or a frozen old publication. */
46
48
  function ownerFailure(operation, status, data, fromEnv) {
47
49
  const lines = [`Failed to ${operation} (${status}): ${JSON.stringify(data)}`];
48
50
  const code = data?.code;
49
51
  if (status === 403 && (code === undefined || code === "OWNER_AUTHORIZATION_FAILED")) {
50
52
  lines.push(`Wrong owner token for this publication. Use ownerToken from create.${fromEnv ? ` Sent: ${OWNER_TOKEN_ENV}.` : ""}`);
51
53
  }
54
+ if (status === 409 && code === OLD_READ_ONLY_CODE) {
55
+ const republishUrl = data.republishUrl;
56
+ lines.push("Read-only old publication: cannot be updated. Publish the content anew with the matching " +
57
+ "publish tool in the same namespace; give the user the NEW URL and owner token. The old URL keeps its old content " +
58
+ "until it expires." + (typeof republishUrl === "string" ? " Ignore republishUrl (browser-only)." : "") +
59
+ (operation === "set KV" ? " For KV, read all keys with get_kv and publish them together with publish_kv." : ""));
60
+ }
52
61
  return textResult(lines.join("\n"), true);
53
62
  }
54
63
  /** Every BrewPage call identifies the client; source-file downloads use plain fetch. */
@@ -58,12 +67,7 @@ function bpFetch(url, init = {}) {
58
67
  return fetch(url, { ...init, headers });
59
68
  }
60
69
  const PUBLIC_NAMESPACE = "public";
61
- /**
62
- * Generate a short, unlisted private namespace slug used when the caller omits
63
- * `namespace`. Sending an explicit non-public ns prevents the backend from
64
- * applying its `public` default and making the content discovery-eligible.
65
- * Format: `priv-<6 lowercase-alphanumeric chars>`.
66
- */
70
+ /** Private namespace slug `priv-<6 lowercase-alphanumeric chars>` sent when the caller omits `namespace`. */
67
71
  function defaultPrivateNamespace() {
68
72
  const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
69
73
  let slug = "";
@@ -72,20 +76,28 @@ function defaultPrivateNamespace() {
72
76
  }
73
77
  return `priv-${slug}`;
74
78
  }
75
- /**
76
- * Resolve the effective namespace actually sent to the API: the caller's value
77
- * verbatim when provided (including `public`), otherwise a generated private slug.
78
- */
79
+ /** Namespace sent to the API: the caller's value verbatim (including `public`), else a generated private slug. */
79
80
  function resolveNamespace(namespace) {
80
81
  return namespace && namespace.length > 0 ? namespace : defaultPrivateNamespace();
81
82
  }
82
- const UNLISTED_NOTICE = "Private: not listed. Anyone with the link can open it. Use namespace public for gallery & search.";
83
+ const UNLISTED_NOTICE = "Private: not listed. Anyone with the link can open it.";
84
+ const LISTABLE_HINT = " Use namespace public to list it in the gallery and sitemap.";
83
85
  const PROTECTED_UNLISTED_NOTICE = "Password: link + password. Not listed.";
84
- const NAMESPACE_DESCRIPTION = "Optional. Omit to generate an unlisted namespace: anyone with its unpassworded link can read it, " +
85
- "but it is excluded from gallery/search. Pass a custom namespace to group unlisted content, " +
86
- "or pass `public` ONLY when the user explicitly wants gallery/search eligibility. " +
87
- "Password-protected content stays excluded; unlisted, protected or otherwise ineligible native responses carry noindex; eligible public HTML, sites and files may permit indexing. " +
88
- "Lowercase only: 1-32 of a-z, 0-9, hyphen.";
86
+ 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 " +
87
+ "and people can discover it. Use it for anything meant to be found or shared: docs, guides, demos, portfolios, reports, landing pages. ";
88
+ const PUBLIC_NAMESPACE_TAIL = " Anyone can find and read a public page: never put secrets, tokens or personal data there. " +
89
+ "Omit (default): private. Unlisted and not indexed; anyone who has the link can still open it. ";
90
+ const NAMESPACE_FORMAT = "Lowercase only: 1-32 of a-z, 0-9, hyphen.";
91
+ /** `namespace` for publish_html and publish_file. */
92
+ const NAMESPACE_DESCRIPTION = PUBLIC_NAMESPACE_LEAD +
93
+ "Public pages are served right on the brewpage.app domain, which gives them the strongest reach in search; " +
94
+ "a page whose code needs its own origin (cookies, storage) gets its own subdomain automatically." +
95
+ PUBLIC_NAMESPACE_TAIL + NAMESPACE_FORMAT;
96
+ const SITE_NAMESPACE_DESCRIPTION = PUBLIC_NAMESPACE_LEAD + "A site always gets its own subdomain." +
97
+ PUBLIC_NAMESPACE_TAIL + NAMESPACE_FORMAT;
98
+ /** `namespace` for publish_json and publish_kv: data is never listed. */
99
+ const DATA_NAMESPACE_DESCRIPTION = "Omit (default): private. namespace=public: the shared public namespace. JSON and KV are never listed in the gallery " +
100
+ "or indexed, in any namespace; anyone who has the link can open it. " + NAMESPACE_FORMAT;
89
101
  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). " +
90
102
  "The server rounds to whole days and clamps to the allowed range (1-30 by default; owner caps differ), reporting each adjustment in notices; use returned expiresAt.");
91
103
  const DELIVERY_MODE_SCHEMA = z.enum(["path", "subdomain"]).optional().describe("Optional delivery choice for a fresh publication, independent of password/namespace. " +
@@ -172,7 +184,7 @@ async function apiRequest(method, path, body, headers, responseType = "json") {
172
184
  const data = responseType === "text" ? await res.text() : await res.json().catch(() => null);
173
185
  return { ok: res.ok, status: res.status, data, headers: res.headers };
174
186
  }
175
- function formatPublishResponse(data, effectiveNamespace, sentOwnerToken) {
187
+ function formatPublishResponse(data, effectiveNamespace, sentOwnerToken, listable = true) {
176
188
  const lines = [
177
189
  `Published successfully!`,
178
190
  ``,
@@ -188,7 +200,7 @@ function formatPublishResponse(data, effectiveNamespace, sentOwnerToken) {
188
200
  lines.push(...ownerLinkLines(data));
189
201
  const ns = (data.namespace ?? effectiveNamespace) || "";
190
202
  if (ns !== PUBLIC_NAMESPACE) {
191
- lines.push(``, data.access === "protected" ? PROTECTED_UNLISTED_NOTICE : UNLISTED_NOTICE);
203
+ lines.push(``, data.access === "protected" ? PROTECTED_UNLISTED_NOTICE : UNLISTED_NOTICE + (listable ? LISTABLE_HINT : ""));
192
204
  }
193
205
  return lines.join("\n");
194
206
  }
@@ -470,7 +482,7 @@ server.tool("publish_site", "Publish an HTML site to BrewPage. Single-page: pass
470
482
  namespace: z
471
483
  .string()
472
484
  .optional()
473
- .describe(NAMESPACE_DESCRIPTION),
485
+ .describe(SITE_NAMESPACE_DESCRIPTION),
474
486
  password: z
475
487
  .string()
476
488
  .optional()
@@ -557,13 +569,13 @@ server.tool("publish_site", "Publish an HTML site to BrewPage. Single-page: pass
557
569
  lines.push(...ownerLinkLines(site));
558
570
  const ns = (site.namespace || effectiveNamespace) || "";
559
571
  if (ns !== PUBLIC_NAMESPACE) {
560
- lines.push(``, site.access === "protected" ? PROTECTED_UNLISTED_NOTICE : UNLISTED_NOTICE);
572
+ lines.push(``, site.access === "protected" ? PROTECTED_UNLISTED_NOTICE : UNLISTED_NOTICE + LISTABLE_HINT);
561
573
  }
562
574
  return {
563
575
  content: [{ type: "text", text: lines.join("\n") }],
564
576
  };
565
577
  });
566
- 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`. Republishing preserves existing expiry. " + SITE_LIMITS_TEXT, {
578
+ 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`. Republishing preserves existing expiry." + OLD_READ_ONLY_TEXT + " " + SITE_LIMITS_TEXT, {
567
579
  namespace: z.string().describe("Site namespace"),
568
580
  id: z.string().describe("Site ID"),
569
581
  ownerToken: z.string().optional().describe(OWNER_TOKEN_DESCRIPTION),
@@ -656,7 +668,7 @@ server.tool("republish_site", "Republish (full-replace) an existing multi-file B
656
668
  };
657
669
  });
658
670
  server.tool("update_html", "Update an existing BrewPage HTML/Markdown page. Requires owner token. Returns the updated page metadata. " +
659
- "The link and delivery mode never change on update; an advisory deliveryModeReason and delivery notice are included when the new content uses browser features that may not work fully on a BrewPage link.", {
671
+ "The link and delivery mode never change on update; an advisory deliveryModeReason and delivery notice are included when the new content uses browser features that may not work fully on a BrewPage link." + OLD_READ_ONLY_TEXT, {
660
672
  namespace: z.string().describe("Page namespace"),
661
673
  id: z.string().describe("Page ID"),
662
674
  content: z.string().describe("New HTML or Markdown content"),
@@ -696,7 +708,7 @@ server.tool("publish_json", "Publish a JSON document to BrewPage (repository def
696
708
  namespace: z
697
709
  .string()
698
710
  .optional()
699
- .describe(NAMESPACE_DESCRIPTION),
711
+ .describe(DATA_NAMESPACE_DESCRIPTION),
700
712
  password: z
701
713
  .string()
702
714
  .optional()
@@ -748,7 +760,7 @@ server.tool("publish_json", "Publish a JSON document to BrewPage (repository def
748
760
  content: [
749
761
  {
750
762
  type: "text",
751
- text: formatPublishResponse(data, effectiveNamespace, owner),
763
+ text: formatPublishResponse(data, effectiveNamespace, owner, false),
752
764
  },
753
765
  ],
754
766
  };
@@ -786,7 +798,7 @@ server.tool("get_json", "Fetch a published BrewPage JSON document by namespace a
786
798
  ],
787
799
  };
788
800
  });
789
- server.tool("update_json", "Update an existing BrewPage JSON document. Requires owner token. Accepts a JSON object, array, or a JSON-encoded string.", {
801
+ server.tool("update_json", "Update an existing BrewPage JSON document. Requires owner token. Accepts a JSON object, array, or a JSON-encoded string." + OLD_READ_ONLY_TEXT, {
790
802
  namespace: z.string().describe("Document namespace"),
791
803
  id: z.string().describe("Document ID"),
792
804
  json: z
@@ -827,7 +839,7 @@ server.tool("publish_kv", "Publish a key/value entry to BrewPage. Creates a KV s
827
839
  namespace: z
828
840
  .string()
829
841
  .optional()
830
- .describe(NAMESPACE_DESCRIPTION),
842
+ .describe(DATA_NAMESPACE_DESCRIPTION),
831
843
  password: z
832
844
  .string()
833
845
  .optional()
@@ -874,12 +886,12 @@ server.tool("publish_kv", "Publish a key/value entry to BrewPage. Creates a KV s
874
886
  content: [
875
887
  {
876
888
  type: "text",
877
- text: formatPublishResponse(data, effectiveNamespace, owner),
889
+ text: formatPublishResponse(data, effectiveNamespace, owner, false),
878
890
  },
879
891
  ],
880
892
  };
881
893
  });
882
- server.tool("set_kv", "Set or update a key in an existing BrewPage KV store. Requires owner token. The key name `hosting` is reserved.", {
894
+ server.tool("set_kv", "Set or update a key in an existing BrewPage KV store. Requires owner token. The key name `hosting` is reserved." + OLD_READ_ONLY_TEXT, {
883
895
  namespace: z.string().describe("KV store namespace"),
884
896
  id: z.string().describe("KV store ID"),
885
897
  key: z.string().describe("Key to set"),
@@ -1031,7 +1043,7 @@ server.tool("search_gallery", "Search the BrewPage public gallery. Optional filt
1031
1043
  ],
1032
1044
  };
1033
1045
  });
1034
- server.tool("set_visibility", "Hide or show a public publication in gallery & search. Link unchanged.", {
1046
+ server.tool("set_visibility", "Hide or show a public publication in the gallery and sitemap. Link unchanged.", {
1035
1047
  type: z.enum(["html", "site", "file", "json", "kv"]).describe("Publication type; only public html, site and file can be listed"),
1036
1048
  namespace: z.string().describe("Publication namespace"),
1037
1049
  id: z.string().describe("Publication ID"),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brewpage-mcp",
3
- "version": "1.13.0",
3
+ "version": "1.13.2",
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",