@databrainhq/mcp-server 0.3.0-rc.12 → 0.3.0-rc.14

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 (46) 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-347U6GXR.js → chunk-AWICHHDU.js} +153 -61
  10. package/dist/chunk-AWICHHDU.js.map +1 -0
  11. package/dist/{chunk-Z3ZHN4UW.js → chunk-CEGHG5U3.js} +59 -18
  12. package/dist/chunk-CEGHG5U3.js.map +1 -0
  13. package/dist/{chunk-ZTHGF27R.js → chunk-NIC5MAGC.js} +317 -147
  14. package/dist/chunk-NIC5MAGC.js.map +1 -0
  15. package/dist/{chunk-V3UW3B6U.js → chunk-S65KA3Z7.js} +26 -11
  16. package/dist/chunk-S65KA3Z7.js.map +1 -0
  17. package/dist/{chunk-OLOC2MTA.js → chunk-UYFFFAYV.js} +11722 -10560
  18. package/dist/chunk-UYFFFAYV.js.map +1 -0
  19. package/dist/{chunk-LACJHQYH.js → chunk-VQ5G5AZV.js} +10 -7
  20. package/dist/chunk-VQ5G5AZV.js.map +1 -0
  21. package/dist/cli.d.ts +16 -15
  22. package/dist/cli.js +134 -36
  23. package/dist/cli.js.map +1 -1
  24. package/dist/custom-chart/custom-runtime.js +6 -6
  25. package/dist/custom-chart/template/chart.ts +116 -28
  26. package/dist/custom-chart/template/icon.svg +1 -1
  27. package/dist/custom-chart/template/manifest.json +66 -12
  28. package/dist/custom-chart/template/sample-data.json +21 -3
  29. package/dist/index.d.ts +1 -2
  30. package/dist/index.js +20 -10
  31. package/dist/index.js.map +1 -1
  32. package/dist/{main-IUV24DMP.js → main-QCBVJSSR.js} +363 -155
  33. package/dist/main-QCBVJSSR.js.map +1 -0
  34. package/dist/{test-only-K7QTSVO4.js → test-only-6474NMTR.js} +12 -8
  35. package/dist/test-only-6474NMTR.js.map +1 -0
  36. package/package.json +20 -9
  37. package/dist/chunk-347U6GXR.js.map +0 -1
  38. package/dist/chunk-LACJHQYH.js.map +0 -1
  39. package/dist/chunk-OLOC2MTA.js.map +0 -1
  40. package/dist/chunk-Q5EYDKSA.js.map +0 -1
  41. package/dist/chunk-V3UW3B6U.js.map +0 -1
  42. package/dist/chunk-Z3ZHN4UW.js.map +0 -1
  43. package/dist/chunk-ZTHGF27R.js.map +0 -1
  44. package/dist/main-IUV24DMP.js.map +0 -1
  45. package/dist/test-only-K7QTSVO4.js.map +0 -1
  46. /package/dist/{bundle-TDQXWIPM.js.map → bundle-QJNCUMNY.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  registerChartApps
4
- } from "./chunk-ZTHGF27R.js";
4
+ } from "./chunk-NIC5MAGC.js";
5
5
  import {
6
6
  PREVIEW_PATH,
7
7
  WRITE_CREATE,
@@ -9,7 +9,7 @@ import {
9
9
  registerAllPrompts,
10
10
  registerAllResources,
11
11
  registerAllTools
12
- } from "./chunk-OLOC2MTA.js";
12
+ } from "./chunk-UYFFFAYV.js";
13
13
  import {
14
14
  DatabrainApiError,
15
15
  DatabrainClient,
@@ -20,15 +20,15 @@ import {
20
20
  toolContext,
21
21
  toolError,
22
22
  toolSuccess
23
- } from "./chunk-347U6GXR.js";
24
- import "./chunk-Q5EYDKSA.js";
23
+ } from "./chunk-AWICHHDU.js";
24
+ import "./chunk-2F7V6RZR.js";
25
25
  import {
26
26
  FilePatStore,
27
27
  HOSTED_SCOPES,
28
28
  HostedAuthError,
29
29
  createBearerVerifier,
30
30
  principalSchema
31
- } from "./chunk-Z3ZHN4UW.js";
31
+ } from "./chunk-CEGHG5U3.js";
32
32
 
33
33
  // src/hosted/main.ts
34
34
  import { isAbsolute } from "path";
@@ -37,16 +37,21 @@ import { pathToFileURL } from "url";
37
37
  // src/hosted/config.ts
38
38
  function loadHostedConfig(env = process.env) {
39
39
  const testOnly = env.DATABRAIN_HOSTED_TEST_ONLY === "1";
40
- if (testOnly && env.NODE_ENV === "production") throw new Error("Hosted test mode is forbidden in production.");
41
- if (env.DATABRAIN_HOSTED_TEST_ONLY && !testOnly) throw new Error("Invalid DATABRAIN_HOSTED_TEST_ONLY.");
40
+ if (testOnly && env.NODE_ENV === "production")
41
+ throw new Error("Hosted test mode is forbidden in production.");
42
+ if (env.DATABRAIN_HOSTED_TEST_ONLY && !testOnly)
43
+ throw new Error("Invalid DATABRAIN_HOSTED_TEST_ONLY.");
42
44
  const validUrl = (value, field) => {
43
45
  try {
44
46
  if (!value) throw new Error();
45
47
  const url = new URL(value);
46
- if (url.username || url.password || url.search || url.hash || url.protocol !== "https:") throw new Error();
48
+ if (url.username || url.password || url.search || url.hash || url.protocol !== "https:")
49
+ throw new Error();
47
50
  return url;
48
51
  } catch {
49
- throw new Error(`Configure ${field} as an HTTPS URL without credentials, query or fragment.`);
52
+ throw new Error(
53
+ `Configure ${field} as an HTTPS URL without credentials, query or fragment.`
54
+ );
50
55
  }
51
56
  };
52
57
  let publicValue = env.DATABRAIN_HOSTED_PUBLIC_URL;
@@ -58,13 +63,31 @@ function loadHostedConfig(env = process.env) {
58
63
  }
59
64
  const publicUrl = validUrl(publicValue, "DATABRAIN_HOSTED_PUBLIC_URL");
60
65
  validUrl(issuerValue, "DATABRAIN_HOSTED_ISSUER");
61
- if (publicUrl.pathname !== "/mcp") throw new Error("DATABRAIN_HOSTED_PUBLIC_URL must end in /mcp.");
66
+ if (publicUrl.pathname !== "/mcp")
67
+ throw new Error("DATABRAIN_HOSTED_PUBLIC_URL must end in /mcp.");
62
68
  const port = Number(env.DATABRAIN_HOSTED_PORT || "3144");
63
69
  const bind = env.DATABRAIN_HOSTED_BIND || "127.0.0.1";
64
- if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error("Invalid DATABRAIN_HOSTED_PORT.");
70
+ if (!Number.isInteger(port) || port < 1024 || port > 65535)
71
+ throw new Error("Invalid DATABRAIN_HOSTED_PORT.");
72
+ let apiUrl;
73
+ if (env.DATABRAIN_HOSTED_API_URL) {
74
+ let url;
75
+ try {
76
+ url = new URL(env.DATABRAIN_HOSTED_API_URL);
77
+ } catch {
78
+ url = void 0;
79
+ }
80
+ if (!url || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== "/") {
81
+ throw new Error(
82
+ "Configure DATABRAIN_HOSTED_API_URL as an origin without credentials, path, query or fragment."
83
+ );
84
+ }
85
+ apiUrl = url.origin;
86
+ }
65
87
  return {
66
88
  publicUrl: publicValue,
67
89
  issuer: issuerValue,
90
+ ...apiUrl ? { apiUrl } : {},
68
91
  port,
69
92
  bind,
70
93
  testOnly
@@ -72,64 +95,11 @@ function loadHostedConfig(env = process.env) {
72
95
  }
73
96
 
74
97
  // src/hosted/http.ts
75
- import { createServer } from "http";
98
+ import {
99
+ createServer
100
+ } from "http";
76
101
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
77
102
 
78
- // src/hosted/server.ts
79
- import { AsyncLocalStorage } from "async_hooks";
80
- import { z as z2 } from "zod";
81
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
82
- import { instrument } from "@posthog/mcp";
83
- import { PostHog } from "posthog-node";
84
-
85
- // src/tools/infrastructure/create-workspace.ts
86
- import { z } from "zod";
87
- function registerCreateWorkspace(server, client) {
88
- server.tool(
89
- "create_workspace",
90
- 'Create a new workspace to connect a datasource or datamart for dashboard building. Works only when this sign-in granted "All workspaces I can access" and your role may create workspaces; the new workspace is usable on the next call.',
91
- {
92
- name: z.string().describe("Unique workspace name"),
93
- connectionType: z.enum(["DATASOURCE", "DATAMART", "MULTI_DATASOURCE"]).optional().describe("Connection type \u2014 DATASOURCE, DATAMART, or MULTI_DATASOURCE"),
94
- datasourceName: z.string().optional().describe("Datasource name (required when connectionType is DATASOURCE)"),
95
- datamartName: z.string().optional().describe("Datamart name (required when connectionType is DATAMART)"),
96
- body: z.record(z.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/workspace")
97
- },
98
- WRITE_CREATE,
99
- async (params) => {
100
- try {
101
- const body = params.body ? { ...params.body } : {
102
- name: params.name,
103
- connectionType: params.connectionType,
104
- datasourceName: params.datasourceName,
105
- datamartName: params.datamartName
106
- };
107
- const result = await client.request({
108
- method: "POST",
109
- path: "/api/v2/workspace",
110
- body,
111
- auth: "service",
112
- invalidateCache: ["list_workspaces"]
113
- });
114
- if (result.error) {
115
- return toolError(`Workspace creation failed: ${stringify(result.error)}`);
116
- }
117
- return toolSuccess(
118
- `Workspace "${result.name || params.name}" created successfully.
119
-
120
- Next steps:
121
- 1. Use \`create_data_app\` to create a data app linked to this workspace
122
- 2. Build dashboards in the Databrain UI using this workspace
123
- 3. Use \`create_embed\` to embed dashboards`
124
- );
125
- } catch (err) {
126
- if (err instanceof DatabrainApiError) return err.toToolResponse();
127
- return toolError(`Unexpected error: ${err.message}`);
128
- }
129
- }
130
- );
131
- }
132
-
133
103
  // src/hosted/authorization.ts
134
104
  var TOOL_SCOPES = {
135
105
  whoami: [],
@@ -158,7 +128,11 @@ var TOOL_SCOPES = {
158
128
  create_dashboard: ["mcp:dashboards:write"],
159
129
  get_dashboard: ["mcp:dashboards:read"],
160
130
  dashboard_snapshot: ["mcp:dashboards:read"],
161
- generate_dashboard_tiles: ["mcp:dashboards:write", "mcp:query", "mcp:embeds:write"],
131
+ generate_dashboard_tiles: [
132
+ "mcp:dashboards:write",
133
+ "mcp:query",
134
+ "mcp:embeds:write"
135
+ ],
162
136
  ask_question: ["mcp:query"],
163
137
  get_semantic_layer: ["mcp:datamarts:read"],
164
138
  update_semantic_layer: ["mcp:datamarts:write"],
@@ -170,8 +144,16 @@ var TOOL_SCOPES = {
170
144
  apply_semantic_change: ["mcp:datamarts:write"],
171
145
  eval_semantic_model: ["mcp:datamarts:read", "mcp:query"],
172
146
  list_widgets: ["mcp:dashboards:read"],
173
- validate_metric_spec: ["mcp:dashboards:read", "mcp:query", "mcp:embeds:write"],
174
- create_or_update_metric_from_query: ["mcp:dashboards:write", "mcp:query", "mcp:embeds:write"],
147
+ validate_metric_spec: [
148
+ "mcp:dashboards:read",
149
+ "mcp:query",
150
+ "mcp:embeds:write"
151
+ ],
152
+ create_or_update_metric_from_query: [
153
+ "mcp:dashboards:write",
154
+ "mcp:query",
155
+ "mcp:embeds:write"
156
+ ],
175
157
  create_workspace_metric: ["mcp:dashboards:write", "mcp:query"],
176
158
  add_widget: ["mcp:dashboards:write", "mcp:embeds:write"],
177
159
  update_widget: ["mcp:dashboards:write", "mcp:embeds:write"],
@@ -199,12 +181,15 @@ var TOOL_SCOPES = {
199
181
  change_date_range: ["mcp:dashboards:read", "mcp:query"]
200
182
  };
201
183
  function requiredScopes(name) {
202
- if (!Object.hasOwn(TOOL_SCOPES, name)) throw new HostedAuthError(403, "tool_not_authorized");
184
+ if (!Object.hasOwn(TOOL_SCOPES, name))
185
+ throw new HostedAuthError(403, "tool_not_authorized");
203
186
  return TOOL_SCOPES[name];
204
187
  }
205
188
  function authorizeTool(name, principal) {
206
- if (principal.expiresAt !== void 0 && principal.expiresAt <= Date.now() / 1e3) throw new HostedAuthError(401, "invalid_token");
207
- if (requiredScopes(name).some((scope) => !principal.scopes.includes(scope))) throw new HostedAuthError(403, "insufficient_scope");
189
+ if (principal.expiresAt !== void 0 && principal.expiresAt <= Date.now() / 1e3)
190
+ throw new HostedAuthError(401, "invalid_token");
191
+ if (requiredScopes(name).some((scope) => !principal.scopes.includes(scope)))
192
+ throw new HostedAuthError(403, "insufficient_scope");
208
193
  }
209
194
  var TOOL_ARGUMENT_VALUES = {
210
195
  list_dashboards: { scope: ["auto", "data_app", "global"] }
@@ -228,7 +213,8 @@ function assertToolArguments(name, value, principal, delegated = false) {
228
213
  rest[key] = child;
229
214
  continue;
230
215
  }
231
- if (typeof child !== "string" || !allowed[key].includes(child)) throw new HostedAuthError(403, "identity_argument_forbidden");
216
+ if (typeof child !== "string" || !allowed[key].includes(child))
217
+ throw new HostedAuthError(403, "identity_argument_forbidden");
232
218
  }
233
219
  assertBoundArguments(rest, principal, { target, delegated, opaque });
234
220
  return target;
@@ -247,7 +233,9 @@ var WORKSPACE_FREE_TOOLS = /* @__PURE__ */ new Set([
247
233
  "list_custom_charts",
248
234
  "publish_custom_chart"
249
235
  ]);
250
- var WORKSPACE_SPANNING_TOOLS = /* @__PURE__ */ new Set(["list_dashboards"]);
236
+ var WORKSPACE_SPANNING_TOOLS = /* @__PURE__ */ new Set([
237
+ "list_dashboards"
238
+ ]);
251
239
  var WORKSPACE_INPUT = "workspace";
252
240
  var WorkspaceSelectionError = class extends HostedAuthError {
253
241
  constructor(status, code, reason) {
@@ -260,7 +248,9 @@ function grantedWorkspaces(principal) {
260
248
  return principal.workspaceId ? [{ id: principal.workspaceId, name: principal.workspaceName }] : [];
261
249
  }
262
250
  function grantedNames(principal) {
263
- const names = grantedWorkspaces(principal).map((item) => item.name ?? item.id);
251
+ const names = grantedWorkspaces(principal).map(
252
+ (item) => item.name ?? item.id
253
+ );
264
254
  return names.length > 20 ? `${names.slice(0, 20).join(", ")} and ${names.length - 20} more` : names.join(", ");
265
255
  }
266
256
  function findGranted(principal, key, value) {
@@ -297,7 +287,9 @@ function selectWorkspace(args, principal, keys = [WORKSPACE_INPUT, "workspaceNam
297
287
  }
298
288
  function namesWorkspace(value) {
299
289
  if (!value || typeof value !== "object") return false;
300
- return Object.entries(value).some(([key, child]) => [WORKSPACE_INPUT, "workspaceName", "workspaceId"].includes(key) && child !== void 0 || namesWorkspace(child));
290
+ return Object.entries(value).some(
291
+ ([key, child]) => [WORKSPACE_INPUT, "workspaceName", "workspaceId"].includes(key) && child !== void 0 || namesWorkspace(child)
292
+ );
301
293
  }
302
294
  function tenantAllowed(child, principal, delegated) {
303
295
  if (typeof child !== "string") return false;
@@ -343,18 +335,85 @@ function assertBoundArguments(value, principal, binding = {}) {
343
335
  }
344
336
 
345
337
  // src/hosted/server.ts
346
- var schemaInstrumentation = new PostHog("hosted-contract-only", { disabled: true });
338
+ import { AsyncLocalStorage } from "async_hooks";
339
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
340
+ import { instrument } from "@posthog/mcp";
341
+ import { PostHog } from "posthog-node";
342
+ import { z as z2 } from "zod";
343
+
344
+ // src/tools/infrastructure/create-workspace.ts
345
+ import { z } from "zod";
346
+ function registerCreateWorkspace(server, client) {
347
+ server.tool(
348
+ "create_workspace",
349
+ 'Create a new workspace to connect a datasource or datamart for dashboard building. Works only when this sign-in granted "All workspaces I can access" and your role may create workspaces; the new workspace is usable on the next call.',
350
+ {
351
+ name: z.string().describe("Unique workspace name"),
352
+ connectionType: z.enum(["DATASOURCE", "DATAMART", "MULTI_DATASOURCE"]).optional().describe(
353
+ "Connection type \u2014 DATASOURCE, DATAMART, or MULTI_DATASOURCE"
354
+ ),
355
+ datasourceName: z.string().optional().describe(
356
+ "Datasource name (required when connectionType is DATASOURCE)"
357
+ ),
358
+ datamartName: z.string().optional().describe("Datamart name (required when connectionType is DATAMART)"),
359
+ body: z.record(z.string(), z.unknown()).optional().describe("Raw API body passthrough for POST /api/v2/workspace")
360
+ },
361
+ WRITE_CREATE,
362
+ async (params) => {
363
+ try {
364
+ const body = params.body ? { ...params.body } : {
365
+ name: params.name,
366
+ connectionType: params.connectionType,
367
+ datasourceName: params.datasourceName,
368
+ datamartName: params.datamartName
369
+ };
370
+ const result = await client.request({
371
+ method: "POST",
372
+ path: "/api/v2/workspace",
373
+ body,
374
+ auth: "service",
375
+ invalidateCache: ["list_workspaces"]
376
+ });
377
+ if (result.error) {
378
+ return toolError(
379
+ `Workspace creation failed: ${stringify(result.error)}`
380
+ );
381
+ }
382
+ return toolSuccess(
383
+ `Workspace "${result.name || params.name}" created successfully.
384
+
385
+ Next steps:
386
+ 1. Use \`create_data_app\` to create a data app linked to this workspace
387
+ 2. Build dashboards in the Databrain UI using this workspace
388
+ 3. Use \`create_embed\` to embed dashboards`
389
+ );
390
+ } catch (err) {
391
+ if (err instanceof DatabrainApiError) return err.toToolResponse();
392
+ return toolError(`Unexpected error: ${err.message}`);
393
+ }
394
+ }
395
+ );
396
+ }
397
+
398
+ // src/hosted/server.ts
399
+ var schemaInstrumentation = new PostHog("hosted-contract-only", {
400
+ disabled: true
401
+ });
347
402
  var callWorkspace = new AsyncLocalStorage();
348
403
  var WORKSPACE_HINT = "Call whoami to see the workspaces this sign-in granted, then name one.";
349
404
  function workspaceError(error) {
350
- return toolError(`Error: ${error.reason} (${error.code})
405
+ return toolError(
406
+ `Error: ${error.reason} (${error.code})
351
407
 
352
- Hint: ${WORKSPACE_HINT}`);
408
+ Hint: ${WORKSPACE_HINT}`
409
+ );
353
410
  }
354
411
  function scopeError(missing) {
355
- return toolError(`Error: This connection cannot do this. It needs ${missing.join(", ")}, which your role did not allow when you connected. (INSUFFICIENT_SCOPE)
412
+ return toolError(
413
+ `Error: This connection cannot do this. It needs ${missing.join(", ")}, which your role did not allow when you connected. (INSUFFICIENT_SCOPE)
356
414
 
357
- Hint: Ask a Databrain admin for a role that allows it, then connect again. If your role allows it in a workspace you did not choose, connect again and choose that workspace.`);
415
+ Hint: Ask a Databrain admin for a role that allows it, then connect again. If your role allows it in a workspace you did not choose, connect again and choose that workspace.`
416
+ );
358
417
  }
359
418
  function onlyWorkspace(raw, target) {
360
419
  const keep = (items) => Array.isArray(items) ? items.filter((item) => {
@@ -362,12 +421,16 @@ function onlyWorkspace(raw, target) {
362
421
  return record.id === target.id || record._id === target.id || record.id === void 0 && record._id === void 0 && target.name !== void 0 && record.name === target.name;
363
422
  }) : items;
364
423
  if (Array.isArray(raw)) return keep(raw);
365
- if (raw && typeof raw === "object" && "data" in raw) return { ...raw, data: keep(raw.data) };
424
+ if (raw && typeof raw === "object" && "data" in raw)
425
+ return { ...raw, data: keep(raw.data) };
366
426
  return raw;
367
427
  }
368
428
  var HostedClient = class extends DatabrainClient {
369
429
  constructor(adapter, principal, delegation) {
370
- super({ apiUrl: "https://api.usedatabrain.com", demoDomain: "https://demo.usedatabrain.com" });
430
+ super({
431
+ apiUrl: "https://api.usedatabrain.com",
432
+ demoDomain: "https://demo.usedatabrain.com"
433
+ });
371
434
  this.adapter = adapter;
372
435
  this.principal = principal;
373
436
  this.delegation = delegation;
@@ -384,23 +447,46 @@ var HostedClient = class extends DatabrainClient {
384
447
  if (!name) throw new HostedAuthError(403, "tool_not_authorized");
385
448
  authorizeTool(name, this.principal);
386
449
  const named = callWorkspace.getStore()?.target;
387
- const target = named ?? (selectWorkspace(options.body, this.principal, ["workspaceName", "workspaceId"]) ?? selectWorkspace(options.query, this.principal, ["workspaceName", "workspaceId"]));
450
+ const target = named ?? selectWorkspace(options.body, this.principal, [
451
+ "workspaceName",
452
+ "workspaceId"
453
+ ]) ?? selectWorkspace(options.query, this.principal, [
454
+ "workspaceName",
455
+ "workspaceId"
456
+ ]);
388
457
  const delegated = this.delegation !== void 0;
389
- const opaque = options.method === "POST" && options.path === PREVIEW_PATH ? ["rows"] : options.method === "POST" && /^\/api\/v2\/custom-charts\/[a-z][a-z0-9_-]*\/bryn\/turn$/.test(options.path) ? ["draft"] : void 0;
390
- assertBoundArguments(options.body, this.principal, { target, delegated, opaque });
391
- assertBoundArguments(options.query, this.principal, { target, delegated });
458
+ const opaque = options.method === "POST" && options.path === PREVIEW_PATH ? ["rows"] : options.method === "POST" && /^\/api\/v2\/custom-charts\/[a-z][a-z0-9_-]*\/bryn\/turn$/.test(
459
+ options.path
460
+ ) ? ["draft"] : void 0;
461
+ assertBoundArguments(options.body, this.principal, {
462
+ target,
463
+ delegated,
464
+ opaque
465
+ });
466
+ assertBoundArguments(options.query, this.principal, {
467
+ target,
468
+ delegated
469
+ });
392
470
  let raw = await this.adapter.executeRequest(
393
471
  structuredClone(options),
394
472
  structuredClone(this.principal),
395
473
  this.delegation,
396
474
  target && structuredClone(target)
397
475
  );
398
- if (named && options.method === "GET" && options.path === "/api/v2/workspace") raw = onlyWorkspace(raw, named);
399
- if (!options.skipDataUnwrap && raw && typeof raw === "object" && !Array.isArray(raw) && "data" in raw && raw.data !== null && raw.data !== void 0) return raw.data;
476
+ if (named && options.method === "GET" && options.path === "/api/v2/workspace")
477
+ raw = onlyWorkspace(raw, named);
478
+ if (!options.skipDataUnwrap && raw && typeof raw === "object" && !Array.isArray(raw) && "data" in raw && raw.data !== null && raw.data !== void 0)
479
+ return raw.data;
400
480
  return raw;
401
481
  } catch (error) {
402
482
  if (error instanceof WorkspaceSelectionError) {
403
- throw new DatabrainApiError(error.status, error.code, `${error.reason} (${error.code})`, void 0, WORKSPACE_HINT);
483
+ throw new DatabrainApiError(
484
+ error.status,
485
+ error.code,
486
+ `${error.reason} (${error.code})`,
487
+ void 0,
488
+ WORKSPACE_HINT
489
+ );
404
490
  }
405
491
  const status = error instanceof HostedAuthError ? error.status : 503;
406
492
  const detail = error instanceof HostedAuthError ? error.detail : void 0;
@@ -442,37 +528,56 @@ function workspaceLine(name, input) {
442
528
  }
443
529
  function whoami(principal) {
444
530
  const granted = grantedWorkspaces(principal);
445
- return toolSuccess(JSON.stringify({
446
- status: "authenticated",
447
- activeBearer: principal.credentialType,
448
- actorKind: principal.actorKind,
449
- userId: principal.userId,
450
- companyId: principal.companyId,
451
- workspaceGrant: principal.workspaces?.mode === "all" ? "All workspaces I can access (new ones are included)" : "Only the workspaces chosen at sign-in",
452
- grantedWorkspaces: granted.map((item) => ({ id: item.id, name: item.name })),
453
- ...granted.length === 1 ? { workspaceId: granted[0].id, workspaceName: granted[0].name } : {},
454
- note: granted.length === 1 ? "Every call works in this workspace." : `Name the workspace on each call that works in one (the \`${WORKSPACE_INPUT}\` or \`workspaceName\` input): ${grantedNames(principal)}.`,
455
- oauthClientId: principal.clientId,
456
- scopes: principal.scopes
457
- }, null, 2));
531
+ return toolSuccess(
532
+ JSON.stringify(
533
+ {
534
+ status: "authenticated",
535
+ activeBearer: principal.credentialType,
536
+ actorKind: principal.actorKind,
537
+ userId: principal.userId,
538
+ companyId: principal.companyId,
539
+ workspaceGrant: principal.workspaces?.mode === "all" ? "All workspaces I can access (new ones are included)" : "Only the workspaces chosen at sign-in",
540
+ grantedWorkspaces: granted.map((item) => ({
541
+ id: item.id,
542
+ name: item.name
543
+ })),
544
+ ...granted.length === 1 ? { workspaceId: granted[0].id, workspaceName: granted[0].name } : {},
545
+ note: granted.length === 1 ? "Every call works in this workspace." : `Name the workspace on each call that works in one (the \`${WORKSPACE_INPUT}\` or \`workspaceName\` input): ${grantedNames(principal)}.`,
546
+ oauthClientId: principal.clientId,
547
+ scopes: principal.scopes
548
+ },
549
+ null,
550
+ 2
551
+ )
552
+ );
458
553
  }
459
554
  function createHostedToolServer(principal, adapter, delegation) {
460
555
  const bound = structuredClone(principal);
461
556
  const delegated = delegation !== void 0;
462
- const server = new McpServer({ name: "databrain", version: MCP_SERVER_VERSION }, {
463
- instructions: "Hosted Databrain: each request is authenticated as the signed-in user, with that user's own role in each workspace. Use whoami to see the verified identity, scopes and the workspaces this sign-in granted. When more than one workspace is granted, name the workspace on each call (the workspace or workspaceName input). Never ask for a service token or put credentials in tool arguments. Workspace and tenant access come from the bearer principal. Existing dry-run and production-confirmation rules still apply. For existing saved charts, call list_widgets with dashboardId or dashboardName and no clientId; no embed, data app or datamart is needed. Use render_metric_tile to draw a metricRef directly. Use ask_question for new data questions. Chart controls and save_chart_as_dashboard use the same workspace and role; saving requires the existing production confirmation. Hosts without MCP Apps can read the text answer."
464
- });
557
+ const server = new McpServer(
558
+ { name: "databrain", version: MCP_SERVER_VERSION },
559
+ {
560
+ instructions: "Hosted Databrain: each request is authenticated as the signed-in user, with that user's own role in each workspace. Use whoami to see the verified identity, scopes and the workspaces this sign-in granted. When more than one workspace is granted, name the workspace on each call (the workspace or workspaceName input). Never ask for a service token or put credentials in tool arguments. Workspace and tenant access come from the bearer principal. Existing dry-run and production-confirmation rules still apply. For existing saved charts, call list_widgets with dashboardId or dashboardName and no clientId; no embed, data app or datamart is needed. Use render_metric_tile to draw a metricRef directly. Use ask_question for new data questions. Chart controls and save_chart_as_dashboard use the same workspace and role; saving requires the existing production confirmation. Hosts without MCP Apps can read the text answer."
561
+ }
562
+ );
465
563
  const register = server.tool.bind(server);
466
564
  server.tool = ((...args) => {
467
565
  const name = args[0];
468
566
  const index = args.length - 1;
469
567
  const handler = args[index];
470
568
  const shape = typeof args[1] === "string" && args[2] && typeof args[2] === "object" && !("readOnlyHint" in args[2]) ? args[2] : void 0;
471
- const ownSelector = shape && ["workspaceName", "workspaceId"].find((key) => Object.hasOwn(shape, key));
569
+ const ownSelector = shape && ["workspaceName", "workspaceId"].find(
570
+ (key) => Object.hasOwn(shape, key)
571
+ );
472
572
  const addsInput = shape !== void 0 && !ownSelector && !WORKSPACE_FREE_TOOLS.has(name) && !Object.hasOwn(shape, WORKSPACE_INPUT);
473
573
  if (shape && !WORKSPACE_FREE_TOOLS.has(name)) {
474
574
  if (addsInput) {
475
- args[2] = { ...shape, [WORKSPACE_INPUT]: z2.string().min(1).max(512).optional().describe(WORKSPACE_SPANNING_TOOLS.has(name) ? "Workspace id or name. Leave it out to cover every granted workspace." : "Workspace id or name. Needed when you granted more than one workspace.") };
575
+ args[2] = {
576
+ ...shape,
577
+ [WORKSPACE_INPUT]: z2.string().min(1).max(512).optional().describe(
578
+ WORKSPACE_SPANNING_TOOLS.has(name) ? "Workspace id or name. Leave it out to cover every granted workspace." : "Workspace id or name. Needed when you granted more than one workspace."
579
+ )
580
+ };
476
581
  }
477
582
  args[1] = `${args[1]}${workspaceLine(name, addsInput ? WORKSPACE_INPUT : ownSelector)}`;
478
583
  }
@@ -482,7 +587,10 @@ function createHostedToolServer(principal, adapter, delegation) {
482
587
  const target = assertToolArguments(name, values[0], bound, delegated);
483
588
  if (name === "whoami") return whoami(bound);
484
589
  const input = values[0] && typeof values[0] === "object" ? values[0] : void 0;
485
- if (input?.projectDir !== void 0) return toolError("projectDir is not available on the hosted server. Pass chartTs, manifest, bundleCss and iconSvg to compile in memory.");
590
+ if (input?.projectDir !== void 0)
591
+ return toolError(
592
+ "projectDir is not available on the hosted server. Pass chartTs, manifest, bundleCss and iconSvg to compile in memory."
593
+ );
486
594
  if (!target && !WORKSPACE_FREE_TOOLS.has(name) && !WORKSPACE_SPANNING_TOOLS.has(name) && !namesWorkspace(input)) {
487
595
  throw new WorkspaceSelectionError(
488
596
  400,
@@ -493,18 +601,29 @@ function createHostedToolServer(principal, adapter, delegation) {
493
601
  if (input) {
494
602
  if (addsInput) delete input[WORKSPACE_INPUT];
495
603
  if (shape && target) {
496
- if (Object.hasOwn(shape, "workspaceId")) input.workspaceId = target.id;
497
- if (Object.hasOwn(shape, "workspaceName") && target.name !== void 0) input.workspaceName = target.name;
604
+ if (Object.hasOwn(shape, "workspaceId"))
605
+ input.workspaceId = target.id;
606
+ if (Object.hasOwn(shape, "workspaceName") && target.name !== void 0)
607
+ input.workspaceName = target.name;
498
608
  }
499
609
  }
500
- const result = await callWorkspace.run({ target }, () => handler(...values));
610
+ const result = await callWorkspace.run(
611
+ { target },
612
+ () => handler(...values)
613
+ );
501
614
  if (target && result?._meta?.["ui/resourceUri"]) {
502
- return { ...result, _meta: { ...result._meta, "databrain/workspace": target } };
615
+ return {
616
+ ...result,
617
+ _meta: { ...result._meta, "databrain/workspace": target }
618
+ };
503
619
  }
504
620
  return result;
505
621
  } catch (error) {
506
- if (error instanceof WorkspaceSelectionError) return workspaceError(error);
507
- return toolError("Hosted authorization denied this tool call. Check the granted scopes, workspace and tenant access.");
622
+ if (error instanceof WorkspaceSelectionError)
623
+ return workspaceError(error);
624
+ return toolError(
625
+ "Hosted authorization denied this tool call. Check the granted scopes, workspace and tenant access."
626
+ );
508
627
  }
509
628
  };
510
629
  return register(...args);
@@ -527,7 +646,10 @@ function healthPath(url) {
527
646
  return path === "/health";
528
647
  }
529
648
  function reply(res, status, body) {
530
- res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" });
649
+ res.writeHead(status, {
650
+ "Content-Type": "application/json",
651
+ "Cache-Control": "no-store"
652
+ });
531
653
  res.end(JSON.stringify(body));
532
654
  }
533
655
  async function readBody(req) {
@@ -565,7 +687,11 @@ function createHostedHttpServer(config, adapter, pats) {
565
687
  if (config.testOnly) res.setHeader("X-Databrain-Test-Only", "true");
566
688
  try {
567
689
  const localPort = server.address()?.port;
568
- if (![publicUrl.host, `127.0.0.1:${localPort}`, `localhost:${localPort}`].includes(req.headers.host || "")) {
690
+ if (![
691
+ publicUrl.host,
692
+ `127.0.0.1:${localPort}`,
693
+ `localhost:${localPort}`
694
+ ].includes(req.headers.host || "")) {
569
695
  reply(res, 403, { error: "invalid_host" });
570
696
  return;
571
697
  }
@@ -576,10 +702,15 @@ function createHostedHttpServer(config, adapter, pats) {
576
702
  if (req.headers.origin) {
577
703
  res.setHeader("Access-Control-Allow-Origin", publicUrl.origin);
578
704
  res.setHeader("Vary", "Origin");
579
- res.setHeader("Access-Control-Expose-Headers", "WWW-Authenticate, MCP-Protocol-Version");
705
+ res.setHeader(
706
+ "Access-Control-Expose-Headers",
707
+ "WWW-Authenticate, MCP-Protocol-Version"
708
+ );
580
709
  }
581
710
  const url = new URL(req.url || "/", publicUrl.origin);
582
- if (req.method === "GET" && [metadataPath, "/.well-known/oauth-protected-resource"].includes(url.pathname)) {
711
+ if (req.method === "GET" && [metadataPath, "/.well-known/oauth-protected-resource"].includes(
712
+ url.pathname
713
+ )) {
583
714
  reply(res, 200, {
584
715
  resource: config.publicUrl,
585
716
  authorization_servers: [config.issuer],
@@ -598,15 +729,24 @@ function createHostedHttpServer(config, adapter, pats) {
598
729
  return;
599
730
  }
600
731
  if (req.method === "OPTIONS") {
601
- res.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS");
602
- res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, MCP-Protocol-Version, MCP-Session-Id");
732
+ res.setHeader(
733
+ "Access-Control-Allow-Methods",
734
+ "POST, GET, DELETE, OPTIONS"
735
+ );
736
+ res.setHeader(
737
+ "Access-Control-Allow-Headers",
738
+ "Authorization, Content-Type, MCP-Protocol-Version, MCP-Session-Id"
739
+ );
603
740
  res.writeHead(204);
604
741
  res.end();
605
742
  return;
606
743
  }
607
- const authFields = req.rawHeaders.filter((_, i) => i % 2 === 0 && req.rawHeaders[i].toLowerCase() === "authorization");
744
+ const authFields = req.rawHeaders.filter(
745
+ (_, i) => i % 2 === 0 && req.rawHeaders[i].toLowerCase() === "authorization"
746
+ );
608
747
  const match = /^Bearer ([^\s]+)$/i.exec(req.headers.authorization || "");
609
- if (authFields.length !== 1 || !match) throw new HostedAuthError(401, "invalid_token");
748
+ if (authFields.length !== 1 || !match)
749
+ throw new HostedAuthError(401, "invalid_token");
610
750
  const principal = await verify(match[1]);
611
751
  if (req.method !== "POST") {
612
752
  res.setHeader("Allow", "POST, OPTIONS");
@@ -639,22 +779,40 @@ function createHostedHttpServer(config, adapter, pats) {
639
779
  if (body?.method === "tools/call" && typeof body.params?.name === "string") {
640
780
  try {
641
781
  authorizeTool(body.params.name, principal);
642
- assertToolArguments(body.params.name, body.params.arguments, principal, delegation !== void 0);
782
+ assertToolArguments(
783
+ body.params.name,
784
+ body.params.arguments,
785
+ principal,
786
+ delegation !== void 0
787
+ );
643
788
  } catch (error) {
644
789
  if (error instanceof WorkspaceSelectionError) {
645
- reply(res, 200, { jsonrpc: "2.0", id: body.id ?? null, result: workspaceError(error) });
790
+ reply(res, 200, {
791
+ jsonrpc: "2.0",
792
+ id: body.id ?? null,
793
+ result: workspaceError(error)
794
+ });
646
795
  return;
647
796
  }
648
797
  if (error instanceof HostedAuthError && error.code === "insufficient_scope") {
649
- const missing = requiredScopes(body.params.name).filter((scope) => !principal.scopes.includes(scope));
650
- reply(res, 200, { jsonrpc: "2.0", id: body.id ?? null, result: scopeError(missing) });
798
+ const missing = requiredScopes(body.params.name).filter(
799
+ (scope) => !principal.scopes.includes(scope)
800
+ );
801
+ reply(res, 200, {
802
+ jsonrpc: "2.0",
803
+ id: body.id ?? null,
804
+ result: scopeError(missing)
805
+ });
651
806
  return;
652
807
  }
653
808
  throw error;
654
809
  }
655
810
  }
656
811
  const mcp = createHostedToolServer(principal, adapter, delegation);
657
- const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0, enableJsonResponse: true });
812
+ const transport = new StreamableHTTPServerTransport({
813
+ sessionIdGenerator: void 0,
814
+ enableJsonResponse: true
815
+ });
658
816
  let closed = false;
659
817
  const close = () => {
660
818
  if (!closed) {
@@ -678,8 +836,14 @@ function createHostedHttpServer(config, adapter, pats) {
678
836
  return;
679
837
  }
680
838
  const status = error instanceof HostedAuthError ? error.status : 503;
681
- if (status === 401) res.setHeader("WWW-Authenticate", `Bearer error="invalid_token", resource_metadata="${metadataUrl}"`);
682
- reply(res, status, { error: error instanceof HostedAuthError ? error.code : "temporarily_unavailable" });
839
+ if (status === 401)
840
+ res.setHeader(
841
+ "WWW-Authenticate",
842
+ `Bearer error="invalid_token", resource_metadata="${metadataUrl}"`
843
+ );
844
+ reply(res, status, {
845
+ error: error instanceof HostedAuthError ? error.code : "temporarily_unavailable"
846
+ });
683
847
  }
684
848
  });
685
849
  server.requestTimeout = 3e4;
@@ -694,7 +858,12 @@ var USER_AGENT = `databrain-mcp-server/${MCP_SERVER_VERSION} (hosted)`;
694
858
  var REQUEST_TIMEOUT_MS = 3e4;
695
859
  var FORWARDED_HEADERS = /* @__PURE__ */ new Set(["idempotency-key"]);
696
860
  var WORKSPACE_HEADER = "X-Databrain-Workspace";
697
- var GRANT_DENIALS = /* @__PURE__ */ new Set(["ROLE_NOT_ALLOWED", "WORKSPACE_CREATE_NOT_ALLOWED", "forbidden_permission", "insufficient_scope"]);
861
+ var GRANT_DENIALS = /* @__PURE__ */ new Set([
862
+ "ROLE_NOT_ALLOWED",
863
+ "WORKSPACE_CREATE_NOT_ALLOWED",
864
+ "forbidden_permission",
865
+ "insufficient_scope"
866
+ ]);
698
867
  function errorDetail(status, text, bearer) {
699
868
  let parsed;
700
869
  try {
@@ -734,7 +903,8 @@ function createProviderAdapter(config) {
734
903
  if (issuer.protocol !== "https:" || issuer.username || issuer.password || issuer.search || issuer.hash) {
735
904
  throw new Error("Invalid hosted authorization server configuration.");
736
905
  }
737
- const apiBase = config.issuer.replace(/\/$/, "");
906
+ const apiBase = (config.apiUrl ?? config.issuer).replace(/\/$/, "");
907
+ const apiOrigin = new URL(apiBase).origin;
738
908
  const endpoint = `${apiBase}/oauth/principal`;
739
909
  const publicOrigin = new URL(config.publicUrl).origin;
740
910
  const bearers = /* @__PURE__ */ new WeakMap();
@@ -743,16 +913,23 @@ function createProviderAdapter(config) {
743
913
  async verifyOAuth(token, resource) {
744
914
  try {
745
915
  const response = await fetch(endpoint, {
746
- headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
916
+ headers: {
917
+ Authorization: `Bearer ${token}`,
918
+ Accept: "application/json"
919
+ },
747
920
  redirect: "error",
748
921
  signal: AbortSignal.timeout(1e4)
749
922
  });
750
923
  if (!response.ok) {
751
- if (response.status === 429 || response.status >= 500) throw new HostedAuthError(503, "temporarily_unavailable");
924
+ if (response.status === 429 || response.status >= 500)
925
+ throw new HostedAuthError(503, "temporarily_unavailable");
752
926
  throw new HostedAuthError(401, "invalid_token");
753
927
  }
754
- const { issuer: _issuer, ...principal } = responseSchema.parse(await response.json());
755
- if (principal.resource !== resource || principal.credentialType !== "oauth") throw new HostedAuthError(401, "invalid_token");
928
+ const { issuer: _issuer, ...principal } = responseSchema.parse(
929
+ await response.json()
930
+ );
931
+ if (principal.resource !== resource || principal.credentialType !== "oauth")
932
+ throw new HostedAuthError(401, "invalid_token");
756
933
  return principal;
757
934
  } catch (error) {
758
935
  if (error instanceof HostedAuthError) throw error;
@@ -763,18 +940,25 @@ function createProviderAdapter(config) {
763
940
  throw new HostedAuthError(401, "pat_membership_adapter_required");
764
941
  },
765
942
  delegate(token) {
766
- const handle = Object.freeze({ kind: "hosted-delegation" });
943
+ const handle = Object.freeze({
944
+ kind: "hosted-delegation"
945
+ });
767
946
  bearers.set(handle, token);
768
947
  return handle;
769
948
  },
770
949
  async executeRequest(options, principal, delegation, workspace) {
771
950
  const bearer = delegation ? bearers.get(delegation) : void 0;
772
- if (options.auth !== "none" && !bearer) throw new HostedAuthError(503, "delegated_backend_adapter_required");
773
- if (options.auth === "none" && !options.token) throw new HostedAuthError(403, "tool_not_authorized");
774
- if (!options.path.startsWith("/") || options.path.startsWith("//")) throw new HostedAuthError(403, "tool_not_authorized");
951
+ if (options.auth !== "none" && !bearer)
952
+ throw new HostedAuthError(503, "delegated_backend_adapter_required");
953
+ if (options.auth === "none" && !options.token)
954
+ throw new HostedAuthError(403, "tool_not_authorized");
955
+ if (!options.path.startsWith("/") || options.path.startsWith("//"))
956
+ throw new HostedAuthError(403, "tool_not_authorized");
775
957
  const url = new URL(`${apiBase}${options.path}`);
776
- if (url.origin !== issuer.origin) throw new HostedAuthError(403, "tool_not_authorized");
777
- for (const [key, value] of Object.entries(options.query ?? {})) url.searchParams.set(key, value);
958
+ if (url.origin !== apiOrigin)
959
+ throw new HostedAuthError(403, "tool_not_authorized");
960
+ for (const [key, value] of Object.entries(options.query ?? {}))
961
+ url.searchParams.set(key, value);
778
962
  const headers = {
779
963
  Accept: "application/json",
780
964
  "Content-Type": "application/json",
@@ -797,7 +981,10 @@ function createProviderAdapter(config) {
797
981
  } else {
798
982
  headers.Authorization = `Bearer ${bearer}`;
799
983
  if (workspace) {
800
- if (!principal.grantedWorkspaces?.some((item) => item.id === workspace.id) && principal.workspaceId !== workspace.id) throw new HostedAuthError(403, "principal_mismatch");
984
+ if (!principal.grantedWorkspaces?.some(
985
+ (item) => item.id === workspace.id
986
+ ) && principal.workspaceId !== workspace.id)
987
+ throw new HostedAuthError(403, "principal_mismatch");
801
988
  headers[WORKSPACE_HEADER] = workspace.id;
802
989
  }
803
990
  }
@@ -815,7 +1002,10 @@ function createProviderAdapter(config) {
815
1002
  throw new HostedAuthError(
816
1003
  403,
817
1004
  "delegated_request_denied",
818
- forbidden.code === "insufficient_scope" && options.path === "/api/v2/guest-token/create" ? { ...forbidden, message: "Guest/tenant operations require mcp:embeds:write. Ask an admin for a role that allows it, then connect again." } : forbidden
1005
+ forbidden.code === "insufficient_scope" && options.path === "/api/v2/guest-token/create" ? {
1006
+ ...forbidden,
1007
+ message: "Guest/tenant operations require mcp:embeds:write. Ask an admin for a role that allows it, then connect again."
1008
+ } : forbidden
819
1009
  );
820
1010
  }
821
1011
  const status = response.status === 403 && options.responseFormat === "studio-sse" ? 403 : response.status === 401 ? 401 : response.status >= 500 ? 503 : response.status === 403 || response.status === 404 ? 404 : 400;
@@ -837,33 +1027,51 @@ async function startHosted() {
837
1027
  let adapter;
838
1028
  if (config.testOnly) {
839
1029
  const file = process.env.DATABRAIN_HOSTED_TEST_FIXTURES;
840
- if (!file || !isAbsolute(file)) throw new Error("Hosted test mode requires an absolute fixture store path.");
841
- const { createTestOnlyAdapter } = await import("./test-only-K7QTSVO4.js");
1030
+ if (!file || !isAbsolute(file))
1031
+ throw new Error(
1032
+ "Hosted test mode requires an absolute fixture store path."
1033
+ );
1034
+ const { createTestOnlyAdapter } = await import("./test-only-6474NMTR.js");
842
1035
  adapter = createTestOnlyAdapter(file);
843
1036
  } else {
844
1037
  const file = process.env.DATABRAIN_HOSTED_ADAPTER_MODULE;
845
1038
  if (!file) {
846
1039
  adapter = createProviderAdapter(config);
847
- process.stderr.write("[databrain] OAuth configured: data tools run as the signed-in user through the issuer; PAT membership requires a trusted adapter.\n");
1040
+ process.stderr.write(
1041
+ "[databrain] OAuth configured: data tools run as the signed-in user through the issuer; PAT membership requires a trusted adapter.\n"
1042
+ );
848
1043
  } else {
849
- if (!isAbsolute(file)) throw new Error("DATABRAIN_HOSTED_ADAPTER_MODULE must be an absolute path.");
1044
+ if (!isAbsolute(file))
1045
+ throw new Error(
1046
+ "DATABRAIN_HOSTED_ADAPTER_MODULE must be an absolute path."
1047
+ );
850
1048
  try {
851
1049
  adapter = (await import(pathToFileURL(file).href)).adapter;
852
1050
  } catch {
853
1051
  throw new Error("Could not load the trusted hosted adapter.");
854
1052
  }
855
1053
  }
856
- if (!adapter || ["verifyOAuth", "resolvePatGrant", "executeRequest"].some((key) => typeof adapter[key] !== "function")) throw new Error("Invalid trusted hosted adapter.");
1054
+ if (!adapter || ["verifyOAuth", "resolvePatGrant", "executeRequest"].some(
1055
+ (key) => typeof adapter[key] !== "function"
1056
+ ))
1057
+ throw new Error("Invalid trusted hosted adapter.");
857
1058
  }
858
1059
  const patPath = process.env.DATABRAIN_HOSTED_PAT_STORE;
859
- if (patPath && !isAbsolute(patPath)) throw new Error("Hosted PAT store must be an absolute path.");
860
- const server = createHostedHttpServer(config, adapter, patPath ? new FilePatStore(patPath) : void 0);
1060
+ if (patPath && !isAbsolute(patPath))
1061
+ throw new Error("Hosted PAT store must be an absolute path.");
1062
+ const server = createHostedHttpServer(
1063
+ config,
1064
+ adapter,
1065
+ patPath ? new FilePatStore(patPath) : void 0
1066
+ );
861
1067
  await new Promise((resolve, reject) => {
862
1068
  server.once("error", reject);
863
1069
  server.listen(config.port, config.bind, resolve);
864
1070
  });
865
- process.stderr.write(`[databrain] hosted HTTP ready on ${config.bind}:${config.port}/mcp${config.testOnly ? " \u2014 TEST ONLY: synthetic identity and data; no OAuth consent" : ""}
866
- `);
1071
+ process.stderr.write(
1072
+ `[databrain] hosted HTTP ready on ${config.bind}:${config.port}/mcp${config.testOnly ? " \u2014 TEST ONLY: synthetic identity and data; no OAuth consent" : ""}
1073
+ `
1074
+ );
867
1075
  const shutdown = () => {
868
1076
  server.close(() => process.exit(0));
869
1077
  setTimeout(() => {
@@ -877,4 +1085,4 @@ async function startHosted() {
877
1085
  export {
878
1086
  startHosted
879
1087
  };
880
- //# sourceMappingURL=main-IUV24DMP.js.map
1088
+ //# sourceMappingURL=main-QCBVJSSR.js.map