@databrainhq/mcp-server 0.3.0-rc.11 → 0.3.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/dist/apps/server.d.ts +35 -7
- package/dist/apps/server.js +4 -4
- package/dist/apps/stdio.js +5 -5
- package/dist/{chunk-NBFUKVWP.js → chunk-233VGFHY.js} +99 -15
- package/dist/chunk-233VGFHY.js.map +1 -0
- package/dist/{chunk-RHK62ZJ6.js → chunk-AEQTEE5C.js} +3 -3
- package/dist/chunk-AEQTEE5C.js.map +1 -0
- package/dist/{chunk-DM7S4R57.js → chunk-DTFYHLYN.js} +2 -2
- package/dist/{chunk-IBYBK5ZE.js → chunk-HCYZN7I3.js} +4 -4
- package/dist/{chunk-ES2LUP4U.js → chunk-PSJVYD4W.js} +245 -89
- package/dist/chunk-PSJVYD4W.js.map +1 -0
- package/dist/{chunk-Z3ZHN4UW.js → chunk-X44C5GW2.js} +1 -1
- package/dist/chunk-X44C5GW2.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +4 -4
- package/dist/{main-JEYYFGJM.js → main-VEKNKWL5.js} +47 -24
- package/dist/main-VEKNKWL5.js.map +1 -0
- package/dist/{test-only-K7QTSVO4.js → test-only-LJGOY2QA.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-ES2LUP4U.js.map +0 -1
- package/dist/chunk-NBFUKVWP.js.map +0 -1
- package/dist/chunk-RHK62ZJ6.js.map +0 -1
- package/dist/chunk-Z3ZHN4UW.js.map +0 -1
- package/dist/main-JEYYFGJM.js.map +0 -1
- /package/dist/{chunk-DM7S4R57.js.map → chunk-DTFYHLYN.js.map} +0 -0
- /package/dist/{chunk-IBYBK5ZE.js.map → chunk-HCYZN7I3.js.map} +0 -0
- /package/dist/{test-only-K7QTSVO4.js.map → test-only-LJGOY2QA.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hosted/types.ts","../src/hosted/auth.ts"],"sourcesContent":["import type { RequestOptions } from '../client/types.js';\n\nexport const HOSTED_SCOPES = [\n 'mcp:dashboards:read', 'mcp:dashboards:write',\n 'mcp:datamarts:read', 'mcp:datamarts:write', 'mcp:embeds:write', 'mcp:query',\n 'mcp:custom-charts:read', 'mcp:custom-charts:write',\n] as const;\nexport type HostedScope = typeof HOSTED_SCOPES[number];\n\n/** A workspace this sign-in may act in, as the issuer resolved it for this request. */\nexport interface GrantedWorkspace {\n id: string;\n name?: string;\n}\n\n/** What the user approved at consent: a list of workspaces, or every workspace they can access. */\nexport type GrantWorkspaces = { mode: 'all' } | { mode: 'list'; ids: string[] };\n\n/** Only the trusted auth adapter may produce this context. Never populate it from MCP input. */\nexport interface HostedPrincipal {\n actorKind: 'user';\n credentialType: 'oauth' | 'pat';\n userId: string;\n companyId: string;\n /** Set only when exactly one workspace is granted: the default for every call. */\n workspaceId?: string;\n workspaceName?: string;\n /** The grant as approved. Absent from issuers that grant one workspace only. */\n workspaces?: GrantWorkspaces;\n /**\n * Live: the granted workspaces the user can still access, read on every request.\n * Absent only on a principal built before multi-workspace grants (then `workspaceId`).\n */\n grantedWorkspaces?: GrantedWorkspace[];\n /** OAuth application ID, NOT the tenant identifier called clientId by legacy tools. */\n clientId: string;\n resource: string;\n scopes: HostedScope[];\n /** Required for stored PATs. OAuth may delegate expiry to fresh provider validation. */\n expiresAt?: number;\n /** Explicit tenant allow-list resolved by the backend; absent means no tenant access. */\n tenantIds?: string[];\n}\n\nexport interface PatRecord {\n hash: string;\n revokedAt: number | null;\n principal: HostedPrincipal;\n}\n\nexport interface PatStore {\n findByHash(hash: string): Promise<PatRecord | undefined>;\n}\n\n/**\n * Opaque per-request handle from `HostedAdapter.delegate`. It carries nothing a tool,\n * log or serializer can read; only the adapter that made it can act with it.\n */\nexport interface HostedDelegation {\n readonly kind: 'hosted-delegation';\n}\n\n/** Production integration must recheck membership, policy and revocation on every call. */\nexport interface HostedAdapter {\n verifyOAuth(token: string, resource: string): Promise<HostedPrincipal>;\n resolvePatGrant(principal: HostedPrincipal): Promise<HostedPrincipal>;\n /**\n * Optional. Bind this request's verified OAuth bearer to an opaque handle, so\n * executeRequest can act as this user at the configured issuer, which re-verifies\n * the token and enforces workspace, object and role checks on every call.\n */\n delegate?(token: string, principal: HostedPrincipal): HostedDelegation;\n /**\n * Execute as this user/workspace with backend resource and tenant checks and auditing.\n * Receives NO incoming bearer or server service credential: only the opaque handle.\n * Do not impersonate a static service token.\n * Return the raw API result: the shared client performs its usual data unwrapping.\n * `workspace` is the granted workspace this tool call acts in, when one is known; the\n * adapter names it to the issuer and nowhere else.\n */\n executeRequest(options: RequestOptions, principal: HostedPrincipal,\n delegation?: HostedDelegation, workspace?: GrantedWorkspace): Promise<unknown>;\n}\n\nexport interface HostedConfig {\n publicUrl: string;\n issuer: string;\n /** Where the issuer's API is reached, when not at the issuer origin itself. */\n apiUrl?: string;\n port: number;\n bind: string;\n testOnly: boolean;\n}\n\n/** The backend's own status, code and message for a 4xx that is not an auth denial. */\nexport interface HostedErrorDetail { status: number; code?: string; message?: string }\n\nexport class HostedAuthError extends Error {\n constructor(public readonly status: 400 | 401 | 403 | 404 | 503, public readonly code: string,\n public readonly detail?: HostedErrorDetail) {\n super(code);\n }\n}\n","import { createHash, timingSafeEqual } from 'node:crypto';\nimport { open } from 'node:fs/promises';\nimport { constants } from 'node:fs';\nimport { z } from 'zod';\nimport { HOSTED_SCOPES, HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedPrincipal, PatRecord, PatStore } from './types.js';\n\nconst identity = z.string().min(1).max(512).regex(/^[^\\x00-\\x1f\\x7f]+$/);\nconst workspaceSchema = z.object({ id: identity, name: identity.optional() }).strip();\nexport const principalSchema = z.object({\n actorKind: z.literal('user'), credentialType: z.enum(['oauth', 'pat']),\n userId: identity, companyId: identity, workspaceId: identity.optional(), workspaceName: identity.optional(),\n workspaces: z.union([\n z.object({ mode: z.literal('all') }).strip(),\n z.object({ mode: z.literal('list'), ids: z.array(identity).min(1).max(100) }).strip(),\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, resource: z.string().url(), scopes: z.array(z.enum(HOSTED_SCOPES)),\n expiresAt: z.number().finite().positive().optional(), tenantIds: z.array(identity).optional(),\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(value: z.infer<typeof principalSchema>): HostedPrincipal {\n const { defaultWorkspace: _default, workspaceId, workspaceName, grantedWorkspaces, ...rest } = value;\n const granted = grantedWorkspaces\n ?? (workspaceId ? [workspaceName === undefined ? { id: workspaceId } : { id: workspaceId, name: workspaceName }] : []);\n if (!granted.length || new Set(granted.map(item => item.id)).size !== granted.length ||\n (workspaceId !== undefined && !granted.some(item => item.id === workspaceId))) {\n throw new HostedAuthError(401, 'invalid_token');\n }\n const only = granted.length === 1 ? granted[0] : undefined;\n return {\n ...rest, grantedWorkspaces: granted,\n ...(only ? { workspaceId: only.id } : {}), ...(only?.name !== undefined ? { workspaceName: only.name } : {}),\n };\n}\nconst recordSchema = z.object({\n hash: z.string().regex(/^[a-f0-9]{64}$/), revokedAt: z.number().finite().nullable(),\n principal: principalSchema,\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(this.path, constants.O_RDONLY | constants.O_NOFOLLOW);\n try {\n const stat = await handle.stat();\n if (!stat.isFile() || (stat.mode & 0o077) !== 0 || stat.size > 1_048_576 ||\n (process.getuid && stat.uid !== process.getuid())) throw new Error('Invalid credential store');\n const records = z.array(recordSchema).max(1000).parse(JSON.parse(await handle.readFile('utf8')));\n const matches = records.filter(record => timingSafeEqual(Buffer.from(record.hash, 'hex'), Buffer.from(hash, 'hex')));\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 { await handle.close(); }\n }\n}\n\nfunction validatePrincipal(value: unknown, resource: string, kind: 'oauth' | 'pat'): HostedPrincipal {\n const parsed = principalSchema.safeParse(value);\n if (!parsed.success || parsed.data.resource !== resource || parsed.data.credentialType !== kind ||\n (parsed.data.expiresAt !== undefined && parsed.data.expiresAt <= Date.now() / 1000) ||\n (kind === 'pat' && parsed.success && parsed.data.expiresAt === undefined)) throw new HostedAuthError(401, 'invalid_token');\n return normalizeWorkspaces(parsed.data);\n}\n\nexport function createBearerVerifier(adapter: HostedAdapter, resource: string, pats?: PatStore) {\n return async (token: string): Promise<HostedPrincipal> => {\n try {\n if (!/^[A-Za-z0-9._~+\\/-]+=*$/.test(token) || token.length > 8192) throw new HostedAuthError(401, 'invalid_token');\n if (!token.startsWith('dbp_')) {\n return validatePrincipal(await adapter.verifyOAuth(token, resource), resource, 'oauth');\n }\n if (!/^dbp_[A-Za-z0-9_-]{32,}$/.test(token)) throw new HostedAuthError(401, 'invalid_token');\n const record = await pats?.findByHash(hashCredential(token));\n if (!record || record.revokedAt !== null || record.hash !== hashCredential(token)) throw new HostedAuthError(401, 'invalid_token');\n const stored = validatePrincipal(record.principal, resource, 'pat');\n const resolved = validatePrincipal(await adapter.resolvePatGrant(structuredClone(stored)), resource, 'pat');\n // Membership resolution may narrow a stored grant, never move or expand it.\n for (const key of ['userId', 'companyId', 'clientId', 'resource'] as const) {\n if (resolved[key] !== stored[key]) throw new HostedAuthError(401, 'invalid_token');\n }\n if (resolved.grantedWorkspaces!.some(item => !stored.grantedWorkspaces!.some(old => old.id === item.id))) {\n throw new HostedAuthError(401, 'invalid_token');\n }\n if (resolved.expiresAt! > stored.expiresAt! || resolved.scopes.some(scope => !stored.scopes.includes(scope)) ||\n resolved.tenantIds?.some(id => !stored.tenantIds?.includes(id))) 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":[]}
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -3,17 +3,17 @@ import {
|
|
|
3
3
|
DEFAULT_POSTHOG_API_KEY,
|
|
4
4
|
DEFAULT_POSTHOG_HOST,
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DTFYHLYN.js";
|
|
7
7
|
import {
|
|
8
8
|
registerAllPrompts,
|
|
9
9
|
registerAllResources,
|
|
10
10
|
registerAllTools
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-PSJVYD4W.js";
|
|
12
12
|
import {
|
|
13
13
|
DatabrainClient,
|
|
14
14
|
MCP_SERVER_VERSION,
|
|
15
15
|
logger
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-AEQTEE5C.js";
|
|
17
17
|
import "./chunk-Q5EYDKSA.js";
|
|
18
18
|
|
|
19
19
|
// src/index.ts
|
|
@@ -118,7 +118,7 @@ function banner(msg) {
|
|
|
118
118
|
async function main() {
|
|
119
119
|
try {
|
|
120
120
|
if (process.argv.includes("--http")) {
|
|
121
|
-
const { startHosted } = await import("./main-
|
|
121
|
+
const { startHosted } = await import("./main-VEKNKWL5.js");
|
|
122
122
|
await startHosted();
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
registerChartApps
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-233VGFHY.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-
|
|
12
|
+
} from "./chunk-PSJVYD4W.js";
|
|
13
13
|
import {
|
|
14
14
|
DatabrainApiError,
|
|
15
15
|
DatabrainClient,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
toolContext,
|
|
21
21
|
toolError,
|
|
22
22
|
toolSuccess
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-AEQTEE5C.js";
|
|
24
24
|
import "./chunk-Q5EYDKSA.js";
|
|
25
25
|
import {
|
|
26
26
|
FilePatStore,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
HostedAuthError,
|
|
29
29
|
createBearerVerifier,
|
|
30
30
|
principalSchema
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-X44C5GW2.js";
|
|
32
32
|
|
|
33
33
|
// src/hosted/main.ts
|
|
34
34
|
import { isAbsolute } from "path";
|
|
@@ -62,9 +62,23 @@ function loadHostedConfig(env = process.env) {
|
|
|
62
62
|
const port = Number(env.DATABRAIN_HOSTED_PORT || "3144");
|
|
63
63
|
const bind = env.DATABRAIN_HOSTED_BIND || "127.0.0.1";
|
|
64
64
|
if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error("Invalid DATABRAIN_HOSTED_PORT.");
|
|
65
|
+
let apiUrl;
|
|
66
|
+
if (env.DATABRAIN_HOSTED_API_URL) {
|
|
67
|
+
let url;
|
|
68
|
+
try {
|
|
69
|
+
url = new URL(env.DATABRAIN_HOSTED_API_URL);
|
|
70
|
+
} catch {
|
|
71
|
+
url = void 0;
|
|
72
|
+
}
|
|
73
|
+
if (!url || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== "/") {
|
|
74
|
+
throw new Error("Configure DATABRAIN_HOSTED_API_URL as an origin without credentials, path, query or fragment.");
|
|
75
|
+
}
|
|
76
|
+
apiUrl = url.origin;
|
|
77
|
+
}
|
|
65
78
|
return {
|
|
66
79
|
publicUrl: publicValue,
|
|
67
80
|
issuer: issuerValue,
|
|
81
|
+
...apiUrl ? { apiUrl } : {},
|
|
68
82
|
port,
|
|
69
83
|
bind,
|
|
70
84
|
testOnly
|
|
@@ -158,7 +172,7 @@ var TOOL_SCOPES = {
|
|
|
158
172
|
create_dashboard: ["mcp:dashboards:write"],
|
|
159
173
|
get_dashboard: ["mcp:dashboards:read"],
|
|
160
174
|
dashboard_snapshot: ["mcp:dashboards:read"],
|
|
161
|
-
generate_dashboard_tiles: ["mcp:dashboards:write", "mcp:query"],
|
|
175
|
+
generate_dashboard_tiles: ["mcp:dashboards:write", "mcp:query", "mcp:embeds:write"],
|
|
162
176
|
ask_question: ["mcp:query"],
|
|
163
177
|
get_semantic_layer: ["mcp:datamarts:read"],
|
|
164
178
|
update_semantic_layer: ["mcp:datamarts:write"],
|
|
@@ -170,13 +184,13 @@ var TOOL_SCOPES = {
|
|
|
170
184
|
apply_semantic_change: ["mcp:datamarts:write"],
|
|
171
185
|
eval_semantic_model: ["mcp:datamarts:read", "mcp:query"],
|
|
172
186
|
list_widgets: ["mcp:dashboards:read"],
|
|
173
|
-
validate_metric_spec: ["mcp:dashboards:read", "mcp:query"],
|
|
174
|
-
create_or_update_metric_from_query: ["mcp:dashboards:write", "mcp:query"],
|
|
187
|
+
validate_metric_spec: ["mcp:dashboards:read", "mcp:query", "mcp:embeds:write"],
|
|
188
|
+
create_or_update_metric_from_query: ["mcp:dashboards:write", "mcp:query", "mcp:embeds:write"],
|
|
175
189
|
create_workspace_metric: ["mcp:dashboards:write", "mcp:query"],
|
|
176
|
-
add_widget: ["mcp:dashboards:write"],
|
|
177
|
-
update_widget: ["mcp:dashboards:write"],
|
|
178
|
-
remove_widget: ["mcp:dashboards:write"],
|
|
179
|
-
generate_widget: ["mcp:dashboards:write", "mcp:query"],
|
|
190
|
+
add_widget: ["mcp:dashboards:write", "mcp:embeds:write"],
|
|
191
|
+
update_widget: ["mcp:dashboards:write", "mcp:embeds:write"],
|
|
192
|
+
remove_widget: ["mcp:dashboards:write", "mcp:embeds:write"],
|
|
193
|
+
generate_widget: ["mcp:dashboards:write", "mcp:query", "mcp:embeds:write"],
|
|
180
194
|
list_scheduled_reports: ["mcp:dashboards:read"],
|
|
181
195
|
custom_chart_edit: ["mcp:custom-charts:write"],
|
|
182
196
|
custom_chart_preview: ["mcp:custom-charts:read"],
|
|
@@ -191,11 +205,12 @@ var TOOL_SCOPES = {
|
|
|
191
205
|
// dashboard save; listed so a hosted composition is not refused for want of a policy.
|
|
192
206
|
preview_chart: ["mcp:query"],
|
|
193
207
|
save_chart_as_dashboard: ["mcp:dashboards:write", "mcp:query"],
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
// Hosted platform reads do not mint embed credentials. Guest rendering retains its backend boundary.
|
|
209
|
+
render_metric_tile: ["mcp:dashboards:read", "mcp:query"],
|
|
210
|
+
render_chart: ["mcp:dashboards:read", "mcp:query"],
|
|
211
|
+
refresh_widget: ["mcp:dashboards:read", "mcp:query"],
|
|
212
|
+
drill_down: ["mcp:dashboards:read", "mcp:query"],
|
|
213
|
+
change_date_range: ["mcp:dashboards:read", "mcp:query"]
|
|
199
214
|
};
|
|
200
215
|
function requiredScopes(name) {
|
|
201
216
|
if (!Object.hasOwn(TOOL_SCOPES, name)) throw new HostedAuthError(403, "tool_not_authorized");
|
|
@@ -459,7 +474,7 @@ function createHostedToolServer(principal, adapter, delegation) {
|
|
|
459
474
|
const bound = structuredClone(principal);
|
|
460
475
|
const delegated = delegation !== void 0;
|
|
461
476
|
const server = new McpServer({ name: "databrain", version: MCP_SERVER_VERSION }, {
|
|
462
|
-
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.
|
|
477
|
+
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."
|
|
463
478
|
});
|
|
464
479
|
const register = server.tool.bind(server);
|
|
465
480
|
server.tool = ((...args) => {
|
|
@@ -693,7 +708,7 @@ var USER_AGENT = `databrain-mcp-server/${MCP_SERVER_VERSION} (hosted)`;
|
|
|
693
708
|
var REQUEST_TIMEOUT_MS = 3e4;
|
|
694
709
|
var FORWARDED_HEADERS = /* @__PURE__ */ new Set(["idempotency-key"]);
|
|
695
710
|
var WORKSPACE_HEADER = "X-Databrain-Workspace";
|
|
696
|
-
var GRANT_DENIALS = /* @__PURE__ */ new Set(["ROLE_NOT_ALLOWED", "WORKSPACE_CREATE_NOT_ALLOWED"]);
|
|
711
|
+
var GRANT_DENIALS = /* @__PURE__ */ new Set(["ROLE_NOT_ALLOWED", "WORKSPACE_CREATE_NOT_ALLOWED", "forbidden_permission", "insufficient_scope"]);
|
|
697
712
|
function errorDetail(status, text, bearer) {
|
|
698
713
|
let parsed;
|
|
699
714
|
try {
|
|
@@ -733,7 +748,8 @@ function createProviderAdapter(config) {
|
|
|
733
748
|
if (issuer.protocol !== "https:" || issuer.username || issuer.password || issuer.search || issuer.hash) {
|
|
734
749
|
throw new Error("Invalid hosted authorization server configuration.");
|
|
735
750
|
}
|
|
736
|
-
const apiBase = config.issuer.replace(/\/$/, "");
|
|
751
|
+
const apiBase = (config.apiUrl ?? config.issuer).replace(/\/$/, "");
|
|
752
|
+
const apiOrigin = new URL(apiBase).origin;
|
|
737
753
|
const endpoint = `${apiBase}/oauth/principal`;
|
|
738
754
|
const publicOrigin = new URL(config.publicUrl).origin;
|
|
739
755
|
const bearers = /* @__PURE__ */ new WeakMap();
|
|
@@ -746,7 +762,10 @@ function createProviderAdapter(config) {
|
|
|
746
762
|
redirect: "error",
|
|
747
763
|
signal: AbortSignal.timeout(1e4)
|
|
748
764
|
});
|
|
749
|
-
if (!response.ok)
|
|
765
|
+
if (!response.ok) {
|
|
766
|
+
if (response.status === 429 || response.status >= 500) throw new HostedAuthError(503, "temporarily_unavailable");
|
|
767
|
+
throw new HostedAuthError(401, "invalid_token");
|
|
768
|
+
}
|
|
750
769
|
const { issuer: _issuer, ...principal } = responseSchema.parse(await response.json());
|
|
751
770
|
if (principal.resource !== resource || principal.credentialType !== "oauth") throw new HostedAuthError(401, "invalid_token");
|
|
752
771
|
return principal;
|
|
@@ -769,7 +788,7 @@ function createProviderAdapter(config) {
|
|
|
769
788
|
if (options.auth === "none" && !options.token) throw new HostedAuthError(403, "tool_not_authorized");
|
|
770
789
|
if (!options.path.startsWith("/") || options.path.startsWith("//")) throw new HostedAuthError(403, "tool_not_authorized");
|
|
771
790
|
const url = new URL(`${apiBase}${options.path}`);
|
|
772
|
-
if (url.origin !==
|
|
791
|
+
if (url.origin !== apiOrigin) throw new HostedAuthError(403, "tool_not_authorized");
|
|
773
792
|
for (const [key, value] of Object.entries(options.query ?? {})) url.searchParams.set(key, value);
|
|
774
793
|
const headers = {
|
|
775
794
|
Accept: "application/json",
|
|
@@ -808,7 +827,11 @@ function createProviderAdapter(config) {
|
|
|
808
827
|
if (!response.ok) {
|
|
809
828
|
const forbidden = response.status === 403 ? errorDetail(403, text, bearer) : void 0;
|
|
810
829
|
if (forbidden?.code && GRANT_DENIALS.has(forbidden.code)) {
|
|
811
|
-
throw new HostedAuthError(
|
|
830
|
+
throw new HostedAuthError(
|
|
831
|
+
403,
|
|
832
|
+
"delegated_request_denied",
|
|
833
|
+
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
|
|
834
|
+
);
|
|
812
835
|
}
|
|
813
836
|
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;
|
|
814
837
|
const detail = status === 401 || status === 503 || response.status === 403 ? void 0 : errorDetail(response.status, text, bearer);
|
|
@@ -830,7 +853,7 @@ async function startHosted() {
|
|
|
830
853
|
if (config.testOnly) {
|
|
831
854
|
const file = process.env.DATABRAIN_HOSTED_TEST_FIXTURES;
|
|
832
855
|
if (!file || !isAbsolute(file)) throw new Error("Hosted test mode requires an absolute fixture store path.");
|
|
833
|
-
const { createTestOnlyAdapter } = await import("./test-only-
|
|
856
|
+
const { createTestOnlyAdapter } = await import("./test-only-LJGOY2QA.js");
|
|
834
857
|
adapter = createTestOnlyAdapter(file);
|
|
835
858
|
} else {
|
|
836
859
|
const file = process.env.DATABRAIN_HOSTED_ADAPTER_MODULE;
|
|
@@ -869,4 +892,4 @@ async function startHosted() {
|
|
|
869
892
|
export {
|
|
870
893
|
startHosted
|
|
871
894
|
};
|
|
872
|
-
//# sourceMappingURL=main-
|
|
895
|
+
//# sourceMappingURL=main-VEKNKWL5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hosted/main.ts","../src/hosted/config.ts","../src/hosted/http.ts","../src/hosted/server.ts","../src/tools/infrastructure/create-workspace.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 configured: data tools run as the signed-in user through the issuer; PAT membership requires a trusted 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, config.bind, resolve);\n });\n process.stderr.write(`[databrain] hosted HTTP ready on ${config.bind}:${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 // Self-hosted installs set no new variable: with neither URL given, both come from the\n // HOSTED_APP_URL origin the image already has (app, API and /mcp on one origin). HTTPS only.\n let publicValue = env.DATABRAIN_HOSTED_PUBLIC_URL;\n let issuerValue = env.DATABRAIN_HOSTED_ISSUER;\n if (!publicValue && !issuerValue && env.HOSTED_APP_URL) {\n const origin = validUrl(env.HOSTED_APP_URL, 'HOSTED_APP_URL').origin;\n publicValue = `${origin}/mcp`;\n issuerValue = origin;\n }\n const publicUrl = validUrl(publicValue, 'DATABRAIN_HOSTED_PUBLIC_URL');\n validUrl(issuerValue, '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 // Loopback by default so a laptop run is not silently on the network. A\n // container has to be told, because binding loopback inside a task means the\n // load balancer health check can never reach it and the service never\n // stabilises. Set DATABRAIN_HOSTED_BIND=0.0.0.0 in ECS.\n const bind = env.DATABRAIN_HOSTED_BIND || '127.0.0.1';\n if (!Number.isInteger(port) || port < 1024 || port > 65535) throw new Error('Invalid DATABRAIN_HOSTED_PORT.');\n // Optional private address of the same API (for example ECS service discovery), so\n // the token check and data calls stay inside the network. The issuer is still the\n // identity the principal must name; this only changes where the requests go.\n let apiUrl: string | undefined;\n if (env.DATABRAIN_HOSTED_API_URL) {\n let url: URL | undefined;\n try { url = new URL(env.DATABRAIN_HOSTED_API_URL); } catch { url = undefined; }\n if (!url || !['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search\n || url.hash || url.pathname !== '/') {\n throw new Error('Configure DATABRAIN_HOSTED_API_URL as an origin without credentials, path, query or fragment.');\n }\n apiUrl = url.origin;\n }\n return { publicUrl: publicValue!, issuer: issuerValue!, ...(apiUrl ? { apiUrl } : {}), port,\n bind, testOnly };\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';\nimport { createHostedToolServer, scopeError, workspaceError } from './server.js';\nimport { createBearerVerifier } from './auth.js';\nimport { WorkspaceSelectionError, authorizeTool, assertToolArguments, requiredScopes } from './authorization.js';\nimport { HOSTED_SCOPES, HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedConfig, PatStore } from './types.js';\n\nconst HEALTH_BODY = JSON.stringify({ status: 'ok' });\n\n/** Exactly `/health`, with or without a query string; `/health/x` is not health. */\nfunction healthPath(url: string | undefined): boolean {\n const path = (url || '/').split('?')[0];\n return path === '/health';\n}\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 // Liveness for the load balancer. It arrives with the target IP:port as Host,\n // so it runs before the Host/Origin checks and sets no CORS headers. It is\n // unauthenticated, reads no body and never echoes configuration.\n if (healthPath(req.url)) {\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n res.writeHead(405, { Allow: 'GET, HEAD', 'Cache-Control': 'no-store' }); res.end(); return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store',\n 'Content-Length': Buffer.byteLength(HEALTH_BODY) });\n res.end(req.method === 'HEAD' ? undefined : HEALTH_BODY); return;\n }\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 // Only an OAuth bearer delegates; a PAT never leaves this process.\n const delegation = match[1].startsWith('dbp_') ? undefined : adapter.delegate?.(match[1], principal);\n if (body?.method === 'tools/call' && typeof body.params?.name === 'string') {\n try {\n authorizeTool(body.params.name, principal);\n assertToolArguments(body.params.name, body.params.arguments, principal, delegation !== undefined);\n } catch (error) {\n // A workspace the grant does not hold, or two that disagree: answer the call as a\n // tool error with the granted names, which an AI can act on. No tool runs.\n if (error instanceof WorkspaceSelectionError) {\n reply(res, 200, { jsonrpc: '2.0', id: body.id ?? null, result: workspaceError(error) }); return;\n }\n // A scope the grant lacks: consent already trimmed the scopes to the role, so a\n // step-up challenge (403 insufficient_scope) cannot get more. It made Claude Code\n // drop the whole connection to \"needs authentication\". Answer the call instead.\n if (error instanceof HostedAuthError && error.code === 'insufficient_scope') {\n const missing = requiredScopes(body.params.name).filter(scope => !principal.scopes.includes(scope));\n reply(res, 200, { jsonrpc: '2.0', id: body.id ?? null, result: scopeError(missing) }); return;\n }\n throw error;\n }\n }\n const mcp = createHostedToolServer(principal, adapter, delegation);\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 { AsyncLocalStorage } from 'node:async_hooks';\nimport { z } from 'zod';\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.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 { registerCreateWorkspace } from '../tools/infrastructure/create-workspace.js';\nimport { PREVIEW_PATH } from '../apps/preview.js';\nimport { enableChartApps } from '../apps/feature.js';\nimport { registerChartApps } from '../apps/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 {\n WORKSPACE_FREE_TOOLS, WORKSPACE_INPUT, WORKSPACE_SPANNING_TOOLS, WorkspaceSelectionError, assertBoundArguments,\n assertToolArguments, authorizeTool, grantedNames, grantedWorkspaces, namesWorkspace, selectWorkspace,\n} from './authorization.js';\nimport { HostedAuthError } from './types.js';\nimport type { GrantedWorkspace, HostedAdapter, HostedDelegation, 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/** The granted workspace the running tool call acts in. Set by the tool wrapper only. */\nconst callWorkspace = new AsyncLocalStorage<{ target?: GrantedWorkspace }>();\n\nconst WORKSPACE_HINT = 'Call whoami to see the workspaces this sign-in granted, then name one.';\n\n/** A workspace selection problem, as the tool result the AI reads: reason, code, next step. */\nexport function workspaceError(error: WorkspaceSelectionError) {\n return toolError(`Error: ${error.reason} (${error.code})\\n\\nHint: ${WORKSPACE_HINT}`);\n}\n\n/** A tool this sign-in's scopes do not cover, as a tool result: the connection stays up. */\nexport function scopeError(missing: readonly string[]) {\n return toolError(`Error: This connection cannot do this. It needs ${missing.join(', ')}, which your role did not allow ` +\n 'when you connected. (INSUFFICIENT_SCOPE)\\n\\nHint: Ask a Databrain admin for a role that allows it, then connect ' +\n 'again. If your role allows it in a workspace you did not choose, connect again and choose that workspace.');\n}\n\n/** The workspace list answer (bare or under `data`) cut to one workspace. */\nfunction onlyWorkspace(raw: unknown, target: GrantedWorkspace): unknown {\n const keep = (items: unknown) => Array.isArray(items) ? items.filter(item => {\n const record = item && typeof item === 'object' ? item as Record<string, unknown> : {};\n return record.id === target.id || record._id === target.id ||\n (record.id === undefined && record._id === undefined && target.name !== undefined && record.name === target.name);\n }) : items;\n if (Array.isArray(raw)) return keep(raw);\n if (raw && typeof raw === 'object' && 'data' in raw) return { ...raw, data: keep((raw as { data: unknown }).data) };\n return raw;\n}\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 private readonly delegation?: HostedDelegation) {\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 override isHosted(): boolean { return true; }\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 const named = callWorkspace.getStore()?.target;\n // With no workspace for the call (a tool that walks every granted workspace), a request\n // that names one top-level workspace acts in it, and says so in the header too.\n const target = named ?? (selectWorkspace(options.body, this.principal, ['workspaceName', 'workspaceId'])\n ?? selectWorkspace(options.query, this.principal, ['workspaceName', 'workspaceId']));\n const delegated = this.delegation !== undefined;\n // Chart preview rows are query output (a row may have a userId column); the issuer\n // treats them as data too. Every other body field is still checked.\n const opaque = options.method === 'POST' && options.path === PREVIEW_PATH ? ['rows']\n : options.method === 'POST' && /^\\/api\\/v2\\/custom-charts\\/[a-z][a-z0-9_-]*\\/bryn\\/turn$/.test(options.path) ? ['draft'] : undefined;\n assertBoundArguments(options.body, this.principal, { target, delegated, opaque });\n assertBoundArguments(options.query, this.principal, { target, delegated });\n let raw = await this.adapter.executeRequest(structuredClone(options), structuredClone(this.principal),\n this.delegation, target && structuredClone(target));\n // A call that named its workspace reads the workspace list as that workspace only, so a\n // tool that walks the list (list_dashboards) stays in it.\n if (named && options.method === 'GET' && options.path === '/api/v2/workspace') raw = onlyWorkspace(raw, named);\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 if (error instanceof WorkspaceSelectionError) {\n throw new DatabrainApiError(error.status, error.code, `${error.reason} (${error.code})`, undefined, WORKSPACE_HINT);\n }\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 const detail = error instanceof HostedAuthError ? error.detail : undefined;\n // A 403 reaches the tool only with a reason about this user's own grant (the adapter\n // keeps no other 403 body): the role in that workspace, or workspace creation.\n if (status === 403 && detail?.code) {\n throw new DatabrainApiError(0, detail.code, `${detail.message ?? 'Your role does not allow this here.'} (${detail.code})`,\n undefined, 'Ask a Databrain admin for access, or pick a workspace where your role allows it.');\n }\n if (status === 401 || status === 403) {\n // Status 0 carries no API-key hint: a hosted user has no key to rotate, only a sign-in.\n throw new DatabrainApiError(0, 'HOSTED_REQUEST_DENIED',\n 'The signed-in Databrain user cannot do this: the sign-in expired or was revoked, or the granted scopes, role or workspace do not allow it. Sign in again or ask an admin.');\n }\n if (detail && (detail.code || detail.message)) {\n // The code rides with the message: a short message (\"The workspace does not allow\n // this.\") only makes sense next to its reason (DATA_APP_REQUIRED).\n const message = detail.message ?? (status === 404 ? 'Not found in the workspace this sign-in approved.'\n : 'The backend refused this request.');\n throw new DatabrainApiError(detail.status, detail.code ?? 'HOSTED_REQUEST_FAILED',\n detail.code && !message.includes(detail.code) ? `${message} (${detail.code})` : message,\n { error: { code: detail.code, message: detail.message } });\n }\n throw new DatabrainApiError(status, 'HOSTED_REQUEST_DENIED',\n status === 404\n ? 'Not found in the workspace this sign-in approved.'\n : 'The authorized backend could not complete this request. Check workspace access and hosted integration.');\n }\n }\n}\n\n/** The hosted description line for a tool that works in one workspace. */\nfunction workspaceLine(name: string, input: string): string {\n return WORKSPACE_SPANNING_TOOLS.has(name)\n ? ` With more than one granted workspace, name one in \\`${input}\\` to list only that one; leave it out to list them all.`\n : ` Needed when you granted more than one workspace: name it in \\`${input}\\`.`;\n}\n\nfunction whoami(principal: HostedPrincipal) {\n const granted = grantedWorkspaces(principal);\n return toolSuccess(JSON.stringify({\n status: 'authenticated', activeBearer: principal.credentialType,\n actorKind: principal.actorKind, userId: principal.userId, companyId: principal.companyId,\n workspaceGrant: principal.workspaces?.mode === 'all'\n ? 'All workspaces I can access (new ones are included)' : 'Only the workspaces chosen at sign-in',\n grantedWorkspaces: granted.map(item => ({ id: item.id, name: item.name })),\n ...(granted.length === 1 ? { workspaceId: granted[0].id, workspaceName: granted[0].name } : {}),\n note: granted.length === 1\n ? 'Every call works in this workspace.'\n : `Name the workspace on each call that works in one (the \\`${WORKSPACE_INPUT}\\` or \\`workspaceName\\` input): ${grantedNames(principal)}.`,\n oauthClientId: principal.clientId, scopes: principal.scopes,\n }, null, 2));\n}\n\nexport function createHostedToolServer(principal: HostedPrincipal, adapter: HostedAdapter,\n delegation?: HostedDelegation): McpServer {\n const bound = structuredClone(principal);\n const delegated = delegation !== undefined;\n const server = new McpServer({ name: 'databrain', version: MCP_SERVER_VERSION }, {\n instructions: 'Hosted Databrain: each request is authenticated as the signed-in user, with that user\\'s own role in each workspace. ' +\n 'Use whoami to see the verified identity, scopes and the workspaces this sign-in granted. ' +\n 'When more than one workspace is granted, name the workspace on each call (the workspace or workspaceName input). ' +\n '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 '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; ' +\n 'saving requires the existing production confirmation. Hosts without MCP Apps can read the text answer.',\n });\n\n // The existing shared registry already wraps server.tool for intent telemetry. Layer\n // hosted policy underneath it; keep every handler and annotation. A tool that works in one\n // workspace says so, and gains a `workspace` input when its own schema names none.\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[]) => CallToolResult | Promise<CallToolResult>;\n const shape = typeof args[1] === 'string' && args[2] && typeof args[2] === 'object' &&\n !('readOnlyHint' in args[2]) ? args[2] as Record<string, unknown> : undefined;\n const ownSelector = shape && (['workspaceName', 'workspaceId'] as const).find(key => Object.hasOwn(shape, key));\n const addsInput = shape !== undefined && !ownSelector && !WORKSPACE_FREE_TOOLS.has(name) &&\n !Object.hasOwn(shape, WORKSPACE_INPUT);\n if (shape && !WORKSPACE_FREE_TOOLS.has(name)) {\n if (addsInput) {\n args[2] = { ...shape, [WORKSPACE_INPUT]: z.string().min(1).max(512).optional()\n .describe(WORKSPACE_SPANNING_TOOLS.has(name)\n ? 'Workspace id or name. Leave it out to cover every granted workspace.'\n : 'Workspace id or name. Needed when you granted more than one workspace.') };\n }\n args[1] = `${args[1]}${workspaceLine(name, addsInput ? WORKSPACE_INPUT : ownSelector!)}`;\n }\n args[index] = async (...values: any[]) => {\n try {\n authorizeTool(name, bound);\n const target = assertToolArguments(name, values[0], bound, delegated);\n if (name === 'whoami') return whoami(bound);\n const input = values[0] && typeof values[0] === 'object' ? values[0] as Record<string, unknown> : undefined;\n if (input?.projectDir !== undefined) return toolError('projectDir is not available on the hosted server. Pass chartTs, manifest, bundleCss and iconSvg to compile in memory.');\n if (!target && !WORKSPACE_FREE_TOOLS.has(name) && !WORKSPACE_SPANNING_TOOLS.has(name) && !namesWorkspace(input)) {\n throw new WorkspaceSelectionError(400, 'WORKSPACE_REQUIRED',\n `Name the workspace for this call. Granted: ${grantedNames(bound)}.`);\n }\n if (input) {\n // The new input is a header, never a body field. A selector the tool declares\n // gets the call's workspace when it was left out, as for one granted workspace before.\n if (addsInput) delete input[WORKSPACE_INPUT];\n if (shape && target) {\n if (Object.hasOwn(shape, 'workspaceId')) input.workspaceId = target.id;\n if (Object.hasOwn(shape, 'workspaceName') && target.name !== undefined) input.workspaceName = target.name;\n }\n }\n const result = await callWorkspace.run({ target }, () => handler(...values));\n // The iframe's follow-up calls have no model to repeat the workspace selector.\n // Carry only this verified call's workspace, never a draft's claimed choices.\n if (target && result?._meta?.['ui/resourceUri']) {\n return { ...result, _meta: { ...result._meta, 'databrain/workspace': target } };\n }\n return result;\n } catch (error) {\n if (error instanceof WorkspaceSelectionError) return workspaceError(error);\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 const client = new HostedClient(adapter, bound, delegation);\n enableChartApps(server);\n registerAllTools(server, client);\n // Hosted only: a grant of \"All workspaces I can access\" may create one (the issuer decides).\n registerCreateWorkspace(server, client);\n registerAllResources(server);\n registerAllPrompts(server);\n // Keep Apps on the principal-bound client and under the same registration policy.\n registerChartApps(server, client);\n instrument(server, schemaInstrumentation, { beforeSend: () => null });\n return server;\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { DatabrainClient } from '../../client/databrain-client.js';\nimport { DatabrainApiError, toolError, toolSuccess, stringify } from '../../utils/errors.js';\nimport { WRITE_CREATE } from '../annotations.js';\n\n/**\n * Registered by the hosted server only (stdio dropped it in v0.2.0). The issuer allows it\n * only for a sign-in granted \"All workspaces I can access\" and a role that may create one.\n */\nexport function registerCreateWorkspace(server: McpServer, client: DatabrainClient): void {\n server.tool(\n 'create_workspace',\n 'Create a new workspace to connect a datasource or datamart for dashboard building. ' +\n 'Works only when this sign-in granted \"All workspaces I can access\" and your role may create workspaces; ' +\n 'the new workspace is usable on the next call.',\n {\n name: z.string().describe('Unique workspace name'),\n connectionType: z\n .enum(['DATASOURCE', 'DATAMART', 'MULTI_DATASOURCE'])\n .optional()\n .describe('Connection type — DATASOURCE, DATAMART, or MULTI_DATASOURCE'),\n datasourceName: z\n .string()\n .optional()\n .describe('Datasource name (required when connectionType is DATASOURCE)'),\n datamartName: z\n .string()\n .optional()\n .describe('Datamart name (required when connectionType is DATAMART)'),\n body: z\n .record(z.unknown())\n .optional()\n .describe('Raw API body passthrough for POST /api/v2/workspace'),\n },\n WRITE_CREATE,\n async (params) => {\n try {\n const body: Record<string, unknown> = params.body\n ? { ...params.body }\n : {\n name: params.name,\n connectionType: params.connectionType,\n datasourceName: params.datasourceName,\n datamartName: params.datamartName,\n };\n\n const result = await client.request<{ name?: string; id?: string; _id?: string; error?: unknown }>({\n method: 'POST',\n path: '/api/v2/workspace',\n body,\n auth: 'service',\n invalidateCache: ['list_workspaces'],\n });\n\n if (result.error) {\n return toolError(`Workspace creation failed: ${stringify(result.error)}`);\n }\n\n return toolSuccess(\n `Workspace \"${result.name || params.name}\" created successfully.\\n\\n` +\n 'Next steps:\\n' +\n '1. Use `create_data_app` to create a data app linked to this workspace\\n' +\n '2. Build dashboards in the Databrain UI using this workspace\\n' +\n '3. Use `create_embed` to embed dashboards',\n );\n } catch (err) {\n if (err instanceof DatabrainApiError) return err.toToolResponse();\n return toolError(`Unexpected error: ${(err as Error).message}`);\n }\n },\n );\n}\n","import { HostedAuthError } from './types.js';\nimport type { GrantedWorkspace, 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 create_workspace: ['mcp:datamarts:write'],\n list_dashboards: ['mcp:dashboards:read'], create_dashboard: ['mcp:dashboards:write'],\n get_dashboard: ['mcp:dashboards:read'], dashboard_snapshot: ['mcp:dashboards:read'], generate_dashboard_tiles: ['mcp:dashboards:write', 'mcp:query', 'mcp:embeds:write'],\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 search_semantic_model: ['mcp:datamarts:read'], get_semantic_entity: ['mcp:datamarts:read'],\n propose_semantic_change: ['mcp:datamarts:read'], apply_semantic_change: ['mcp:datamarts:write'],\n eval_semantic_model: ['mcp:datamarts:read', 'mcp:query'],\n list_widgets: ['mcp:dashboards:read'], validate_metric_spec: ['mcp:dashboards:read', 'mcp:query', 'mcp:embeds:write'],\n create_or_update_metric_from_query: ['mcp:dashboards:write', 'mcp:query', 'mcp:embeds:write'],\n create_workspace_metric: ['mcp:dashboards:write', 'mcp:query'],\n add_widget: ['mcp:dashboards:write', 'mcp:embeds:write'], update_widget: ['mcp:dashboards:write', 'mcp:embeds:write'],\n remove_widget: ['mcp:dashboards:write', 'mcp:embeds:write'], generate_widget: ['mcp:dashboards:write', 'mcp:query', 'mcp:embeds:write'],\n list_scheduled_reports: ['mcp:dashboards:read'],\n custom_chart_edit: ['mcp:custom-charts:write'], custom_chart_preview: ['mcp:custom-charts:read'],\n load_custom_chart_bundle: ['mcp:custom-charts:read'],\n load_custom_chart_runtime: ['mcp:custom-charts:read'],\n custom_chart_scaffold: [], custom_chart_validate: ['mcp:custom-charts:read'],\n custom_chart_upload: ['mcp:custom-charts:write'], list_custom_charts: ['mcp:custom-charts:read'],\n publish_custom_chart: ['mcp:custom-charts:write'],\n // Chart tile controls (the chart-apps composition). The same routes as ask_question and a\n // dashboard save; listed so a hosted composition is not refused for want of a policy.\n preview_chart: ['mcp:query'], save_chart_as_dashboard: ['mcp:dashboards:write', 'mcp:query'],\n // Hosted platform reads do not mint embed credentials. Guest rendering retains its backend boundary.\n render_metric_tile: ['mcp:dashboards:read', 'mcp:query'],\n render_chart: ['mcp:dashboards:read', 'mcp:query'],\n refresh_widget: ['mcp:dashboards:read', 'mcp:query'],\n drill_down: ['mcp:dashboards:read', 'mcp:query'],\n change_date_range: ['mcp:dashboards:read', 'mcp:query'],\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/**\n * A tool's own top-level argument whose name is also a forbidden identity key, with the\n * only values it may take. `list_dashboards.scope` picks the listing, not an OAuth scope.\n */\nconst TOOL_ARGUMENT_VALUES: Readonly<Record<string, Readonly<Record<string, readonly string[]>>>> = {\n list_dashboards: { scope: ['auto', 'data_app', 'global'] },\n};\n\n/**\n * Top-level tool arguments that carry query rows, not selectors. A row may have a\n * `userId` or `workspaceId` column; the issuer treats the rows as data.\n */\nconst TOOL_OPAQUE_ARGUMENTS: Readonly<Record<string, readonly string[]>> = {\n preview_chart: ['rows'],\n custom_chart_preview: ['rows', 'manifest'],\n custom_chart_edit: ['draft'],\n};\n\n/**\n * Tool-call arguments: the allowed own arguments by exact value, then the shared binding rules\n * against the call's workspace. Returns that workspace (see selectWorkspace).\n */\nexport function assertToolArguments(name: string, value: unknown, principal: HostedPrincipal,\n delegated = false): GrantedWorkspace | undefined {\n const allowed = Object.hasOwn(TOOL_ARGUMENT_VALUES, name) ? TOOL_ARGUMENT_VALUES[name] : {};\n const opaque = Object.hasOwn(TOOL_OPAQUE_ARGUMENTS, name) ? TOOL_OPAQUE_ARGUMENTS[name] : undefined;\n const target = selectWorkspace(value, principal);\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n assertBoundArguments(value, principal, { target, delegated });\n return target;\n }\n const rest: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (!Object.hasOwn(allowed, key)) { rest[key] = child; continue; }\n if (typeof child !== 'string' || !allowed[key].includes(child)) throw new HostedAuthError(403, 'identity_argument_forbidden');\n }\n assertBoundArguments(rest, principal, { target, delegated, opaque });\n return target;\n}\n\n/**\n * Tools that act on no single workspace: identity, the granted list, company-level\n * custom charts, local code generation, and workspace creation. Every other tool\n * reaches a workspace-scoped route, so it can name its workspace.\n */\nexport const WORKSPACE_FREE_TOOLS: ReadonlySet<string> = new Set([\n 'whoami', 'list_workspaces', 'create_workspace', 'generate_embed_code',\n 'custom_chart_preview', 'load_custom_chart_bundle', 'load_custom_chart_runtime',\n 'custom_chart_scaffold', 'custom_chart_validate', 'custom_chart_upload', 'list_custom_charts', 'publish_custom_chart',\n]);\n\n/**\n * Tools that walk every granted workspace when the call names none (each request then names\n * its own workspace), and only the named one when it does. They never ask for a workspace.\n */\nexport const WORKSPACE_SPANNING_TOOLS: ReadonlySet<string> = new Set(['list_dashboards']);\n\n/** The workspace selector a hosted tool gains when its own schema has none. Sent only as a header. */\nexport const WORKSPACE_INPUT = 'workspace';\n\n/** Raised before any request when a call names its workspace wrongly; the tool shows the message. */\nexport class WorkspaceSelectionError extends HostedAuthError {\n constructor(status: 400 | 404, code: string, public readonly reason: string) {\n super(status, code);\n }\n}\n\nexport function grantedWorkspaces(principal: HostedPrincipal): GrantedWorkspace[] {\n if (principal.grantedWorkspaces) return principal.grantedWorkspaces;\n return principal.workspaceId ? [{ id: principal.workspaceId, name: principal.workspaceName }] : [];\n}\n\n/** \"A, B, C\", at most 20 names, then \"and N more\". Only the user's own granted names. */\nexport function grantedNames(principal: HostedPrincipal): string {\n const names = grantedWorkspaces(principal).map(item => item.name ?? item.id);\n return names.length > 20 ? `${names.slice(0, 20).join(', ')} and ${names.length - 20} more` : names.join(', ');\n}\n\n/** A selector is a granted workspace's id, else its exact name. Nothing else, and never two. */\nfunction findGranted(principal: HostedPrincipal, key: string, value: unknown): GrantedWorkspace {\n const granted = grantedWorkspaces(principal);\n const matches = typeof value !== 'string' ? []\n : key === 'workspaceId' ? granted.filter(item => item.id === value)\n : key === 'workspaceName' ? granted.filter(item => item.name === value)\n : granted.some(item => item.id === value) ? granted.filter(item => item.id === value)\n : granted.filter(item => item.name === value);\n if (matches.length !== 1) {\n throw new WorkspaceSelectionError(404, 'NOT_FOUND_IN_GRANT',\n `Workspace ${JSON.stringify(String(value).slice(0, 100))} is not one this sign-in granted. Granted: ${grantedNames(principal)}.`);\n }\n return matches[0];\n}\n\n/**\n * The one workspace a tool call acts in: the top-level `workspace`, `workspaceName` or\n * `workspaceId` inputs (all must name the same granted workspace), else the only granted\n * workspace, else none (the issuer then asks for one, or the route is company-level).\n */\nexport function selectWorkspace(args: unknown, principal: HostedPrincipal,\n keys: readonly string[] = [WORKSPACE_INPUT, 'workspaceName', 'workspaceId']): GrantedWorkspace | undefined {\n let target: GrantedWorkspace | undefined;\n if (args && typeof args === 'object' && !Array.isArray(args)) {\n for (const key of keys) {\n const value = (args as Record<string, unknown>)[key];\n if (value === undefined) continue;\n const found = findGranted(principal, key, value);\n if (target && target.id !== found.id) {\n throw new WorkspaceSelectionError(400, 'WORKSPACE_CONFLICT',\n 'These inputs name different workspaces. Name one workspace for this call.');\n }\n target = found;\n }\n }\n const granted = grantedWorkspaces(principal);\n return target ?? (granted.length === 1 ? granted[0] : undefined);\n}\n\n/** True when a workspace selector appears anywhere in the arguments, raw passthroughs included. */\nexport function namesWorkspace(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false;\n return Object.entries(value).some(([key, child]) =>\n ([WORKSPACE_INPUT, 'workspaceName', 'workspaceId'].includes(key) && child !== undefined) || namesWorkspace(child));\n}\n\n/** What one tool call is bound to, beyond the principal. */\nexport interface ArgumentBinding {\n /** The call's workspace (selectWorkspace). Every workspace selector must name it. */\n target?: GrantedWorkspace;\n /**\n * The call runs as a delegated OAuth request: the issuer checks any tenant id against its\n * route policy on that very call, as it does for the app user, and refuses a route that\n * claims none. Without it (a PAT, a non-delegating adapter) a tenant needs `tenantIds`.\n */\n delegated?: boolean;\n /** Top-level keys holding data rather than selectors (query rows), never scanned. */\n opaque?: readonly string[];\n}\n\nfunction tenantAllowed(child: unknown, principal: HostedPrincipal, delegated: boolean): boolean {\n if (typeof child !== 'string') return false;\n if (principal.tenantIds) return principal.tenantIds.includes(child);\n return delegated && principal.credentialType === 'oauth';\n}\n\n/**\n * Reject conflicting selectors even in raw body passthroughs before a handler can use them.\n * A workspace selector must name the call's target when it has one, else a granted workspace.\n */\nexport function assertBoundArguments(value: unknown, principal: HostedPrincipal, binding: ArgumentBinding = {}): void {\n if (!value || typeof value !== 'object') return;\n const { opaque, ...nested } = binding;\n for (const [key, child] of Object.entries(value)) {\n if (opaque?.includes(key) && !Array.isArray(value)) continue;\n if (key === 'workspaceId' || key === 'workspaceName') {\n const target = binding.target;\n if (target ? child !== (key === 'workspaceId' ? target.id : target.name) : !findGranted(principal, key, child)) {\n throw new HostedAuthError(403, 'principal_mismatch');\n }\n }\n if (['companyId', 'userId'].includes(key) && child !== principal[key as '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) && !tenantAllowed(child, principal, binding.delegated === true)) {\n throw new HostedAuthError(403, 'tenant_not_authorized');\n }\n assertBoundArguments(child, principal, nested);\n }\n}\n","import { parseStudioStream } from '../custom-chart/studio.js';\nimport { z } from 'zod';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport { toolContext } from '../utils/tool-context.js';\nimport { principalSchema } from './auth.js';\nimport { parseErrorBody } from '../client/error-body.js';\nimport { HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedConfig, HostedDelegation, HostedErrorDetail } from './types.js';\n\nconst USER_AGENT = `databrain-mcp-server/${MCP_SERVER_VERSION} (hosted)`;\nconst REQUEST_TIMEOUT_MS = 30_000;\n/** Request headers a tool may set; nothing that can carry or replace a credential. */\nconst FORWARDED_HEADERS = new Set(['idempotency-key']);\n/** The call's workspace, for the issuer's delegated path. Only this adapter sets it. */\nexport const WORKSPACE_HEADER = 'X-Databrain-Workspace';\n/**\n * 403 codes whose reason is about the caller's own grant in a workspace it named and holds,\n * so they reveal nothing about other workspaces or companies. Every other 403 stays opaque.\n */\nconst GRANT_DENIALS = new Set(['ROLE_NOT_ALLOWED', 'WORKSPACE_CREATE_NOT_ALLOWED', 'forbidden_permission', 'insufficient_scope']);\n\n/** Code and message only, as short plain text; never other body fields or the bearer. */\nfunction errorDetail(status: number, text: string, bearer: string | undefined): HostedErrorDetail {\n let parsed: unknown;\n try { parsed = JSON.parse(text); } catch { return { status }; }\n const envelope = parseErrorBody(parsed);\n // A dashboard write the API refuses tile by tile (422 `{tileVerdicts}`) carries no\n // envelope; its problems are the only reason a tool can act on.\n const tiles = tileProblems(parsed);\n const code = envelope.code ?? (tiles ? 'TILE_VALIDATION_FAILED' : undefined);\n const message = envelope.message ?? tiles;\n const clean = (value: string | undefined, max: number) => {\n if (!value) return undefined;\n let out = value.replace(/[\\u0000-\\u001f\\u007f]+/g, ' ').trim();\n if (bearer) out = out.split(bearer).join('[redacted]');\n return out.slice(0, max) || undefined;\n };\n // Long enough for the issuer's list of up to 20 granted workspace names.\n return { status, code: code && /^[A-Za-z0-9_.:-]{1,64}$/.test(code) ? code : undefined,\n message: clean(message, 1200) };\n}\n\n/** \"Tile t1: problem; problem. Tile t2: ...\" for the tiles a dashboard write refused. */\nfunction tileProblems(body: unknown): string | undefined {\n const verdicts = (body as { tileVerdicts?: unknown } | null)?.tileVerdicts;\n if (!Array.isArray(verdicts)) return undefined;\n const lines = verdicts.flatMap((verdict) => {\n const { id, ok, problems } = (verdict ?? {}) as { id?: unknown; ok?: unknown; problems?: unknown };\n if (ok !== false || !Array.isArray(problems)) return [];\n const words = problems.filter((p): p is string => typeof p === 'string');\n return words.length ? [`Tile ${typeof id === 'string' ? id : '?'}: ${words.join(' ')}`] : [];\n });\n return lines.length ? `The dashboard was not saved. ${lines.join(' ')}` : undefined;\n}\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 apiBase = (config.apiUrl ?? config.issuer).replace(/\\/$/, '');\n const apiOrigin = new URL(apiBase).origin;\n const endpoint = `${apiBase}/oauth/principal`;\n const publicOrigin = new URL(config.publicUrl).origin;\n // Handle -> bearer, for this process only. A handle is unreadable and dies with its request.\n const bearers = new WeakMap<HostedDelegation, string>();\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: here to verify it,\n // and in executeRequest for its delegated REST path. No redirects, other origins,\n // cache or service-token fallback.\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) {\n if (response.status === 429 || response.status >= 500) throw new HostedAuthError(503, 'temporarily_unavailable');\n throw new HostedAuthError(401, 'invalid_token');\n }\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 delegate(token) {\n const handle: HostedDelegation = Object.freeze({ kind: 'hosted-delegation' as const });\n bearers.set(handle, token);\n return handle;\n },\n async executeRequest(options, principal, delegation, workspace) {\n // The backend's delegated path re-verifies this bearer for this resource on every\n // call, then binds workspace, objects, role and audit. Only the configured issuer\n // receives it, exactly as for /oauth/principal. A guest-token call carries only the\n // guest token the backend minted for this user's earlier, authorized call.\n const bearer = delegation ? bearers.get(delegation) : undefined;\n if (options.auth !== 'none' && !bearer) throw new HostedAuthError(503, 'delegated_backend_adapter_required');\n if (options.auth === 'none' && !options.token) throw new HostedAuthError(403, 'tool_not_authorized');\n if (!options.path.startsWith('/') || options.path.startsWith('//')) throw new HostedAuthError(403, 'tool_not_authorized');\n const url = new URL(`${apiBase}${options.path}`);\n if (url.origin !== apiOrigin) throw new HostedAuthError(403, 'tool_not_authorized');\n for (const [key, value] of Object.entries(options.query ?? {})) url.searchParams.set(key, value);\n const headers: Record<string, string> = {\n Accept: 'application/json', 'Content-Type': 'application/json', 'User-Agent': USER_AGENT,\n 'X-Databrain-Client': USER_AGENT,\n };\n for (const [key, value] of Object.entries(options.headers ?? {})) {\n if (FORWARDED_HEADERS.has(key.toLowerCase())) headers[key] = value;\n }\n const tool = toolContext.getStore();\n if (tool) {\n headers['X-Databrain-MCP-Tool'] = tool.tool;\n headers['X-Databrain-MCP-Tool-Category'] = tool.category;\n headers['X-Databrain-MCP-Workflow-Intent'] = tool.workflowIntent;\n headers['X-Databrain-MCP-Input-Mode'] = tool.inputMode;\n }\n if (options.auth === 'none') {\n headers['x-plugin-token'] = options.token!;\n headers.Origin = publicOrigin;\n } else {\n headers.Authorization = `Bearer ${bearer}`;\n // Same rule as the bearer: only with it, only to the issuer, and only a workspace\n // this principal holds. The issuer re-checks it against the live grant.\n if (workspace) {\n if (!principal.grantedWorkspaces?.some(item => item.id === workspace.id) &&\n principal.workspaceId !== workspace.id) throw new HostedAuthError(403, 'principal_mismatch');\n headers[WORKSPACE_HEADER] = workspace.id;\n }\n }\n const response = await fetch(url, {\n method: options.method, headers, redirect: 'error',\n body: options.body === undefined ? undefined : JSON.stringify(options.body),\n signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS),\n });\n const text = await response.text();\n if (!response.ok) {\n // A wrong-workspace object is a 404, like a missing one. 401, 403 and 5xx keep the\n // status class only. Other 4xx keep the backend's code and message (a validation\n // reason, `layer_not_found`), so a tool can recover as it does on stdio.\n const forbidden = response.status === 403 ? errorDetail(403, text, bearer) : undefined;\n if (forbidden?.code && GRANT_DENIALS.has(forbidden.code)) {\n throw new HostedAuthError(403, 'delegated_request_denied',\n forbidden.code === 'insufficient_scope' && options.path === '/api/v2/guest-token/create'\n ? { ...forbidden, message: 'Guest/tenant operations require mcp:embeds:write. Ask an admin for a role that allows it, then connect again.' }\n : forbidden);\n }\n const status = response.status === 403 && options.responseFormat === 'studio-sse' ? 403 : response.status === 401 ? 401 : response.status >= 500 ? 503\n : response.status === 403 || response.status === 404 ? 404 : 400;\n const detail = status === 401 || status === 503 || response.status === 403 ? undefined\n : errorDetail(response.status, text, bearer);\n throw new HostedAuthError(status, 'delegated_request_denied', detail);\n }\n try { return options.responseFormat === 'studio-sse' ? parseStudioStream(text) : text ? JSON.parse(text) : null; }\n catch { throw new HostedAuthError(503, 'delegated_response_invalid'); }\n },\n };\n}\n"],"mappings":"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","names":["z","z","z","z"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
FilePatStore,
|
|
4
4
|
HostedAuthError,
|
|
5
5
|
hashCredential
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-X44C5GW2.js";
|
|
7
7
|
|
|
8
8
|
// src/hosted/test-only.ts
|
|
9
9
|
function createTestOnlyAdapter(path) {
|
|
@@ -35,4 +35,4 @@ function createTestOnlyAdapter(path) {
|
|
|
35
35
|
export {
|
|
36
36
|
createTestOnlyAdapter
|
|
37
37
|
};
|
|
38
|
-
//# sourceMappingURL=test-only-
|
|
38
|
+
//# sourceMappingURL=test-only-LJGOY2QA.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@databrainhq/mcp-server",
|
|
3
3
|
"mcpName": "com.usedatabrain/mcp-server",
|
|
4
|
-
"version": "0.3.0-rc.
|
|
4
|
+
"version": "0.3.0-rc.13",
|
|
5
5
|
"description": "MCP server for Databrain embedded analytics - connect databases, create datamarts, configure embeds, and generate frontend code",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|