brewpage-mcp 1.16.9 → 1.17.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
@@ -8,7 +8,7 @@ MCP server for [BrewPage](https://brewpage.app) -- publish and manage HTML, KV,
8
8
 
9
9
  ## What is BrewPage
10
10
 
11
- [BrewPage](https://brewpage.app) is a free instant hosting platform designed for AI agents and developers. One `POST` request publishes HTML, Markdown, a multi-file site, a JSON document, or a binary file and returns its primary HTTPS link -- no account needed, no API keys, no infrastructure setup. Every resource carries an **owner token** returned at creation time: use it to update content in place, delete the resource, or authenticate list operations across sessions. `brewpage-mcp` exposes this API as twenty typed MCP tools so any compatible agent -- Claude, Codex, Gemini, Cursor, Cline -- can publish, update, fetch, and manage BrewPage HTML, JSON, KV, and file content without leaving the conversation.
11
+ [BrewPage](https://brewpage.app) is a free instant hosting platform designed for AI agents and developers. One `POST` request publishes HTML, Markdown, a multi-file site, a JSON document, or a binary file and returns its primary HTTPS link -- no account needed, no API keys, no infrastructure setup. Every resource carries an **owner token** returned at creation time: use it to update content in place, delete the resource, or authenticate list operations across sessions. `brewpage-mcp` exposes this API as twenty-one typed MCP tools so any compatible agent -- Claude, Codex, Gemini, Cursor, Cline -- can publish, update, fetch, and manage BrewPage HTML, JSON, KV, and file content without leaving the conversation.
12
12
 
13
13
  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.
14
14
 
@@ -17,14 +17,14 @@ 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.16.9
20
+ npx -y brewpage-mcp@1.17.0
21
21
  ```
22
22
 
23
23
  Requires Node.js 20 or newer. This starts the stdio server for an MCP client.
24
24
 
25
25
  ### Remote (no install)
26
26
 
27
- Add `https://brewpage.app/mcp` as a custom connector / remote MCP server by URL in Claude.ai, ChatGPT or any Streamable HTTP client. No auth; same 20 tools.
27
+ Add `https://brewpage.app/mcp` as a custom connector / remote MCP server by URL in Claude.ai, ChatGPT or any Streamable HTTP client. No auth; same 21 tools.
28
28
 
29
29
  Claude Code: `claude mcp add --transport http brewpage https://brewpage.app/mcp`, or in `.mcp.json`:
30
30
 
@@ -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.16.9"]
53
+ "args": ["-y", "brewpage-mcp@1.17.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.16.9
64
+ claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.17.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.16.9"]
78
+ "args": ["-y", "brewpage-mcp@1.17.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.16.9"]
92
+ "args": ["-y", "brewpage-mcp@1.17.0"]
93
93
  }
94
94
  }
95
95
  ```
@@ -97,20 +97,20 @@ Open the Cline MCP settings panel and add:
97
97
  ### Global Install
98
98
 
99
99
  ```bash
100
- npm install -g brewpage-mcp@1.16.9
100
+ npm install -g brewpage-mcp@1.17.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` (local server only). Supported password inputs use `X-Password`. Secrets allowed; likely ones are flagged in notices. For a secret: private namespace + password.
106
+ Twenty-one 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
  |------|---------|
110
110
  | `publish_html` | Publish HTML, Markdown, code or JSON |
111
- | `update_html` | Replace page content; omitted format preserves stored format |
111
+ | `update_html` | Replace page content; omitted format preserves stored format, except a whole HTML document makes a Markdown page an HTML page |
112
112
  | `get_page` | Fetch raw page content |
113
- | `publish_file` | Fetch a source URL and upload the file |
113
+ | `publish_file` | Fetch a source URL and upload the file; HTML and Markdown become pages |
114
114
  | `publish_site` | Publish a single page or complete multi-file site bundle |
115
115
  | `republish_site` | Replace the complete site bundle at the same ID/link |
116
116
  | `publish_json` | Publish a JSON object, array, or JSON-encoded string |
@@ -122,6 +122,7 @@ Twenty tools are available. Creation returns an owner token; keep it to modify o
122
122
  | `update_hosting` | Change eligible delivery mode with owner token and hosting version |
123
123
  | `delete_resource` | Delete an HTML page, site, JSON document, KV store or file |
124
124
  | `set_visibility` | Hide or show a public publication in the gallery and sitemap; link unchanged |
125
+ | `set_title` | Rename an HTML page or site; the title stays on republish; link unchanged |
125
126
  | `bind_token` | Add an owner token to your profile (admin token) |
126
127
  | `mint_token` | Mint a profile token, shown once (admin token) |
127
128
  | `search_gallery` | Browse public content or list owned resources |
@@ -176,7 +177,7 @@ publish_html(content="<h1>Report</h1>...", format="HTML", ttlDays=15)
176
177
 
177
178
  Update an existing HTML or Markdown page in place, preserving its short URL. Requires the original `ownerToken` returned at creation.
178
179
 
179
- Parameters: `namespace` (string), `id` (string), `content` (string, the new body), `ownerToken` (string), `format` (optional HTML/Markdown alias or supported code language such as `json`, `yaml`, or `typescript`; omitted preserves the stored format), `tags` (optional; replace the stored tags, `[]` clears, omitted keeps them).
180
+ Parameters: `namespace` (string), `id` (string), `content` (string, the new body), `ownerToken` (string), `format` (optional HTML/Markdown alias or supported code language such as `json`, `yaml`, or `typescript`; omitted preserves the stored format, except that a whole HTML document makes a Markdown page an HTML page), `tags` (optional; replace the stored tags, `[]` clears, omitted keeps them).
180
181
 
181
182
  An update never changes the link or delivery mode; an advisory `deliveryModeReason` and `Delivery notice:` line are printed when the server returns them.
182
183
 
@@ -216,6 +217,8 @@ Upload a file to BrewPage by fetching it from a URL. Returns a public URL and ow
216
217
 
217
218
  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.
218
219
 
220
+ An HTML or Markdown file becomes a page in any namespace and the returned link opens the page (notice `html-file-as-page` or `markdown-file-as-page`). A known file name extension decides, then the source's Content-Type, then the content; `.txt` and anything unclear stay files. An HTML file over the page limit or in no readable encoding stays a file shown as source (notice `html-file-as-source`). A file read in a guessed encoding is saved as UTF-8 (notice `charset-guessed`).
221
+
219
222
  Example prompt:
220
223
 
221
224
  > "Upload this PNG and give me a shareable link."
@@ -408,6 +411,20 @@ Output: `LISTED: false · link unchanged`.
408
411
 
409
412
  ---
410
413
 
414
+ ### `set_title`
415
+
416
+ Rename an HTML page or site: the title shown in the gallery, profile, search and link cards (`POST /api/{html|sites}/{ns}/{id}/title`). The new title stays when you republish; the link stays the same. Works when the publication's owner token is in a BrewPage profile: sign in at https://account.brewpage.app, then `bind_token`; otherwise 403 with a hint.
417
+
418
+ Parameters: `type` (`html` | `site`), `namespace`, `id`, `title` (optional: 1-200 characters, one line; empty string restores the title from the content; omitted shows the current title), `ownerToken` (optional).
419
+
420
+ ```
421
+ set_title(type="html", namespace="public", id="aBcDeFgHiJ", title="Trip notes")
422
+ ```
423
+
424
+ Output: `TITLE: Trip notes · set by owner, kept on republish · link unchanged` (after a reset: `· from the content`).
425
+
426
+ ---
427
+
411
428
  ### `bind_token`
412
429
 
413
430
  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.
@@ -530,9 +547,17 @@ Every publish response includes an **owner token** -- the only credential that a
530
547
 
531
548
  ## Changelog
532
549
 
550
+ ## 1.17.0 -- 2026-11-09
551
+
552
+ - New tool `set_title`: rename an HTML page or site you own (owner token in a BrewPage profile); the title shows in the gallery, profile, search and link cards and stays when you republish. Empty title restores the one from the content. Twenty-one tools. `User-Agent: brewpage-mcp/1.17.0`.
553
+
554
+ ## 1.16.10 -- 2026-11-05
555
+
556
+ - `publish_html`, `publish_file` and `update_html` texts state the format order: `format`, known file name extension, then for files the source's Content-Type, then the content. `publish_file`: HTML and Markdown become pages in any namespace. `update_html`: a whole HTML document makes a Markdown page an HTML page. Contract 2.10.2. `User-Agent: brewpage-mcp/1.16.10`.
557
+
533
558
  ## 1.16.9 -- 2026-11-05
534
559
 
535
- - A publish result without an owner token (`result.extras.isOwner` false) prints a neutral line instead of naming another owner. Contract 2.9.6. `User-Agent: brewpage-mcp/1.16.9`.
560
+ - A publish result without an owner token (`result.extras.isOwner` false) prints a neutral line instead of naming another owner. Contract 2.9.6. `User-Agent: brewpage-mcp/1.16.10`.
536
561
 
537
562
  ## 1.16.8 -- 2026-11-02
538
563
 
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.16.9";
3
+ export declare const USER_AGENT = "brewpage-mcp/1.17.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.16.9";
4
+ export const USER_AGENT = "brewpage-mcp/1.17.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 };
@@ -22,6 +22,8 @@ const SECRETS = "Secrets allowed; likely ones are flagged in notices. Private na
22
22
  const MANAGE_URL_TEXT = "Returns manageUrl, a private management link (grants control; not for public sharing).";
23
23
  const OLD_READ_ONLY_CODE = "OLD_PUBLICATION_READ_ONLY";
24
24
  const WRONG_TYPE_CODE = "WRONG_RESOURCE_TYPE";
25
+ /** 403 message of a rename on a publication no profile owns. */
26
+ const RENAME_NEEDS_PROFILE = "Rename needs a profile";
25
27
  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.`;
26
28
  /** Notice codes the agent must relay to the user. */
27
29
  const WARNING_CODES = new Set(["secret-detected"]);
@@ -64,11 +66,14 @@ function retryAfterText(status, headers) {
64
66
  const PUBLISH_TOOL_FOR = {
65
67
  "update HTML": "publish_html", "republish site": "publish_site", "update JSON": "publish_json", "set KV": "publish_kv",
66
68
  };
67
- /** Owner-tool failure: server answer verbatim plus a hint for wrong-token 403, wrong-type or missing row, frozen old publication. */
69
+ /** Owner-tool failure: server answer verbatim plus a hint for wrong-token or no-profile 403, wrong type/missing row, frozen old row. */
68
70
  function ownerFailure(operation, status, data, fromEnv, headers) {
69
71
  const lines = [`Failed to ${operation} (${status}): ${JSON.stringify(data)}${retryAfterText(status, headers)}`];
70
- const code = data?.code;
71
- if (status === 403 && (code === undefined || code === "OWNER_AUTHORIZATION_FAILED")) {
72
+ const { code, message, result } = (data ?? {});
73
+ if (status === 403 && code === "OWNER_AUTHORIZATION_FAILED" && message === RENAME_NEEDS_PROFILE) {
74
+ lines.push("Rename needs a profile: sign in at https://account.brewpage.app, add this publication's owner token with bind_token, then retry.");
75
+ }
76
+ else if (status === 403 && (code === "OWNER_AUTHORIZATION_FAILED" || (code === undefined && result === undefined))) {
72
77
  lines.push(`Wrong owner token for this publication. Use ownerToken from create.${fromEnv ? ` Sent: ${OWNER_TOKEN_ENV}.` : ""}`);
73
78
  }
74
79
  const resourceUrl = data?.resourceUrl;
@@ -223,7 +228,7 @@ export function createServer(options = {}) {
223
228
  const apiFetch = options.fetch ?? fetch;
224
229
  const server = new McpServer({
225
230
  name: "brewpage-mcp",
226
- version: "1.16.9",
231
+ version: "1.17.0",
227
232
  });
228
233
  /** Caller argument first, then the configured default; `fromEnv` marks the configured one. */
229
234
  function ownerTokenFor(argument) {
@@ -299,7 +304,7 @@ export function createServer(options = {}) {
299
304
  server.registerTool("publish_html", {
300
305
  ...toolMeta("Publish HTML, Markdown, code or JSON page", ADDITIVE),
301
306
  description: "Publish HTML, Markdown, code or JSON to BrewPage (max 5 MiB). " + PUBLIC_HINT +
302
- "Format omitted -> server detects it. Returns its primary share link and owner token. Optional password, TTL, filename, top toolbar (showTopBar). " +
307
+ "Format order: `format`, then a known filename extension, else the server detects it. Returns its primary share link and owner token. Optional password, TTL, filename, top toolbar (showTopBar). " +
303
308
  "A `public` page without deliveryMode may get its own subdomain if it needs browser features a BrewPage link lacks; " +
304
309
  "the result then has deliveryModeReason and a delivery notice. Markdown raw HTML is allowlisted: scripts, styles, event handlers, forms, embeds removed. " +
305
310
  MANAGE_URL_TEXT + " " + SECRETS,
@@ -310,7 +315,7 @@ export function createServer(options = {}) {
310
315
  .string()
311
316
  .min(1)
312
317
  .optional()
313
- .describe("Omit: server detects html, markdown, code or JSON. `html` keeps code/JSON as HTML; also markdown/md or a code language."),
318
+ .describe("Omit: a known filename extension decides, else the server detects html, markdown, code or JSON. `html` keeps code/JSON as HTML; also markdown/md or a code language. A whole HTML document sent as markdown becomes an HTML page."),
314
319
  namespace: z
315
320
  .string()
316
321
  .optional()
@@ -324,7 +329,7 @@ export function createServer(options = {}) {
324
329
  .string()
325
330
  .max(200)
326
331
  .optional()
327
- .describe("Optional original filename (≤200 chars). Used as title fallback when no <title>/<h1>; immutable on update."),
332
+ .describe("Optional original filename (≤200 chars). Without `format`, a known extension sets the format (.md stays Markdown, even a whole HTML document). Used as title fallback when no <title>/<h1>; immutable on update."),
328
333
  showTopBar: z
329
334
  .boolean()
330
335
  .optional()
@@ -371,6 +376,8 @@ export function createServer(options = {}) {
371
376
  server.registerTool("publish_file", {
372
377
  ...toolMeta("Publish file from URL", ADDITIVE),
373
378
  description: "Upload a file from a URL. " + PUBLIC_HINT + "Max 5 MiB, video 20 MiB; no password or TTL. Returns its primary share link and owner token. " +
379
+ "HTML and Markdown files become pages in any namespace and the link opens the page: a known file name extension decides, then the source's Content-Type, then the content; `.txt` and anything unclear stay files. " +
380
+ "An HTML file over the page limit or in no readable encoding stays a file shown as source (notice html-file-as-source). " +
374
381
  MANAGE_URL_TEXT + " " + SECRETS,
375
382
  inputSchema: {
376
383
  url: z.string().url().describe("URL of the file to upload"),
@@ -379,7 +386,7 @@ export function createServer(options = {}) {
379
386
  .string()
380
387
  .optional()
381
388
  .describe(NAMESPACE_DESCRIPTION),
382
- filename: z.string().optional().describe("Custom filename (optional)"),
389
+ filename: z.string().optional().describe("Custom filename (optional); replaces the URL's name, so its extension decides whether the file becomes a page"),
383
390
  ownerToken: z.string().optional().describe(CREATE_OWNER_TOKEN_DESCRIPTION),
384
391
  },
385
392
  }, async ({ url: fileUrl, namespace, filename, deliveryMode, ownerToken }) => {
@@ -756,6 +763,7 @@ export function createServer(options = {}) {
756
763
  server.registerTool("update_html", {
757
764
  ...toolMeta("Update page", DESTRUCTIVE),
758
765
  description: "Update an existing BrewPage HTML, Markdown, code or JSON page. Requires owner token. Returns the updated page metadata. " +
766
+ "Format omitted keeps the stored format, except a whole HTML document makes a Markdown page an HTML page. " +
759
767
  "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 + " " + NO_PUBLIC_CREDENTIALS,
760
768
  inputSchema: {
761
769
  namespace: z.string().describe("Page namespace"),
@@ -765,7 +773,7 @@ export function createServer(options = {}) {
765
773
  format: z
766
774
  .string()
767
775
  .optional()
768
- .describe("Content format. Accepts (case-insensitive on html/markdown): 'html', 'markdown'/'md', or a code language — yaml, json, xml, csv, tsv, log, toml, ini, sql, sh, bat, env, javascript, typescript, tsx, jsx, css, properties, docker, txt. Omit to preserve the stored format."),
776
+ .describe("Content format. Accepts (case-insensitive on html/markdown): 'html', 'markdown'/'md', or a code language — yaml, json, xml, csv, tsv, log, toml, ini, sql, sh, bat, env, javascript, typescript, tsx, jsx, css, properties, docker, txt. Omit to preserve the stored format, except a whole HTML document makes a Markdown page an HTML page."),
769
777
  tags: z
770
778
  .array(z.string())
771
779
  .optional()
@@ -1168,6 +1176,30 @@ export function createServer(options = {}) {
1168
1176
  const result = data;
1169
1177
  return textResult([...credentialAlertLines(result), `LISTED: ${result.listed} · link unchanged`, ...noticeLines(result)].join("\n"));
1170
1178
  });
1179
+ server.registerTool("set_title", {
1180
+ ...toolMeta("Rename page or site", ADDITIVE),
1181
+ description: "Rename an HTML page or site: the title shown in the gallery, profile, search and link cards. " +
1182
+ "Works when the publication's owner token is in a BrewPage profile (free sign-in at https://account.brewpage.app). " +
1183
+ "The new title stays when you republish. Link unchanged.",
1184
+ inputSchema: {
1185
+ type: z.enum(["html", "site"]).describe("Publication type"),
1186
+ namespace: z.string().describe("Publication namespace"),
1187
+ id: z.string().describe("Publication ID"),
1188
+ title: z.string().optional().describe("New title, 1-200 characters, one line. Empty string: back to the title from the content. Omit: show the current title."),
1189
+ ownerToken: z.string().optional().describe(OWNER_TOKEN_DESCRIPTION),
1190
+ },
1191
+ }, async ({ type, namespace, id, title, ownerToken }) => {
1192
+ const auth = ownerTokenFor(ownerToken);
1193
+ if (!auth.token)
1194
+ return textResult(NO_OWNER_TOKEN, true);
1195
+ const path = apiPath(TYPE_SEGMENTS[type], namespace, id, "title");
1196
+ const { ok, status, data, headers: answerHeaders } = await apiRequest("POST", path, title === undefined ? {} : { title }, { "X-Owner-Token": auth.token });
1197
+ if (!ok)
1198
+ return ownerFailure("set title", status, data, auth.fromEnv, answerHeaders);
1199
+ const result = data;
1200
+ const source = result.custom === true ? "set by owner, kept on republish" : "from the content";
1201
+ return textResult([...credentialAlertLines(result), `TITLE: ${result.title} · ${source} · link unchanged`, ...noticeLines(result)].join("\n"));
1202
+ });
1171
1203
  server.registerTool("bind_token", {
1172
1204
  ...toolMeta("Add token to profile", ADDITIVE),
1173
1205
  description: "Add an owner token to your BrewPage profile (as publish). Needs an admin token of that profile.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brewpage-mcp",
3
- "version": "1.16.9",
3
+ "version": "1.17.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",