calibreweb-mcp 0.1.3 → 0.3.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
@@ -2,23 +2,31 @@
2
2
 
3
3
  [![CI](https://img.shields.io/github/actions/workflow/status/ni-c/calibreweb-mcp/ci.yml?branch=main&label=CI)](https://github.com/ni-c/calibreweb-mcp/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/calibreweb-mcp)](https://www.npmjs.com/package/calibreweb-mcp)
5
+ [![npm downloads](https://img.shields.io/npm/dm/calibreweb-mcp)](https://www.npmjs.com/package/calibreweb-mcp)
5
6
  [![node](https://img.shields.io/node/v/calibreweb-mcp)](https://nodejs.org)
6
7
  [![license](https://img.shields.io/npm/l/calibreweb-mcp)](LICENSE)
7
- [![container](https://img.shields.io/badge/ghcr.io-ni--c%2Fcalibreweb--mcp-2496ed?logo=docker&logoColor=white)](https://github.com/ni-c/calibreweb-mcp/pkgs/container/calibreweb-mcp)
8
- [![docs](https://img.shields.io/badge/docs-calibreweb--mcp.ni--c.de-4f46e5)](https://calibreweb-mcp.ni-c.de)
8
+ [![container](https://img.shields.io/badge/ghcr.io-ni--c%2Fcalibreweb--mcp-blue)](https://github.com/ni-c/calibreweb-mcp/pkgs/container/calibreweb-mcp)
9
+ [![docs](https://img.shields.io/badge/docs-calibreweb--mcp.ni--c.de-informational)](https://calibreweb-mcp.ni-c.de)
10
+ [![HTTP • via mcp-hub](https://img.shields.io/badge/HTTP-via%20mcp--hub-6f42c1)](https://mcp-hub.ni-c.de)
9
11
  [![sponsor](https://img.shields.io/badge/sponsor-ni--c-ea4aaa?logo=githubsponsors&logoColor=white)](https://github.com/sponsors/ni-c)
10
12
 
11
- A read-only [Model Context Protocol](https://modelcontextprotocol.io) server for
13
+ A read-only [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for
12
14
  [Calibre-Web](https://github.com/janeczku/calibre-web) (and
13
15
  [Calibre-Web Automated](https://github.com/crocodilestick/Calibre-Web-Automated)),
14
16
  the self-hosted ebook library web UI.
15
17
 
16
- Calibre-Web has no REST API its only stable machine-readable interface is the
17
- OPDS catalog feed it serves for e-reader apps. This server speaks that feed:
18
- Atom XML with HTTP Basic auth in, structured book data out. Search the library,
19
- browse the curated views and shelves, follow per-format download links, and pull
20
- cover images straight into the conversation. It never writes anything: every
21
- tool is a GET against the OPDS routes.
18
+ Lets MCP clients like Claude Code, Claude Desktop or Codex search your library,
19
+ browse the curated views and shelves, follow per-format download links and pull cover
20
+ images straight into the conversation. It never writes anything: every tool is a GET.
21
+
22
+ Six tools is the ceiling, not the floor: `CALIBRE_WEB_ALLOW_TOOLS=essential`
23
+ registers a curated five instead, and a model picks the right tool far more
24
+ reliably from five than from six — see
25
+ [choosing which tools load](#choosing-which-tools-load).
26
+
27
+ Calibre-Web has no REST API — its only stable machine-readable interface is the OPDS
28
+ catalog feed it serves for e-reader apps. This server speaks that feed: Atom XML with
29
+ HTTP Basic auth in, structured book data out.
22
30
 
23
31
  <!-- <picture> is resolved against the colour scheme of the page showing it, so GitHub
24
32
  picks the variant that matches its own theme toggle. npm strips <picture> and
@@ -35,7 +43,17 @@ tool is a GET against the OPDS routes.
35
43
 
36
44
  ![Demo: listing the tools, searching the library and reading the stats through the MCP Inspector CLI](https://calibreweb-mcp.ni-c.de/demo.gif)
37
45
 
38
- **📖 Full documentation: [calibreweb-mcp.ni-c.de](https://calibreweb-mcp.ni-c.de)**
46
+ ## What makes it different
47
+
48
+ **The API Calibre-Web never had.** Calibre-Web exposes no REST API — its only
49
+ stable machine interface is the OPDS Atom feed built for e-reader apps. These
50
+ tools parse that feed into structured book data with numeric ids, per-format
51
+ download URLs and bounded summaries.
52
+
53
+ **Read-only by construction.** All six tools are GETs. Redirects are refused so
54
+ Basic credentials never travel, XML carrying a DOCTYPE is rejected outright,
55
+ hrefs are locked to the configured origin, and metadata is marked as the
56
+ untrusted data it is.
39
57
 
40
58
  ## Requirements
41
59
 
@@ -55,10 +73,35 @@ tool is a GET against the OPDS routes.
55
73
  | `CALIBRE_WEB_USERNAME` | yes¹ | Username of the Calibre-Web account. |
56
74
  | `CALIBRE_WEB_PASSWORD` | yes¹ | Password of that account (the web login password). |
57
75
  | `CALIBRE_WEB_INSECURE_TLS` | no | `true` to accept self-signed certificates — scoped to the configured host only. |
76
+ | `CALIBRE_WEB_ALLOW_TOOLS` | no | Comma-separated tool names, `list_*` prefixes, or `essential` for a curated preset |
77
+ | `CALIBRE_WEB_DENY_TOOLS` | no | Same syntax; removed from whatever `CALIBRE_WEB_ALLOW_TOOLS` left |
58
78
 
59
79
  ¹ Leave **both** unset for an instance that allows anonymous browsing; setting
60
80
  only one of them is a configuration error.
61
81
 
82
+ ### Choosing which tools load
83
+
84
+ `CALIBRE_WEB_ALLOW_TOOLS` and `CALIBRE_WEB_DENY_TOOLS` take comma-separated tool names;
85
+ a trailing `*` matches a whole family. `essential` is a curated preset of
86
+ five: `search_books`, `list_books`, `list_shelves`, `get_shelf_books`, `get_stats`.
87
+
88
+ ```sh
89
+ CALIBRE_WEB_ALLOW_TOOLS=essential
90
+ CALIBRE_WEB_ALLOW_TOOLS=search_books,list_shelves
91
+ CALIBRE_WEB_DENY_TOOLS=get_cover
92
+ ```
93
+
94
+ An entry that matches no tool aborts startup and names it, so a typo cannot
95
+ silently hide a tool — an absent tool is not something anyone traces back to an
96
+ environment variable. A filtered tool is never registered, so it is absent from
97
+ `tools/list` and unknown to `tools/call` alike.
98
+
99
+ If you run several of these servers at once, [mcp-hub](https://mcp-hub.ni-c.de)
100
+ is the other answer — its `/hub` endpoint replaces every server's tools with six
101
+ meta-tools.
102
+
103
+ ## Installation
104
+
62
105
  ### Claude Code
63
106
 
64
107
  ```sh
@@ -96,6 +139,46 @@ args = ["calibreweb-mcp"]
96
139
  env = { CALIBRE_WEB_URL = "https://books.example.com", CALIBRE_WEB_USERNAME = "reader", CALIBRE_WEB_PASSWORD = "..." }
97
140
  ```
98
141
 
142
+ ### Docker
143
+
144
+ ```sh
145
+ docker run -i --rm \
146
+ -e CALIBRE_WEB_URL=https://books.example.com \
147
+ -e CALIBRE_WEB_USERNAME=reader \
148
+ -e CALIBRE_WEB_PASSWORD=... \
149
+ ghcr.io/ni-c/calibreweb-mcp
150
+ ```
151
+
152
+ ### Through mcp-hub
153
+
154
+ A client that cannot spawn a local process — ChatGPT connectors, Claude on the web,
155
+ Cursor, LibreChat — reaches calibreweb-mcp through [mcp-hub](https://mcp-hub.ni-c.de): one
156
+ container serves many stdio MCP servers over Streamable HTTP, with an OAuth 2.1 login
157
+ behind a single password and long-lived tokens for the clients that cannot do OAuth. Its
158
+ `/hub` endpoint puts every server behind six meta-tools, so one connector reaches all of
159
+ them without N×tool schemas in the model's context, and it speaks both protocol revisions
160
+ — a question this server asks travels through it to the person at the far end.
161
+
162
+ Its `/config/mcp.json` uses Claude Code's format, so the entry is the one you already
163
+ have:
164
+
165
+ ```json
166
+ {
167
+ "mcpServers": {
168
+ "calibreweb": {
169
+ "command": "npx",
170
+ "args": ["-y", "calibreweb-mcp"],
171
+ "env": { "CALIBRE_WEB_ALLOW_TOOLS": "essential" },
172
+ "denyTools": ["get_cover"]
173
+ }
174
+ }
175
+ }
176
+ ```
177
+
178
+ `allowTools` and `denyTools` there are the hub's **own** per-server filter, which is not
179
+ the same thing as `*_ALLOW_TOOLS` in `env` — the difference, and the mistake it invites,
180
+ are in the [client guide](https://calibreweb-mcp.ni-c.de/guide/clients#through-mcp-hub).
181
+
99
182
  ## Tools
100
183
 
101
184
  All tools are read-only (`readOnlyHint: true`).
@@ -113,6 +196,34 @@ Book entries include authors, tags, series (with index), rating, a bounded
113
196
  summary, a cover URL and per-format download URLs — ready-made links a human can
114
197
  open, since the model itself has no reason to download an EPUB.
115
198
 
199
+ ### Structured output
200
+
201
+ Every tool declares an `outputSchema` and answers with `structuredContent`
202
+ alongside the text block, so a client can use the result without parsing prose:
203
+
204
+ ```jsonc
205
+ {
206
+ "untrusted": true,
207
+ "source": "calibre-web",
208
+ "totalFound": 2,
209
+ "truncated": false,
210
+ "books": [{ "id": 7, "title": "Dune", "authors": ["Frank Herbert"] }],
211
+ "notes": ["Book titles, authors, tags, series and summaries come from …"],
212
+ }
213
+ ```
214
+
215
+ The `untrusted` marker is a field and not only a line in `notes`, because a
216
+ client that reads the structured half would otherwise have to find the warning
217
+ in a list of sentences. The two tools without it are `get_stats`, which is four
218
+ counters this server has checked are numbers, and `get_cover`, which reports an
219
+ id, a media type from a four-entry allowlist and a byte count — the image itself
220
+ stays in the content block where a client renders it.
221
+
222
+ An over-budget result drops book summaries as before. Where that is still not
223
+ enough it is now an **error** rather than JSON cut at the ceiling: unparseable
224
+ text was tolerable in a text block and is not something `structuredContent` can
225
+ carry, and the two channels have to hold the same value.
226
+
116
227
  ### Pagination
117
228
 
118
229
  Feeds are paginated by the instance's _books per page_ setting (default 60); the
@@ -120,7 +231,7 @@ page size is not client-controllable. Every listing returns
120
231
  `pagination.nextOffset` when more pages exist — pass it as `offset` in the next
121
232
  call. The `discover` view is a random selection and not paginated.
122
233
 
123
- ### Deliberately out of scope
234
+ ## Not exposed, on purpose
124
235
 
125
236
  - **No writes.** The OPDS feed has none, and this server would not add any.
126
237
  - **No file downloads.** Tools return download URLs, not ebook payloads.
@@ -144,15 +255,10 @@ call. The `discover` view is a random selection and not paginated.
144
255
  - The password is scrubbed from the process environment at startup, and URLs
145
256
  are credential-redacted before they appear in any log or result.
146
257
 
147
- ## Container
258
+ ## Documentation
148
259
 
149
- ```sh
150
- docker run -i --rm \
151
- -e CALIBRE_WEB_URL=https://books.example.com \
152
- -e CALIBRE_WEB_USERNAME=reader \
153
- -e CALIBRE_WEB_PASSWORD=... \
154
- ghcr.io/ni-c/calibreweb-mcp
155
- ```
260
+ The full guide, tool reference and security notes live at
261
+ **[calibreweb-mcp.ni-c.de](https://calibreweb-mcp.ni-c.de)** (source in [`docs/`](docs/)).
156
262
 
157
263
  ## Development
158
264
 
@@ -171,6 +277,13 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
171
277
  2. `npm run lint && npm run test:coverage && npm run build`
172
278
  3. Tag the release: `git tag -s vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Z`
173
279
 
280
+ ## Contributing
281
+
282
+ Issues, discussions and pull requests are welcome — see
283
+ [CONTRIBUTING.md](CONTRIBUTING.md). For vulnerabilities please use
284
+ [private reporting](https://github.com/ni-c/calibreweb-mcp/security/advisories/new)
285
+ rather than a public issue; the policy is in [SECURITY.md](SECURITY.md).
286
+
174
287
  ## License
175
288
 
176
- [MIT](LICENSE)
289
+ [MIT](LICENSE) © Willi Thiel
package/dist/api.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/config.d.ts CHANGED
@@ -9,7 +9,15 @@ export interface Config {
9
9
  username: string | undefined;
10
10
  /** The normal web-login password of the Calibre-Web user (OPDS uses HTTP Basic auth). */
11
11
  password: string | undefined;
12
- insecureTls: boolean;
12
+ insecureTls: boolean; /**
13
+ * Raw value of `CALIBRE_WEB_ALLOW_TOOLS` — comma-separated tool names, `list_*`
14
+ * prefixes, or `essential`. Kept unparsed on purpose: this file is a mirror of
15
+ * the environment, and the names can only be checked against the tool
16
+ * catalogue, which `buildToolFilter` does.
17
+ */
18
+ allowTools: string | undefined;
19
+ /** Raw value of `CALIBRE_WEB_DENY_TOOLS`, same shape, subtracted from the above. */
20
+ denyTools: string | undefined;
13
21
  }
14
22
  /** Shown when the configuration is incomplete — at startup and on every API call. */
15
23
  export declare function missingConfigMessage(missing: string[]): string;
package/dist/config.js CHANGED
@@ -1,4 +1,4 @@
1
- import { internalHostKind } from './hosts.js';
1
+ import { internalHostKind } from 'mcp-internal-hosts';
2
2
  import { redactUrlCredentials } from './redact.js';
3
3
  /** Shown when the configuration is incomplete — at startup and on every API call. */
4
4
  export function missingConfigMessage(missing) {
@@ -39,6 +39,8 @@ export function loadConfig(env = process.env) {
39
39
  const username = env.CALIBRE_WEB_USERNAME;
40
40
  const password = env.CALIBRE_WEB_PASSWORD;
41
41
  const insecureTls = env.CALIBRE_WEB_INSECURE_TLS === 'true';
42
+ const allowTools = env.CALIBRE_WEB_ALLOW_TOOLS;
43
+ const denyTools = env.CALIBRE_WEB_DENY_TOOLS;
42
44
  // Don't keep the password in process.env for the process lifetime: it would be
43
45
  // inherited by child processes and show up in env dumps. (The kernel's
44
46
  // /proc/<pid>/environ snapshot is NOT rewritten by this — it always keeps the
@@ -47,7 +49,14 @@ export function loadConfig(env = process.env) {
47
49
  delete env.CALIBRE_WEB_PASSWORD;
48
50
  if (!url) {
49
51
  console.error(`calibreweb-mcp: ${missingConfigMessage(['CALIBRE_WEB_URL'])}`);
50
- return { url: undefined, username, password, insecureTls };
52
+ return {
53
+ url: undefined,
54
+ username,
55
+ password,
56
+ insecureTls,
57
+ allowTools,
58
+ denyTools,
59
+ };
51
60
  }
52
61
  if (!username !== !password) {
53
62
  console.error('calibreweb-mcp: CALIBRE_WEB_USERNAME and CALIBRE_WEB_PASSWORD must be ' +
@@ -84,6 +93,8 @@ export function loadConfig(env = process.env) {
84
93
  username,
85
94
  password,
86
95
  insecureTls,
96
+ allowTools,
97
+ denyTools,
87
98
  };
88
99
  }
89
100
  function isLoopbackHost(hostname) {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,19 +1,52 @@
1
1
  #!/usr/bin/env node
2
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
+ import { serveStdio } from '@modelcontextprotocol/server/stdio';
3
3
  import { loadConfig } from './config.js';
4
4
  import { createServer } from './server.js';
5
+ import { ToolFilterError } from 'mcp-tool-allowlist';
5
6
  async function main() {
6
7
  const config = loadConfig();
7
8
  if (config.insecureTls) {
8
9
  console.error('calibreweb-mcp: CALIBRE_WEB_INSECURE_TLS=true — TLS certificate validation is disabled for the Calibre-Web connection');
9
10
  }
10
- const server = createServer(config);
11
+ // Built before anything is served, so a rejected tool filter still ends
12
+ // the process rather than surfacing as a failed handshake once a client
13
+ // has already connected.
14
+ let pending;
15
+ try {
16
+ pending = createServer(config);
17
+ }
18
+ catch (error) {
19
+ // A bad tool list is operator feedback, not a crash: print the
20
+ // sentence on its own rather than behind "fatal error:".
21
+ if (error instanceof ToolFilterError) {
22
+ console.error(`calibreweb-mcp: ${error.message}`);
23
+ process.exit(1);
24
+ }
25
+ throw error;
26
+ }
11
27
  // stdout belongs to the protocol; everything human-readable goes to stderr.
12
- await server.connect(new StdioServerTransport());
28
+ // `serveStdio` owns the era decision for the connection: the opening
29
+ // exchange selects 2025-11-25 or 2026-07-28 and pins one instance from
30
+ // this factory for its lifetime. A hand-wired `StdioServerTransport`
31
+ // serves only the 2025 era, which is why a negotiating client’s
32
+ // `server/discover` probe was answered with "Method not found".
33
+ //
34
+ // The instance built above serves the first connection; a second call — a
35
+ // modern probe followed by the real connection — builds a fresh one, which
36
+ // is safe because `createServer` only registers tools.
37
+ serveStdio(() => {
38
+ const server = pending ?? createServer(config);
39
+ pending = undefined;
40
+ return server;
41
+ });
13
42
  console.error(config.url
14
43
  ? `calibreweb-mcp: connected, targeting ${config.url}`
15
44
  : 'calibreweb-mcp: connected without configuration — tools are listed but every call will fail');
16
45
  }
46
+ // In a container node runs as PID 1 with no default signal disposition, so
47
+ // without this handler `docker stop` waits out the grace period and SIGKILLs.
48
+ process.on('SIGTERM', () => process.exit(0));
49
+ process.on('SIGINT', () => process.exit(0));
17
50
  main().catch((error) => {
18
51
  console.error('calibreweb-mcp: fatal error:', error);
19
52
  process.exit(1);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CACX,uHAAuH,CACxH,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,4EAA4E;IAC5E,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;IACjD,OAAO,CAAC,KAAK,CACX,MAAM,CAAC,GAAG;QACR,CAAC,CAAC,wCAAwC,MAAM,CAAC,GAAG,EAAE;QACtD,CAAC,CAAC,6FAA6F,CAClG,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;IACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAEhE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CACX,uHAAuH,CACxH,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,wEAAwE;IACxE,yBAAyB;IACzB,IAAI,OAA8B,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,+DAA+D;QAC/D,yDAAyD;QACzD,IAAI,KAAK,YAAY,eAAe,EAAE,CAAC;YACrC,OAAO,CAAC,KAAK,CAAC,mBAAmB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,4EAA4E;IAC5E,qEAAqE;IACrE,uEAAuE;IACvE,qEAAqE;IACrE,gEAAgE;IAChE,gEAAgE;IAChE,EAAE;IACF,0EAA0E;IAC1E,2EAA2E;IAC3E,uDAAuD;IACvD,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,MAAM,GAAG,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;QAC/C,OAAO,GAAG,SAAS,CAAC;QACpB,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,CACX,MAAM,CAAC,GAAG;QACR,CAAC,CAAC,wCAAwC,MAAM,CAAC,GAAG,EAAE;QACtD,CAAC,CAAC,6FAA6F,CAClG,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7C,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAE5C,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;IACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/result.d.ts CHANGED
@@ -1,14 +1,36 @@
1
- import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
1
+ import type { CallToolResult } from '@modelcontextprotocol/server';
2
2
  export declare function textResult(text: string): CallToolResult;
3
3
  /**
4
- * Serializes a result, stripping book summaries if the payload is still
5
- * pathologically large after the per-tool truncation.
4
+ * An answer in both channels at once, with book summaries stripped if the
5
+ * payload is still pathologically large after the per-tool truncation.
6
6
  *
7
7
  * A Calibre library can hold book descriptions of arbitrary length, and the
8
8
  * OPDS search endpoint returns every match in one feed. Everything downstream
9
9
  * of this function assumes the budget held; this is what guarantees it.
10
+ *
11
+ * `structuredContent` is the machine-readable half and the reason every tool
12
+ * here declares an `outputSchema`; the text block stays because the SDK does
13
+ * NOT synthesize one for an object-shaped value, and a client that reads only
14
+ * `content` would otherwise get an empty answer. Both carry the same object —
15
+ * which is why the over-budget path rebuilds the *value* rather than editing
16
+ * its serialization.
10
17
  */
11
- export declare function jsonResult(data: unknown): CallToolResult;
18
+ export declare function jsonResult(data: Record<string, unknown>): CallToolResult;
19
+ /**
20
+ * {@link jsonResult}, with the untrusted-content marker on the object.
21
+ *
22
+ * The note has always gone out in `notes`, which is in the text block and in
23
+ * the structured half alike. The two fields are what a client reading only
24
+ * `structuredContent` can *check* rather than have to find in a list of
25
+ * sentences — and they are stripped from the payload before they are set, so
26
+ * the guard cannot be switched off by the content it guards against.
27
+ */
28
+ export declare function untrustedResult(data: Record<string, unknown>): CallToolResult;
29
+ /** Raised by {@link jsonResult}; `run` turns it into an error result. */
30
+ export declare class ResultTooLargeError extends Error {
31
+ }
32
+ /** A value in both channels, with no budget applied. */
33
+ export declare function structuredResult(data: Record<string, unknown>): CallToolResult;
12
34
  export declare function errorResult(text: string): CallToolResult;
13
35
  /** Thrown by tools for problems detected before any request goes out. */
14
36
  export declare class ToolInputError extends Error {
package/dist/result.js CHANGED
@@ -9,31 +9,72 @@ export function textResult(text) {
9
9
  */
10
10
  const MAX_RESULT_BYTES = 400_000;
11
11
  /**
12
- * Serializes a result, stripping book summaries if the payload is still
13
- * pathologically large after the per-tool truncation.
12
+ * An answer in both channels at once, with book summaries stripped if the
13
+ * payload is still pathologically large after the per-tool truncation.
14
14
  *
15
15
  * A Calibre library can hold book descriptions of arbitrary length, and the
16
16
  * OPDS search endpoint returns every match in one feed. Everything downstream
17
17
  * of this function assumes the budget held; this is what guarantees it.
18
+ *
19
+ * `structuredContent` is the machine-readable half and the reason every tool
20
+ * here declares an `outputSchema`; the text block stays because the SDK does
21
+ * NOT synthesize one for an object-shaped value, and a client that reads only
22
+ * `content` would otherwise get an empty answer. Both carry the same object —
23
+ * which is why the over-budget path rebuilds the *value* rather than editing
24
+ * its serialization.
18
25
  */
19
26
  export function jsonResult(data) {
20
- const text = JSON.stringify(data, null, 2);
21
- if (text.length <= MAX_RESULT_BYTES)
22
- return textResult(text);
23
- const stripped = JSON.stringify(data, (key, value) => key === 'summary' && typeof value === 'string'
27
+ if (JSON.stringify(data).length <= MAX_RESULT_BYTES) {
28
+ return structuredResult(data);
29
+ }
30
+ const stripped = JSON.parse(JSON.stringify(data, (key, value) => key === 'summary' && typeof value === 'string'
24
31
  ? '(omitted: result too large)'
25
- : value, 2);
26
- const note = `\n\nNote: the result exceeded ${MAX_RESULT_BYTES} characters, so book summaries were dropped. Narrow the request to get them back.`;
27
- if (stripped.length <= MAX_RESULT_BYTES)
28
- return textResult(stripped + note);
29
- // Dropping summaries is not always enough: the bulk can sit in fields this
32
+ : value));
33
+ if (JSON.stringify(stripped).length <= MAX_RESULT_BYTES) {
34
+ return structuredResult({
35
+ ...stripped,
36
+ notes: [
37
+ ...(Array.isArray(stripped.notes) ? stripped.notes : []),
38
+ `The result exceeded ${MAX_RESULT_BYTES} characters, so book summaries were dropped. Narrow the request to get them back.`,
39
+ ],
40
+ });
41
+ }
42
+ // Dropping summaries is not always enough: the bulk can sit in fields the
30
43
  // replacer does not touch — a feed of thousands of books is all titles and
31
- // URLs. Without this the "hard ceiling" would not be one, so the payload is
32
- // cut off even though that leaves the JSON unparseable. Truncated JSON the
33
- // model can see is still better than megabytes of context.
34
- return textResult(`${stripped.slice(0, MAX_RESULT_BYTES)}\n\n… (truncated: the result exceeded ` +
35
- `${MAX_RESULT_BYTES} characters even without book summaries, so the JSON above ` +
36
- 'is incomplete. Narrow the request — use a more specific query or the offset parameter.)');
44
+ // URLs. This used to answer with the JSON cut at the ceiling, unparseable but
45
+ // visible. That is no longer an option: `structuredContent` has to parse, and
46
+ // the two channels have to carry the same value. So it is an error, which is
47
+ // the honest description of "there is no answer this size".
48
+ throw new ResultTooLargeError(`The result exceeds ${MAX_RESULT_BYTES} characters even without book ` +
49
+ 'summaries. Narrow the request — use a more specific query, a lower ' +
50
+ 'limit, or the offset parameter.');
51
+ }
52
+ /**
53
+ * {@link jsonResult}, with the untrusted-content marker on the object.
54
+ *
55
+ * The note has always gone out in `notes`, which is in the text block and in
56
+ * the structured half alike. The two fields are what a client reading only
57
+ * `structuredContent` can *check* rather than have to find in a list of
58
+ * sentences — and they are stripped from the payload before they are set, so
59
+ * the guard cannot be switched off by the content it guards against.
60
+ */
61
+ export function untrustedResult(data) {
62
+ const { untrusted: _untrusted, source: _source, ...rest } = data;
63
+ return jsonResult({
64
+ untrusted: true,
65
+ source: 'calibre-web',
66
+ ...rest,
67
+ });
68
+ }
69
+ /** Raised by {@link jsonResult}; `run` turns it into an error result. */
70
+ export class ResultTooLargeError extends Error {
71
+ }
72
+ /** A value in both channels, with no budget applied. */
73
+ export function structuredResult(data) {
74
+ return {
75
+ content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
76
+ structuredContent: data,
77
+ };
37
78
  }
38
79
  export function errorResult(text) {
39
80
  return { content: [{ type: 'text', text }], isError: true };
@@ -51,7 +92,10 @@ const UNSAFE_CHARS =
51
92
  */
52
93
  function sanitizeErrorBody(body) {
53
94
  const trimmed = body.replace(UNSAFE_CHARS, '').trim();
54
- if (/^(<!doctype\s|<html[\s>])/i.test(trimmed)) {
95
+ // Anything markup-shaped: a reverse proxy's error page or a WAF block page.
96
+ // The check is deliberately loose — an XML declaration, a leading comment or
97
+ // a doctype followed by a newline are all the same thing here.
98
+ if (/^(<!doctype|<html[\s>]|<\?xml|<!--)/i.test(trimmed)) {
55
99
  return '(HTML error page omitted)';
56
100
  }
57
101
  if (trimmed.length > MAX_ERROR_BODY_LENGTH) {
@@ -92,7 +136,8 @@ export async function run(fn) {
92
136
  return await fn();
93
137
  }
94
138
  catch (error) {
95
- if (error instanceof ToolInputError) {
139
+ if (error instanceof ToolInputError ||
140
+ error instanceof ResultTooLargeError) {
96
141
  return errorResult(error.message);
97
142
  }
98
143
  if (error instanceof CalibreWebApiError) {
@@ -1 +1 @@
1
- {"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAEjC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,UAAU,CAAC,IAA6B;IACtD,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACpD,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CACzB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,KAAc,EAAE,EAAE,CAC3C,GAAG,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ;QAC5C,CAAC,CAAC,6BAA6B;QAC/B,CAAC,CAAC,KAAK,CACV,CACyB,CAAC;IAC7B,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACxD,OAAO,gBAAgB,CAAC;YACtB,GAAG,QAAQ;YACX,KAAK,EAAE;gBACL,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxD,uBAAuB,gBAAgB,mFAAmF;aAC3H;SACF,CAAC,CAAC;IACL,CAAC;IAED,0EAA0E;IAC1E,2EAA2E;IAC3E,8EAA8E;IAC9E,8EAA8E;IAC9E,6EAA6E;IAC7E,4DAA4D;IAC5D,MAAM,IAAI,mBAAmB,CAC3B,sBAAsB,gBAAgB,gCAAgC;QACpE,qEAAqE;QACrE,iCAAiC,CACpC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAAC,IAA6B;IAC3D,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;IACjE,OAAO,UAAU,CAAC;QAChB,SAAS,EAAE,IAAa;QACxB,MAAM,EAAE,aAAsB;QAC9B,GAAG,IAAI;KACR,CAAC,CAAC;AACL,CAAC;AAED,yEAAyE;AACzE,MAAM,OAAO,mBAAoB,SAAQ,KAAK;CAAG;AAEjD,wDAAwD;AACxD,MAAM,UAAU,gBAAgB,CAC9B,IAA6B;IAE7B,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAChE,iBAAiB,EAAE,IAAI;KACxB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC,yEAAyE;AACzE,uEAAuE;AACvE,MAAM,YAAY;AAChB,4CAA4C;AAC5C,8FAA8F,CAAC;AAEjG;;;;GAIG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtD,4EAA4E;IAC5E,6EAA6E;IAC7E,+DAA+D;IAC/D,IAAI,sCAAsC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACzD,OAAO,2BAA2B,CAAC;IACrC,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;QAC3C,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC;IACnE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,OAAO,CAAC,MAAc;IAC7B,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,GAAG;YACN,OAAO,CACL,8EAA8E;gBAC9E,4EAA4E;gBAC5E,8DAA8D,CAC/D,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,CACL,uEAAuE;gBACvE,sDAAsD,CACvD,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,CACL,+EAA+E;gBAC/E,6EAA6E;gBAC7E,6CAA6C,CAC9C,CAAC;QACJ;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,EAAiC;IAEjC,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IACE,KAAK,YAAY,cAAc;YAC/B,KAAK,YAAY,mBAAmB,EACpC,CAAC;YACD,OAAO,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;YACxC,OAAO,WAAW,CAChB,GAAG,KAAK,CAAC,OAAO,KAAK,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAC7E,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO,WAAW,CAAC,mBAAmB,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
package/dist/server.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { McpServer } from '@modelcontextprotocol/server';
2
2
  import type { Config } from './config.js';
3
3
  export declare function createServer(config: Config): McpServer;
package/dist/server.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { createRequire } from 'node:module';
2
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { McpServer } from '@modelcontextprotocol/server';
3
+ import { buildToolFilter, installToolFilter } from 'mcp-tool-allowlist';
4
+ import { ALL_TOOLS, ESSENTIAL_TOOLS } from './tools/catalogue.js';
3
5
  import { CalibreWebApi } from './api.js';
4
6
  import { registerBookTools } from './tools/books.js';
5
7
  import { registerCoverTools } from './tools/covers.js';
@@ -16,11 +18,29 @@ function packageVersion() {
16
18
  }
17
19
  }
18
20
  export function createServer(config) {
21
+ // Before anything is built: an unusable tool list should fail on the
22
+ // way in, not leave a server running with tools quietly missing.
23
+ const filter = buildToolFilter({
24
+ allowTools: config.allowTools,
25
+ denyTools: config.denyTools,
26
+ catalogue: {
27
+ all: ALL_TOOLS,
28
+ essential: ESSENTIAL_TOOLS,
29
+ },
30
+ names: {
31
+ allow: 'CALIBRE_WEB_ALLOW_TOOLS',
32
+ deny: 'CALIBRE_WEB_DENY_TOOLS',
33
+ server: 'calibreweb-mcp',
34
+ },
35
+ });
19
36
  const api = new CalibreWebApi(config);
20
37
  const server = new McpServer({
21
38
  name: 'calibreweb-mcp',
22
39
  version: packageVersion(),
23
40
  });
41
+ // Wraps server.registerTool, so it has to sit before the first
42
+ // register call and does not care how they are organised.
43
+ installToolFilter(server, filter);
24
44
  // Every tool is read-only: the OPDS feed has no write surface, so there is
25
45
  // no read-only mode to configure and nothing destructive to confirm.
26
46
  registerBookTools(server, api);
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;QAC9D,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,MAAM,GAAG,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;IAEtC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,cAAc,EAAE;KAC1B,CAAC,CAAC;IAEH,2EAA2E;IAC3E,qEAAqE;IACrE,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEhC,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAExE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAElE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;QAC9D,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,qEAAqE;IACrE,iEAAiE;IACjE,MAAM,MAAM,GAAG,eAAe,CAAC;QAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,SAAS,EAAE;YACT,GAAG,EAAE,SAAS;YACd,SAAS,EAAE,eAAe;SAC3B;QACD,KAAK,EAAE;YACL,KAAK,EAAE,yBAAyB;YAChC,IAAI,EAAE,wBAAwB;YAC9B,MAAM,EAAE,gBAAgB;SACzB;KACF,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;IAEtC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,cAAc,EAAE;KAC1B,CAAC,CAAC;IAEH,+DAA+D;IAC/D,0DAA0D;IAC1D,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAElC,2EAA2E;IAC3E,qEAAqE;IACrE,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,kBAAkB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEhC,OAAO,MAAM,CAAC;AAChB,CAAC"}