@databrainhq/mcp-server 0.3.0-rc.13 → 0.3.0-rc.15

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 (50) hide show
  1. package/.mcp.json +6 -1
  2. package/dist/apps/server.d.ts +102 -389
  3. package/dist/apps/server.js +5 -5
  4. package/dist/apps/stdio.d.ts +1 -2
  5. package/dist/apps/stdio.js +6 -6
  6. package/dist/{bundle-TDQXWIPM.js → bundle-QJNCUMNY.js} +2 -2
  7. package/dist/{chunk-Q5EYDKSA.js → chunk-2F7V6RZR.js} +511 -107
  8. package/dist/chunk-2F7V6RZR.js.map +1 -0
  9. package/dist/chunk-3AH56XX7.js +28 -0
  10. package/dist/chunk-3AH56XX7.js.map +1 -0
  11. package/dist/{chunk-AEQTEE5C.js → chunk-4E57ZRZO.js} +302 -63
  12. package/dist/chunk-4E57ZRZO.js.map +1 -0
  13. package/dist/{chunk-PSJVYD4W.js → chunk-6MD7KOAU.js} +11723 -10560
  14. package/dist/chunk-6MD7KOAU.js.map +1 -0
  15. package/dist/{chunk-HCYZN7I3.js → chunk-FJLRZJ4A.js} +10 -7
  16. package/dist/chunk-FJLRZJ4A.js.map +1 -0
  17. package/dist/{chunk-X44C5GW2.js → chunk-MUZ3KEJX.js} +63 -40
  18. package/dist/chunk-MUZ3KEJX.js.map +1 -0
  19. package/dist/{chunk-DTFYHLYN.js → chunk-NXE2A4WV.js} +26 -11
  20. package/dist/chunk-NXE2A4WV.js.map +1 -0
  21. package/dist/{chunk-233VGFHY.js → chunk-QQ6KNULX.js} +317 -147
  22. package/dist/chunk-QQ6KNULX.js.map +1 -0
  23. package/dist/chunk-V5JINLOA.js +506 -0
  24. package/dist/chunk-V5JINLOA.js.map +1 -0
  25. package/dist/cli.d.ts +16 -15
  26. package/dist/cli.js +134 -36
  27. package/dist/cli.js.map +1 -1
  28. package/dist/custom-chart/custom-runtime.js +6 -6
  29. package/dist/custom-chart/template/chart.ts +116 -28
  30. package/dist/custom-chart/template/icon.svg +1 -1
  31. package/dist/custom-chart/template/manifest.json +66 -12
  32. package/dist/custom-chart/template/sample-data.json +21 -3
  33. package/dist/index.d.ts +1 -2
  34. package/dist/index.js +67 -13
  35. package/dist/index.js.map +1 -1
  36. package/dist/{main-VEKNKWL5.js → main-2FKQQLWX.js} +489 -174
  37. package/dist/main-2FKQQLWX.js.map +1 -0
  38. package/dist/{test-only-LJGOY2QA.js → test-only-VH3FF5ML.js} +15 -9
  39. package/dist/test-only-VH3FF5ML.js.map +1 -0
  40. package/package.json +20 -9
  41. package/dist/chunk-233VGFHY.js.map +0 -1
  42. package/dist/chunk-AEQTEE5C.js.map +0 -1
  43. package/dist/chunk-DTFYHLYN.js.map +0 -1
  44. package/dist/chunk-HCYZN7I3.js.map +0 -1
  45. package/dist/chunk-PSJVYD4W.js.map +0 -1
  46. package/dist/chunk-Q5EYDKSA.js.map +0 -1
  47. package/dist/chunk-X44C5GW2.js.map +0 -1
  48. package/dist/main-VEKNKWL5.js.map +0 -1
  49. package/dist/test-only-LJGOY2QA.js.map +0 -1
  50. /package/dist/{bundle-TDQXWIPM.js.map → bundle-QJNCUMNY.js.map} +0 -0
@@ -3,7 +3,7 @@ import {
3
3
  isId,
4
4
  readTemplateFiles,
5
5
  validateManifest
6
- } from "./chunk-Q5EYDKSA.js";
6
+ } from "./chunk-2F7V6RZR.js";
7
7
 
8
8
  // src/utils/errors.ts
9
9
  var DatabrainApiError = class extends Error {
@@ -36,9 +36,19 @@ var DatabrainApiError = class extends Error {
36
36
  formatDetails() {
37
37
  if (this.status < 400 || this.status >= 500) return "";
38
38
  if (!this.details || typeof this.details !== "object") return "";
39
- const skipKeys = /* @__PURE__ */ new Set(["message", "detail", "error", "code", "data", "hint", "retryable"]);
39
+ const skipKeys = /* @__PURE__ */ new Set([
40
+ "message",
41
+ "detail",
42
+ "error",
43
+ "code",
44
+ "data",
45
+ "hint",
46
+ "retryable"
47
+ ]);
40
48
  const extra = {};
41
- for (const [k, v] of Object.entries(this.details)) {
49
+ for (const [k, v] of Object.entries(
50
+ this.details
51
+ )) {
42
52
  if (skipKeys.has(k)) continue;
43
53
  if (v === null || v === void 0) continue;
44
54
  extra[k] = v;
@@ -143,7 +153,8 @@ var logger = {
143
153
  var package_default = {
144
154
  name: "@databrainhq/mcp-server",
145
155
  mcpName: "com.usedatabrain/mcp-server",
146
- version: "0.3.0-rc.13",
156
+ version: "0.3.0-rc.15",
157
+ packageManager: "pnpm@12.3.4",
147
158
  description: "MCP server for Databrain embedded analytics - connect databases, create datamarts, configure embeds, and generate frontend code",
148
159
  type: "module",
149
160
  bin: {
@@ -161,13 +172,16 @@ var package_default = {
161
172
  ".mcp.json"
162
173
  ],
163
174
  scripts: {
164
- build: "tsup && node scripts/copy-custom-chart-assets.mjs",
175
+ build: "tsup && node scripts/build-declarations.mjs && node scripts/copy-custom-chart-assets.mjs",
165
176
  dev: "tsx src/index.ts",
166
177
  test: "vitest run",
167
178
  "test:watch": "vitest",
168
179
  "test:live": "vitest run tests/integration/prompt-flows.test.ts",
169
- lint: "tsc --noEmit",
170
- prepublishOnly: "npm run build"
180
+ lint: "biome check .",
181
+ "lint:fix": "biome check --write .",
182
+ format: "biome format --write .",
183
+ typecheck: "tsc --noEmit",
184
+ prepublishOnly: "pnpm run build"
171
185
  },
172
186
  keywords: [
173
187
  "mcp",
@@ -182,16 +196,23 @@ var package_default = {
182
196
  "@posthog/mcp": "^0.10.7",
183
197
  esbuild: "0.27.3",
184
198
  fflate: "0.8.3",
185
- "posthog-node": "^5.21.2",
199
+ "posthog-node": "5.60.1",
186
200
  yaml: "^2.9.1",
187
- zod: "^3.24.0"
201
+ zod: "^4.1.12"
188
202
  },
189
203
  devDependencies: {
204
+ "@biomejs/biome": "2.5.15",
205
+ "@microsoft/api-extractor": "7.59.4",
190
206
  "@types/node": "^25.3.5",
191
207
  tsup: "^8.4.0",
192
208
  tsx: "^4.19.0",
193
- typescript: "^5.7.0",
194
- vitest: "^3.0.0"
209
+ typescript: "7.0.2",
210
+ vitest: "^4.1.12"
211
+ },
212
+ overrides: {
213
+ "@posthog/core": "1.61.2",
214
+ "@posthog/types": "1.418.0",
215
+ rollup: "4.66.0"
195
216
  },
196
217
  publishConfig: {
197
218
  access: "public"
@@ -201,27 +222,175 @@ var package_default = {
201
222
  url: "git+https://github.com/databrainhq/mcp-server.git"
202
223
  },
203
224
  engines: {
204
- node: ">=18.0.0"
225
+ node: ">=20.0.0"
205
226
  }
206
227
  };
207
228
 
208
229
  // src/version.ts
209
230
  var MCP_SERVER_VERSION = package_default.version;
210
231
 
232
+ // src/bryn/catalogueCache.ts
233
+ var RateLimitedError = class extends Error {
234
+ constructor(retryAfterMs) {
235
+ super("rate_limited");
236
+ this.retryAfterMs = retryAfterMs;
237
+ this.name = "RateLimitedError";
238
+ }
239
+ };
240
+ function parseRetryAfter(header, now = Date.now()) {
241
+ const value = header?.trim();
242
+ if (!value) return void 0;
243
+ if (/^\d{1,9}$/.test(value)) return Number(value) * 1e3;
244
+ if (!/^[A-Za-z]{3},/.test(value)) return void 0;
245
+ const at = Date.parse(value);
246
+ return Number.isNaN(at) ? void 0 : Math.max(0, at - now);
247
+ }
248
+ var BrynCatalogueCache = class {
249
+ entries = /* @__PURE__ */ new Map();
250
+ ttlMs;
251
+ defaultBackoffMs;
252
+ maxBackoffMs;
253
+ maxKeys;
254
+ now;
255
+ constructor(options = {}) {
256
+ this.ttlMs = options.ttlMs ?? 6e4;
257
+ this.defaultBackoffMs = options.defaultBackoffMs ?? 5e3;
258
+ this.maxBackoffMs = options.maxBackoffMs ?? 12e4;
259
+ this.maxKeys = options.maxKeys ?? 512;
260
+ this.now = options.now ?? Date.now;
261
+ }
262
+ async get(key, load) {
263
+ let entry = this.entries.get(key);
264
+ if (!entry) {
265
+ entry = { fetchedAt: 0, blockedUntil: 0 };
266
+ this.entries.set(key, entry);
267
+ this.evict();
268
+ } else {
269
+ this.entries.delete(key);
270
+ this.entries.set(key, entry);
271
+ }
272
+ const now = this.now();
273
+ if (entry.value && now - entry.fetchedAt < this.ttlMs)
274
+ return { catalogue: entry.value, degraded: false };
275
+ if (now < entry.blockedUntil) return this.degraded(entry, now);
276
+ entry.inflight ??= this.refresh(entry, load);
277
+ await entry.inflight;
278
+ return entry.blockedUntil > this.now() || !entry.value ? this.degraded(entry, this.now()) : { catalogue: entry.value, degraded: false };
279
+ }
280
+ async refresh(entry, load) {
281
+ try {
282
+ entry.value = await load();
283
+ entry.fetchedAt = this.now();
284
+ entry.blockedUntil = 0;
285
+ } catch (error) {
286
+ const wait = error instanceof RateLimitedError && error.retryAfterMs !== void 0 ? error.retryAfterMs : this.defaultBackoffMs;
287
+ entry.blockedUntil = this.now() + Math.min(Math.max(wait, 1e3), this.maxBackoffMs);
288
+ } finally {
289
+ entry.inflight = void 0;
290
+ }
291
+ }
292
+ degraded(entry, now) {
293
+ return {
294
+ catalogue: entry.value ?? { tools: [], turns: [] },
295
+ degraded: true,
296
+ retryAfterSeconds: Math.max(
297
+ 1,
298
+ Math.ceil((entry.blockedUntil - now) / 1e3)
299
+ )
300
+ };
301
+ }
302
+ evict() {
303
+ while (this.entries.size > this.maxKeys) {
304
+ const oldest = this.entries.keys().next().value;
305
+ if (oldest === void 0) return;
306
+ this.entries.delete(oldest);
307
+ }
308
+ }
309
+ };
310
+
311
+ // src/bryn/stream.ts
312
+ var MAX_BRYN_STREAM_BYTES = 8 * 1024 * 1024;
313
+ async function readBrynStream(response) {
314
+ if (!response.body) throw new Error("Bryn stream is empty");
315
+ const reader = response.body.getReader();
316
+ const decoder = new TextDecoder();
317
+ let size = 0;
318
+ let text = "";
319
+ try {
320
+ for (; ; ) {
321
+ const { done, value } = await reader.read();
322
+ if (done) break;
323
+ size += value.byteLength;
324
+ if (size > MAX_BRYN_STREAM_BYTES)
325
+ throw new Error("Bryn stream too large");
326
+ text += decoder.decode(value, { stream: true });
327
+ }
328
+ return text + decoder.decode();
329
+ } finally {
330
+ await reader.cancel().catch(() => void 0);
331
+ reader.releaseLock();
332
+ }
333
+ }
334
+ function parseBrynStream(text) {
335
+ if (text.length > MAX_BRYN_STREAM_BYTES)
336
+ throw new Error("Bryn stream too large");
337
+ let sessionId;
338
+ let final;
339
+ let failed = false;
340
+ const results = [];
341
+ for (const frame of text.split(/\r?\n\r?\n/)) {
342
+ const line = frame.split(/\r?\n/).filter((part) => part.startsWith("data:")).map((part) => part.slice(5).trimStart()).join("\n");
343
+ if (!line || line === "[DONE]") continue;
344
+ const event = JSON.parse(line);
345
+ if (event.v !== 2) continue;
346
+ if (event.type === "session" && typeof event.sessionId === "string")
347
+ sessionId = event.sessionId;
348
+ if (event.type === "error" || event.type === "aborted") failed = true;
349
+ if (event.type === "final" && event.data && typeof event.data === "object")
350
+ final = event.data;
351
+ if (event.type === "tool_execution_end" && typeof event.name === "string") {
352
+ results.push({
353
+ name: event.name,
354
+ status: typeof event.status === "string" ? event.status : "unknown",
355
+ ...typeof event.summary === "string" ? { summary: event.summary } : {},
356
+ // The signed-in caller may review draft effects. Results are never
357
+ // persisted by MCP, and the 4 MiB frame budget bounds this projection.
358
+ ...event.name === "update_theme" || event.name === "update_custom_chart" ? { result: event.result } : {}
359
+ });
360
+ }
361
+ }
362
+ if (!final || failed) throw new Error("Bryn turn did not complete");
363
+ const stopReason = typeof final.stopReason === "string" ? final.stopReason : typeof final.reason === "string" ? final.reason : void 0;
364
+ return {
365
+ ...sessionId ? { sessionId } : {},
366
+ message: typeof final.message === "string" ? final.message : "",
367
+ ...stopReason ? { stopReason } : {},
368
+ completed: stopReason === "end_turn" || stopReason === "goal_complete" || stopReason === "awaiting_user",
369
+ results
370
+ };
371
+ }
372
+
211
373
  // src/custom-chart/studio.ts
212
374
  import { z } from "zod";
213
- var studioCell = z.union([z.string().max(4096), z.number().finite(), z.boolean(), z.null()]);
375
+ var studioCell = z.union([
376
+ z.string().max(4096),
377
+ z.number().finite(),
378
+ z.boolean(),
379
+ z.null()
380
+ ]);
214
381
  var studioDraftSchema = z.object({
215
382
  code: z.string().max(64e3),
216
383
  css: z.string().max(32e3),
217
- manifest: z.record(z.unknown()),
384
+ manifest: z.record(z.string(), z.unknown()),
218
385
  sampleData: z.object({
219
- columns: z.array(z.object({
220
- id: z.string(),
221
- label: z.string(),
222
- type: z.enum(["string", "number", "boolean"]),
223
- slotId: z.string().optional()
224
- }).strict()).max(64),
386
+ columns: z.array(
387
+ z.object({
388
+ id: z.string(),
389
+ label: z.string(),
390
+ type: z.enum(["string", "number", "boolean"]),
391
+ slotId: z.string().optional()
392
+ }).strict()
393
+ ).max(64),
225
394
  rows: z.array(z.array(studioCell)).max(200)
226
395
  }).strict(),
227
396
  name: z.string().max(256),
@@ -237,7 +406,8 @@ function parseStudioStream(text) {
237
406
  const line = frame.split(/\r?\n/).filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trimStart()).join("\n");
238
407
  if (!line || line === "[DONE]") continue;
239
408
  const event = JSON.parse(line);
240
- if (event.type === "session" && typeof event.sessionId === "string") sessionId = event.sessionId;
409
+ if (event.type === "session" && typeof event.sessionId === "string")
410
+ sessionId = event.sessionId;
241
411
  if (event.type === "error") failure = true;
242
412
  if (event.type === "tool_execution_end" && event.name === "update_custom_chart" && event.status === "completed" && event.result?.kind === "applied") {
243
413
  draft = studioDraftSchema.parse(event.result.draft);
@@ -247,7 +417,8 @@ function parseStudioStream(text) {
247
417
  return { draft, sessionId, applied: !!draft };
248
418
  }
249
419
  async function editWithBryn(client, input) {
250
- if (!client.isHosted()) return { available: false, draft: input.draft, applied: false };
420
+ if (!client.isHosted())
421
+ return { available: false, draft: input.draft, applied: false };
251
422
  try {
252
423
  const result = await client.request({
253
424
  method: "POST",
@@ -265,7 +436,12 @@ async function editWithBryn(client, input) {
265
436
  timeoutMs: 18e4,
266
437
  noRetryOnTimeout: true
267
438
  });
268
- return { available: true, draft: result.draft ?? input.draft, applied: result.applied, sessionId: result.sessionId };
439
+ return {
440
+ available: true,
441
+ draft: result.draft ?? input.draft,
442
+ applied: result.applied,
443
+ sessionId: result.sessionId
444
+ };
269
445
  } catch (error) {
270
446
  if (error instanceof DatabrainApiError && error.status === 404) {
271
447
  return { available: false, draft: input.draft, applied: false };
@@ -301,6 +477,10 @@ var TtlCache = class {
301
477
  }
302
478
  };
303
479
 
480
+ // src/utils/tool-context.ts
481
+ import { AsyncLocalStorage } from "async_hooks";
482
+ var toolContext = new AsyncLocalStorage();
483
+
304
484
  // src/client/error-body.ts
305
485
  var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
306
486
  var str = (v) => typeof v === "string" && v.trim() ? v : void 0;
@@ -324,10 +504,6 @@ function parseErrorBody(body) {
324
504
  return { message: str(body.error) };
325
505
  }
326
506
 
327
- // src/utils/tool-context.ts
328
- import { AsyncLocalStorage } from "async_hooks";
329
- var toolContext = new AsyncLocalStorage();
330
-
331
507
  // src/client/oauth-client-credentials.ts
332
508
  import { z as z2 } from "zod";
333
509
  var oauthHttpsUrl = z2.string().refine((value) => {
@@ -367,7 +543,9 @@ var TOKEN_TIMEOUT_MS = 1e4;
367
543
  var FAILURE_COOLDOWN_MS = 3e4;
368
544
  var MAX_REFRESH_SKEW_MS = 6e4;
369
545
  function authError() {
370
- return new Error("OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes.");
546
+ return new Error(
547
+ "OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes."
548
+ );
371
549
  }
372
550
  var OAuthClientCredentials = class {
373
551
  config;
@@ -386,7 +564,8 @@ var OAuthClientCredentials = class {
386
564
  return "configured";
387
565
  }
388
566
  async getToken() {
389
- if (this.cached && Date.now() < this.cached.renewAt) return this.cached.value;
567
+ if (this.cached && Date.now() < this.cached.renewAt)
568
+ return this.cached.value;
390
569
  if (this.pending) return this.pending;
391
570
  if (Date.now() < this.retryAt) throw authError();
392
571
  this.cached = void 0;
@@ -402,11 +581,13 @@ var OAuthClientCredentials = class {
402
581
  if (this.tokenEndpoint) return this.tokenEndpoint;
403
582
  const issuer = new URL(this.config.issuer);
404
583
  const path = issuer.pathname.replace(/\/$/, "");
405
- const candidates = [.../* @__PURE__ */ new Set([
406
- `${issuer.origin}/.well-known/oauth-authorization-server${path}`,
407
- `${issuer.origin}/.well-known/openid-configuration${path}`,
408
- `${issuer.origin}${path}/.well-known/openid-configuration`
409
- ])];
584
+ const candidates = [
585
+ .../* @__PURE__ */ new Set([
586
+ `${issuer.origin}/.well-known/oauth-authorization-server${path}`,
587
+ `${issuer.origin}/.well-known/openid-configuration${path}`,
588
+ `${issuer.origin}${path}/.well-known/openid-configuration`
589
+ ])
590
+ ];
410
591
  for (const url of candidates) {
411
592
  const response = await fetch(url, {
412
593
  headers: { Accept: "application/json" },
@@ -417,7 +598,9 @@ var OAuthClientCredentials = class {
417
598
  if (!response.ok) throw authError();
418
599
  const metadata = metadataSchema.parse(await response.json());
419
600
  if (metadata.issuer !== this.config.issuer) throw authError();
420
- const methods = metadata.token_endpoint_auth_methods_supported ?? ["client_secret_basic"];
601
+ const methods = metadata.token_endpoint_auth_methods_supported ?? [
602
+ "client_secret_basic"
603
+ ];
421
604
  if (!methods.includes(this.config.authMethod)) throw authError();
422
605
  if (metadata.grant_types_supported && !metadata.grant_types_supported.includes("client_credentials")) {
423
606
  throw authError();
@@ -459,7 +642,8 @@ var OAuthClientCredentials = class {
459
642
  if (token.expires_in !== void 0) {
460
643
  const lifetimeMs = token.expires_in * 1e3;
461
644
  const expiresAt = startedAt + lifetimeMs;
462
- if (!Number.isFinite(expiresAt) || Date.now() >= expiresAt) throw authError();
645
+ if (!Number.isFinite(expiresAt) || Date.now() >= expiresAt)
646
+ throw authError();
463
647
  this.cached = {
464
648
  value: token.access_token,
465
649
  renewAt: expiresAt - Math.min(MAX_REFRESH_SKEW_MS, lifetimeMs * 0.1)
@@ -491,7 +675,9 @@ var DatabrainClient = class {
491
675
  registerSecret(config.apiToken);
492
676
  if (config.oauth) {
493
677
  if (!oauthHttpsUrl.safeParse(config.apiUrl).success) {
494
- throw new Error("OAuth requires an HTTPS Databrain API URL without userinfo or fragments.");
678
+ throw new Error(
679
+ "OAuth requires an HTTPS Databrain API URL without userinfo or fragments."
680
+ );
495
681
  }
496
682
  this.oauth = new OAuthClientCredentials(config.oauth);
497
683
  this.config = Object.freeze({ ...config });
@@ -528,11 +714,16 @@ var DatabrainClient = class {
528
714
  */
529
715
  getActiveAuth() {
530
716
  if (this.oauth) {
531
- if (this.oauthFallback) return { source: "service-token", oauthFallback: true };
532
- return { source: "oauth-client-credentials", status: this.oauth.getStatus() };
717
+ if (this.oauthFallback)
718
+ return { source: "service-token", oauthFallback: true };
719
+ return {
720
+ source: "oauth-client-credentials",
721
+ status: this.oauth.getStatus()
722
+ };
533
723
  }
534
724
  if (this.config.serviceToken) return { source: "service-token" };
535
- if (this.runtimeApiToken || this.config.apiToken) return { source: "api-token" };
725
+ if (this.runtimeApiToken || this.config.apiToken)
726
+ return { source: "api-token" };
536
727
  return { source: "none" };
537
728
  }
538
729
  async getToken(auth) {
@@ -551,7 +742,10 @@ var DatabrainClient = class {
551
742
  "OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes."
552
743
  );
553
744
  }
554
- if (!this.oauthFallback) logger.warn("OAuth unavailable; using the configured static service token.");
745
+ if (!this.oauthFallback)
746
+ logger.warn(
747
+ "OAuth unavailable; using the configured static service token."
748
+ );
555
749
  this.oauthFallback = true;
556
750
  }
557
751
  }
@@ -639,7 +833,9 @@ var DatabrainClient = class {
639
833
  );
640
834
  }
641
835
  const errorBody = await response.text();
642
- logger.warn(`[API Error] ${options.method} ${options.path} status=${response.status} body=${errorBody.slice(0, 500)}`);
836
+ logger.warn(
837
+ `[API Error] ${options.method} ${options.path} status=${response.status} body=${errorBody.slice(0, 500)}`
838
+ );
643
839
  let parsed = {};
644
840
  try {
645
841
  parsed = JSON.parse(errorBody);
@@ -657,19 +853,23 @@ var DatabrainClient = class {
657
853
  );
658
854
  const isTransient = envelope.retryable !== void 0 ? envelope.retryable && (response.status === 429 || response.status >= 500) : response.status === 429 || response.status >= 500 && !NON_TRANSIENT_PATTERNS.some((p) => errorMsg.includes(p));
659
855
  if (isTransient && attempt < MAX_RETRIES) {
660
- const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 8e3);
661
- logger.warn(`Retrying in ${delay}ms (attempt ${attempt}/${MAX_RETRIES})`, {
662
- status: response.status,
663
- path: options.path
664
- });
856
+ const retryAfter = response.status === 429 ? parseRetryAfter(response.headers.get("retry-after")) : void 0;
857
+ const delay = retryAfter !== void 0 ? Math.min(Math.max(retryAfter, 1e3), 3e4) : Math.min(1e3 * 2 ** (attempt - 1), 8e3);
858
+ logger.warn(
859
+ `Retrying in ${delay}ms (attempt ${attempt}/${MAX_RETRIES})`,
860
+ {
861
+ status: response.status,
862
+ path: options.path
863
+ }
864
+ );
665
865
  await new Promise((r) => setTimeout(r, delay));
666
866
  lastError = error;
667
867
  continue;
668
868
  }
669
869
  throw error;
670
870
  }
671
- const text = await response.text();
672
- let data = options.responseFormat === "studio-sse" ? parseStudioStream(text) : text ? JSON.parse(text) : {};
871
+ const text = options.responseFormat === "bryn-sse" ? await readBrynStream(response) : await response.text();
872
+ let data = options.responseFormat === "studio-sse" ? parseStudioStream(text) : options.responseFormat === "bryn-sse" ? parseBrynStream(text) : text ? JSON.parse(text) : {};
673
873
  if (!options.skipDataUnwrap && data && typeof data === "object" && !Array.isArray(data) && "data" in data) {
674
874
  const inner = data.data;
675
875
  if (inner !== null && inner !== void 0) {
@@ -683,8 +883,10 @@ var DatabrainClient = class {
683
883
  } catch (err) {
684
884
  if (err instanceof DatabrainApiError) throw err;
685
885
  if (attempt < MAX_RETRIES && err?.name === "TimeoutError" && !options.noRetryOnTimeout) {
686
- const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 8e3);
687
- logger.warn(`Request timed out, retrying in ${delay}ms`, { path: options.path });
886
+ const delay = Math.min(1e3 * 2 ** (attempt - 1), 8e3);
887
+ logger.warn(`Request timed out, retrying in ${delay}ms`, {
888
+ path: options.path
889
+ });
688
890
  await new Promise((r) => setTimeout(r, delay));
689
891
  lastError = err;
690
892
  continue;
@@ -797,7 +999,12 @@ function apiProblems(err) {
797
999
  }
798
1000
  function planUpload(key, existing, versionsKnown) {
799
1001
  if (!existing) {
800
- return { action: "create chart", key, nextVersionNumber: 1, summary: `will create chart custom:${key} (version 1, status private)` };
1002
+ return {
1003
+ action: "create chart",
1004
+ key,
1005
+ nextVersionNumber: 1,
1006
+ summary: `will create chart custom:${key} (version 1, status private)`
1007
+ };
801
1008
  }
802
1009
  const count = typeof versionsKnown === "number" ? versionsKnown : typeof existing.versionsCount === "number" ? existing.versionsCount : void 0;
803
1010
  const next = count !== void 0 ? count + 1 : NaN;
@@ -813,9 +1020,28 @@ function planUpload(key, existing, versionsKnown) {
813
1020
 
814
1021
  // src/custom-chart/scaffold.ts
815
1022
  var DEFAULT_SLOTS = [
816
- { id: "category", label: "Category", role: "dimension", types: ["string"], required: true, description: "One bar per value." },
817
- { id: "value", label: "Value", role: "measure", types: ["number"], required: true },
818
- { id: "target", label: "Target", role: "measure", types: ["number"], required: false }
1023
+ {
1024
+ id: "category",
1025
+ label: "Category",
1026
+ role: "dimension",
1027
+ types: ["string"],
1028
+ required: true,
1029
+ description: "One bar per value."
1030
+ },
1031
+ {
1032
+ id: "value",
1033
+ label: "Value",
1034
+ role: "measure",
1035
+ types: ["number"],
1036
+ required: true
1037
+ },
1038
+ {
1039
+ id: "target",
1040
+ label: "Target",
1041
+ role: "measure",
1042
+ types: ["number"],
1043
+ required: false
1044
+ }
819
1045
  ];
820
1046
  function scaffoldProject(input) {
821
1047
  const template = readTemplateFiles();
@@ -830,7 +1056,11 @@ function scaffoldProject(input) {
830
1056
  }
831
1057
  const name = typeof input.name === "string" ? input.name.trim() : "";
832
1058
  if (name.length === 0 || name.length > 256) {
833
- problems.push({ code: "manifest.name.invalid", path: "/name", message: "name must be 1 to 256 characters." });
1059
+ problems.push({
1060
+ code: "manifest.name.invalid",
1061
+ path: "/name",
1062
+ message: "name must be 1 to 256 characters."
1063
+ });
834
1064
  }
835
1065
  const manifest = {
836
1066
  schemaVersion: 2,
@@ -846,8 +1076,12 @@ function scaffoldProject(input) {
846
1076
  packageJson.name = input.key;
847
1077
  const files = {};
848
1078
  for (const [file, content] of Object.entries(template)) {
849
- if (file === "manifest.json") files[file] = JSON.stringify(manifest, null, 2) + "\n";
850
- else if (file === "package.json") files[file] = JSON.stringify(packageJson, null, 2) + "\n";
1079
+ if (file === "manifest.json")
1080
+ files[file] = `${JSON.stringify(manifest, null, 2)}
1081
+ `;
1082
+ else if (file === "package.json")
1083
+ files[file] = `${JSON.stringify(packageJson, null, 2)}
1084
+ `;
851
1085
  else files[file] = content;
852
1086
  }
853
1087
  return {
@@ -859,21 +1093,26 @@ function scaffoldProject(input) {
859
1093
  }
860
1094
 
861
1095
  export {
862
- oauthHttpsUrl,
863
- oauthConfigSchema,
864
1096
  DatabrainApiError,
865
1097
  toolError,
866
1098
  stringify,
867
1099
  toolSuccess,
1100
+ RateLimitedError,
1101
+ parseRetryAfter,
1102
+ BrynCatalogueCache,
1103
+ readBrynStream,
1104
+ parseBrynStream,
868
1105
  studioDraftSchema,
869
1106
  parseStudioStream,
870
1107
  editWithBryn,
871
1108
  registerSecret,
872
1109
  redactSecrets,
873
1110
  logger,
874
- parseErrorBody,
875
- MCP_SERVER_VERSION,
876
1111
  toolContext,
1112
+ MCP_SERVER_VERSION,
1113
+ parseErrorBody,
1114
+ oauthHttpsUrl,
1115
+ oauthConfigSchema,
877
1116
  DatabrainClient,
878
1117
  CustomChartsApi,
879
1118
  createCustomChartsApi,
@@ -881,4 +1120,4 @@ export {
881
1120
  planUpload,
882
1121
  scaffoldProject
883
1122
  };
884
- //# sourceMappingURL=chunk-AEQTEE5C.js.map
1123
+ //# sourceMappingURL=chunk-4E57ZRZO.js.map