brewpage-mcp 1.20.0 → 1.21.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 +11 -5
- package/dist/http.js +30 -8
- package/dist/metrics.d.ts +24 -0
- package/dist/metrics.js +84 -0
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ Claude Code: `claude mcp add --transport http brewpage --scope user https://brew
|
|
|
38
38
|
For clients that only start local servers; the same tools as the server URL.
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
npx -y brewpage-mcp@1.
|
|
41
|
+
npx -y brewpage-mcp@1.21.0
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
Requires Node.js 20 or newer. This starts the stdio server for an MCP client.
|
|
@@ -63,7 +63,7 @@ Local fallback (stdio, needs Node.js; the same tools): add to `~/Library/Applica
|
|
|
63
63
|
"mcpServers": {
|
|
64
64
|
"brewpage": {
|
|
65
65
|
"command": "npx",
|
|
66
|
-
"args": ["-y", "brewpage-mcp@1.
|
|
66
|
+
"args": ["-y", "brewpage-mcp@1.21.0"]
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -86,7 +86,7 @@ Custom servers follow your ChatGPT plan and workspace settings.
|
|
|
86
86
|
The remote command above is the default. Local fallback: register a project-scoped stdio server:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
-
claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.
|
|
89
|
+
claude mcp add --transport stdio --scope project brewpage -- npx -y brewpage-mcp@1.21.0
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
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).
|
|
@@ -110,7 +110,7 @@ Local fallback (stdio, needs Node.js; the same tools): open **Settings > MCP** a
|
|
|
110
110
|
"mcpServers": {
|
|
111
111
|
"brewpage": {
|
|
112
112
|
"command": "npx",
|
|
113
|
-
"args": ["-y", "brewpage-mcp@1.
|
|
113
|
+
"args": ["-y", "brewpage-mcp@1.21.0"]
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -124,7 +124,7 @@ Open the Cline MCP settings panel and add:
|
|
|
124
124
|
{
|
|
125
125
|
"brewpage": {
|
|
126
126
|
"command": "npx",
|
|
127
|
-
"args": ["-y", "brewpage-mcp@1.
|
|
127
|
+
"args": ["-y", "brewpage-mcp@1.21.0"]
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
```
|
|
@@ -731,12 +731,14 @@ Every publish response includes an **owner token** -- the only credential that a
|
|
|
731
731
|
- Memory: run with `--max-old-space-size=192` and a 512 MiB container limit.
|
|
732
732
|
- Never reads `BREWPAGE_OWNER_TOKEN`; refuses to start when it is set. Pass `ownerToken` per call.
|
|
733
733
|
- One JSON log line per request on stdout (method, tool, status, outcome, ms); no arguments, tokens or IPs. `SIGTERM` drains for up to 30 s.
|
|
734
|
+
- `GET /metrics` (Prometheus text) only for peers in `BREWPAGE_METRICS_CIDRS`, else 404: `brewpage_mcp_requests_total{method,client}`, `brewpage_mcp_tool_calls_total{tool,client,outcome}` and the gauge `brewpage_mcp_clients_today{client}` (distinct caller addresses with a tool call since 00:00 UTC, kept only as hashes with a random salt drawn each day; resets on restart). `client` is `claude` (an address in `BREWPAGE_SHARED_EGRESS_CIDRS` or a `Claude...` User-Agent), `chatgpt` (an address in a published egress list or an `openai-mcp`/`ChatGPT` User-Agent), else `other`; `outcome` is `ok`, `tool_error`, `error` or `client_closed`. No IPs, arguments or content in any label.
|
|
734
735
|
|
|
735
736
|
| Variable | Default | Description |
|
|
736
737
|
|----------|---------|-------------|
|
|
737
738
|
| `BREWPAGE_TRUSTED_PROXY_CIDRS` | required | Comma list of reverse-proxy CIDRs or IPs |
|
|
738
739
|
| `BREWPAGE_SHARED_EGRESS_CIDRS` | empty | Comma list of platform egress CIDRs (one IP, many users): global limits only, no per-IP caps |
|
|
739
740
|
| `BREWPAGE_SHARED_EGRESS_LIST_URLS` | empty | Comma list of `https` URLs of published egress lists (`{"prefixes":[{"ipv4Prefix"}]}`, at least 100 prefixes, at most 1 MiB, no redirects); fetched at startup and daily, last good list kept |
|
|
741
|
+
| `BREWPAGE_METRICS_CIDRS` | empty | Comma list of CIDRs or IPs allowed to read `GET /metrics`; empty = `/metrics` answers 404 |
|
|
740
742
|
| `BREWPAGE_BACKEND_URL` | `http://brewpage-backend-mcp:8080` | API base URL (internal address) |
|
|
741
743
|
| `BREWPAGE_PUBLIC_ORIGIN` | `https://brewpage.app` | Origin for returned links and forwarded proto/host |
|
|
742
744
|
| `BREWPAGE_MCP_PORT` | `8787` | Listen port |
|
|
@@ -755,6 +757,10 @@ Every publish response includes an **owner token** -- the only credential that a
|
|
|
755
757
|
|
|
756
758
|
## Changelog
|
|
757
759
|
|
|
760
|
+
## 1.21.0 -- 2026-11-12
|
|
761
|
+
|
|
762
|
+
- Remote mode only: optional `GET /metrics` with usage counts for the operator (calls per tool, Claude, ChatGPT or other clients, distinct clients per day), served only to the addresses in `BREWPAGE_METRICS_CIDRS`. No addresses, arguments or content are kept or shown. Tools, inputs and stdio are unchanged: thirty-two tools, contract 2.27.0. `User-Agent: brewpage-mcp/1.21.0`.
|
|
763
|
+
|
|
758
764
|
## 1.20.0 -- 2026-11-12
|
|
759
765
|
|
|
760
766
|
- Two new tools. `make_private` turns a public publication into a private one: new private link, the public link stops working, views and stats stay; one-way. `set_badge` hides or shows the BrewPage badge on a publication of your profile, up to 5 hidden. `set_visibility` reads as list or unlist; inputs unchanged. The server instructions and the tools that send page code say that page code must not hide or alter the badge. Thirty-two tools. Contract 2.27.0. `User-Agent: brewpage-mcp/1.20.0`.
|
package/dist/http.js
CHANGED
|
@@ -8,6 +8,7 @@ import { pathToFileURL } from "node:url";
|
|
|
8
8
|
import { createMcpHandler, InMemoryTransport, isLegacyRequest, LATEST_PROTOCOL_VERSION } from "@modelcontextprotocol/server";
|
|
9
9
|
import { NodeStreamableHTTPServerTransport, toWebRequest } from "@modelcontextprotocol/node";
|
|
10
10
|
import { createServer, OWNER_TOKEN_ENV, USER_AGENT } from "./tools.js";
|
|
11
|
+
import { classifyClient, UsageMetrics } from "./metrics.js";
|
|
11
12
|
const MIB = 1024 * 1024;
|
|
12
13
|
/** Fits the largest site (25 MiB backend request: 20 MiB of inline text plus JSON escaping).
|
|
13
14
|
* Two 32 MiB sites + 8x1 MiB + 32 light per round peaked at 373 MiB (heap 192): run with mem_limit 512m. */
|
|
@@ -143,6 +144,8 @@ function loadConfig() {
|
|
|
143
144
|
/** Platform egress (one IP, many users): global pools only, no per-client caps or download budget. */
|
|
144
145
|
sharedEgress: parseCidrs("BREWPAGE_SHARED_EGRESS_CIDRS", false),
|
|
145
146
|
sharedEgressLists: parseListUrls("BREWPAGE_SHARED_EGRESS_LIST_URLS"),
|
|
147
|
+
/** Peers allowed to read GET /metrics (the Prometheus network); empty = /metrics answers 404. */
|
|
148
|
+
metricsPeers: parseCidrs("BREWPAGE_METRICS_CIDRS", false),
|
|
146
149
|
host: process.env.BREWPAGE_MCP_HOST || "0.0.0.0",
|
|
147
150
|
port,
|
|
148
151
|
};
|
|
@@ -457,7 +460,10 @@ async function main() {
|
|
|
457
460
|
const heavy = new Pool(MAX_HEAVY_IN_FLIGHT, MAX_HEAVY_PER_CLIENT);
|
|
458
461
|
let draining = false;
|
|
459
462
|
const listed = new Map();
|
|
460
|
-
|
|
463
|
+
const staticEgress = blockList(config.sharedEgress);
|
|
464
|
+
let listedEgress = blockList([]);
|
|
465
|
+
const metricsPeers = config.metricsPeers.length > 0 ? blockList(config.metricsPeers) : undefined;
|
|
466
|
+
const metrics = new UsageMetrics([...toolNames]);
|
|
461
467
|
/** Never throws: a failed fetch or parse keeps the last good set and logs a warning. */
|
|
462
468
|
async function loadEgressList(url) {
|
|
463
469
|
try {
|
|
@@ -469,7 +475,7 @@ async function main() {
|
|
|
469
475
|
}
|
|
470
476
|
const ranges = parseEgressList(await readCapped(res, SHARED_LIST_MAX_BYTES));
|
|
471
477
|
listed.set(url, ranges);
|
|
472
|
-
|
|
478
|
+
listedEgress = blockList([...listed.values()].flat());
|
|
473
479
|
log({ event: "shared_egress_list", url, ranges: ranges.length });
|
|
474
480
|
}
|
|
475
481
|
catch (error) {
|
|
@@ -479,7 +485,7 @@ async function main() {
|
|
|
479
485
|
const refreshEgressLists = () => config.sharedEgressLists.forEach((url) => void loadEgressList(url));
|
|
480
486
|
refreshEgressLists();
|
|
481
487
|
setInterval(refreshEgressLists, SHARED_LIST_REFRESH_MS).unref();
|
|
482
|
-
async function handleMcp(req, res, entry) {
|
|
488
|
+
async function handleMcp(req, res, entry, usage) {
|
|
483
489
|
if (req.headers.origin !== undefined)
|
|
484
490
|
return rpcError(res, 403, SERVER_ERROR, "Origin not allowed.");
|
|
485
491
|
const peer = peerAddress(req);
|
|
@@ -502,7 +508,9 @@ async function main() {
|
|
|
502
508
|
rpcError(res, 503, SERVER_ERROR, "Busy, retry shortly.", { "Retry-After": "1" });
|
|
503
509
|
};
|
|
504
510
|
const known = Number.isFinite(declared);
|
|
505
|
-
const
|
|
511
|
+
const family = isIP(client) === 6 ? "ipv6" : "ipv4";
|
|
512
|
+
const egress = staticEgress.check(client, family) ? "static" : listedEgress.check(client, family) ? "listed" : undefined;
|
|
513
|
+
const shared = egress !== undefined;
|
|
506
514
|
const capKey = shared ? undefined : client;
|
|
507
515
|
// Registered first: a client that leaves at any point, even while queued, aborts the call.
|
|
508
516
|
const abort = new AbortController();
|
|
@@ -562,6 +570,8 @@ async function main() {
|
|
|
562
570
|
const toolName = rpc.method === "tools/call" ? rpc.params?.name : undefined;
|
|
563
571
|
entry.method = typeof rpc.method === "string" ? rpc.method.slice(0, MAX_LOGGED_METHOD) : null;
|
|
564
572
|
entry.tool = rpc.method === "tools/call" ? (typeof toolName === "string" && toolNames.has(toolName) ? toolName : "unknown") : null;
|
|
573
|
+
usage.client = classifyClient(egress, (req.headers["user-agent"] ?? "").slice(0, MAX_LOGGED_METHOD));
|
|
574
|
+
usage.address = client;
|
|
565
575
|
// Stateless: no server-push streams, as the 2025 GET stream (405); a refused auto-open never fails a connect.
|
|
566
576
|
if (rpc.method === "subscriptions/listen") {
|
|
567
577
|
const id = message.id;
|
|
@@ -634,18 +644,24 @@ async function main() {
|
|
|
634
644
|
slot?.give(capKey);
|
|
635
645
|
}
|
|
636
646
|
}
|
|
637
|
-
function handle(req, res, entry) {
|
|
647
|
+
function handle(req, res, entry, usage) {
|
|
638
648
|
const path = (req.url ?? "/").split("?")[0];
|
|
639
649
|
if (path === "/healthz") {
|
|
640
650
|
res.writeHead(draining ? 503 : 200, { "Content-Type": "application/json" });
|
|
641
651
|
res.end(JSON.stringify({ status: draining ? "draining" : "ok", version: VERSION }));
|
|
642
652
|
return Promise.resolve();
|
|
643
653
|
}
|
|
654
|
+
const peer = peerAddress(req);
|
|
655
|
+
if (path === "/metrics" && req.method === "GET" && isIP(peer) !== 0 && metricsPeers?.check(peer, isIP(peer) === 6 ? "ipv6" : "ipv4")) {
|
|
656
|
+
res.writeHead(200, { "Content-Type": "text/plain; version=0.0.4; charset=utf-8" });
|
|
657
|
+
res.end(metrics.render());
|
|
658
|
+
return Promise.resolve();
|
|
659
|
+
}
|
|
644
660
|
if (path !== "/mcp") {
|
|
645
661
|
rpcError(res, 404, SERVER_ERROR, "Not found.");
|
|
646
662
|
return Promise.resolve();
|
|
647
663
|
}
|
|
648
|
-
return handleMcp(req, res, entry).catch((error) => {
|
|
664
|
+
return handleMcp(req, res, entry, usage).catch((error) => {
|
|
649
665
|
entry.outcome = "exception";
|
|
650
666
|
log({ event: "request_failed", message: error instanceof Error ? error.message : String(error) });
|
|
651
667
|
if (!res.headersSent)
|
|
@@ -657,11 +673,12 @@ async function main() {
|
|
|
657
673
|
const httpServer = createHttpServer((req, res) => {
|
|
658
674
|
const started = performance.now();
|
|
659
675
|
const entry = { http: req.method ?? "", method: null, tool: null, status: 0, outcome: null, ms: 0 };
|
|
660
|
-
const
|
|
676
|
+
const usage = {};
|
|
677
|
+
const logged = !(req.url ?? "").startsWith("/healthz") && !(req.url ?? "").startsWith("/metrics");
|
|
661
678
|
const closedEarly = new Promise((resolve) => res.once("close", () => resolve(!res.writableFinished)));
|
|
662
679
|
let done;
|
|
663
680
|
try {
|
|
664
|
-
done = handle(req, res, entry);
|
|
681
|
+
done = handle(req, res, entry, usage);
|
|
665
682
|
}
|
|
666
683
|
catch (error) {
|
|
667
684
|
entry.outcome = "exception";
|
|
@@ -681,6 +698,11 @@ async function main() {
|
|
|
681
698
|
entry.outcome ??= res.statusCode < 400 ? "ok" : `http_${res.statusCode}`;
|
|
682
699
|
entry.ms = Math.round(performance.now() - started);
|
|
683
700
|
log(entry);
|
|
701
|
+
if (usage.client === undefined || typeof entry.method !== "string")
|
|
702
|
+
return;
|
|
703
|
+
metrics.request(entry.method, usage.client);
|
|
704
|
+
if (typeof entry.tool === "string")
|
|
705
|
+
metrics.toolCall(entry.tool, usage.client, String(entry.outcome), usage.address);
|
|
684
706
|
});
|
|
685
707
|
});
|
|
686
708
|
// Whole request incl. queue wait: 30 s queue + 2 s grace + 32 MiB at the body floor (~131 s) + margin.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Client class: Claude (Anthropic egress or Claude UA), ChatGPT (OpenAI egress or UA), else other. */
|
|
2
|
+
export type ClientClass = "claude" | "chatgpt" | "other";
|
|
3
|
+
/** Egress source of the caller's IP: static shared CIDRs = Anthropic, published lists = OpenAI (deploy contract). */
|
|
4
|
+
export type Egress = "static" | "listed" | undefined;
|
|
5
|
+
export declare function classifyClient(egress: Egress, userAgent: string): ClientClass;
|
|
6
|
+
/** Bounded outcome label from the request log outcome. */
|
|
7
|
+
export declare function outcomeClass(outcome: string | number | null | undefined): string;
|
|
8
|
+
/** In-memory MCP usage counters in Prometheus text format; labels are closed sets, never IPs or arguments. */
|
|
9
|
+
export declare class UsageMetrics {
|
|
10
|
+
private readonly tools;
|
|
11
|
+
private readonly now;
|
|
12
|
+
private readonly requests;
|
|
13
|
+
private readonly toolCalls;
|
|
14
|
+
private day;
|
|
15
|
+
private salt;
|
|
16
|
+
private readonly daily;
|
|
17
|
+
constructor(tools: readonly string[], now?: () => Date);
|
|
18
|
+
request(method: string, client: ClientClass): void;
|
|
19
|
+
/** One tools/call; `address` only feeds the daily distinct set as a keyed hash. */
|
|
20
|
+
toolCall(tool: string, client: ClientClass, outcome: string, address: string): void;
|
|
21
|
+
render(): string;
|
|
22
|
+
/** New UTC day: forget every hash and draw a new random salt, so days cannot be linked. */
|
|
23
|
+
private roll;
|
|
24
|
+
}
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createHmac, randomBytes } from "node:crypto";
|
|
2
|
+
const CLIENTS = ["claude", "chatgpt", "other"];
|
|
3
|
+
const METHODS = ["initialize", "notifications/initialized", "tools/list", "tools/call", "ping", "server/discover", "other"];
|
|
4
|
+
const OUTCOMES = ["ok", "tool_error", "error", "client_closed"];
|
|
5
|
+
/** Distinct-client memory bound (~100 bytes each); past it the day's gauge stops growing. */
|
|
6
|
+
const MAX_DAILY_CLIENTS = 100_000;
|
|
7
|
+
export function classifyClient(egress, userAgent) {
|
|
8
|
+
if (egress === "static" || /^claude/i.test(userAgent))
|
|
9
|
+
return "claude";
|
|
10
|
+
if (egress === "listed" || /^openai-mcp|chatgpt/i.test(userAgent))
|
|
11
|
+
return "chatgpt";
|
|
12
|
+
return "other";
|
|
13
|
+
}
|
|
14
|
+
/** Bounded outcome label from the request log outcome. */
|
|
15
|
+
export function outcomeClass(outcome) {
|
|
16
|
+
return outcome === "ok" || outcome === "tool_error" || outcome === "client_closed" ? outcome : "error";
|
|
17
|
+
}
|
|
18
|
+
/** In-memory MCP usage counters in Prometheus text format; labels are closed sets, never IPs or arguments. */
|
|
19
|
+
export class UsageMetrics {
|
|
20
|
+
tools;
|
|
21
|
+
now;
|
|
22
|
+
requests = new Map();
|
|
23
|
+
toolCalls = new Map();
|
|
24
|
+
day = "";
|
|
25
|
+
salt = Buffer.alloc(0);
|
|
26
|
+
daily = new Map();
|
|
27
|
+
constructor(tools, now = () => new Date()) {
|
|
28
|
+
this.tools = tools;
|
|
29
|
+
this.now = now;
|
|
30
|
+
// Every series born at 0, so increase() sees the first call after a restart.
|
|
31
|
+
for (const client of CLIENTS) {
|
|
32
|
+
for (const method of METHODS)
|
|
33
|
+
this.requests.set(`${method}\t${client}`, 0);
|
|
34
|
+
for (const tool of [...tools, "unknown"])
|
|
35
|
+
for (const outcome of OUTCOMES)
|
|
36
|
+
this.toolCalls.set(`${tool}\t${client}\t${outcome}`, 0);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
request(method, client) {
|
|
40
|
+
const key = `${METHODS.includes(method) ? method : "other"}\t${client}`;
|
|
41
|
+
this.requests.set(key, (this.requests.get(key) ?? 0) + 1);
|
|
42
|
+
}
|
|
43
|
+
/** One tools/call; `address` only feeds the daily distinct set as a keyed hash. */
|
|
44
|
+
toolCall(tool, client, outcome, address) {
|
|
45
|
+
const key = `${this.tools.includes(tool) ? tool : "unknown"}\t${client}\t${outcomeClass(outcome)}`;
|
|
46
|
+
this.toolCalls.set(key, (this.toolCalls.get(key) ?? 0) + 1);
|
|
47
|
+
this.roll();
|
|
48
|
+
const seen = this.daily.get(client);
|
|
49
|
+
if ([...this.daily.values()].reduce((sum, set) => sum + set.size, 0) >= MAX_DAILY_CLIENTS)
|
|
50
|
+
return;
|
|
51
|
+
seen.add(createHmac("sha256", this.salt).update(address).digest("base64").slice(0, 22));
|
|
52
|
+
}
|
|
53
|
+
render() {
|
|
54
|
+
this.roll();
|
|
55
|
+
const lines = [
|
|
56
|
+
"# HELP brewpage_mcp_requests_total Remote MCP JSON-RPC requests by method and client class.",
|
|
57
|
+
"# TYPE brewpage_mcp_requests_total counter",
|
|
58
|
+
...[...this.requests].map(([key, value]) => {
|
|
59
|
+
const [method, client] = key.split("\t");
|
|
60
|
+
return `brewpage_mcp_requests_total{method="${method}",client="${client}"} ${value}`;
|
|
61
|
+
}),
|
|
62
|
+
"# HELP brewpage_mcp_tool_calls_total Remote MCP tools/call by tool, client class and outcome.",
|
|
63
|
+
"# TYPE brewpage_mcp_tool_calls_total counter",
|
|
64
|
+
...[...this.toolCalls].map(([key, value]) => {
|
|
65
|
+
const [tool, client, outcome] = key.split("\t");
|
|
66
|
+
return `brewpage_mcp_tool_calls_total{tool="${tool}",client="${client}",outcome="${outcome}"} ${value}`;
|
|
67
|
+
}),
|
|
68
|
+
"# HELP brewpage_mcp_clients_today Distinct client addresses with a tools/call since 00:00 UTC (since start if later).",
|
|
69
|
+
"# TYPE brewpage_mcp_clients_today gauge",
|
|
70
|
+
...CLIENTS.map((client) => `brewpage_mcp_clients_today{client="${client}"} ${this.daily.get(client).size}`),
|
|
71
|
+
];
|
|
72
|
+
return `${lines.join("\n")}\n`;
|
|
73
|
+
}
|
|
74
|
+
/** New UTC day: forget every hash and draw a new random salt, so days cannot be linked. */
|
|
75
|
+
roll() {
|
|
76
|
+
const day = this.now().toISOString().slice(0, 10);
|
|
77
|
+
if (day === this.day)
|
|
78
|
+
return;
|
|
79
|
+
this.day = day;
|
|
80
|
+
this.salt = randomBytes(32);
|
|
81
|
+
for (const client of CLIENTS)
|
|
82
|
+
this.daily.set(client, new Set());
|
|
83
|
+
}
|
|
84
|
+
}
|
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.
|
|
3
|
+
export declare const USER_AGENT = "brewpage-mcp/1.21.0";
|
|
4
4
|
export declare const OWNER_TOKEN_ENV = "BREWPAGE_OWNER_TOKEN";
|
|
5
5
|
/** Server instructions of the handshake: one paragraph per line; the first stands alone within 512 chars. */
|
|
6
6
|
export declare const SERVER_INSTRUCTIONS: string;
|
package/dist/tools.js
CHANGED
|
@@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/server";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { PAGE_DATA_CALLS, PAGE_DATA_GUIDE } from "./guides/page-data.js";
|
|
4
4
|
export const DEFAULT_BASE_URL = "https://brewpage.app";
|
|
5
|
-
export const USER_AGENT = "brewpage-mcp/1.
|
|
5
|
+
export const USER_AGENT = "brewpage-mcp/1.21.0";
|
|
6
6
|
export const OWNER_TOKEN_ENV = "BREWPAGE_OWNER_TOKEN";
|
|
7
7
|
/** Server instructions of the handshake: one paragraph per line; the first stands alone within 512 chars. */
|
|
8
8
|
export const SERVER_INSTRUCTIONS = [
|
|
@@ -518,7 +518,7 @@ export function createServer(options = {}) {
|
|
|
518
518
|
const apiFetch = options.fetch ?? fetch;
|
|
519
519
|
const server = new McpServer({
|
|
520
520
|
name: "brewpage-mcp",
|
|
521
|
-
version: "1.
|
|
521
|
+
version: "1.21.0",
|
|
522
522
|
}, { instructions: SERVER_INSTRUCTIONS });
|
|
523
523
|
/** Caller argument first, then the configured default; `fromEnv` marks the configured one. */
|
|
524
524
|
function ownerTokenFor(argument) {
|
package/package.json
CHANGED