@databrainhq/mcp-server 0.3.0-rc.1 → 0.3.0-rc.11

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/README.md +158 -7
  2. package/dist/apps/server.d.ts +82 -44
  3. package/dist/apps/server.js +5 -2
  4. package/dist/apps/stdio.js +6 -3
  5. package/dist/apps/stdio.js.map +1 -1
  6. package/dist/bundle-TDQXWIPM.js +24 -0
  7. package/dist/bundle-TDQXWIPM.js.map +1 -0
  8. package/dist/{chunk-RF3HTNO7.js → chunk-DM7S4R57.js} +2 -2
  9. package/dist/{chunk-7DS7OLWP.js → chunk-ES2LUP4U.js} +2030 -1214
  10. package/dist/chunk-ES2LUP4U.js.map +1 -0
  11. package/dist/chunk-IBYBK5ZE.js +34 -0
  12. package/dist/chunk-IBYBK5ZE.js.map +1 -0
  13. package/dist/chunk-NBFUKVWP.js +603 -0
  14. package/dist/chunk-NBFUKVWP.js.map +1 -0
  15. package/dist/chunk-Q5EYDKSA.js +1224 -0
  16. package/dist/chunk-Q5EYDKSA.js.map +1 -0
  17. package/dist/chunk-RHK62ZJ6.js +884 -0
  18. package/dist/chunk-RHK62ZJ6.js.map +1 -0
  19. package/dist/{chunk-JCXUZ3QZ.js → chunk-Z3ZHN4UW.js} +34 -6
  20. package/dist/chunk-Z3ZHN4UW.js.map +1 -0
  21. package/dist/cli.d.ts +15 -0
  22. package/dist/cli.js +497 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/custom-chart/custom-runtime.js +63 -0
  25. package/dist/custom-chart/runtime-shim.js +146 -0
  26. package/dist/custom-chart/template/AGENTS.md +374 -0
  27. package/dist/custom-chart/template/README.md +374 -0
  28. package/dist/custom-chart/template/chart.css +28 -0
  29. package/dist/custom-chart/template/chart.ts +192 -0
  30. package/dist/custom-chart/template/custom-chart.d.ts +78 -0
  31. package/dist/custom-chart/template/icon.svg +1 -0
  32. package/dist/custom-chart/template/manifest.json +23 -0
  33. package/dist/custom-chart/template/package.json +14 -0
  34. package/dist/custom-chart/template/sample-data.json +18 -0
  35. package/dist/custom-chart/template/tsconfig.json +12 -0
  36. package/dist/index.js +15 -8
  37. package/dist/index.js.map +1 -1
  38. package/dist/main-JEYYFGJM.js +872 -0
  39. package/dist/main-JEYYFGJM.js.map +1 -0
  40. package/dist/{test-only-EJ5O4WN5.js → test-only-K7QTSVO4.js} +7 -6
  41. package/dist/test-only-K7QTSVO4.js.map +1 -0
  42. package/package.json +9 -4
  43. package/dist/chunk-7DS7OLWP.js.map +0 -1
  44. package/dist/chunk-JCXUZ3QZ.js.map +0 -1
  45. package/dist/chunk-NDVW3ZSJ.js +0 -310
  46. package/dist/chunk-NDVW3ZSJ.js.map +0 -1
  47. package/dist/main-7QNRH6X4.js +0 -438
  48. package/dist/main-7QNRH6X4.js.map +0 -1
  49. package/dist/test-only-EJ5O4WN5.js.map +0 -1
  50. /package/dist/{chunk-RF3HTNO7.js.map → chunk-DM7S4R57.js.map} +0 -0
@@ -1,438 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- DatabrainApiError,
4
- DatabrainClient,
5
- MCP_SERVER_VERSION,
6
- registerAllPrompts,
7
- registerAllResources,
8
- registerAllTools,
9
- toolContext,
10
- toolError,
11
- toolSuccess
12
- } from "./chunk-7DS7OLWP.js";
13
- import {
14
- FilePatStore,
15
- HOSTED_SCOPES,
16
- HostedAuthError,
17
- createBearerVerifier,
18
- principalSchema
19
- } from "./chunk-JCXUZ3QZ.js";
20
-
21
- // src/hosted/main.ts
22
- import { isAbsolute } from "path";
23
- import { pathToFileURL } from "url";
24
-
25
- // src/hosted/config.ts
26
- function loadHostedConfig(env = process.env) {
27
- const testOnly = env.DATABRAIN_HOSTED_TEST_ONLY === "1";
28
- if (testOnly && env.NODE_ENV === "production") throw new Error("Hosted test mode is forbidden in production.");
29
- if (env.DATABRAIN_HOSTED_TEST_ONLY && !testOnly) throw new Error("Invalid DATABRAIN_HOSTED_TEST_ONLY.");
30
- const validUrl = (value, field) => {
31
- try {
32
- if (!value) throw new Error();
33
- const url = new URL(value);
34
- if (url.username || url.password || url.search || url.hash || url.protocol !== "https:") throw new Error();
35
- return url;
36
- } catch {
37
- throw new Error(`Configure ${field} as an HTTPS URL without credentials, query or fragment.`);
38
- }
39
- };
40
- const publicUrl = validUrl(env.DATABRAIN_HOSTED_PUBLIC_URL, "DATABRAIN_HOSTED_PUBLIC_URL");
41
- validUrl(env.DATABRAIN_HOSTED_ISSUER, "DATABRAIN_HOSTED_ISSUER");
42
- if (publicUrl.pathname !== "/mcp") throw new Error("DATABRAIN_HOSTED_PUBLIC_URL must end in /mcp.");
43
- const port = Number(env.DATABRAIN_HOSTED_PORT || "3144");
44
- if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error("Invalid DATABRAIN_HOSTED_PORT.");
45
- return { publicUrl: env.DATABRAIN_HOSTED_PUBLIC_URL, issuer: env.DATABRAIN_HOSTED_ISSUER, port, testOnly };
46
- }
47
-
48
- // src/hosted/http.ts
49
- import { createServer } from "http";
50
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
51
-
52
- // src/hosted/server.ts
53
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
54
- import { instrument } from "@posthog/mcp";
55
- import { PostHog } from "posthog-node";
56
-
57
- // src/hosted/authorization.ts
58
- var TOOL_SCOPES = {
59
- whoami: [],
60
- list_datasources: ["mcp:datamarts:read"],
61
- discover_datasource_schema: ["mcp:datamarts:read"],
62
- list_datamarts: ["mcp:datamarts:read"],
63
- list_workspaces: ["mcp:datamarts:read"],
64
- sync_datasource: ["mcp:datamarts:write"],
65
- create_datamart: ["mcp:datamarts:write"],
66
- list_data_apps: ["mcp:dashboards:read"],
67
- list_tenants: ["mcp:dashboards:read"],
68
- create_data_app: ["mcp:embeds:write"],
69
- list_api_tokens: ["mcp:embeds:write"],
70
- create_api_token: ["mcp:embeds:write"],
71
- rotate_api_token: ["mcp:embeds:write"],
72
- create_embed: ["mcp:embeds:write"],
73
- list_embeds: ["mcp:dashboards:read"],
74
- get_embed_details: ["mcp:dashboards:read"],
75
- update_embed: ["mcp:embeds:write"],
76
- customize_embed_theme: ["mcp:embeds:write"],
77
- get_demo_link: ["mcp:embeds:write"],
78
- generate_guest_token: ["mcp:embeds:write"],
79
- generate_embed_code: ["mcp:dashboards:read"],
80
- list_dashboards: ["mcp:dashboards:read"],
81
- create_dashboard: ["mcp:dashboards:write"],
82
- get_dashboard: ["mcp:dashboards:read"],
83
- generate_dashboard_tiles: ["mcp:dashboards:write", "mcp:query"],
84
- ask_question: ["mcp:query"],
85
- get_semantic_layer: ["mcp:datamarts:read"],
86
- update_semantic_layer: ["mcp:datamarts:write"],
87
- start_semantic_layer_generation: ["mcp:datamarts:write"],
88
- get_semantic_layer_generation_status: ["mcp:datamarts:read"],
89
- list_widgets: ["mcp:dashboards:read"],
90
- validate_metric_spec: ["mcp:dashboards:read", "mcp:query"],
91
- create_or_update_metric_from_query: ["mcp:dashboards:write", "mcp:query"],
92
- create_workspace_metric: ["mcp:dashboards:write", "mcp:query"],
93
- add_widget: ["mcp:dashboards:write"],
94
- update_widget: ["mcp:dashboards:write"],
95
- remove_widget: ["mcp:dashboards:write"],
96
- generate_widget: ["mcp:dashboards:write", "mcp:query"],
97
- list_scheduled_reports: ["mcp:dashboards:read"]
98
- };
99
- function requiredScopes(name) {
100
- if (!Object.hasOwn(TOOL_SCOPES, name)) throw new HostedAuthError(403, "tool_not_authorized");
101
- return TOOL_SCOPES[name];
102
- }
103
- function authorizeTool(name, principal) {
104
- if (principal.expiresAt !== void 0 && principal.expiresAt <= Date.now() / 1e3) throw new HostedAuthError(401, "invalid_token");
105
- if (requiredScopes(name).some((scope) => !principal.scopes.includes(scope))) throw new HostedAuthError(403, "insufficient_scope");
106
- }
107
- function assertBoundArguments(value, principal) {
108
- if (!value || typeof value !== "object") return;
109
- for (const [key, child] of Object.entries(value)) {
110
- if (["workspaceId", "workspaceName", "companyId", "userId"].includes(key) && child !== principal[key]) {
111
- throw new HostedAuthError(403, "principal_mismatch");
112
- }
113
- if ([
114
- "scope",
115
- "scopes",
116
- "authUser",
117
- "actorKind",
118
- "credentialType",
119
- "oauthClientId",
120
- "operatorEmail",
121
- "session_variables",
122
- "isAllClient",
123
- "isAdmin",
124
- "serviceToken",
125
- "apiToken",
126
- "authorization"
127
- ].includes(key)) {
128
- throw new HostedAuthError(403, "identity_argument_forbidden");
129
- }
130
- if (["clientId", "tenantId"].includes(key) && (typeof child !== "string" || !principal.tenantIds?.includes(child))) {
131
- throw new HostedAuthError(403, "tenant_not_authorized");
132
- }
133
- assertBoundArguments(child, principal);
134
- }
135
- }
136
-
137
- // src/hosted/server.ts
138
- var schemaInstrumentation = new PostHog("hosted-contract-only", { disabled: true });
139
- var HostedClient = class extends DatabrainClient {
140
- constructor(adapter, principal) {
141
- super({ apiUrl: "https://api.usedatabrain.com", demoDomain: "https://demo.usedatabrain.com" });
142
- this.adapter = adapter;
143
- this.principal = principal;
144
- }
145
- // A generated API credential must never replace the authorizing hosted principal.
146
- setRuntimeApiToken(_token) {
147
- }
148
- async request(options) {
149
- try {
150
- const name = toolContext.getStore()?.tool;
151
- if (!name) throw new HostedAuthError(403, "tool_not_authorized");
152
- authorizeTool(name, this.principal);
153
- assertBoundArguments(options.body, this.principal);
154
- assertBoundArguments(options.query, this.principal);
155
- const raw = await this.adapter.executeRequest(structuredClone(options), structuredClone(this.principal));
156
- if (!options.skipDataUnwrap && raw && typeof raw === "object" && !Array.isArray(raw) && "data" in raw && raw.data !== null && raw.data !== void 0) return raw.data;
157
- return raw;
158
- } catch (error) {
159
- const status = error instanceof HostedAuthError ? error.status : 503;
160
- throw new DatabrainApiError(
161
- status,
162
- "HOSTED_REQUEST_DENIED",
163
- "The authorized backend could not complete this request. Check workspace access and hosted integration."
164
- );
165
- }
166
- }
167
- };
168
- function createHostedToolServer(principal, adapter) {
169
- const bound = structuredClone(principal);
170
- const server = new McpServer({ name: "databrain", version: MCP_SERVER_VERSION }, {
171
- instructions: "Hosted Databrain: each request is authenticated as a user in one authorized workspace. Use whoami to inspect the verified identity and scopes. 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."
172
- });
173
- const register = server.tool.bind(server);
174
- server.tool = ((...args) => {
175
- const name = args[0];
176
- const index = args.length - 1;
177
- const handler = args[index];
178
- const shape = args[2];
179
- args[index] = async (...values) => {
180
- try {
181
- authorizeTool(name, bound);
182
- assertBoundArguments(values[0], bound);
183
- if (name === "whoami") {
184
- return toolSuccess(JSON.stringify({
185
- status: "authenticated",
186
- activeBearer: bound.credentialType,
187
- actorKind: bound.actorKind,
188
- userId: bound.userId,
189
- companyId: bound.companyId,
190
- workspaceId: bound.workspaceId,
191
- workspaceName: bound.workspaceName,
192
- oauthClientId: bound.clientId,
193
- scopes: bound.scopes
194
- }));
195
- }
196
- if (shape && typeof shape === "object" && values[0] && typeof values[0] === "object") {
197
- for (const key of ["workspaceName", "workspaceId"]) {
198
- if (Object.hasOwn(shape, key) && bound[key] !== void 0) values[0][key] = bound[key];
199
- }
200
- }
201
- return await handler(...values);
202
- } catch {
203
- return toolError("Hosted authorization denied this tool call. Check the granted scopes, workspace and tenant access.");
204
- }
205
- };
206
- return register(...args);
207
- });
208
- registerAllTools(server, new HostedClient(adapter, bound));
209
- registerAllResources(server);
210
- registerAllPrompts(server);
211
- instrument(server, schemaInstrumentation, { beforeSend: () => null });
212
- return server;
213
- }
214
-
215
- // src/hosted/http.ts
216
- function reply(res, status, body) {
217
- res.writeHead(status, { "Content-Type": "application/json", "Cache-Control": "no-store" });
218
- res.end(JSON.stringify(body));
219
- }
220
- async function readBody(req) {
221
- let size = 0;
222
- const chunks = [];
223
- for await (const chunk of req) {
224
- size += chunk.length;
225
- if (size > 1048576) throw new Error("body_too_large");
226
- chunks.push(Buffer.from(chunk));
227
- }
228
- return JSON.parse(Buffer.concat(chunks).toString("utf8"));
229
- }
230
- function createHostedHttpServer(config, adapter, pats) {
231
- const publicUrl = new URL(config.publicUrl);
232
- const metadataPath = `/.well-known/oauth-protected-resource${publicUrl.pathname === "/" ? "" : publicUrl.pathname}`;
233
- const metadataUrl = `${publicUrl.origin}${metadataPath}`;
234
- const verify = createBearerVerifier(adapter, config.publicUrl, pats);
235
- let active = 0;
236
- const server = createServer(async (req, res) => {
237
- res.setHeader("X-Content-Type-Options", "nosniff");
238
- if (config.testOnly) res.setHeader("X-Databrain-Test-Only", "true");
239
- try {
240
- const localPort = server.address()?.port;
241
- if (![publicUrl.host, `127.0.0.1:${localPort}`, `localhost:${localPort}`].includes(req.headers.host || "")) {
242
- reply(res, 403, { error: "invalid_host" });
243
- return;
244
- }
245
- if (req.headers.origin && req.headers.origin !== publicUrl.origin) {
246
- reply(res, 403, { error: "invalid_origin" });
247
- return;
248
- }
249
- if (req.headers.origin) {
250
- res.setHeader("Access-Control-Allow-Origin", publicUrl.origin);
251
- res.setHeader("Vary", "Origin");
252
- res.setHeader("Access-Control-Expose-Headers", "WWW-Authenticate, MCP-Protocol-Version");
253
- }
254
- const url = new URL(req.url || "/", publicUrl.origin);
255
- if (req.method === "GET" && [metadataPath, "/.well-known/oauth-protected-resource"].includes(url.pathname)) {
256
- reply(res, 200, {
257
- resource: config.publicUrl,
258
- authorization_servers: [config.issuer],
259
- scopes_supported: HOSTED_SCOPES,
260
- bearer_methods_supported: ["header"],
261
- resource_name: config.testOnly ? "Databrain (TEST ONLY; fixture identity)" : "Databrain"
262
- });
263
- return;
264
- }
265
- if (url.pathname !== publicUrl.pathname) {
266
- reply(res, 404, { error: "not_found" });
267
- return;
268
- }
269
- if (url.search) {
270
- reply(res, 400, { error: "query_parameters_not_supported" });
271
- return;
272
- }
273
- if (req.method === "OPTIONS") {
274
- res.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, OPTIONS");
275
- res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, MCP-Protocol-Version, MCP-Session-Id");
276
- res.writeHead(204);
277
- res.end();
278
- return;
279
- }
280
- const authFields = req.rawHeaders.filter((_, i) => i % 2 === 0 && req.rawHeaders[i].toLowerCase() === "authorization");
281
- const match = /^Bearer ([^\s]+)$/i.exec(req.headers.authorization || "");
282
- if (authFields.length !== 1 || !match) throw new HostedAuthError(401, "invalid_token");
283
- const principal = await verify(match[1]);
284
- if (req.method !== "POST") {
285
- res.setHeader("Allow", "POST, OPTIONS");
286
- reply(res, 405, { error: "method_not_allowed" });
287
- return;
288
- }
289
- if (!req.headers["content-type"]?.toLowerCase().startsWith("application/json")) {
290
- reply(res, 415, { error: "unsupported_media_type" });
291
- return;
292
- }
293
- if (active >= 32) {
294
- reply(res, 503, { error: "server_busy" });
295
- return;
296
- }
297
- active++;
298
- let body;
299
- try {
300
- body = await readBody(req);
301
- } catch {
302
- active--;
303
- reply(res, 400, { error: "invalid_request" });
304
- return;
305
- }
306
- try {
307
- if (Array.isArray(body)) {
308
- reply(res, 400, { error: "batch_not_supported" });
309
- return;
310
- }
311
- if (body?.method === "tools/call" && typeof body.params?.name === "string") {
312
- try {
313
- authorizeTool(body.params.name, principal);
314
- assertBoundArguments(body.params.arguments, principal);
315
- } catch (error) {
316
- if (error instanceof HostedAuthError && error.code === "insufficient_scope") {
317
- res.setHeader("WWW-Authenticate", `Bearer error="insufficient_scope", resource_metadata="${metadataUrl}", scope="${requiredScopes(body.params.name).join(" ")}"`);
318
- }
319
- throw error;
320
- }
321
- }
322
- const mcp = createHostedToolServer(principal, adapter);
323
- const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0, enableJsonResponse: true });
324
- let closed = false;
325
- const close = () => {
326
- if (!closed) {
327
- closed = true;
328
- void mcp.close();
329
- }
330
- };
331
- res.once("close", close);
332
- try {
333
- await mcp.connect(transport);
334
- await transport.handleRequest(req, res, body);
335
- } finally {
336
- if (res.writableEnded) close();
337
- }
338
- } finally {
339
- active--;
340
- }
341
- } catch (error) {
342
- if (res.headersSent) {
343
- res.end();
344
- return;
345
- }
346
- const status = error instanceof HostedAuthError ? error.status : 503;
347
- if (status === 401) res.setHeader("WWW-Authenticate", `Bearer error="invalid_token", resource_metadata="${metadataUrl}"`);
348
- reply(res, status, { error: error instanceof HostedAuthError ? error.code : "temporarily_unavailable" });
349
- }
350
- });
351
- server.requestTimeout = 3e4;
352
- server.headersTimeout = 1e4;
353
- server.keepAliveTimeout = 5e3;
354
- return server;
355
- }
356
-
357
- // src/hosted/provider.ts
358
- import { z } from "zod";
359
- function createProviderAdapter(config) {
360
- const issuer = new URL(config.issuer);
361
- if (issuer.protocol !== "https:" || issuer.username || issuer.password || issuer.search || issuer.hash) {
362
- throw new Error("Invalid hosted authorization server configuration.");
363
- }
364
- const endpoint = `${config.issuer.replace(/\/$/, "")}/oauth/principal`;
365
- const responseSchema = principalSchema.extend({ issuer: z.literal(config.issuer) }).strip();
366
- return {
367
- async verifyOAuth(token, resource) {
368
- try {
369
- const response = await fetch(endpoint, {
370
- headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
371
- redirect: "error",
372
- signal: AbortSignal.timeout(1e4)
373
- });
374
- if (!response.ok) throw new HostedAuthError(response.status >= 500 ? 503 : 401, "invalid_token");
375
- const { issuer: _issuer, ...principal } = responseSchema.parse(await response.json());
376
- if (principal.resource !== resource || principal.credentialType !== "oauth") throw new HostedAuthError(401, "invalid_token");
377
- return principal;
378
- } catch (error) {
379
- if (error instanceof HostedAuthError) throw error;
380
- throw new HostedAuthError(401, "invalid_token");
381
- }
382
- },
383
- async resolvePatGrant() {
384
- throw new HostedAuthError(401, "pat_membership_adapter_required");
385
- },
386
- async executeRequest() {
387
- throw new HostedAuthError(503, "delegated_backend_adapter_required");
388
- }
389
- };
390
- }
391
-
392
- // src/hosted/main.ts
393
- async function startHosted() {
394
- const config = loadHostedConfig();
395
- let adapter;
396
- if (config.testOnly) {
397
- const file = process.env.DATABRAIN_HOSTED_TEST_FIXTURES;
398
- if (!file || !isAbsolute(file)) throw new Error("Hosted test mode requires an absolute fixture store path.");
399
- const { createTestOnlyAdapter } = await import("./test-only-EJ5O4WN5.js");
400
- adapter = createTestOnlyAdapter(file);
401
- } else {
402
- const file = process.env.DATABRAIN_HOSTED_ADAPTER_MODULE;
403
- if (!file) {
404
- adapter = createProviderAdapter(config);
405
- process.stderr.write("[databrain] OAuth verification configured; backend data operations and PAT membership require a delegated adapter.\n");
406
- } else {
407
- if (!isAbsolute(file)) throw new Error("DATABRAIN_HOSTED_ADAPTER_MODULE must be an absolute path.");
408
- try {
409
- adapter = (await import(pathToFileURL(file).href)).adapter;
410
- } catch {
411
- throw new Error("Could not load the trusted hosted adapter.");
412
- }
413
- }
414
- if (!adapter || ["verifyOAuth", "resolvePatGrant", "executeRequest"].some((key) => typeof adapter[key] !== "function")) throw new Error("Invalid trusted hosted adapter.");
415
- }
416
- const patPath = process.env.DATABRAIN_HOSTED_PAT_STORE;
417
- if (patPath && !isAbsolute(patPath)) throw new Error("Hosted PAT store must be an absolute path.");
418
- const server = createHostedHttpServer(config, adapter, patPath ? new FilePatStore(patPath) : void 0);
419
- await new Promise((resolve, reject) => {
420
- server.once("error", reject);
421
- server.listen(config.port, "127.0.0.1", resolve);
422
- });
423
- process.stderr.write(`[databrain] hosted HTTP ready on 127.0.0.1:${config.port}/mcp${config.testOnly ? " \u2014 TEST ONLY: synthetic identity and data; no OAuth consent" : ""}
424
- `);
425
- const shutdown = () => {
426
- server.close(() => process.exit(0));
427
- setTimeout(() => {
428
- server.closeAllConnections();
429
- process.exit(0);
430
- }, 5e3).unref();
431
- };
432
- process.once("SIGINT", shutdown);
433
- process.once("SIGTERM", shutdown);
434
- }
435
- export {
436
- startHosted
437
- };
438
- //# sourceMappingURL=main-7QNRH6X4.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hosted/main.ts","../src/hosted/config.ts","../src/hosted/http.ts","../src/hosted/server.ts","../src/hosted/authorization.ts","../src/hosted/provider.ts"],"sourcesContent":["import { isAbsolute } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { loadHostedConfig } from './config.js';\nimport { FilePatStore } from './auth.js';\nimport { createHostedHttpServer } from './http.js';\nimport type { HostedAdapter } from './types.js';\nimport { createProviderAdapter } from './provider.js';\n\nexport async function startHosted(): Promise<void> {\n const config = loadHostedConfig();\n let adapter: HostedAdapter;\n if (config.testOnly) {\n const file = process.env.DATABRAIN_HOSTED_TEST_FIXTURES;\n if (!file || !isAbsolute(file)) throw new Error('Hosted test mode requires an absolute fixture store path.');\n const { createTestOnlyAdapter } = await import('./test-only.js');\n adapter = createTestOnlyAdapter(file);\n } else {\n const file = process.env.DATABRAIN_HOSTED_ADAPTER_MODULE;\n if (!file) {\n adapter = createProviderAdapter(config);\n process.stderr.write('[databrain] OAuth verification configured; backend data operations and PAT membership require a delegated adapter.\\n');\n } else {\n if (!isAbsolute(file)) throw new Error('DATABRAIN_HOSTED_ADAPTER_MODULE must be an absolute path.');\n try { adapter = (await import(pathToFileURL(file).href)).adapter; }\n catch { throw new Error('Could not load the trusted hosted adapter.'); }\n }\n if (!adapter || ['verifyOAuth', 'resolvePatGrant', 'executeRequest'].some(key =>\n typeof adapter[key as keyof HostedAdapter] !== 'function')) throw new Error('Invalid trusted hosted adapter.');\n }\n const patPath = process.env.DATABRAIN_HOSTED_PAT_STORE;\n if (patPath && !isAbsolute(patPath)) throw new Error('Hosted PAT store must be an absolute path.');\n const server = createHostedHttpServer(config, adapter, patPath ? new FilePatStore(patPath) : undefined);\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(config.port, '127.0.0.1', resolve);\n });\n process.stderr.write(`[databrain] hosted HTTP ready on 127.0.0.1:${config.port}/mcp${config.testOnly ? ' — TEST ONLY: synthetic identity and data; no OAuth consent' : ''}\\n`);\n const shutdown = () => {\n server.close(() => process.exit(0));\n setTimeout(() => { server.closeAllConnections(); process.exit(0); }, 5000).unref();\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n}\n","import type { HostedConfig } from './types.js';\n\nexport function loadHostedConfig(env: NodeJS.ProcessEnv = process.env): HostedConfig {\n const testOnly = env.DATABRAIN_HOSTED_TEST_ONLY === '1';\n if (testOnly && env.NODE_ENV === 'production') throw new Error('Hosted test mode is forbidden in production.');\n if (env.DATABRAIN_HOSTED_TEST_ONLY && !testOnly) throw new Error('Invalid DATABRAIN_HOSTED_TEST_ONLY.');\n const validUrl = (value: string | undefined, field: string) => {\n try {\n if (!value) throw new Error();\n const url = new URL(value);\n if (url.username || url.password || url.search || url.hash || url.protocol !== 'https:') throw new Error();\n return url;\n } catch { throw new Error(`Configure ${field} as an HTTPS URL without credentials, query or fragment.`); }\n };\n const publicUrl = validUrl(env.DATABRAIN_HOSTED_PUBLIC_URL, 'DATABRAIN_HOSTED_PUBLIC_URL');\n validUrl(env.DATABRAIN_HOSTED_ISSUER, 'DATABRAIN_HOSTED_ISSUER');\n if (publicUrl.pathname !== '/mcp') throw new Error('DATABRAIN_HOSTED_PUBLIC_URL must end in /mcp.');\n const port = Number(env.DATABRAIN_HOSTED_PORT || '3144');\n if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error('Invalid DATABRAIN_HOSTED_PORT.');\n return { publicUrl: env.DATABRAIN_HOSTED_PUBLIC_URL!, issuer: env.DATABRAIN_HOSTED_ISSUER!, port, testOnly };\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { createHostedToolServer } from './server.js';\nimport { createBearerVerifier } from './auth.js';\nimport { authorizeTool, assertBoundArguments, requiredScopes } from './authorization.js';\nimport { HOSTED_SCOPES, HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedConfig, PatStore } from './types.js';\n\nfunction reply(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });\n res.end(JSON.stringify(body));\n}\n\nasync function readBody(req: IncomingMessage): Promise<unknown> {\n let size = 0;\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n size += chunk.length;\n if (size > 1_048_576) throw new Error('body_too_large');\n chunks.push(Buffer.from(chunk));\n }\n return JSON.parse(Buffer.concat(chunks).toString('utf8'));\n}\n\nexport function createHostedHttpServer(config: HostedConfig, adapter: HostedAdapter, pats?: PatStore) {\n const publicUrl = new URL(config.publicUrl);\n const metadataPath = `/.well-known/oauth-protected-resource${publicUrl.pathname === '/' ? '' : publicUrl.pathname}`;\n const metadataUrl = `${publicUrl.origin}${metadataPath}`;\n const verify = createBearerVerifier(adapter, config.publicUrl, pats);\n let active = 0;\n\n const server = createServer(async (req, res) => {\n res.setHeader('X-Content-Type-Options', 'nosniff');\n if (config.testOnly) res.setHeader('X-Databrain-Test-Only', 'true');\n try {\n // Tailscale Serve may preserve the public Host or use the loopback destination.\n // Never use Host/Forwarded headers to construct discovery URLs or trust an identity.\n const localPort = (server.address() as { port: number } | null)?.port;\n if (![publicUrl.host, `127.0.0.1:${localPort}`, `localhost:${localPort}`].includes(req.headers.host || '')) {\n reply(res, 403, { error: 'invalid_host' }); return;\n }\n if (req.headers.origin && req.headers.origin !== publicUrl.origin) {\n reply(res, 403, { error: 'invalid_origin' }); return;\n }\n if (req.headers.origin) {\n res.setHeader('Access-Control-Allow-Origin', publicUrl.origin);\n res.setHeader('Vary', 'Origin');\n res.setHeader('Access-Control-Expose-Headers', 'WWW-Authenticate, MCP-Protocol-Version');\n }\n const url = new URL(req.url || '/', publicUrl.origin);\n if (req.method === 'GET' && [metadataPath, '/.well-known/oauth-protected-resource'].includes(url.pathname)) {\n reply(res, 200, {\n resource: config.publicUrl, authorization_servers: [config.issuer],\n scopes_supported: HOSTED_SCOPES, bearer_methods_supported: ['header'],\n resource_name: config.testOnly ? 'Databrain (TEST ONLY; fixture identity)' : 'Databrain',\n }); return;\n }\n if (url.pathname !== publicUrl.pathname) { reply(res, 404, { error: 'not_found' }); return; }\n if (url.search) { reply(res, 400, { error: 'query_parameters_not_supported' }); return; }\n if (req.method === 'OPTIONS') {\n res.setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type, MCP-Protocol-Version, MCP-Session-Id');\n res.writeHead(204); res.end(); return;\n }\n // Duplicate Authorization fields and cookies never select a principal.\n const authFields = req.rawHeaders.filter((_, i) => i % 2 === 0 && req.rawHeaders[i].toLowerCase() === 'authorization');\n const match = /^Bearer ([^\\s]+)$/i.exec(req.headers.authorization || '');\n if (authFields.length !== 1 || !match) throw new HostedAuthError(401, 'invalid_token');\n const principal = await verify(match[1]);\n if (req.method !== 'POST') {\n res.setHeader('Allow', 'POST, OPTIONS');\n reply(res, 405, { error: 'method_not_allowed' }); return;\n }\n if (!req.headers['content-type']?.toLowerCase().startsWith('application/json')) {\n reply(res, 415, { error: 'unsupported_media_type' }); return;\n }\n if (active >= 32) { reply(res, 503, { error: 'server_busy' }); return; }\n active++;\n let body: any;\n try { body = await readBody(req); }\n catch { active--; reply(res, 400, { error: 'invalid_request' }); return; }\n try {\n // The installed SDK accepts batches; this endpoint does not. A batch must\n // never skip the per-call HTTP authorization/challenge path below.\n if (Array.isArray(body)) { reply(res, 400, { error: 'batch_not_supported' }); return; }\n if (body?.method === 'tools/call' && typeof body.params?.name === 'string') {\n try {\n authorizeTool(body.params.name, principal);\n assertBoundArguments(body.params.arguments, principal);\n } catch (error) {\n if (error instanceof HostedAuthError && error.code === 'insufficient_scope') {\n res.setHeader('WWW-Authenticate', `Bearer error=\"insufficient_scope\", resource_metadata=\"${metadataUrl}\", scope=\"${requiredScopes(body.params.name).join(' ')}\"`);\n }\n throw error;\n }\n }\n const mcp = createHostedToolServer(principal, adapter);\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });\n let closed = false;\n const close = () => { if (!closed) { closed = true; void mcp.close(); } };\n res.once('close', close);\n try {\n await mcp.connect(transport);\n await transport.handleRequest(req, res, body);\n } finally { if (res.writableEnded) close(); }\n } finally { active--; }\n } catch (error) {\n if (res.headersSent) { res.end(); return; }\n const status = error instanceof HostedAuthError ? error.status : 503;\n if (status === 401) res.setHeader('WWW-Authenticate', `Bearer error=\"invalid_token\", resource_metadata=\"${metadataUrl}\"`);\n reply(res, status, { error: error instanceof HostedAuthError ? error.code : 'temporarily_unavailable' });\n }\n });\n server.requestTimeout = 30_000;\n server.headersTimeout = 10_000;\n server.keepAliveTimeout = 5_000;\n return server;\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { instrument } from '@posthog/mcp';\nimport { PostHog } from 'posthog-node';\nimport { DatabrainClient } from '../client/databrain-client.js';\nimport type { RequestOptions } from '../client/types.js';\nimport { registerAllTools } from '../tools/registry.js';\nimport { registerAllResources } from '../resources/registry.js';\nimport { registerAllPrompts } from '../prompts/registry.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport { DatabrainApiError, toolError, toolSuccess } from '../utils/errors.js';\nimport { toolContext } from '../utils/tool-context.js';\nimport { assertBoundArguments, authorizeTool } from './authorization.js';\nimport { HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedPrincipal } from './types.js';\n\n// Stdio's instrumentation changes the wire schema (required `context`). Use that\n// same implementation, with capture disabled until hosted telemetry has an owner.\n// No bearer, request headers, tool arguments or results are sent to analytics.\nconst schemaInstrumentation = new PostHog('hosted-contract-only', { disabled: true });\n\n/** A request-local client: shared APIs/tools run unchanged, through a delegated executor. */\nclass HostedClient extends DatabrainClient {\n constructor(private readonly adapter: HostedAdapter, private readonly principal: HostedPrincipal) {\n super({ apiUrl: 'https://api.usedatabrain.com', demoDomain: 'https://demo.usedatabrain.com' });\n }\n // A generated API credential must never replace the authorizing hosted principal.\n override setRuntimeApiToken(_token: string): void {}\n\n override async request<T>(options: RequestOptions): Promise<T> {\n try {\n const name = toolContext.getStore()?.tool;\n if (!name) throw new HostedAuthError(403, 'tool_not_authorized');\n authorizeTool(name, this.principal);\n assertBoundArguments(options.body, this.principal);\n assertBoundArguments(options.query, this.principal);\n const raw = await this.adapter.executeRequest(structuredClone(options), structuredClone(this.principal));\n if (!options.skipDataUnwrap && raw && typeof raw === 'object' && !Array.isArray(raw) &&\n 'data' in raw && raw.data !== null && raw.data !== undefined) return raw.data as T;\n return raw as T;\n } catch (error) {\n // The adapter may encounter secret-bearing remote bodies. Do not pass them to tools/logs.\n const status = error instanceof HostedAuthError ? error.status : 503;\n throw new DatabrainApiError(status, 'HOSTED_REQUEST_DENIED',\n 'The authorized backend could not complete this request. Check workspace access and hosted integration.');\n }\n }\n}\n\nexport function createHostedToolServer(principal: HostedPrincipal, adapter: HostedAdapter): McpServer {\n const bound = structuredClone(principal);\n const server = new McpServer({ name: 'databrain', version: MCP_SERVER_VERSION }, {\n instructions: 'Hosted Databrain: each request is authenticated as a user in one authorized workspace. ' +\n 'Use whoami to inspect the verified identity and scopes. Never ask for a service token or put credentials in tool arguments. ' +\n 'Workspace and tenant access come from the bearer principal. Existing dry-run and production-confirmation rules still apply.',\n });\n\n // The existing shared registry already wraps server.tool for intent telemetry. Layer\n // hosted policy underneath it; keep every schema, description, annotation and handler.\n const register = server.tool.bind(server) as (...args: any[]) => unknown;\n server.tool = ((...args: any[]) => {\n const name = args[0] as string;\n const index = args.length - 1;\n const handler = args[index] as (...values: any[]) => unknown;\n const shape = args[2] as Record<string, unknown>;\n args[index] = async (...values: any[]) => {\n try {\n authorizeTool(name, bound);\n assertBoundArguments(values[0], bound);\n if (name === 'whoami') {\n return toolSuccess(JSON.stringify({\n status: 'authenticated', activeBearer: bound.credentialType,\n actorKind: bound.actorKind, userId: bound.userId, companyId: bound.companyId,\n workspaceId: bound.workspaceId, workspaceName: bound.workspaceName,\n oauthClientId: bound.clientId, scopes: bound.scopes,\n }));\n }\n // Required selectors retain their public schema; matching inputs are always replaced\n // by the trusted value. Optional selectors are supplied when that tool declares them.\n if (shape && typeof shape === 'object' && values[0] && typeof values[0] === 'object') {\n for (const key of ['workspaceName', 'workspaceId'] as const) {\n if (Object.hasOwn(shape, key) && bound[key] !== undefined) values[0][key] = bound[key];\n }\n }\n return await handler(...values);\n } catch {\n return toolError('Hosted authorization denied this tool call. Check the granted scopes, workspace and tenant access.');\n }\n };\n return register(...args);\n }) as McpServer['tool'];\n\n registerAllTools(server, new HostedClient(adapter, bound));\n registerAllResources(server);\n registerAllPrompts(server);\n instrument(server, schemaInstrumentation, { beforeSend: () => null });\n return server;\n}\n","import { HostedAuthError } from './types.js';\nimport type { HostedPrincipal, HostedScope } from './types.js';\n\n/** Explicit exhaustive policy: a newly registered tool is denied until it gets a policy. */\nexport const TOOL_SCOPES: Readonly<Record<string, readonly HostedScope[]>> = {\n whoami: [],\n list_datasources: ['mcp:datamarts:read'], discover_datasource_schema: ['mcp:datamarts:read'],\n list_datamarts: ['mcp:datamarts:read'], list_workspaces: ['mcp:datamarts:read'],\n sync_datasource: ['mcp:datamarts:write'], create_datamart: ['mcp:datamarts:write'],\n list_data_apps: ['mcp:dashboards:read'], list_tenants: ['mcp:dashboards:read'],\n create_data_app: ['mcp:embeds:write'], list_api_tokens: ['mcp:embeds:write'],\n create_api_token: ['mcp:embeds:write'], rotate_api_token: ['mcp:embeds:write'],\n create_embed: ['mcp:embeds:write'], list_embeds: ['mcp:dashboards:read'],\n get_embed_details: ['mcp:dashboards:read'], update_embed: ['mcp:embeds:write'],\n customize_embed_theme: ['mcp:embeds:write'], get_demo_link: ['mcp:embeds:write'],\n generate_guest_token: ['mcp:embeds:write'], generate_embed_code: ['mcp:dashboards:read'],\n list_dashboards: ['mcp:dashboards:read'], create_dashboard: ['mcp:dashboards:write'],\n get_dashboard: ['mcp:dashboards:read'], generate_dashboard_tiles: ['mcp:dashboards:write', 'mcp:query'],\n ask_question: ['mcp:query'], get_semantic_layer: ['mcp:datamarts:read'],\n update_semantic_layer: ['mcp:datamarts:write'], start_semantic_layer_generation: ['mcp:datamarts:write'],\n get_semantic_layer_generation_status: ['mcp:datamarts:read'],\n list_widgets: ['mcp:dashboards:read'], validate_metric_spec: ['mcp:dashboards:read', 'mcp:query'],\n create_or_update_metric_from_query: ['mcp:dashboards:write', 'mcp:query'],\n create_workspace_metric: ['mcp:dashboards:write', 'mcp:query'],\n add_widget: ['mcp:dashboards:write'], update_widget: ['mcp:dashboards:write'],\n remove_widget: ['mcp:dashboards:write'], generate_widget: ['mcp:dashboards:write', 'mcp:query'],\n list_scheduled_reports: ['mcp:dashboards:read'],\n};\n\nexport function requiredScopes(name: string): readonly HostedScope[] {\n if (!Object.hasOwn(TOOL_SCOPES, name)) throw new HostedAuthError(403, 'tool_not_authorized');\n return TOOL_SCOPES[name];\n}\n\nexport function authorizeTool(name: string, principal: HostedPrincipal): void {\n if (principal.expiresAt !== undefined && principal.expiresAt <= Date.now() / 1000) throw new HostedAuthError(401, 'invalid_token');\n if (requiredScopes(name).some(scope => !principal.scopes.includes(scope))) throw new HostedAuthError(403, 'insufficient_scope');\n}\n\n/** Reject conflicting selectors even in raw body passthroughs before a handler can use them. */\nexport function assertBoundArguments(value: unknown, principal: HostedPrincipal): void {\n if (!value || typeof value !== 'object') return;\n for (const [key, child] of Object.entries(value)) {\n if (['workspaceId', 'workspaceName', 'companyId', 'userId'].includes(key) &&\n child !== principal[key as 'workspaceId' | 'workspaceName' | 'companyId' | 'userId']) {\n throw new HostedAuthError(403, 'principal_mismatch');\n }\n if (['scope', 'scopes', 'authUser', 'actorKind', 'credentialType', 'oauthClientId', 'operatorEmail',\n 'session_variables', 'isAllClient', 'isAdmin', 'serviceToken', 'apiToken', 'authorization'].includes(key)) {\n throw new HostedAuthError(403, 'identity_argument_forbidden');\n }\n if (['clientId', 'tenantId'].includes(key) &&\n (typeof child !== 'string' || !principal.tenantIds?.includes(child))) {\n throw new HostedAuthError(403, 'tenant_not_authorized');\n }\n assertBoundArguments(child, principal);\n }\n}\n","import { z } from 'zod';\nimport { principalSchema } from './auth.js';\nimport { HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedConfig } from './types.js';\n\n/** BE-3's documented principal endpoint is the trust boundary for opaque OAuth tokens. */\nexport function createProviderAdapter(config: HostedConfig): HostedAdapter {\n const issuer = new URL(config.issuer);\n if (issuer.protocol !== 'https:' || issuer.username || issuer.password || issuer.search || issuer.hash) {\n throw new Error('Invalid hosted authorization server configuration.');\n }\n const endpoint = `${config.issuer.replace(/\\/$/, '')}/oauth/principal`;\n const responseSchema = principalSchema.extend({ issuer: z.literal(config.issuer) }).strip();\n return {\n async verifyOAuth(token, resource) {\n try {\n // Only the explicitly configured issuer ever receives the token, for verification.\n // No redirects, arbitrary token URLs, cache, service-token fallback or REST replay.\n const response = await fetch(endpoint, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n redirect: 'error', signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) throw new HostedAuthError(response.status >= 500 ? 503 : 401, 'invalid_token');\n const { issuer: _issuer, ...principal } = responseSchema.parse(await response.json());\n if (principal.resource !== resource || principal.credentialType !== 'oauth') throw new HostedAuthError(401, 'invalid_token');\n return principal;\n } catch (error) {\n if (error instanceof HostedAuthError) throw error;\n throw new HostedAuthError(401, 'invalid_token');\n }\n },\n async resolvePatGrant() { throw new HostedAuthError(401, 'pat_membership_adapter_required'); },\n async executeRequest() { throw new HostedAuthError(503, 'delegated_backend_adapter_required'); },\n };\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hosted/test-only.ts"],"sourcesContent":["import { FilePatStore } from './auth.js';\nimport { hashCredential } from './auth.js';\nimport { HostedAuthError } from './types.js';\nimport type { HostedAdapter } from './types.js';\n\n/** Explicit fixture harness. No login, consent, token issuer, real membership or live data. */\nexport function createTestOnlyAdapter(path: string): HostedAdapter {\n const fixtures = new FilePatStore(path);\n return {\n async verifyOAuth(token, resource) {\n const record = await fixtures.findByHash(hashCredential(token));\n if (!record || record.revokedAt !== null || record.principal.resource !== resource) {\n throw new HostedAuthError(401, 'invalid_token');\n }\n return record.principal;\n },\n async resolvePatGrant(principal) { return principal; },\n async executeRequest(options, principal) {\n if (options.method === 'GET' && options.path === '/api/v2/workspace') {\n return { data: [{ id: principal.workspaceId, name: principal.workspaceName,\n datamart: { name: 'TEST ONLY — synthetic fixture, no live data' } }] };\n }\n throw new HostedAuthError(503, 'test_fixture_operation_unavailable');\n },\n };\n}\n"],"mappings":";;;;;;;;AAMO,SAAS,sBAAsB,MAA6B;AACjE,QAAM,WAAW,IAAI,aAAa,IAAI;AACtC,SAAO;AAAA,IACL,MAAM,YAAY,OAAO,UAAU;AACjC,YAAM,SAAS,MAAM,SAAS,WAAW,eAAe,KAAK,CAAC;AAC9D,UAAI,CAAC,UAAU,OAAO,cAAc,QAAQ,OAAO,UAAU,aAAa,UAAU;AAClF,cAAM,IAAI,gBAAgB,KAAK,eAAe;AAAA,MAChD;AACA,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,MAAM,gBAAgB,WAAW;AAAE,aAAO;AAAA,IAAW;AAAA,IACrD,MAAM,eAAe,SAAS,WAAW;AACvC,UAAI,QAAQ,WAAW,SAAS,QAAQ,SAAS,qBAAqB;AACpE,eAAO,EAAE,MAAM,CAAC;AAAA,UAAE,IAAI,UAAU;AAAA,UAAa,MAAM,UAAU;AAAA,UAC3D,UAAU,EAAE,MAAM,mDAA8C;AAAA,QAAE,CAAC,EAAE;AAAA,MACzE;AACA,YAAM,IAAI,gBAAgB,KAAK,oCAAoC;AAAA,IACrE;AAAA,EACF;AACF;","names":[]}