brewpage-mcp 1.15.5 → 1.16.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
@@ -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.15.5
20
+ npx -y brewpage-mcp@1.16.0
21
21
  ```
22
22
 
23
23
  Requires Node.js 20 or newer. This starts the stdio server for an MCP client.
@@ -50,7 +50,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
50
50
  "mcpServers": {
51
51
  "brewpage": {
52
52
  "command": "npx",
53
- "args": ["-y", "brewpage-mcp@1.15.5"]
53
+ "args": ["-y", "brewpage-mcp@1.16.0"]
54
54
  }
55
55
  }
56
56
  }
@@ -61,7 +61,7 @@ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
61
61
  Register a project-scoped stdio server:
62
62
 
63
63
  ```bash
64
- claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.15.5
64
+ claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.16.0
65
65
  ```
66
66
 
67
67
  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).
@@ -75,7 +75,7 @@ Open **Settings > MCP** and add:
75
75
  "mcpServers": {
76
76
  "brewpage": {
77
77
  "command": "npx",
78
- "args": ["-y", "brewpage-mcp@1.15.5"]
78
+ "args": ["-y", "brewpage-mcp@1.16.0"]
79
79
  }
80
80
  }
81
81
  }
@@ -89,7 +89,7 @@ Open the Cline MCP settings panel and add:
89
89
  {
90
90
  "brewpage": {
91
91
  "command": "npx",
92
- "args": ["-y", "brewpage-mcp@1.15.5"]
92
+ "args": ["-y", "brewpage-mcp@1.16.0"]
93
93
  }
94
94
  }
95
95
  ```
@@ -97,13 +97,13 @@ Open the Cline MCP settings panel and add:
97
97
  ### Global Install
98
98
 
99
99
  ```bash
100
- npm install -g brewpage-mcp@1.15.5
100
+ npm install -g brewpage-mcp@1.16.0
101
101
  brewpage-mcp
102
102
  ```
103
103
 
104
104
  ## Tools
105
105
 
106
- Twenty tools are available. Creation returns an owner token; keep it to modify or delete the resource later. Owner tools take `ownerToken`, else `BREWPAGE_OWNER_TOKEN`. Supported password inputs use `X-Password`. Secrets allowed; likely ones are flagged in notices. For a secret: private namespace + password.
106
+ Twenty tools are available. Creation returns an owner token; keep it to modify or delete the resource later. Owner tools take `ownerToken`, else `BREWPAGE_OWNER_TOKEN` (local server only). Supported password inputs use `X-Password`. Secrets allowed; likely ones are flagged in notices. For a secret: private namespace + password.
107
107
 
108
108
  | Tool | Purpose |
109
109
  |------|---------|
@@ -130,7 +130,7 @@ Twenty tools are available. Creation returns an owner token; keep it to modify o
130
130
 
131
131
  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.
132
132
 
133
- 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.
133
+ 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` prints as `WARNING (code): message`: relay it to the user. A live BrewPage token or manage link in public content (`credential-published`) prints first as `SECURITY ALERT (credential-published): …`: tell the user to remove and reset it; that result is hidden from the gallery, so it prints no `VISIBILITY` line. 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.
134
134
 
135
135
  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.
136
136
 
@@ -232,7 +232,7 @@ publish_file(url="https://example.com/diagram.png")
232
232
 
233
233
  Publish a single-page or multi-file HTML site. Pass `entryContent` for a single page or `files` (array of `{path, content}`) for a multi-file site. Supports password protection, TTL, and owner token grouping. The result prints `Title:` (entry page title) after `ID:` when the server derives one.
234
234
 
235
- 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, as does a PDF/Office file that is unreadable or password-protected; 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; 503 means busy: retry after `Retry-After` seconds (failed publish tools print it).
235
+ Limits (current values from `get_limits`): a request is at most 25 MiB; a site at most 200 files, 5 MiB per file and 20 MiB 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, as does a PDF/Office file that is unreadable or password-protected; 413 means the request is over 25 MiB, has too many files and form fields, or a file name/path is too long; 422 means no servable file is left; 503 means busy: retry after `Retry-After` seconds (failed publish tools print it).
236
236
 
237
237
  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`).
238
238
 
@@ -382,7 +382,7 @@ search_gallery(mine=true, ownerToken="tok_...", sort="date")
382
382
 
383
383
  Delete a BrewPage resource (HTML page, site, KV store, JSON document, or file) using the owner token received at creation.
384
384
 
385
- Parameters: `type` (`html` | `site` | `kv` | `json` | `file`), `namespace` (string), `id` (string), `ownerToken` (optional; default `BREWPAGE_OWNER_TOKEN`). Path segments are URL-encoded.
385
+ Parameters: `type` (`html` | `site` | `kv` | `json` | `file`), `namespace` (string), `id` (string), `ownerToken` (optional; default `BREWPAGE_OWNER_TOKEN`, local server only). Path segments are URL-encoded.
386
386
 
387
387
  Example prompt:
388
388
 
@@ -412,7 +412,7 @@ Output: `LISTED: false · link unchanged`.
412
412
 
413
413
  Add an owner token to your BrewPage profile (as `publish`) with an `admin` token of that profile (`POST /api/owner-token/bind`). Output: `BOUND …ab12 · 3/20 active`; tokens are never printed in full.
414
414
 
415
- Parameters: `token`, `ownerToken` (admin token; optional, default `BREWPAGE_OWNER_TOKEN`).
415
+ Parameters: `token`, `ownerToken` (admin token; optional, default `BREWPAGE_OWNER_TOKEN`, local server only).
416
416
 
417
417
  ---
418
418
 
@@ -442,9 +442,9 @@ get_stats()
442
442
 
443
443
  ### `get_limits`
444
444
 
445
- Read the current limits from `GET /api/limits` (no auth; with `BREWPAGE_OWNER_TOKEN` also `reach` and `ownerMaxTtlDays`). Sizes are bytes, TTLs days: `requestMaxBytes`; `html`, `json`, `kv` and `files` max size, counts and TTL default/maximum; `profileMaxTtlDays` (TTL cap for a token in a profile); `sites.maxFiles`, `maxFileBytes`, `maxTotalBytes` (uncompressed), `maxArchiveExpansionBytes`, `maxPathChars`, `maxPathBytes`, `maxNameBytes`, `defaultTtlDays`, `maxTtlDays`, `allowedExtensions`, `skippedFileNames` and `skippedRootFolders`. Call it before publishing a large or multi-file site.
445
+ Read the current limits from `GET /api/limits` (no auth needed; an owner token goes as `X-Owner-Token`). Always returns `reach`: `profile` only when the owner token is active in a profile, else `token` (also with no token); with a usable owner token also `ownerMaxTtlDays`. Sizes are bytes, TTLs days: `requestMaxBytes`; `html`, `json`, `kv` and `files` max size, counts and TTL default/maximum; `profileMaxTtlDays` (TTL cap for a token in a profile); `sites.maxFiles`, `maxFileBytes`, `maxTotalBytes` (uncompressed), `maxArchiveExpansionBytes`, `maxPathChars`, `maxPathBytes`, `maxNameBytes`, `defaultTtlDays`, `maxTtlDays`, `allowedExtensions`, `skippedFileNames` and `skippedRootFolders`. Call it before publishing a large or multi-file site.
446
446
 
447
- Parameters: none.
447
+ Parameters: `ownerToken` (optional; default `BREWPAGE_OWNER_TOKEN`, local server only).
448
448
 
449
449
  Example prompt:
450
450
 
@@ -530,6 +530,10 @@ Every publish response includes an **owner token** -- the only credential that a
530
530
 
531
531
  ## Changelog
532
532
 
533
+ ## 1.16.0 -- 2026-10-29
534
+
535
+ - `get_limits` accepts `ownerToken` (remote `/mcp` gets `reach`/`ownerMaxTtlDays`). Results open with `SECURITY ALERT (credential-published)` when public content holds a live BrewPage token or manage link. Wording: limits reach, local-only env token, MiB.
536
+
533
537
  ## 1.15.5 -- 2026-10-29
534
538
 
535
539
  - `publish_html` prints the page title (`Title:`) when the server reports one; tool texts state plain size limits; `get_limits` names the per-type limits and the profile TTL cap.
package/dist/tools.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/server";
2
2
  export declare const DEFAULT_BASE_URL = "https://brewpage.app";
3
- export declare const USER_AGENT = "brewpage-mcp/1.15.5";
3
+ export declare const USER_AGENT = "brewpage-mcp/1.16.0";
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/server";
2
2
  import { z } from "zod";
3
3
  export const DEFAULT_BASE_URL = "https://brewpage.app";
4
- export const USER_AGENT = "brewpage-mcp/1.15.5";
4
+ export const USER_AGENT = "brewpage-mcp/1.16.0";
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 };
@@ -10,15 +10,21 @@ const ADDITIVE = { readOnlyHint: false, destructiveHint: false };
10
10
  function toolMeta(title, hints) {
11
11
  return { title, annotations: { title, ...hints } };
12
12
  }
13
- const NO_OWNER_TOKEN = `No owner token: pass ownerToken or set ${OWNER_TOKEN_ENV}.`;
14
- const CREATE_OWNER_TOKEN_DESCRIPTION = `Optional. Default: ${OWNER_TOKEN_ENV}, else a new token per create.`;
15
- const OWNER_TOKEN_DESCRIPTION = `Owner token from create. Default: ${OWNER_TOKEN_ENV}.`;
16
- const SECRETS = "Secrets allowed; likely ones are flagged in notices. Private namespace + password suit secrets.";
13
+ /** Env default exists only in the local (stdio) server; remote /mcp takes ownerToken per call. */
14
+ const ENV_DEFAULT = `env ${OWNER_TOKEN_ENV} (local server only)`;
15
+ const NO_OWNER_TOKEN = `No owner token: pass ownerToken (local server: or set ${OWNER_TOKEN_ENV}).`;
16
+ const CREATE_OWNER_TOKEN_DESCRIPTION = `Optional. Default: ${ENV_DEFAULT}, else a new token per create.`;
17
+ const OWNER_TOKEN_DESCRIPTION = `Owner token from create. Default: ${ENV_DEFAULT}.`;
18
+ const SECRETS = "Secrets allowed; likely ones are flagged in notices. Private namespace + password suit secrets. " +
19
+ "BrewPage tokens and manage links must never be in public content.";
17
20
  const MANAGE_URL_TEXT = "Returns manageUrl, a private management link (grants control; not for public sharing).";
18
21
  const OLD_READ_ONLY_CODE = "OLD_PUBLICATION_READ_ONLY";
19
22
  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
23
  /** Notice codes the agent must relay to the user. */
21
- const WARNING_CODES = new Set(["secret-detected", "manage-link-disabled"]);
24
+ const WARNING_CODES = new Set(["secret-detected"]);
25
+ /** Live BrewPage token or manage link in public content: printed first as a security alert, never listed. */
26
+ const CREDENTIAL_CODE = "credential-published";
27
+ const CREDENTIAL_ALERT_TAIL = "Tell the user now: anyone with this link can use that token; they must remove it and reset it.";
22
28
  /** MCP type name -> REST path segment. */
23
29
  const TYPE_SEGMENTS = { html: "html", site: "sites", file: "files", json: "json", kv: "kv" };
24
30
  /** `/api/<segments>` with each segment encoded; empty, `.` and `..` refused (URL parsing would resolve them away). */
@@ -108,11 +114,11 @@ const DELIVERY_MODE_SCHEMA = z.enum(["path", "subdomain"]).optional().describe("
108
114
  "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. " +
109
115
  "When creation returns an existing publication, it keeps its actual mode and URL.");
110
116
  /** Site upload rules shared by publish_site and republish_site; current values come from get_limits. */
111
- 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; " +
117
+ const SITE_LIMITS_TEXT = "Default limits (current values: GET /api/limits): a request is at most 25 MiB; a site at most 200 files, 5 MiB per file and 20 MiB in total; " +
112
118
  "each path at most 380 characters. System files (.DS_Store, __MACOSX/ and similar) are skipped and never count; files with an unsupported extension " +
113
119
  "or a too-long path are skipped and listed in notices. Entry: `entry`, else root index.html, else the shallowest HTML file. " +
114
120
  "Errors: 400 = a site limit exceeded (the message names it), invalid path, no HTML file, or a PDF/Office file unreadable " +
115
- "or password-protected; 413 = request over 25 MB, too many files and form fields, or a file name/path too long; " +
121
+ "or password-protected; 413 = request over 25 MiB, too many files and form fields, or a file name/path too long; " +
116
122
  "422 = no servable file left; 503 = busy, retry after Retry-After seconds.";
117
123
  function hostingResponseLines(data) {
118
124
  const fields = ["routingCohort", "deliveryMode", "requestedDeliveryMode", "effectiveDeliveryMode",
@@ -126,14 +132,26 @@ function hostingResponseLines(data) {
126
132
  lines.push(...noticeLines(data));
127
133
  return lines;
128
134
  }
129
- /** Server `notices` ({code, message}) on publish/update responses: each tolerated-input adjustment, verbatim. */
130
- function noticeLines(data) {
135
+ /** Server `notices` ({code, message}) on publish/update responses, well-formed only. */
136
+ function notices(data) {
131
137
  if (!Array.isArray(data.notices))
132
138
  return [];
133
- return data.notices
134
- .filter((n) => typeof n === "object" && n !== null && typeof n.code === "string" && typeof n.message === "string")
139
+ return data.notices.filter((n) => typeof n === "object" && n !== null && typeof n.code === "string" && typeof n.message === "string");
140
+ }
141
+ /** Each tolerated-input adjustment, verbatim; the credential alert prints separately, first. */
142
+ function noticeLines(data) {
143
+ return notices(data).filter((n) => n.code !== CREDENTIAL_CODE)
135
144
  .map((n) => `${WARNING_CODES.has(n.code) ? "WARNING" : "Notice"} (${n.code}): ${n.message}`);
136
145
  }
146
+ function hasCredentialNotice(data) {
147
+ return notices(data).some((n) => n.code === CREDENTIAL_CODE);
148
+ }
149
+ /** Opening lines of a result whose public content holds a live BrewPage credential, else none. */
150
+ function credentialAlertLines(data) {
151
+ const alerts = notices(data).filter((n) => n.code === CREDENTIAL_CODE)
152
+ .map((n) => `SECURITY ALERT (${CREDENTIAL_CODE}): ${n.message} ${CREDENTIAL_ALERT_TAIL}`);
153
+ return alerts.length ? [...alerts, ``] : [];
154
+ }
137
155
  const SIGN_IN_HINT = "Optional: sign in free (Google or any email) at https://account.brewpage.app to never lose your pages or tokens and to mint tokens that never expire. Not needed to publish.";
138
156
  /** Private management link (present only when minted) and the public listing state. */
139
157
  function ownerLinkLines(data) {
@@ -141,7 +159,7 @@ function ownerLinkLines(data) {
141
159
  if (typeof data.manageUrl === "string" && data.manageUrl.length > 0) {
142
160
  lines.push(``, `MANAGE LINK (private): ${data.manageUrl}`, `Give it to the user privately; never publish it.`);
143
161
  }
144
- if (data.listed === true)
162
+ if (data.listed === true && !hasCredentialNotice(data))
145
163
  lines.push(``, `VISIBILITY: public, listed. Hide: set_visibility listed=false`);
146
164
  return lines;
147
165
  }
@@ -157,6 +175,7 @@ function deliveryReasonLines(data) {
157
175
  }
158
176
  function formatUpdateResponse(data) {
159
177
  const lines = [
178
+ ...credentialAlertLines(data),
160
179
  `Updated successfully!`,
161
180
  ``,
162
181
  `URL: ${data.link || data.url || "N/A"}`,
@@ -188,7 +207,7 @@ export function createServer(options = {}) {
188
207
  const apiFetch = options.fetch ?? fetch;
189
208
  const server = new McpServer({
190
209
  name: "brewpage-mcp",
191
- version: "1.15.5",
210
+ version: "1.16.0",
192
211
  });
193
212
  /** Caller argument first, then the configured default; `fromEnv` marks the configured one. */
194
213
  function ownerTokenFor(argument) {
@@ -242,6 +261,7 @@ export function createServer(options = {}) {
242
261
  }
243
262
  function formatPublishResponse(data, effectiveNamespace, sentOwnerToken, listable = true) {
244
263
  const lines = [
264
+ ...credentialAlertLines(data),
245
265
  `Published successfully!`,
246
266
  ``,
247
267
  `URL: ${data.link || data.url || "N/A"}`,
@@ -595,6 +615,7 @@ export function createServer(options = {}) {
595
615
  }
596
616
  const site = data;
597
617
  const lines = [
618
+ ...credentialAlertLines(site),
598
619
  `Site published successfully!`,
599
620
  ``,
600
621
  `URL: ${site.link || "N/A"}`,
@@ -687,6 +708,7 @@ export function createServer(options = {}) {
687
708
  return ownerFailure("republish site", res.status, data, auth.fromEnv, res.headers);
688
709
  const site = data;
689
710
  const lines = [
711
+ ...credentialAlertLines(site),
690
712
  `Site republished successfully (full replace)!`,
691
713
  ``,
692
714
  `URL: ${site.link || "N/A"}`,
@@ -1014,10 +1036,13 @@ export function createServer(options = {}) {
1014
1036
  description: "Get the current BrewPage limits (GET /api/limits): request size; per type (html, json, kv, files, sites) max size, " +
1015
1037
  "counts and TTL default/maximum; profileMaxTtlDays (TTL cap for a token in a profile); site ZIP expansion, path and name lengths, " +
1016
1038
  "allowed file extensions and skipped system files. Sizes are bytes, TTLs days. " +
1017
- `With ${OWNER_TOKEN_ENV} set: also reach (token|profile) and ownerMaxTtlDays.`,
1018
- inputSchema: {},
1019
- }, async () => {
1020
- const headers = defaultOwnerToken ? { "X-Owner-Token": defaultOwnerToken } : {};
1039
+ `The answer always has reach: \`profile\` only when the owner token is active in a profile, else \`token\` (also with no token). With a usable owner token also ownerMaxTtlDays.`,
1040
+ inputSchema: {
1041
+ ownerToken: z.string().optional().describe(OWNER_TOKEN_DESCRIPTION),
1042
+ },
1043
+ }, async ({ ownerToken }) => {
1044
+ const { token } = ownerTokenFor(ownerToken);
1045
+ const headers = token ? { "X-Owner-Token": token } : {};
1021
1046
  const { ok, status, data } = await apiRequest("GET", "/api/limits", undefined, headers);
1022
1047
  if (!ok) {
1023
1048
  return {
@@ -1068,7 +1093,7 @@ export function createServer(options = {}) {
1068
1093
  ownerToken: z
1069
1094
  .string()
1070
1095
  .optional()
1071
- .describe(`Owner token; only sent as X-Owner-Token when 'mine' is true. Default: ${OWNER_TOKEN_ENV}.`),
1096
+ .describe(`Owner token; only sent as X-Owner-Token when 'mine' is true. Default: ${ENV_DEFAULT}.`),
1072
1097
  },
1073
1098
  }, async ({ q, page, size, sort, mine, ownerToken }) => {
1074
1099
  const auth = ownerTokenFor(ownerToken);
@@ -1119,14 +1144,14 @@ export function createServer(options = {}) {
1119
1144
  if (!ok)
1120
1145
  return ownerFailure("set visibility", status, data, auth.fromEnv);
1121
1146
  const result = data;
1122
- return textResult([`LISTED: ${result.listed} · link unchanged`, ...noticeLines(result)].join("\n"));
1147
+ return textResult([...credentialAlertLines(result), `LISTED: ${result.listed} · link unchanged`, ...noticeLines(result)].join("\n"));
1123
1148
  });
1124
1149
  server.registerTool("bind_token", {
1125
1150
  ...toolMeta("Add token to profile", ADDITIVE),
1126
1151
  description: "Add an owner token to your BrewPage profile (as publish). Needs an admin token of that profile.",
1127
1152
  inputSchema: {
1128
1153
  token: z.string().describe("Owner token to add"),
1129
- ownerToken: z.string().optional().describe(`Admin token of the profile. Default: ${OWNER_TOKEN_ENV}.`),
1154
+ ownerToken: z.string().optional().describe(`Admin token of the profile. Default: ${ENV_DEFAULT}.`),
1130
1155
  },
1131
1156
  }, async ({ token, ownerToken }) => {
1132
1157
  const auth = ownerTokenFor(ownerToken);
@@ -1145,7 +1170,7 @@ export function createServer(options = {}) {
1145
1170
  label: z.string().optional().describe("Optional label shown in the profile"),
1146
1171
  scope: z.enum(["publish", "admin"]).optional().describe("publish (default) or admin (can manage tokens)"),
1147
1172
  expiresIn: z.enum(["30d", "90d", "1y", "never"]).optional().describe("Default: admin 90d, publish never"),
1148
- ownerToken: z.string().optional().describe(`Admin token of the profile. Default: ${OWNER_TOKEN_ENV}.`),
1173
+ ownerToken: z.string().optional().describe(`Admin token of the profile. Default: ${ENV_DEFAULT}.`),
1149
1174
  },
1150
1175
  }, async ({ label, scope, expiresIn, ownerToken }) => {
1151
1176
  const auth = ownerTokenFor(ownerToken);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brewpage-mcp",
3
- "version": "1.15.5",
3
+ "version": "1.16.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",