@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.
- package/.mcp.json +6 -1
- package/dist/apps/server.d.ts +102 -389
- package/dist/apps/server.js +5 -5
- package/dist/apps/stdio.d.ts +1 -2
- package/dist/apps/stdio.js +6 -6
- package/dist/{bundle-TDQXWIPM.js → bundle-QJNCUMNY.js} +2 -2
- package/dist/{chunk-Q5EYDKSA.js → chunk-2F7V6RZR.js} +511 -107
- package/dist/chunk-2F7V6RZR.js.map +1 -0
- package/dist/chunk-3AH56XX7.js +28 -0
- package/dist/chunk-3AH56XX7.js.map +1 -0
- package/dist/{chunk-AEQTEE5C.js → chunk-4E57ZRZO.js} +302 -63
- package/dist/chunk-4E57ZRZO.js.map +1 -0
- package/dist/{chunk-PSJVYD4W.js → chunk-6MD7KOAU.js} +11723 -10560
- package/dist/chunk-6MD7KOAU.js.map +1 -0
- package/dist/{chunk-HCYZN7I3.js → chunk-FJLRZJ4A.js} +10 -7
- package/dist/chunk-FJLRZJ4A.js.map +1 -0
- package/dist/{chunk-X44C5GW2.js → chunk-MUZ3KEJX.js} +63 -40
- package/dist/chunk-MUZ3KEJX.js.map +1 -0
- package/dist/{chunk-DTFYHLYN.js → chunk-NXE2A4WV.js} +26 -11
- package/dist/chunk-NXE2A4WV.js.map +1 -0
- package/dist/{chunk-233VGFHY.js → chunk-QQ6KNULX.js} +317 -147
- package/dist/chunk-QQ6KNULX.js.map +1 -0
- package/dist/chunk-V5JINLOA.js +506 -0
- package/dist/chunk-V5JINLOA.js.map +1 -0
- package/dist/cli.d.ts +16 -15
- package/dist/cli.js +134 -36
- package/dist/cli.js.map +1 -1
- package/dist/custom-chart/custom-runtime.js +6 -6
- package/dist/custom-chart/template/chart.ts +116 -28
- package/dist/custom-chart/template/icon.svg +1 -1
- package/dist/custom-chart/template/manifest.json +66 -12
- package/dist/custom-chart/template/sample-data.json +21 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.js +67 -13
- package/dist/index.js.map +1 -1
- package/dist/{main-VEKNKWL5.js → main-2FKQQLWX.js} +489 -174
- package/dist/main-2FKQQLWX.js.map +1 -0
- package/dist/{test-only-LJGOY2QA.js → test-only-VH3FF5ML.js} +15 -9
- package/dist/test-only-VH3FF5ML.js.map +1 -0
- package/package.json +20 -9
- package/dist/chunk-233VGFHY.js.map +0 -1
- package/dist/chunk-AEQTEE5C.js.map +0 -1
- package/dist/chunk-DTFYHLYN.js.map +0 -1
- package/dist/chunk-HCYZN7I3.js.map +0 -1
- package/dist/chunk-PSJVYD4W.js.map +0 -1
- package/dist/chunk-Q5EYDKSA.js.map +0 -1
- package/dist/chunk-X44C5GW2.js.map +0 -1
- package/dist/main-VEKNKWL5.js.map +0 -1
- package/dist/test-only-LJGOY2QA.js.map +0 -1
- /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-
|
|
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([
|
|
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(
|
|
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.
|
|
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: "
|
|
170
|
-
|
|
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": "
|
|
199
|
+
"posthog-node": "5.60.1",
|
|
186
200
|
yaml: "^2.9.1",
|
|
187
|
-
zod: "^
|
|
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: "
|
|
194
|
-
vitest: "^
|
|
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: ">=
|
|
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([
|
|
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(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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")
|
|
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())
|
|
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 {
|
|
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(
|
|
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)
|
|
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 = [
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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 ?? [
|
|
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)
|
|
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(
|
|
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)
|
|
532
|
-
|
|
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)
|
|
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)
|
|
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(
|
|
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
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
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 *
|
|
687
|
-
logger.warn(`Request timed out, retrying in ${delay}ms`, {
|
|
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 {
|
|
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
|
-
{
|
|
817
|
-
|
|
818
|
-
|
|
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({
|
|
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")
|
|
850
|
-
|
|
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-
|
|
1123
|
+
//# sourceMappingURL=chunk-4E57ZRZO.js.map
|