@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
@@ -1,25 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  registerChartApps
4
- } from "./chunk-233VGFHY.js";
4
+ } from "./chunk-QQ6KNULX.js";
5
5
  import {
6
6
  enableChartApps,
7
7
  registerAllPrompts,
8
8
  registerAllResources,
9
9
  registerAllTools
10
- } from "./chunk-PSJVYD4W.js";
10
+ } from "./chunk-6MD7KOAU.js";
11
11
  import {
12
12
  DatabrainClient,
13
13
  MCP_SERVER_VERSION
14
- } from "./chunk-AEQTEE5C.js";
14
+ } from "./chunk-4E57ZRZO.js";
15
15
 
16
16
  // src/apps/server.ts
17
17
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
18
18
  function createChartAppServer(config, backend) {
19
19
  const client = new DatabrainClient(config);
20
- const server = new McpServer({ name: "databrain-chart-apps", version: MCP_SERVER_VERSION }, {
21
- instructions: "Use read-only chart previews to inspect analytics. Chart interactions inherit guest scope. Never request or expose credentials. Mutation tools retain their existing dry-run and confirmation requirements."
22
- });
20
+ const server = new McpServer(
21
+ { name: "databrain-chart-apps", version: MCP_SERVER_VERSION },
22
+ {
23
+ instructions: "Use read-only chart previews to inspect analytics. Chart interactions inherit guest scope. Never request or expose credentials. Mutation tools retain their existing dry-run and confirmation requirements."
24
+ }
25
+ );
23
26
  enableChartApps(server);
24
27
  registerAllTools(server, client);
25
28
  registerAllResources(server);
@@ -31,4 +34,4 @@ function createChartAppServer(config, backend) {
31
34
  export {
32
35
  createChartAppServer
33
36
  };
34
- //# sourceMappingURL=chunk-HCYZN7I3.js.map
37
+ //# sourceMappingURL=chunk-FJLRZJ4A.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/apps/server.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { DatabrainClient } from '../client/databrain-client.js';\nimport { registerAllPrompts } from '../prompts/registry.js';\nimport { registerAllResources } from '../resources/registry.js';\nimport { registerAllTools } from '../tools/registry.js';\nimport type { Config } from '../utils/config.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport type { ChartBackend } from './backend.js';\nimport { enableChartApps } from './feature.js';\nimport { registerChartApps } from './registry.js';\n\n/** Reviewable composition seam; hosted owners must retain their principal-bound client/policy. */\nexport function createChartAppServer(\n config: Config,\n backend?: ChartBackend\n): McpServer {\n const client = new DatabrainClient(config);\n const server = new McpServer(\n { name: 'databrain-chart-apps', version: MCP_SERVER_VERSION },\n {\n instructions:\n 'Use read-only chart previews to inspect analytics. Chart interactions inherit guest scope. ' +\n 'Never request or expose credentials. Mutation tools retain their existing dry-run and confirmation requirements.',\n }\n );\n enableChartApps(server);\n registerAllTools(server, client);\n registerAllResources(server);\n registerAllPrompts(server);\n registerChartApps(server, client, backend);\n return server;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,iBAAiB;AAYnB,SAAS,qBACd,QACA,SACW;AACX,QAAM,SAAS,IAAI,gBAAgB,MAAM;AACzC,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,wBAAwB,SAAS,mBAAmB;AAAA,IAC5D;AAAA,MACE,cACE;AAAA,IAEJ;AAAA,EACF;AACA,kBAAgB,MAAM;AACtB,mBAAiB,QAAQ,MAAM;AAC/B,uBAAqB,MAAM;AAC3B,qBAAmB,MAAM;AACzB,oBAAkB,QAAQ,QAAQ,OAAO;AACzC,SAAO;AACT;","names":[]}
@@ -1,29 +1,13 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/hosted/types.ts
4
- var HOSTED_SCOPES = [
5
- "mcp:dashboards:read",
6
- "mcp:dashboards:write",
7
- "mcp:datamarts:read",
8
- "mcp:datamarts:write",
9
- "mcp:embeds:write",
10
- "mcp:query",
11
- "mcp:custom-charts:read",
12
- "mcp:custom-charts:write"
13
- ];
14
- var HostedAuthError = class extends Error {
15
- constructor(status, code, detail) {
16
- super(code);
17
- this.status = status;
18
- this.code = code;
19
- this.detail = detail;
20
- }
21
- };
2
+ import {
3
+ HOSTED_SCOPES,
4
+ HostedAuthError
5
+ } from "./chunk-3AH56XX7.js";
22
6
 
23
7
  // src/hosted/auth.ts
24
8
  import { createHash, timingSafeEqual } from "crypto";
25
- import { open } from "fs/promises";
26
9
  import { constants } from "fs";
10
+ import { open } from "fs/promises";
27
11
  import { z } from "zod";
28
12
  var identity = z.string().min(1).max(512).regex(/^[^\x00-\x1f\x7f]+$/);
29
13
  var workspaceSchema = z.object({ id: identity, name: identity.optional() }).strip();
@@ -36,7 +20,10 @@ var principalSchema = z.object({
36
20
  workspaceName: identity.optional(),
37
21
  workspaces: z.union([
38
22
  z.object({ mode: z.literal("all") }).strip(),
39
- z.object({ mode: z.literal("list"), ids: z.array(identity).min(1).max(100) }).strip()
23
+ z.object({
24
+ mode: z.literal("list"),
25
+ ids: z.array(identity).min(1).max(100)
26
+ }).strip()
40
27
  ]).optional(),
41
28
  grantedWorkspaces: z.array(workspaceSchema).max(1e3).optional(),
42
29
  // The issuer's own pick; recomputed below from grantedWorkspaces, never trusted on its own.
@@ -48,8 +35,16 @@ var principalSchema = z.object({
48
35
  tenantIds: z.array(identity).optional()
49
36
  }).strict();
50
37
  function normalizeWorkspaces(value) {
51
- const { defaultWorkspace: _default, workspaceId, workspaceName, grantedWorkspaces, ...rest } = value;
52
- const granted = grantedWorkspaces ?? (workspaceId ? [workspaceName === void 0 ? { id: workspaceId } : { id: workspaceId, name: workspaceName }] : []);
38
+ const {
39
+ defaultWorkspace: _default,
40
+ workspaceId,
41
+ workspaceName,
42
+ grantedWorkspaces,
43
+ ...rest
44
+ } = value;
45
+ const granted = grantedWorkspaces ?? (workspaceId ? [
46
+ workspaceName === void 0 ? { id: workspaceId } : { id: workspaceId, name: workspaceName }
47
+ ] : []);
53
48
  if (!granted.length || new Set(granted.map((item) => item.id)).size !== granted.length || workspaceId !== void 0 && !granted.some((item) => item.id === workspaceId)) {
54
49
  throw new HostedAuthError(401, "invalid_token");
55
50
  }
@@ -74,12 +69,21 @@ var FilePatStore = class {
74
69
  this.path = path;
75
70
  }
76
71
  async findByHash(hash) {
77
- const handle = await open(this.path, constants.O_RDONLY | constants.O_NOFOLLOW);
72
+ const handle = await open(
73
+ this.path,
74
+ constants.O_RDONLY | constants.O_NOFOLLOW
75
+ );
78
76
  try {
79
77
  const stat = await handle.stat();
80
- if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > 1048576 || process.getuid && stat.uid !== process.getuid()) throw new Error("Invalid credential store");
78
+ if (!stat.isFile() || (stat.mode & 63) !== 0 || stat.size > 1048576 || process.getuid && stat.uid !== process.getuid())
79
+ throw new Error("Invalid credential store");
81
80
  const records = z.array(recordSchema).max(1e3).parse(JSON.parse(await handle.readFile("utf8")));
82
- const matches = records.filter((record) => timingSafeEqual(Buffer.from(record.hash, "hex"), Buffer.from(hash, "hex")));
81
+ const matches = records.filter(
82
+ (record) => timingSafeEqual(
83
+ Buffer.from(record.hash, "hex"),
84
+ Buffer.from(hash, "hex")
85
+ )
86
+ );
83
87
  if (matches.length > 1) throw new Error("Ambiguous credential store");
84
88
  return matches[0];
85
89
  } finally {
@@ -89,28 +93,49 @@ var FilePatStore = class {
89
93
  };
90
94
  function validatePrincipal(value, resource, kind) {
91
95
  const parsed = principalSchema.safeParse(value);
92
- if (!parsed.success || parsed.data.resource !== resource || parsed.data.credentialType !== kind || parsed.data.expiresAt !== void 0 && parsed.data.expiresAt <= Date.now() / 1e3 || kind === "pat" && parsed.success && parsed.data.expiresAt === void 0) throw new HostedAuthError(401, "invalid_token");
96
+ if (!parsed.success || parsed.data.resource !== resource || parsed.data.credentialType !== kind || parsed.data.expiresAt !== void 0 && parsed.data.expiresAt <= Date.now() / 1e3 || kind === "pat" && parsed.success && parsed.data.expiresAt === void 0)
97
+ throw new HostedAuthError(401, "invalid_token");
93
98
  return normalizeWorkspaces(parsed.data);
94
99
  }
95
100
  function createBearerVerifier(adapter, resource, pats) {
96
101
  return async (token) => {
97
102
  try {
98
- if (!/^[A-Za-z0-9._~+\/-]+=*$/.test(token) || token.length > 8192) throw new HostedAuthError(401, "invalid_token");
103
+ if (!/^[A-Za-z0-9._~+/-]+=*$/.test(token) || token.length > 8192)
104
+ throw new HostedAuthError(401, "invalid_token");
99
105
  if (!token.startsWith("dbp_")) {
100
- return validatePrincipal(await adapter.verifyOAuth(token, resource), resource, "oauth");
106
+ return validatePrincipal(
107
+ await adapter.verifyOAuth(token, resource),
108
+ resource,
109
+ "oauth"
110
+ );
101
111
  }
102
- if (!/^dbp_[A-Za-z0-9_-]{32,}$/.test(token)) throw new HostedAuthError(401, "invalid_token");
112
+ if (!/^dbp_[A-Za-z0-9_-]{32,}$/.test(token))
113
+ throw new HostedAuthError(401, "invalid_token");
103
114
  const record = await pats?.findByHash(hashCredential(token));
104
- if (!record || record.revokedAt !== null || record.hash !== hashCredential(token)) throw new HostedAuthError(401, "invalid_token");
115
+ if (!record || record.revokedAt !== null || record.hash !== hashCredential(token))
116
+ throw new HostedAuthError(401, "invalid_token");
105
117
  const stored = validatePrincipal(record.principal, resource, "pat");
106
- const resolved = validatePrincipal(await adapter.resolvePatGrant(structuredClone(stored)), resource, "pat");
107
- for (const key of ["userId", "companyId", "clientId", "resource"]) {
108
- if (resolved[key] !== stored[key]) throw new HostedAuthError(401, "invalid_token");
118
+ const resolved = validatePrincipal(
119
+ await adapter.resolvePatGrant(structuredClone(stored)),
120
+ resource,
121
+ "pat"
122
+ );
123
+ for (const key of [
124
+ "userId",
125
+ "companyId",
126
+ "clientId",
127
+ "resource"
128
+ ]) {
129
+ if (resolved[key] !== stored[key])
130
+ throw new HostedAuthError(401, "invalid_token");
109
131
  }
110
- if (resolved.grantedWorkspaces.some((item) => !stored.grantedWorkspaces.some((old) => old.id === item.id))) {
132
+ if (resolved.grantedWorkspaces.some(
133
+ (item) => !stored.grantedWorkspaces.some((old) => old.id === item.id)
134
+ )) {
111
135
  throw new HostedAuthError(401, "invalid_token");
112
136
  }
113
- if (resolved.expiresAt > stored.expiresAt || resolved.scopes.some((scope) => !stored.scopes.includes(scope)) || resolved.tenantIds?.some((id) => !stored.tenantIds?.includes(id))) throw new HostedAuthError(401, "invalid_token");
137
+ if (resolved.expiresAt > stored.expiresAt || resolved.scopes.some((scope) => !stored.scopes.includes(scope)) || resolved.tenantIds?.some((id) => !stored.tenantIds?.includes(id)))
138
+ throw new HostedAuthError(401, "invalid_token");
114
139
  return resolved;
115
140
  } catch (error) {
116
141
  if (error instanceof HostedAuthError) throw error;
@@ -120,11 +145,9 @@ function createBearerVerifier(adapter, resource, pats) {
120
145
  }
121
146
 
122
147
  export {
123
- HOSTED_SCOPES,
124
- HostedAuthError,
125
148
  principalSchema,
126
149
  hashCredential,
127
150
  FilePatStore,
128
151
  createBearerVerifier
129
152
  };
130
- //# sourceMappingURL=chunk-X44C5GW2.js.map
153
+ //# sourceMappingURL=chunk-MUZ3KEJX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hosted/auth.ts"],"sourcesContent":["import { createHash, timingSafeEqual } from 'node:crypto';\nimport { constants } from 'node:fs';\nimport { open } from 'node:fs/promises';\nimport { z } from 'zod';\nimport type {\n HostedAdapter,\n HostedPrincipal,\n PatRecord,\n PatStore,\n} from './types.js';\nimport { HOSTED_SCOPES, HostedAuthError } from './types.js';\n\nconst identity = z\n .string()\n .min(1)\n .max(512)\n // biome-ignore lint/suspicious/noControlCharactersInRegex: Identity validation intentionally rejects control characters.\n .regex(/^[^\\x00-\\x1f\\x7f]+$/);\nconst workspaceSchema = z\n .object({ id: identity, name: identity.optional() })\n .strip();\nexport const principalSchema = z\n .object({\n actorKind: z.literal('user'),\n credentialType: z.enum(['oauth', 'pat']),\n userId: identity,\n companyId: identity,\n workspaceId: identity.optional(),\n workspaceName: identity.optional(),\n workspaces: z\n .union([\n z.object({ mode: z.literal('all') }).strip(),\n z\n .object({\n mode: z.literal('list'),\n ids: z.array(identity).min(1).max(100),\n })\n .strip(),\n ])\n .optional(),\n grantedWorkspaces: z.array(workspaceSchema).max(1000).optional(),\n // The issuer's own pick; recomputed below from grantedWorkspaces, never trusted on its own.\n defaultWorkspace: workspaceSchema.optional(),\n clientId: identity,\n resource: z.string().url(),\n scopes: z.array(z.enum(HOSTED_SCOPES)),\n expiresAt: z.number().finite().positive().optional(),\n tenantIds: z.array(identity).optional(),\n })\n .strict();\n\n/**\n * One shape for every issuer: the live granted list, and `workspaceId`/`workspaceName`\n * only when exactly one workspace is granted (the default for each call). An issuer\n * that grants one workspace sends only `workspaceId`; read that as a one-item list.\n * No workspace left, duplicate ids, or a `workspaceId` outside the list is refused.\n */\nfunction normalizeWorkspaces(\n value: z.infer<typeof principalSchema>\n): HostedPrincipal {\n const {\n defaultWorkspace: _default,\n workspaceId,\n workspaceName,\n grantedWorkspaces,\n ...rest\n } = value;\n const granted =\n grantedWorkspaces ??\n (workspaceId\n ? [\n workspaceName === undefined\n ? { id: workspaceId }\n : { id: workspaceId, name: workspaceName },\n ]\n : []);\n if (\n !granted.length ||\n new Set(granted.map((item) => item.id)).size !== granted.length ||\n (workspaceId !== undefined &&\n !granted.some((item) => item.id === workspaceId))\n ) {\n throw new HostedAuthError(401, 'invalid_token');\n }\n const only = granted.length === 1 ? granted[0] : undefined;\n return {\n ...rest,\n grantedWorkspaces: granted,\n ...(only ? { workspaceId: only.id } : {}),\n ...(only?.name !== undefined ? { workspaceName: only.name } : {}),\n };\n}\nconst recordSchema = z\n .object({\n hash: z.string().regex(/^[a-f0-9]{64}$/),\n revokedAt: z.number().finite().nullable(),\n principal: principalSchema,\n })\n .strict();\n\nexport function hashCredential(token: string): string {\n return createHash('sha256').update(token).digest('hex');\n}\n\n/** Reread on every request: an atomic file replacement revokes immediately, without restart. */\nexport class FilePatStore implements PatStore {\n constructor(private readonly path: string) {}\n async findByHash(hash: string): Promise<PatRecord | undefined> {\n const handle = await open(\n this.path,\n constants.O_RDONLY | constants.O_NOFOLLOW\n );\n try {\n const stat = await handle.stat();\n if (\n !stat.isFile() ||\n (stat.mode & 0o077) !== 0 ||\n stat.size > 1_048_576 ||\n (process.getuid && stat.uid !== process.getuid())\n )\n throw new Error('Invalid credential store');\n const records = z\n .array(recordSchema)\n .max(1000)\n .parse(JSON.parse(await handle.readFile('utf8')));\n const matches = records.filter((record) =>\n timingSafeEqual(\n Buffer.from(record.hash, 'hex'),\n Buffer.from(hash, 'hex')\n )\n );\n // Duplicate records are ambiguous, including an active/revoked pair.\n if (matches.length > 1) throw new Error('Ambiguous credential store');\n return matches[0];\n } finally {\n await handle.close();\n }\n }\n}\n\nfunction validatePrincipal(\n value: unknown,\n resource: string,\n kind: 'oauth' | 'pat'\n): HostedPrincipal {\n const parsed = principalSchema.safeParse(value);\n if (\n !parsed.success ||\n parsed.data.resource !== resource ||\n parsed.data.credentialType !== kind ||\n (parsed.data.expiresAt !== undefined &&\n parsed.data.expiresAt <= Date.now() / 1000) ||\n (kind === 'pat' && parsed.success && parsed.data.expiresAt === undefined)\n )\n throw new HostedAuthError(401, 'invalid_token');\n return normalizeWorkspaces(parsed.data);\n}\n\nexport function createBearerVerifier(\n adapter: HostedAdapter,\n resource: string,\n pats?: PatStore\n) {\n return async (token: string): Promise<HostedPrincipal> => {\n try {\n if (!/^[A-Za-z0-9._~+/-]+=*$/.test(token) || token.length > 8192)\n throw new HostedAuthError(401, 'invalid_token');\n if (!token.startsWith('dbp_')) {\n return validatePrincipal(\n await adapter.verifyOAuth(token, resource),\n resource,\n 'oauth'\n );\n }\n if (!/^dbp_[A-Za-z0-9_-]{32,}$/.test(token))\n throw new HostedAuthError(401, 'invalid_token');\n const record = await pats?.findByHash(hashCredential(token));\n if (\n !record ||\n record.revokedAt !== null ||\n record.hash !== hashCredential(token)\n )\n throw new HostedAuthError(401, 'invalid_token');\n const stored = validatePrincipal(record.principal, resource, 'pat');\n const resolved = validatePrincipal(\n await adapter.resolvePatGrant(structuredClone(stored)),\n resource,\n 'pat'\n );\n // Membership resolution may narrow a stored grant, never move or expand it.\n for (const key of [\n 'userId',\n 'companyId',\n 'clientId',\n 'resource',\n ] as const) {\n if (resolved[key] !== stored[key])\n throw new HostedAuthError(401, 'invalid_token');\n }\n if (\n resolved.grantedWorkspaces!.some(\n (item) => !stored.grantedWorkspaces!.some((old) => old.id === item.id)\n )\n ) {\n throw new HostedAuthError(401, 'invalid_token');\n }\n if (\n resolved.expiresAt! > stored.expiresAt! ||\n resolved.scopes.some((scope) => !stored.scopes.includes(scope)) ||\n resolved.tenantIds?.some((id) => !stored.tenantIds?.includes(id))\n )\n throw new HostedAuthError(401, 'invalid_token');\n return resolved;\n } catch (error) {\n if (error instanceof HostedAuthError) throw error;\n // No raw verifier, filesystem or remote errors can disclose credential values.\n throw new HostedAuthError(401, 'invalid_token');\n }\n };\n}\n"],"mappings":"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","names":[]}
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  oauthConfigSchema,
4
4
  oauthHttpsUrl
5
- } from "./chunk-AEQTEE5C.js";
5
+ } from "./chunk-4E57ZRZO.js";
6
6
 
7
7
  // src/utils/config.ts
8
- import { z, ZodError } from "zod";
8
+ import { ZodError, z } from "zod";
9
9
  var DEFAULT_POSTHOG_API_KEY = "phc_YgR4BO3sbtQAUvTfRUEN5xBzzPrJfrEG9STJEy3mNO2";
10
10
  var DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com";
11
11
  var configSchema = z.object({
@@ -22,17 +22,27 @@ var configSchema = z.object({
22
22
  message: "DATABRAIN_API_URL must use HTTPS without userinfo or fragments when OAuth is configured"
23
23
  });
24
24
  function formatConfigError(err) {
25
- const issues = err.issues.map((i) => i.path[0] === "oauth" ? `Invalid or missing OAuth setting: ${i.path.join(".")}` : i.message);
25
+ const issues = err.issues.map(
26
+ (i) => i.path[0] === "oauth" ? `Invalid or missing OAuth setting: ${i.path.join(".")}` : i.message
27
+ );
26
28
  const lines = ["[databrain] Configuration error:"];
27
29
  for (const issue of issues) {
28
30
  lines.push(` - ${issue}`);
29
31
  }
30
32
  lines.push("");
31
- lines.push(" Set DATABRAIN_SERVICE_TOKEN (Settings > Service Tokens) \u2014 org-level token for setup and management.");
32
- lines.push(' Then add to your MCP config: { "env": { "DATABRAIN_SERVICE_TOKEN": "..." } }');
33
- lines.push(" Or configure DATABRAIN_OAUTH_ISSUER, DATABRAIN_OAUTH_CLIENT_ID and DATABRAIN_OAUTH_CLIENT_SECRET for service credentials. See specs/oauth-client-credentials.md.");
33
+ lines.push(
34
+ " Set DATABRAIN_SERVICE_TOKEN (Settings > Service Tokens) \u2014 org-level token for setup and management."
35
+ );
36
+ lines.push(
37
+ ' Then add to your MCP config: { "env": { "DATABRAIN_SERVICE_TOKEN": "..." } }'
38
+ );
39
+ lines.push(
40
+ " Or configure DATABRAIN_OAUTH_ISSUER, DATABRAIN_OAUTH_CLIENT_ID and DATABRAIN_OAUTH_CLIENT_SECRET for service credentials. See specs/oauth-client-credentials.md."
41
+ );
34
42
  lines.push("");
35
- lines.push(" Self-hosted? Also set DATABRAIN_API_URL to your instance's API origin (e.g. https://api.your-databrain.example.com). Tokens minted on cloud do not work against a self-hosted instance and vice versa.");
43
+ lines.push(
44
+ " Self-hosted? Also set DATABRAIN_API_URL to your instance's API origin (e.g. https://api.your-databrain.example.com). Tokens minted on cloud do not work against a self-hosted instance and vice versa."
45
+ );
36
46
  return lines.join("\n");
37
47
  }
38
48
  function loadConfig() {
@@ -45,7 +55,9 @@ function loadConfig() {
45
55
  scope: process.env.DATABRAIN_OAUTH_SCOPE || void 0,
46
56
  authMethod: process.env.DATABRAIN_OAUTH_AUTH_METHOD || void 0
47
57
  };
48
- const oauthConfigured = Object.values(oauthEnv).some((value) => value !== void 0);
58
+ const oauthConfigured = Object.values(oauthEnv).some(
59
+ (value) => value !== void 0
60
+ );
49
61
  return configSchema.parse({
50
62
  serviceToken: process.env.DATABRAIN_SERVICE_TOKEN || void 0,
51
63
  apiToken: process.env.DATABRAIN_API_TOKEN || void 0,
@@ -60,8 +72,11 @@ function loadConfig() {
60
72
  });
61
73
  } catch (err) {
62
74
  if (err instanceof ZodError) {
63
- process.stderr.write(formatConfigError(err) + "\n");
64
- throw new Error("Invalid Databrain configuration. Check the named environment settings.");
75
+ process.stderr.write(`${formatConfigError(err)}
76
+ `);
77
+ throw new Error(
78
+ "Invalid Databrain configuration. Check the named environment settings."
79
+ );
65
80
  }
66
81
  throw err;
67
82
  }
@@ -72,4 +87,4 @@ export {
72
87
  DEFAULT_POSTHOG_HOST,
73
88
  loadConfig
74
89
  };
75
- //# sourceMappingURL=chunk-DTFYHLYN.js.map
90
+ //# sourceMappingURL=chunk-NXE2A4WV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/config.ts"],"sourcesContent":["import { ZodError, z } from 'zod';\nimport {\n oauthConfigSchema,\n oauthHttpsUrl,\n} from '../client/oauth-client-credentials.js';\n\n// PostHog project token for Databrain's own MCP usage analytics. This is a\n// public write-only key (like a web analytics site ID), safe to ship in the\n// package — it lets the CLI send telemetry out of the box, matching how\n// DATABRAIN_API_URL already ships with a working default.\nexport const DEFAULT_POSTHOG_API_KEY =\n 'phc_YgR4BO3sbtQAUvTfRUEN5xBzzPrJfrEG9STJEy3mNO2';\nexport const DEFAULT_POSTHOG_HOST = 'https://us.i.posthog.com';\n\nconst configSchema = z\n .object({\n serviceToken: z.string().min(1).optional(),\n apiToken: z.string().min(1).optional(),\n oauth: oauthConfigSchema.optional(),\n apiUrl: z.string().url().default('https://api.usedatabrain.com'),\n demoDomain: z.string().url().default('https://demo.usedatabrain.com'),\n posthogApiKey: z.string().min(1).optional(),\n posthogHost: z.string().url().optional(),\n })\n .refine((c) => c.serviceToken || c.apiToken || c.oauth, {\n message:\n 'Set DATABRAIN_SERVICE_TOKEN, DATABRAIN_API_TOKEN, or complete DATABRAIN_OAUTH_* client credentials',\n })\n .refine((c) => !c.oauth || oauthHttpsUrl.safeParse(c.apiUrl).success, {\n message:\n 'DATABRAIN_API_URL must use HTTPS without userinfo or fragments when OAuth is configured',\n });\n\nexport type Config = z.infer<typeof configSchema>;\n\nfunction formatConfigError(err: ZodError): string {\n const issues = err.issues.map((i) =>\n i.path[0] === 'oauth'\n ? `Invalid or missing OAuth setting: ${i.path.join('.')}`\n : i.message\n );\n const lines = ['[databrain] Configuration error:'];\n for (const issue of issues) {\n lines.push(` - ${issue}`);\n }\n lines.push('');\n lines.push(\n ' Set DATABRAIN_SERVICE_TOKEN (Settings > Service Tokens) — org-level token for setup and management.'\n );\n lines.push(\n ' Then add to your MCP config: { \"env\": { \"DATABRAIN_SERVICE_TOKEN\": \"...\" } }'\n );\n lines.push(\n ' Or configure DATABRAIN_OAUTH_ISSUER, DATABRAIN_OAUTH_CLIENT_ID and DATABRAIN_OAUTH_CLIENT_SECRET for service credentials. See specs/oauth-client-credentials.md.'\n );\n lines.push('');\n lines.push(\n \" Self-hosted? Also set DATABRAIN_API_URL to your instance's API origin (e.g. https://api.your-databrain.example.com). Tokens minted on cloud do not work against a self-hosted instance and vice versa.\"\n );\n return lines.join('\\n');\n}\n\nexport function loadConfig(): Config {\n try {\n const oauthEnv = {\n issuer: process.env.DATABRAIN_OAUTH_ISSUER || undefined,\n clientId: process.env.DATABRAIN_OAUTH_CLIENT_ID || undefined,\n clientSecret: process.env.DATABRAIN_OAUTH_CLIENT_SECRET || undefined,\n resource: process.env.DATABRAIN_OAUTH_RESOURCE || undefined,\n scope: process.env.DATABRAIN_OAUTH_SCOPE || undefined,\n authMethod: process.env.DATABRAIN_OAUTH_AUTH_METHOD || undefined,\n };\n const oauthConfigured = Object.values(oauthEnv).some(\n (value) => value !== undefined\n );\n return configSchema.parse({\n serviceToken: process.env.DATABRAIN_SERVICE_TOKEN || undefined,\n apiToken: process.env.DATABRAIN_API_TOKEN || undefined,\n oauth: oauthConfigured\n ? {\n ...oauthEnv,\n resource:\n oauthEnv.resource ||\n process.env.DATABRAIN_API_URL ||\n 'https://api.usedatabrain.com',\n }\n : undefined,\n apiUrl: process.env.DATABRAIN_API_URL || 'https://api.usedatabrain.com',\n demoDomain:\n process.env.DATABRAIN_DEMO_DOMAIN || 'https://demo.usedatabrain.com',\n posthogApiKey:\n process.env.POSTHOG_PROJECT_API_KEY || DEFAULT_POSTHOG_API_KEY,\n posthogHost: process.env.POSTHOG_HOST || DEFAULT_POSTHOG_HOST,\n });\n } catch (err) {\n if (err instanceof ZodError) {\n process.stderr.write(`${formatConfigError(err)}\\n`);\n // Zod enum failures can contain the supplied value; index.ts serializes\n // startup errors. Do not expose any OAuth config values to that logger.\n throw new Error(\n 'Invalid Databrain configuration. Check the named environment settings.'\n );\n }\n throw err;\n }\n}\n"],"mappings":";;;;;;;AAAA,SAAS,UAAU,SAAS;AAUrB,IAAM,0BACX;AACK,IAAM,uBAAuB;AAEpC,IAAM,eAAe,EAClB,OAAO;AAAA,EACN,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACzC,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EACrC,OAAO,kBAAkB,SAAS;AAAA,EAClC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,8BAA8B;AAAA,EAC/D,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,+BAA+B;AAAA,EACpE,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS;AAAA,EAC1C,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AACzC,CAAC,EACA,OAAO,CAAC,MAAM,EAAE,gBAAgB,EAAE,YAAY,EAAE,OAAO;AAAA,EACtD,SACE;AACJ,CAAC,EACA,OAAO,CAAC,MAAM,CAAC,EAAE,SAAS,cAAc,UAAU,EAAE,MAAM,EAAE,SAAS;AAAA,EACpE,SACE;AACJ,CAAC;AAIH,SAAS,kBAAkB,KAAuB;AAChD,QAAM,SAAS,IAAI,OAAO;AAAA,IAAI,CAAC,MAC7B,EAAE,KAAK,CAAC,MAAM,UACV,qCAAqC,EAAE,KAAK,KAAK,GAAG,CAAC,KACrD,EAAE;AAAA,EACR;AACA,QAAM,QAAQ,CAAC,kCAAkC;AACjD,aAAW,SAAS,QAAQ;AAC1B,UAAM,KAAK,OAAO,KAAK,EAAE;AAAA,EAC3B;AACA,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ;AAAA,EACF;AACA,QAAM;AAAA,IACJ;AAAA,EACF;AACA,QAAM;AAAA,IACJ;AAAA,EACF;AACA,QAAM,KAAK,EAAE;AACb,QAAM;AAAA,IACJ;AAAA,EACF;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEO,SAAS,aAAqB;AACnC,MAAI;AACF,UAAM,WAAW;AAAA,MACf,QAAQ,QAAQ,IAAI,0BAA0B;AAAA,MAC9C,UAAU,QAAQ,IAAI,6BAA6B;AAAA,MACnD,cAAc,QAAQ,IAAI,iCAAiC;AAAA,MAC3D,UAAU,QAAQ,IAAI,4BAA4B;AAAA,MAClD,OAAO,QAAQ,IAAI,yBAAyB;AAAA,MAC5C,YAAY,QAAQ,IAAI,+BAA+B;AAAA,IACzD;AACA,UAAM,kBAAkB,OAAO,OAAO,QAAQ,EAAE;AAAA,MAC9C,CAAC,UAAU,UAAU;AAAA,IACvB;AACA,WAAO,aAAa,MAAM;AAAA,MACxB,cAAc,QAAQ,IAAI,2BAA2B;AAAA,MACrD,UAAU,QAAQ,IAAI,uBAAuB;AAAA,MAC7C,OAAO,kBACH;AAAA,QACE,GAAG;AAAA,QACH,UACE,SAAS,YACT,QAAQ,IAAI,qBACZ;AAAA,MACJ,IACA;AAAA,MACJ,QAAQ,QAAQ,IAAI,qBAAqB;AAAA,MACzC,YACE,QAAQ,IAAI,yBAAyB;AAAA,MACvC,eACE,QAAQ,IAAI,2BAA2B;AAAA,MACzC,aAAa,QAAQ,IAAI,gBAAgB;AAAA,IAC3C,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,eAAe,UAAU;AAC3B,cAAQ,OAAO,MAAM,GAAG,kBAAkB,GAAG,CAAC;AAAA,CAAI;AAGlD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;","names":[]}