domeneshop-mcp 1.0.4 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +119 -15
  3. package/dist/config.d.ts +20 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +64 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/domeneshop/client.d.ts +3 -3
  8. package/dist/domeneshop/client.d.ts.map +1 -1
  9. package/dist/domeneshop/client.js +1 -1
  10. package/dist/domeneshop/client.js.map +1 -1
  11. package/dist/domeneshop/http.d.ts +46 -1
  12. package/dist/domeneshop/http.d.ts.map +1 -1
  13. package/dist/domeneshop/http.js +214 -14
  14. package/dist/domeneshop/http.js.map +1 -1
  15. package/dist/domeneshop/index.d.ts +2 -2
  16. package/dist/domeneshop/index.d.ts.map +1 -1
  17. package/dist/domeneshop/index.js +1 -1
  18. package/dist/domeneshop/index.js.map +1 -1
  19. package/dist/domeneshop/resources/dns.d.ts +6 -6
  20. package/dist/domeneshop/resources/dns.d.ts.map +1 -1
  21. package/dist/domeneshop/resources/dns.js +12 -8
  22. package/dist/domeneshop/resources/dns.js.map +1 -1
  23. package/dist/domeneshop/resources/domains.d.ts +3 -3
  24. package/dist/domeneshop/resources/domains.d.ts.map +1 -1
  25. package/dist/domeneshop/resources/domains.js +6 -3
  26. package/dist/domeneshop/resources/domains.js.map +1 -1
  27. package/dist/domeneshop/resources/dyndns.d.ts +2 -2
  28. package/dist/domeneshop/resources/dyndns.d.ts.map +1 -1
  29. package/dist/domeneshop/resources/dyndns.js +2 -1
  30. package/dist/domeneshop/resources/dyndns.js.map +1 -1
  31. package/dist/domeneshop/resources/forwards.d.ts +6 -6
  32. package/dist/domeneshop/resources/forwards.d.ts.map +1 -1
  33. package/dist/domeneshop/resources/forwards.js +12 -9
  34. package/dist/domeneshop/resources/forwards.js.map +1 -1
  35. package/dist/domeneshop/resources/invoices.d.ts +6 -5
  36. package/dist/domeneshop/resources/invoices.d.ts.map +1 -1
  37. package/dist/domeneshop/resources/invoices.js +6 -3
  38. package/dist/domeneshop/resources/invoices.js.map +1 -1
  39. package/dist/index.js +73 -12
  40. package/dist/index.js.map +1 -1
  41. package/dist/mcp/annotations.d.ts +20 -4
  42. package/dist/mcp/annotations.d.ts.map +1 -1
  43. package/dist/mcp/annotations.js +33 -12
  44. package/dist/mcp/annotations.js.map +1 -1
  45. package/dist/mcp/fields.d.ts +67 -0
  46. package/dist/mcp/fields.d.ts.map +1 -0
  47. package/dist/mcp/fields.js +93 -0
  48. package/dist/mcp/fields.js.map +1 -0
  49. package/dist/mcp/index.d.ts +1 -0
  50. package/dist/mcp/index.d.ts.map +1 -1
  51. package/dist/mcp/index.js.map +1 -1
  52. package/dist/mcp/records.d.ts +7 -0
  53. package/dist/mcp/records.d.ts.map +1 -0
  54. package/dist/mcp/records.js +20 -0
  55. package/dist/mcp/records.js.map +1 -0
  56. package/dist/mcp/registry.d.ts +31 -0
  57. package/dist/mcp/registry.d.ts.map +1 -0
  58. package/dist/mcp/registry.js +67 -0
  59. package/dist/mcp/registry.js.map +1 -0
  60. package/dist/mcp/result.d.ts +9 -0
  61. package/dist/mcp/result.d.ts.map +1 -1
  62. package/dist/mcp/result.js +19 -1
  63. package/dist/mcp/result.js.map +1 -1
  64. package/dist/mcp/server.d.ts +9 -1
  65. package/dist/mcp/server.d.ts.map +1 -1
  66. package/dist/mcp/server.js +32 -7
  67. package/dist/mcp/server.js.map +1 -1
  68. package/dist/mcp/tool.d.ts +5 -1
  69. package/dist/mcp/tool.d.ts.map +1 -1
  70. package/dist/mcp/tool.js +2 -2
  71. package/dist/mcp/tool.js.map +1 -1
  72. package/dist/mcp/tools/dns.d.ts +10 -3
  73. package/dist/mcp/tools/dns.d.ts.map +1 -1
  74. package/dist/mcp/tools/dns.js +167 -39
  75. package/dist/mcp/tools/dns.js.map +1 -1
  76. package/dist/mcp/tools/domains.d.ts +2 -2
  77. package/dist/mcp/tools/domains.d.ts.map +1 -1
  78. package/dist/mcp/tools/domains.js +19 -12
  79. package/dist/mcp/tools/domains.js.map +1 -1
  80. package/dist/mcp/tools/dyndns.d.ts +2 -2
  81. package/dist/mcp/tools/dyndns.d.ts.map +1 -1
  82. package/dist/mcp/tools/dyndns.js +16 -9
  83. package/dist/mcp/tools/dyndns.js.map +1 -1
  84. package/dist/mcp/tools/forwards.d.ts +2 -2
  85. package/dist/mcp/tools/forwards.d.ts.map +1 -1
  86. package/dist/mcp/tools/forwards.js +31 -30
  87. package/dist/mcp/tools/forwards.js.map +1 -1
  88. package/dist/mcp/tools/invoices.d.ts +2 -2
  89. package/dist/mcp/tools/invoices.d.ts.map +1 -1
  90. package/dist/mcp/tools/invoices.js +15 -14
  91. package/dist/mcp/tools/invoices.js.map +1 -1
  92. package/package.json +12 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # domeneshop-mcp
2
2
 
3
+ ## 1.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#37](https://github.com/martinzachariassen/domeneshop-mcp/pull/37) [`d24cf89`](https://github.com/martinzachariassen/domeneshop-mcp/commit/d24cf89e1f90d4db3f6d6ff4f28d732e19700224) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - Install with Homebrew: `brew install martinzachariassen/tap/domeneshop-mcp`. Every release now also updates the Homebrew tap.
8
+
9
+ ## 1.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#34](https://github.com/martinzachariassen/domeneshop-mcp/pull/34) [`511a6e9`](https://github.com/martinzachariassen/domeneshop-mcp/commit/511a6e948ec7a8ab0e6b7eb78f9bd7182d082281) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - Add access control and CLI polish. `DOMENESHOP_READ_ONLY` exposes only read-only tools, `DOMENESHOP_TOOLS` / `DOMENESHOP_DISABLED_TOOLS` allow- or deny-list individual tools (misspelled names fail at startup), `DOMENESHOP_TIMEOUT_MS` sets the request timeout and `DOMENESHOP_DEBUG` logs API requests to stderr. The CLI gains `--help`, rejects unknown arguments with exit code 2, trims whitespace around credentials and shuts down cleanly on SIGINT/SIGTERM.
14
+
15
+ - [#34](https://github.com/martinzachariassen/domeneshop-mcp/pull/34) [`7c690b3`](https://github.com/martinzachariassen/domeneshop-mcp/commit/7c690b38aed2ab85a73e8cf62403f70eb476cc0a) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - Add two DNS helper tools:
16
+
17
+ - `patch_dns_record` changes only the fields you pass. It reads the record first, so `ttl`, `priority` and the other fields are no longer reset by accident the way they can be with `update_dns_record`.
18
+ - `upsert_dns_record` updates the single record for a host and type, or creates it if there is none. When the host has several records of that type (common for MX and TXT), it refuses and lists their IDs instead of guessing.
19
+
20
+ - [#34](https://github.com/martinzachariassen/domeneshop-mcp/pull/34) [`872be45`](https://github.com/martinzachariassen/domeneshop-mcp/commit/872be455fcfb4e4ea279ddd2478e0f6d498353eb) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - Polish the MCP surface:
21
+
22
+ - Every tool now has a human-readable title and the `openWorldHint` annotation, and the server sends usage instructions on initialize.
23
+ - Inputs are validated before anything reaches Domeneshop: TTL range and multiple of 60, `http(s)` forward URLs, IP addresses for dynamic DNS (now including comma-separated IPv4/IPv6 lists), enums for the `list_dns_records` type and `list_invoices` status filters, and the fields each record type requires (e.g. `priority` for MX).
24
+ - Read-only tools declare an `outputSchema` and return `structuredContent`; lists are wrapped as `{ domains }`, `{ records }`, `{ forwards }` and `{ invoices }` there, while the text content stays the plain array.
25
+ - `create_dns_record` and `update_dns_record` return the record as stored instead of just an ID or a confirmation, and delete/dyndns messages say what was changed.
26
+ - Cancelling a tool call from the client now aborts the underlying API request.
27
+
28
+ - [#34](https://github.com/martinzachariassen/domeneshop-mcp/pull/34) [`1727632`](https://github.com/martinzachariassen/domeneshop-mcp/commit/17276320bb0447a5e54f5bed251a652a041f372d) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - Make the API client more robust: requests now time out after 30 seconds, `GET`/`PUT`/`DELETE` are retried on 429/502/503/504 and network errors (honouring `Retry-After`, never retrying `POST`), a `User-Agent` header identifies the server, and error messages explain common statuses (401/403/404/409/429/5xx) with HTML bodies stripped and long bodies truncated. Network failures and timeouts surface as `DomeneshopNetworkError`. Node 20.3 or newer is now required.
29
+
30
+ ### Patch Changes
31
+
32
+ - [#34](https://github.com/martinzachariassen/domeneshop-mcp/pull/34) [`855be46`](https://github.com/martinzachariassen/domeneshop-mcp/commit/855be46840521455ec79ceb9755b016506841b9c) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - Publish to the official MCP Registry as `io.github.martinzachariassen/domeneshop-mcp` and attach a one-click Claude Desktop bundle (`.mcpb`) to each GitHub Release. The package is now published with `publishConfig` (public, provenance) and requires at least the MCP SDK and zod versions it is tested against.
33
+
34
+ - [#34](https://github.com/martinzachariassen/domeneshop-mcp/pull/34) [`87163b5`](https://github.com/martinzachariassen/domeneshop-mcp/commit/87163b52dd897ee4f598fe25f825ccb55d8cee6e) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - `update_dyndns` now tells the model it can update several comma-separated hostnames in one call, as the Domeneshop API allows. The `Invoice` type marks `due_date` and `paid_date` as nullable, matching what the API returns for unpaid invoices.
35
+
36
+ - [#34](https://github.com/martinzachariassen/domeneshop-mcp/pull/34) [`7f86965`](https://github.com/martinzachariassen/domeneshop-mcp/commit/7f86965e1fad7f949f728b472c863e210f5ee540) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - Fixes from a review pass:
37
+
38
+ - `update_dyndns` strips spaces around commas in `hostname` and `myip` before calling the API, and treats an empty `myip` as omitted.
39
+ - `upsert_dns_record` keeps the stored `ttl`, `priority` and other optional fields when it updates an existing record and they are left out, instead of resetting them.
40
+ - A create that times out or loses its connection now says it may still have been applied, so the model checks before retrying and does not create a duplicate.
41
+ - A `DELETE` retried after a lost connection treats a `404` as success, since the first attempt already removed the resource.
42
+ - The server logs a warning when the tool filters hide an allowlisted tool (e.g. a write tool in read-only mode) or leave no tools at all.
43
+ - Clearer messages for a `412` from the forwards API and for `update_forward`, which cannot change a forward's host.
44
+
45
+ - [#34](https://github.com/martinzachariassen/domeneshop-mcp/pull/34) [`a584d13`](https://github.com/martinzachariassen/domeneshop-mcp/commit/a584d1344ecba45f60526b15b42dd0708e0c1ac1) Thanks [@martinzachariassen](https://github.com/martinzachariassen)! - Require `@modelcontextprotocol/sdk` 1.32.1 or newer, which fixes several security advisories (GHSA-22jm-h49p-29qw, GHSA-6prh-2h8m-c8cw, GHSA-46jr-4wx8-v453, GHSA-rvq5-wwqv-78pq).
46
+
3
47
  ## 1.0.4
4
48
 
5
49
  ### Patch Changes
package/README.md CHANGED
@@ -1,25 +1,34 @@
1
1
  <h1 align="center">domeneshop-mcp</h1>
2
2
 
3
3
  <p align="center">
4
- Manage your Domeneshop domains, DNS records, HTTP forwards and invoices from any AI assistant.
4
+ Manage your Domeneshop domains, DNS records, HTTP forwards, dynamic DNS and invoices from any AI assistant.
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://github.com/martinzachariassen/domeneshop-mcp/actions/workflows/ci.yml"><img src="https://github.com/martinzachariassen/domeneshop-mcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
9
9
  <a href="https://www.npmjs.com/package/domeneshop-mcp"><img src="https://img.shields.io/npm/v/domeneshop-mcp" alt="npm version"></a>
10
+ <a href="https://www.npmjs.com/package/domeneshop-mcp"><img src="https://img.shields.io/npm/dm/domeneshop-mcp" alt="npm downloads"></a>
11
+ <a href="package.json"><img src="https://img.shields.io/node/v/domeneshop-mcp" alt="Node.js version"></a>
12
+ <a href="https://github.com/martinzachariassen/domeneshop-mcp/actions/workflows/codeql.yml"><img src="https://github.com/martinzachariassen/domeneshop-mcp/actions/workflows/codeql.yml/badge.svg" alt="CodeQL"></a>
13
+ <a href="https://scorecard.dev/viewer/?uri=github.com/martinzachariassen/domeneshop-mcp"><img src="https://api.scorecard.dev/projects/github.com/martinzachariassen/domeneshop-mcp/badge" alt="OpenSSF Scorecard"></a>
14
+ <a href="LICENSE"><img src="https://img.shields.io/npm/l/domeneshop-mcp" alt="License"></a>
10
15
  </p>
11
16
 
12
17
  <p align="center">
13
18
  <a href="https://www.npmjs.com/package/domeneshop-mcp"><b>npm package</b></a>
14
19
  &nbsp;·&nbsp;
20
+ <a href="https://github.com/martinzachariassen/homebrew-tap"><b>Homebrew tap</b></a>
21
+ &nbsp;·&nbsp;
15
22
  <a href="https://api.domeneshop.no/docs/"><b>Domeneshop API</b></a>
16
23
  &nbsp;·&nbsp;
17
24
  <a href="https://www.domeneshop.no/admin?view=api"><b>Get API credentials</b></a>
25
+ &nbsp;·&nbsp;
26
+ <a href="https://registry.modelcontextprotocol.io/v0/servers?search=io.github.martinzachariassen/domeneshop-mcp"><b>MCP Registry</b></a>
18
27
  </p>
19
28
 
20
29
  An [MCP](https://modelcontextprotocol.io) server that turns the
21
30
  [Domeneshop API](https://api.domeneshop.no/docs/) into tools an assistant can call.
22
- Every operation the API offers is covered — 15 tools across domains, DNS, HTTP
31
+ Every operation the API offers is covered — 17 tools across domains, DNS, HTTP
23
32
  forwarding, dynamic DNS and invoices.
24
33
 
25
34
  It talks MCP over stdio, so it works with **any MCP-compatible client**: Claude
@@ -27,7 +36,8 @@ Code, Claude Desktop, Cursor, VS Code, Codex, Windsurf, Cline and others.
27
36
 
28
37
  ## Quick start
29
38
 
30
- You need Node.js 20 or newer and a Domeneshop account.
39
+ You need a Domeneshop account, plus either Node.js 20.3 or newer (for `npx`) or
40
+ [Homebrew](https://brew.sh).
31
41
 
32
42
  **1. Create an API token and secret** at
33
43
  [domeneshop.no/admin?view=api](https://www.domeneshop.no/admin?view=api).
@@ -43,6 +53,13 @@ claude mcp add domeneshop \
43
53
 
44
54
  **3. Ask for something.** "List my domains" is enough to confirm the tools loaded.
45
55
 
56
+ **Using Homebrew instead of npx?** Install once, then register the binary in
57
+ step 2 with `-- domeneshop-mcp` in place of `-- npx -y domeneshop-mcp`:
58
+
59
+ ```sh
60
+ brew install martinzachariassen/tap/domeneshop-mcp
61
+ ```
62
+
46
63
  Using a different client? See [Client setup](#client-setup) below.
47
64
 
48
65
  ## Client setup
@@ -55,6 +72,17 @@ credentials from two environment variables:
55
72
  | `DOMENESHOP_API_TOKEN` | API token from the Domeneshop control panel |
56
73
  | `DOMENESHOP_API_SECRET` | The matching API secret |
57
74
 
75
+ Optional settings (see [Access control](#access-control) and
76
+ [Troubleshooting](#troubleshooting)):
77
+
78
+ | Variable | Description |
79
+ | --- | --- |
80
+ | `DOMENESHOP_READ_ONLY` | `true` to expose only read-only tools |
81
+ | `DOMENESHOP_TOOLS` | Comma-separated allowlist of tool names to expose |
82
+ | `DOMENESHOP_DISABLED_TOOLS` | Comma-separated tool names to hide |
83
+ | `DOMENESHOP_TIMEOUT_MS` | Per-request timeout in milliseconds (default `30000`) |
84
+ | `DOMENESHOP_DEBUG` | `true` to log every API request to stderr |
85
+
58
86
  Most clients take the same **standard config**. Swap in your real token and
59
87
  secret wherever you see the placeholders.
60
88
 
@@ -98,7 +126,12 @@ Or write the standard config straight into `<your-project>/.mcp.json`.
98
126
  <details>
99
127
  <summary><strong>Claude Desktop</strong></summary>
100
128
 
101
- Add the standard config to `claude_desktop_config.json`, reachable from
129
+ The easiest route is the one-click bundle: download `domeneshop-mcp-<version>.mcpb`
130
+ from the [latest release](https://github.com/martinzachariassen/domeneshop-mcp/releases/latest)
131
+ and open it. Claude Desktop asks for your token and secret, stores them securely,
132
+ and offers a read-only toggle.
133
+
134
+ To configure it by hand instead, add the standard config to `claude_desktop_config.json`, reachable from
102
135
  Settings → Developer → Edit Config:
103
136
 
104
137
  | Platform | Full path |
@@ -191,15 +224,41 @@ it at:
191
224
  </details>
192
225
 
193
226
  <details>
194
- <summary><strong>Skipping npx</strong></summary>
227
+ <summary><strong>Skipping npx (npm or Homebrew)</strong></summary>
195
228
 
196
229
  To avoid resolving the package on every launch, install it once and use
197
- `domeneshop-mcp` as the command with no args:
230
+ `domeneshop-mcp` as the command with no args. With npm:
198
231
 
199
232
  ```sh
200
233
  npm install -g domeneshop-mcp
201
234
  ```
202
235
 
236
+ Or with [Homebrew](https://brew.sh) on macOS or Linux, which also installs
237
+ Node.js for you if it is missing:
238
+
239
+ ```sh
240
+ brew install martinzachariassen/tap/domeneshop-mcp
241
+ ```
242
+
243
+ `brew upgrade domeneshop-mcp` picks up new releases. GUI apps such as Claude
244
+ Desktop usually do not see Homebrew's `PATH`, so give them the full path
245
+ (`brew --prefix` prints the prefix: `/opt/homebrew` on Apple silicon,
246
+ `/usr/local` on Intel Macs, `/home/linuxbrew/.linuxbrew` on Linux):
247
+
248
+ ```json
249
+ {
250
+ "mcpServers": {
251
+ "domeneshop": {
252
+ "command": "/opt/homebrew/bin/domeneshop-mcp",
253
+ "env": {
254
+ "DOMENESHOP_API_TOKEN": "your-token",
255
+ "DOMENESHOP_API_SECRET": "your-secret"
256
+ }
257
+ }
258
+ }
259
+ }
260
+ ```
261
+
203
262
  </details>
204
263
 
205
264
  ## What you can ask for
@@ -209,6 +268,7 @@ npm install -g domeneshop-mcp
209
268
  | Domains | "Which domains are on my account, and when do they renew?" |
210
269
  | DNS | "Show every DNS record for example.no" |
211
270
  | DNS | "Point blog.example.no at 203.0.113.10" |
271
+ | DNS | "Lower the TTL on the www record for example.no to 5 minutes" |
212
272
  | DNS | "Add the TXT record Google gave me for domain verification" |
213
273
  | DNS | "Set up Fastmail's MX records on example.no" |
214
274
  | Forwarding | "Forward old.example.no to https://example.no" |
@@ -217,8 +277,11 @@ npm install -g domeneshop-mcp
217
277
 
218
278
  ## Tools
219
279
 
220
- Every tool declares MCP annotations, so clients know which ones are safe to run
221
- without asking you first. The Access column below mirrors those annotations.
280
+ Every tool declares MCP annotations and a title, so clients know which ones are
281
+ safe to run without asking you first. The Access column below mirrors those
282
+ annotations. Arguments are validated before anything is sent to Domeneshop (TTL
283
+ range, URL scheme, IP addresses, and the fields each record type requires), and
284
+ read-only tools also return structured output for clients that support it.
222
285
 
223
286
  ### Domains
224
287
 
@@ -233,14 +296,17 @@ without asking you first. The Access column below mirrors those annotations.
233
296
  | --- | --- | --- |
234
297
  | `list_dns_records` | Read-only | List a domain's records, optionally filtered by host and type |
235
298
  | `get_dns_record` | Read-only | Get a single record by ID |
236
- | `create_dns_record` | Adds | Create a record |
237
- | `update_dns_record` | Overwrites | Replace a record — see the warning below |
299
+ | `create_dns_record` | Adds | Create a record and return it as stored |
300
+ | `update_dns_record` | Overwrites | Replace a record and return it as stored — see the warning below |
301
+ | `patch_dns_record` | Overwrites | Change only the given fields of a record; the rest keep their stored values |
302
+ | `upsert_dns_record` | Overwrites | Update the single record for a host and type, or create it if there is none |
238
303
  | `delete_dns_record` | Deletes | Delete a record |
239
304
 
240
305
  > **Heads up:** `update_dns_record` replaces the whole record rather than
241
306
  > patching it. Any field left out is dropped, and omitting `ttl` resets it to
242
- > the default 3600 seconds. Read the record first if you are only changing part
243
- > of it.
307
+ > the default 3600 seconds. Use `patch_dns_record` to change only some fields.
308
+ > `upsert_dns_record` refuses to act when a host has several records of the
309
+ > same type (typical for `MX` and `TXT`) and lists their IDs instead.
244
310
 
245
311
  ### HTTP forwarding
246
312
 
@@ -256,7 +322,7 @@ without asking you first. The Access column below mirrors those annotations.
256
322
 
257
323
  | Tool | Access | Description |
258
324
  | --- | --- | --- |
259
- | `update_dyndns` | Overwrites | Point a hostname at a given IP, or at the caller's apparent IP |
325
+ | `update_dyndns` | Overwrites | Point one or more comma-separated hostnames at up to nine IPv4/IPv6 addresses, or at the caller's apparent IP; creates the record if missing |
260
326
 
261
327
  ### Invoices
262
328
 
@@ -265,6 +331,42 @@ without asking you first. The Access column below mirrors those annotations.
265
331
  | `list_invoices` | Read-only | List invoices from the past three years, optionally filtered by status |
266
332
  | `get_invoice` | Read-only | Get a single invoice |
267
333
 
334
+ ## Access control
335
+
336
+ The Domeneshop API has no scoped tokens: a token can do everything the account
337
+ can. To limit what the assistant can do, limit which tools the server exposes.
338
+ Hidden tools are not registered at all, so the model never sees them.
339
+
340
+ - **Read-only mode.** Set `DOMENESHOP_READ_ONLY=true` to expose only the eight
341
+ read-only tools. This is a good default if you mainly ask questions about your
342
+ domains, DNS and invoices.
343
+ - **Allowlist.** Set `DOMENESHOP_TOOLS=list_domains,list_dns_records,patch_dns_record`
344
+ to expose exactly those tools.
345
+ - **Denylist.** Set `DOMENESHOP_DISABLED_TOOLS=delete_dns_record,delete_forward`
346
+ to hide specific tools.
347
+
348
+ The settings combine, and a misspelled tool name stops the server at startup
349
+ instead of being silently ignored.
350
+
351
+ Most clients ask before running a tool not annotated read-only. Keep that
352
+ confirmation on for this server: write tools change live DNS, and a wrong
353
+ record can take a site or mailbox offline.
354
+
355
+ ## Troubleshooting
356
+
357
+ - **`API returned status 401`**: the token or secret is wrong. Copy both again
358
+ from [domeneshop.no/admin?view=api](https://www.domeneshop.no/admin?view=api).
359
+ Surrounding whitespace is trimmed automatically.
360
+ - **`API returned status 404`**: the domain or record ID does not exist on this
361
+ account. Ask the assistant to list domains or records first.
362
+ - **Timeouts**: requests give up after 30 seconds. Idempotent requests are
363
+ retried on rate limiting and temporary errors. Raise the limit with
364
+ `DOMENESHOP_TIMEOUT_MS` if needed.
365
+ - **The tools do not show up**: run `npx -y domeneshop-mcp --version` to check
366
+ that Node.js 20.3+ and npx work, then check your client's MCP logs. With
367
+ `DOMENESHOP_DEBUG=true` the server logs every API request (method, path,
368
+ status and duration, never credentials) to stderr, which most clients capture.
369
+
268
370
  ## Reference
269
371
 
270
372
  <details>
@@ -300,7 +402,8 @@ spec, but are supported and used by their own client libraries:
300
402
  - `GET /invoices` accepts a `status` filter, which domeneshop.js uses.
301
403
  - `POST /forwards/` answers `201` with an empty body and names the new forward
302
404
  only in the `Location` header.
303
- - `GET /dyndns/update` answers with a plain-text status line, not JSON.
405
+ - `GET /dyndns/update` answers `204` with no body rather than JSON, so the
406
+ client does not try to decode it.
304
407
 
305
408
  </details>
306
409
 
@@ -338,7 +441,8 @@ test/
338
441
  ## Contributing
339
442
 
340
443
  Issues and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md)
341
- for the development workflow and changelog conventions.
444
+ for the development workflow and changelog conventions. Everyone taking part is
445
+ expected to follow the [Code of Conduct](CODE_OF_CONDUCT.md).
342
446
 
343
447
  ## Security
344
448
 
@@ -0,0 +1,20 @@
1
+ /** Thrown for invalid configuration; the CLI prints the message and exits with code 1. */
2
+ export declare class ConfigError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export interface Config {
6
+ token: string;
7
+ secret: string;
8
+ /** Only register tools annotated read-only. */
9
+ readOnly: boolean;
10
+ /** When set, only these tools are registered. */
11
+ tools: ReadonlySet<string> | undefined;
12
+ /** These tools are never registered. */
13
+ disabledTools: ReadonlySet<string>;
14
+ timeoutMs: number | undefined;
15
+ debug: boolean;
16
+ }
17
+ export declare const ENV_HELP = "Environment variables:\n DOMENESHOP_API_TOKEN API token (required), from https://www.domeneshop.no/admin?view=api\n DOMENESHOP_API_SECRET API secret (required)\n DOMENESHOP_READ_ONLY \"true\" or \"1\" to expose only read-only tools\n DOMENESHOP_TOOLS comma-separated allowlist of tool names to expose\n DOMENESHOP_DISABLED_TOOLS comma-separated tool names to hide\n DOMENESHOP_TIMEOUT_MS per-request timeout in milliseconds (default 30000)\n DOMENESHOP_DEBUG \"true\" or \"1\" to log each API request to stderr";
18
+ export declare function loadConfig(env: NodeJS.ProcessEnv): Config;
19
+ export declare function isTruthy(value: string | undefined): boolean;
20
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,QAAQ,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IACvC,wCAAwC;IACxC,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,QAAQ,qjBAOwD,CAAC;AAK9E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAmBzD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE3D"}
package/dist/config.js ADDED
@@ -0,0 +1,64 @@
1
+ /** Thrown for invalid configuration; the CLI prints the message and exits with code 1. */
2
+ export class ConfigError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "ConfigError";
6
+ }
7
+ }
8
+ export const ENV_HELP = `Environment variables:
9
+ DOMENESHOP_API_TOKEN API token (required), from https://www.domeneshop.no/admin?view=api
10
+ DOMENESHOP_API_SECRET API secret (required)
11
+ DOMENESHOP_READ_ONLY "true" or "1" to expose only read-only tools
12
+ DOMENESHOP_TOOLS comma-separated allowlist of tool names to expose
13
+ DOMENESHOP_DISABLED_TOOLS comma-separated tool names to hide
14
+ DOMENESHOP_TIMEOUT_MS per-request timeout in milliseconds (default 30000)
15
+ DOMENESHOP_DEBUG "true" or "1" to log each API request to stderr`;
16
+ const TRUE_VALUES = new Set(["1", "true", "yes", "on"]);
17
+ const FALSE_VALUES = new Set(["", "0", "false", "no", "off"]);
18
+ export function loadConfig(env) {
19
+ const token = env["DOMENESHOP_API_TOKEN"]?.trim();
20
+ const secret = env["DOMENESHOP_API_SECRET"]?.trim();
21
+ if (!token || !secret) {
22
+ throw new ConfigError("DOMENESHOP_API_TOKEN and DOMENESHOP_API_SECRET must be set " +
23
+ "(create credentials at https://www.domeneshop.no/admin?view=api)");
24
+ }
25
+ return {
26
+ token,
27
+ secret,
28
+ readOnly: parseBoolean(env, "DOMENESHOP_READ_ONLY"),
29
+ tools: env["DOMENESHOP_TOOLS"]?.trim() ? parseList(env["DOMENESHOP_TOOLS"]) : undefined,
30
+ disabledTools: parseList(env["DOMENESHOP_DISABLED_TOOLS"]),
31
+ timeoutMs: parseTimeout(env["DOMENESHOP_TIMEOUT_MS"]),
32
+ debug: parseBoolean(env, "DOMENESHOP_DEBUG"),
33
+ };
34
+ }
35
+ export function isTruthy(value) {
36
+ return TRUE_VALUES.has((value ?? "").trim().toLowerCase());
37
+ }
38
+ function parseBoolean(env, name) {
39
+ const value = (env[name] ?? "").trim().toLowerCase();
40
+ if (TRUE_VALUES.has(value)) {
41
+ return true;
42
+ }
43
+ if (FALSE_VALUES.has(value)) {
44
+ return false;
45
+ }
46
+ throw new ConfigError(`${name} must be true or false, got "${env[name] ?? ""}"`);
47
+ }
48
+ function parseList(value) {
49
+ return new Set((value ?? "")
50
+ .split(",")
51
+ .map((item) => item.trim())
52
+ .filter((item) => item !== ""));
53
+ }
54
+ function parseTimeout(value) {
55
+ if (value === undefined || value.trim() === "") {
56
+ return undefined;
57
+ }
58
+ const ms = Number(value);
59
+ if (!Number.isInteger(ms) || ms <= 0) {
60
+ throw new ConfigError(`DOMENESHOP_TIMEOUT_MS must be a positive whole number of milliseconds, got "${value}"`);
61
+ }
62
+ return ms;
63
+ }
64
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
+ import { type HttpTransportOptions } from "./http.js";
1
2
  import { DnsResource } from "./resources/dns.js";
2
3
  import { DomainsResource } from "./resources/domains.js";
3
4
  import { DynDnsResource } from "./resources/dyndns.js";
4
5
  import { ForwardsResource } from "./resources/forwards.js";
5
6
  import { InvoicesResource } from "./resources/invoices.js";
6
- export interface DomeneshopClientOptions {
7
- baseUrl?: string;
8
- }
7
+ /** See HttpTransportOptions for timeout, retry and logging defaults. */
8
+ export type DomeneshopClientOptions = HttpTransportOptions;
9
9
  /** Authenticate with a token/secret pair from https://www.domeneshop.no/admin?view=api. */
10
10
  export declare class DomeneshopClient {
11
11
  readonly domains: DomainsResource;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/domeneshop/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,2FAA2F;AAC3F,qBAAa,gBAAgB;IAC3B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;gBAExB,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B;CAQjF"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/domeneshop/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,wEAAwE;AACxE,MAAM,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAE3D,2FAA2F;AAC3F,qBAAa,gBAAgB;IAC3B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;gBAExB,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B;CAQjF"}
@@ -12,7 +12,7 @@ export class DomeneshopClient {
12
12
  dynDns;
13
13
  invoices;
14
14
  constructor(token, secret, options = {}) {
15
- const transport = new HttpTransport(token, secret, options.baseUrl);
15
+ const transport = new HttpTransport(token, secret, options);
16
16
  this.domains = new DomainsResource(transport);
17
17
  this.dns = new DnsResource(transport);
18
18
  this.forwards = new ForwardsResource(transport);
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/domeneshop/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAM3D,2FAA2F;AAC3F,MAAM,OAAO,gBAAgB;IAClB,OAAO,CAAkB;IACzB,GAAG,CAAc;IACjB,QAAQ,CAAmB;IAC3B,MAAM,CAAiB;IACvB,QAAQ,CAAmB;IAEpC,YAAY,KAAa,EAAE,MAAc,EAAE,UAAmC,EAAE;QAC9E,MAAM,SAAS,GAAG,IAAI,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QACpE,IAAI,CAAC,OAAO,GAAG,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,CAAC,GAAG,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAClD,CAAC;CACF"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/domeneshop/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAA6B,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAK3D,2FAA2F;AAC3F,MAAM,OAAO,gBAAgB;IAClB,OAAO,CAAkB;IACzB,GAAG,CAAc;IACjB,QAAQ,CAAmB;IAC3B,MAAM,CAAiB;IACvB,QAAQ,CAAmB;IAEpC,YAAY,KAAa,EAAE,MAAc,EAAE,UAAmC,EAAE;QAC9E,MAAM,SAAS,GAAG,IAAI,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAC5D,IAAI,CAAC,OAAO,GAAG,IAAI,eAAe,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,CAAC,GAAG,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAClD,CAAC;CACF"}
@@ -1,13 +1,47 @@
1
1
  export declare class DomeneshopApiError extends Error {
2
2
  readonly statusCode: number;
3
3
  readonly body: string;
4
+ /** Human-readable advice for the status code, when there is any. */
5
+ readonly hint: string | undefined;
4
6
  constructor(statusCode: number, body: string);
5
7
  }
8
+ /** The request never produced an HTTP response: timeout, DNS failure, refused connection… */
9
+ export declare class DomeneshopNetworkError extends Error {
10
+ constructor(message: string, options?: {
11
+ cause?: unknown;
12
+ });
13
+ }
14
+ export interface HttpTransportOptions {
15
+ baseUrl?: string | undefined;
16
+ /** Per-attempt timeout. Defaults to 30 seconds. */
17
+ timeoutMs?: number | undefined;
18
+ /** Retries for 429/502/503/504 and network errors on idempotent methods. Defaults to 2. */
19
+ maxRetries?: number | undefined;
20
+ userAgent?: string | undefined;
21
+ /** Base delay for exponential backoff when the server sends no Retry-After. */
22
+ retryBaseDelayMs?: number | undefined;
23
+ /** Called after every attempt, e.g. for debug logging. Never receives credentials. */
24
+ onResponse?: ((event: RequestEvent) => void) | undefined;
25
+ }
26
+ export interface RequestEvent {
27
+ method: string;
28
+ path: string;
29
+ /** Undefined when the attempt failed without a response. */
30
+ status: number | undefined;
31
+ durationMs: number;
32
+ attempt: number;
33
+ }
6
34
  export interface RequestOptions {
7
35
  query?: Record<string, string | undefined>;
8
36
  body?: unknown;
9
37
  /** Set false for endpoints with a non-JSON body, e.g. the dynamic DNS update protocol. */
10
38
  decodeJson?: boolean;
39
+ /** Aborts the request, including any pending retry wait. */
40
+ signal?: AbortSignal | undefined;
41
+ }
42
+ /** Passed through every resource method so a caller can cancel in-flight requests. */
43
+ export interface CallOptions {
44
+ signal?: AbortSignal | undefined;
11
45
  }
12
46
  export interface ApiResponse<T> {
13
47
  data: T | undefined;
@@ -17,7 +51,18 @@ export interface ApiResponse<T> {
17
51
  export declare class HttpTransport {
18
52
  private readonly baseUrl;
19
53
  private readonly authorization;
20
- constructor(token: string, secret: string, baseUrl?: string);
54
+ private readonly timeoutMs;
55
+ private readonly maxRetries;
56
+ private readonly userAgent;
57
+ private readonly retryBaseDelayMs;
58
+ private readonly onResponse;
59
+ /** The third argument may be a bare base URL for backwards compatibility. */
60
+ constructor(token: string, secret: string, options?: HttpTransportOptions | string);
21
61
  request<T>(method: string, path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
62
+ private attemptSignal;
63
+ private backoffDelay;
64
+ private emit;
22
65
  }
66
+ /** Accepts both delta-seconds and an HTTP date, per RFC 9110. */
67
+ export declare function parseRetryAfter(value: string | null): number | undefined;
23
68
  //# sourceMappingURL=http.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/domeneshop/http.ts"],"names":[],"mappings":"AAEA,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAU7C;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0FAA0F;IAC1F,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,qGAAqG;IACrG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,MAAyB;IAKvE,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;CA8B3B"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/domeneshop/http.ts"],"names":[],"mappings":"AAwBA,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;gBAEtB,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAgB7C;AAID,6FAA6F;AAC7F,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,+EAA+E;IAC/E,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,sFAAsF;IACtF,UAAU,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC1D;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0FAA0F;IAC1F,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAClC;AAED,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,qGAAqG;IACrG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8C;IAEzE,6EAA6E;gBACjE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAoB,GAAG,MAAW;IAWhF,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAmF1B,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,IAAI;CAeb;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAUxE"}