@databrainhq/mcp-server 0.3.0-rc.10 → 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.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/errors.ts","../src/utils/logger.ts","../package.json","../src/version.ts","../src/utils/cache.ts","../src/client/error-body.ts","../src/utils/tool-context.ts","../src/client/oauth-client-credentials.ts","../src/client/databrain-client.ts","../src/custom-chart/assets.ts","../src/custom-chart/manifest.ts","../src/custom-chart/types.ts","../src/custom-chart/scaffold.ts","../src/custom-chart/bundle.ts","../src/custom-chart/sanitize-svg.ts","../src/custom-chart/icon.ts","../src/custom-chart/zip.ts","../src/api/custom-charts-api.ts","../src/custom-chart/api.ts"],"sourcesContent":["export class DatabrainApiError extends Error {\n constructor(\n public readonly status: number,\n public readonly code: string,\n message: string,\n public readonly details?: unknown,\n /** The server's own next step for this error class, when it sent one. */\n public readonly serverHint?: string,\n public readonly retryable?: boolean,\n ) {\n super(message);\n this.name = 'DatabrainApiError';\n }\n\n toToolResponse(): { content: Array<{ type: 'text'; text: string }>; isError: true } {\n const parts = [`Error: ${this.message}`];\n const detailDump = this.formatDetails();\n if (detailDump) parts.push(detailDump);\n const hint = this.serverHint ? `Hint: ${this.serverHint}` : this.hint();\n if (hint) parts.push(hint);\n return {\n content: [{ type: 'text', text: parts.join('\\n\\n') }],\n isError: true,\n };\n }\n\n /**\n * Surface API-returned details for 4xx errors so the LLM can recover or\n * report the real reason (validation messages, field names, etc.). Skips\n * fields already collapsed into `message`/`code`, and skips 5xx where the\n * payload usually isn't actionable.\n */\n private formatDetails(): string {\n if (this.status < 400 || this.status >= 500) return '';\n if (!this.details || typeof this.details !== 'object') return '';\n\n const skipKeys = new Set(['message', 'detail', 'error', 'code', 'data', 'hint', 'retryable']);\n const extra: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(this.details as Record<string, unknown>)) {\n if (skipKeys.has(k)) continue;\n if (v === null || v === undefined) continue;\n extra[k] = v;\n }\n if (Object.keys(extra).length === 0) return '';\n try {\n return `API details: ${JSON.stringify(extra)}`;\n } catch {\n return '';\n }\n }\n\n private hint(): string {\n // dashboard_snapshot: the API's own reasons, so the generic status hints below do not mislead.\n if (this.code === 'OAUTH_SIGN_IN_REQUIRED') {\n return 'Hint: Snapshots draw as a signed-in Databrain user. Connect this AI app with a Databrain sign-in (the hosted connector, or /mcp in Claude Code); a service token cannot take them.';\n }\n if (this.code === 'RENDER_IN_PROGRESS') {\n return 'Hint: Your other snapshot is still rendering. Wait a few seconds, then call the tool again.';\n }\n if (this.code === 'SNAPSHOT_TIMEOUT' || this.code === 'SNAPSHOT_TOO_LARGE') {\n return 'Hint: Ask for one chart with metricId, or a smaller width.';\n }\n if (this.code === 'UNAUTHORIZED_ORIGIN') {\n return 'Hint: The embed origin is not on the allowed domains list. Add the domain to the allowed domains in the Databrain settings; the token itself is fine.';\n }\n if (this.status === 401 || this.status === 403) {\n if (this.code === 'OAUTH_API_ERROR') {\n return 'Hint: Check that the backend accepts the configured OAuth issuer and resource, and that the service principal has permission for this operation. Static fallback applies only to token acquisition failures.';\n }\n if (\n this.code === 'INVALID_SERVICE_TOKEN' ||\n this.code === 'MISSING_SERVICE_TOKEN' ||\n this.message.toLowerCase().includes('service token')\n ) {\n return 'Hint: The service token (DATABRAIN_SERVICE_TOKEN) is invalid, expired, or missing. It must be set in the MCP server config (not at runtime). Check Settings > Service Tokens in the Databrain UI.';\n }\n return 'Hint: The per-data-app API token is invalid or expired. Use `create_api_token` to generate a new one. Do not ask the user — manage tokens via MCP tools.';\n }\n if (this.status === 400) {\n return 'Hint: The request was invalid. Check parameter values match expected types and formats. Use the corresponding list tool to verify IDs and names.';\n }\n if (this.status === 404) {\n return 'Hint: The requested resource was not found. Verify the ID or name is correct using the corresponding list tool.';\n }\n if (this.status === 409) {\n return 'Hint: A resource with this name already exists. Use a different name or list existing resources first.';\n }\n if (this.status === 422) {\n return 'Hint: The request parameters are invalid. Check the required fields and their formats.';\n }\n if (this.status === 429) {\n return 'Hint: API rate limit reached. Wait a moment and try again.';\n }\n if (this.status >= 500) {\n return 'Hint: The Databrain API is experiencing issues. Try again in a few moments.';\n }\n return '';\n }\n}\n\nexport function toolError(message: string): { content: Array<{ type: 'text'; text: string }>; isError: true } {\n return {\n content: [{ type: 'text', text: message }],\n isError: true,\n };\n}\n\nexport function stringify(value: unknown): string {\n if (typeof value === 'string') return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\nexport function toolSuccess(text: string): { content: Array<{ type: 'text'; text: string }> } {\n return {\n content: [{ type: 'text', text }],\n };\n}\n","const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 } as const;\ntype LogLevel = keyof typeof LOG_LEVELS;\n\nconst currentLevel: LogLevel =\n (process.env.DATABRAIN_LOG_LEVEL as LogLevel) || 'warn';\n\n/**\n * Credentials the process holds. Every log line is scrubbed of them before it\n * reaches stderr, because an API error body can quote the Authorization\n * header back and stderr is a terminal, a CI log or the MCP host's log.\n */\nconst secrets = new Set<string>();\n\nexport function registerSecret(value: string | undefined | null): void {\n if (typeof value === 'string' && value.length >= 8) secrets.add(value);\n}\n\nexport function redactSecrets(text: string): string {\n let out = text;\n for (const secret of secrets) out = out.split(secret).join('[redacted]');\n return out;\n}\n\nfunction log(level: LogLevel, message: string, data?: unknown): void {\n if (LOG_LEVELS[level] < LOG_LEVELS[currentLevel]) return;\n const line = `[databrain-mcp] [${level.toUpperCase()}] ${message}`;\n const text = data !== undefined ? `${line} ${JSON.stringify(data)}` : line;\n process.stderr.write(`${redactSecrets(text)}\\n`);\n}\n\nexport const logger = {\n debug: (msg: string, data?: unknown) => log('debug', msg, data),\n info: (msg: string, data?: unknown) => log('info', msg, data),\n warn: (msg: string, data?: unknown) => log('warn', msg, data),\n error: (msg: string, data?: unknown) => log('error', msg, data),\n};\n","{\n \"name\": \"@databrainhq/mcp-server\",\n \"mcpName\": \"com.usedatabrain/mcp-server\",\n \"version\": \"0.3.0-rc.10\",\n \"description\": \"MCP server for Databrain embedded analytics - connect databases, create datamarts, configure embeds, and generate frontend code\",\n \"type\": \"module\",\n \"bin\": {\n \"mcp-server\": \"dist/index.js\",\n \"databrain-mcp-server\": \"dist/index.js\",\n \"databrain-mcp-chart-apps\": \"dist/apps/stdio.js\",\n \"databrain\": \"dist/cli.js\"\n },\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"files\": [\n \"dist\",\n \"skills\",\n \".claude-plugin\",\n \".mcp.json\"\n ],\n \"scripts\": {\n \"build\": \"tsup && node scripts/copy-custom-chart-assets.mjs\",\n \"dev\": \"tsx src/index.ts\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"test:live\": \"vitest run tests/integration/prompt-flows.test.ts\",\n \"lint\": \"tsc --noEmit\",\n \"prepublishOnly\": \"npm run build\"\n },\n \"keywords\": [\n \"mcp\",\n \"databrain\",\n \"embedded-analytics\",\n \"ai\",\n \"model-context-protocol\"\n ],\n \"license\": \"MIT\",\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.32.1\",\n \"@posthog/mcp\": \"^0.10.7\",\n \"esbuild\": \"0.27.3\",\n \"fflate\": \"0.8.3\",\n \"posthog-node\": \"^5.21.2\",\n \"yaml\": \"^2.9.1\",\n \"zod\": \"^3.24.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.3.5\",\n \"tsup\": \"^8.4.0\",\n \"tsx\": \"^4.19.0\",\n \"typescript\": \"^5.7.0\",\n \"vitest\": \"^3.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/databrainhq/mcp-server.git\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n }\n}\n","import packageJson from '../package.json';\n\n// tsup embeds this package.json value in dist while tsx resolves the same file\n// during development. The handshake and request headers therefore share the\n// package version without maintaining a second release string.\nexport const MCP_SERVER_VERSION = packageJson.version;\n","interface CacheEntry<T> {\n value: T;\n expiresAt: number;\n}\n\nexport class TtlCache {\n private store = new Map<string, CacheEntry<unknown>>();\n\n get<T>(key: string): T | undefined {\n const entry = this.store.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return entry.value as T;\n }\n\n set<T>(key: string, value: T, ttlMs: number): void {\n this.store.set(key, { value, expiresAt: Date.now() + ttlMs });\n }\n\n invalidateByPrefix(prefix: string): void {\n for (const key of this.store.keys()) {\n if (key.startsWith(prefix)) {\n this.store.delete(key);\n }\n }\n }\n\n clear(): void {\n this.store.clear();\n }\n}\n","/**\n * Reads a Databrain error body into one shape. The backend sends the same\n * envelope `{ code, message, hint, retryable, details }` in three places:\n * flat (`{ code, message }`), under `error` (public REST, datasource API) and\n * under `data.error` (the externalApis routes, which also send `error` as a\n * plain string). Reading only the flat form dropped the code and hint.\n */\nexport interface ParsedErrorBody {\n code?: string;\n message?: string;\n hint?: string;\n retryable?: boolean;\n details?: unknown;\n}\n\nconst isRecord = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v);\n\nconst str = (v: unknown): string | undefined =>\n typeof v === 'string' && v.trim() ? v : undefined;\n\nconst fromEnvelope = (v: unknown): ParsedErrorBody | undefined => {\n if (!isRecord(v)) return undefined;\n const out: ParsedErrorBody = {\n code: str(v.code),\n message: str(v.message) ?? str(v.detail),\n hint: str(v.hint),\n retryable: typeof v.retryable === 'boolean' ? v.retryable : undefined,\n details: v.details,\n };\n return out.code || out.message || out.hint ? out : undefined;\n};\n\nexport function parseErrorBody(body: unknown): ParsedErrorBody {\n if (!isRecord(body)) return {};\n const nested =\n fromEnvelope(isRecord(body.data) ? body.data.error : undefined) ??\n fromEnvelope(body.error) ??\n fromEnvelope(body);\n if (nested) {\n return { ...nested, message: nested.message ?? str(body.error) };\n }\n return { message: str(body.error) };\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\n\n/**\n * Per-tool-call intent metadata. Set when an MCP tool handler runs (see\n * registerAllTools, which wraps every handler), and read by DatabrainClient to\n * tag outbound API requests with X-Databrain-MCP-* headers. The backend\n * (mcpTracking) records these so usage can be attributed to a specific tool and\n * workflow intent — letting us see, e.g., embedding vs internal-analytics flows.\n */\nexport interface ToolCallContext {\n /** The MCP tool name being executed, e.g. 'create_workspace_metric'. */\n tool: string;\n /** Coarse domain bucket, e.g. 'embedding' | 'internal_analytics'. */\n category: string;\n /** Use-case bucket, e.g. 'sql_to_workspace_metric'. */\n workflowIntent: string;\n /** How the request was initiated; 'tool_call' for MCP tool invocations. */\n inputMode: string;\n}\n\nexport const toolContext = new AsyncLocalStorage<ToolCallContext>();\n","import { z } from 'zod';\n\n// Validate without transforming: issuer identity uses exact string comparison.\nexport const oauthHttpsUrl = z.string().refine(value => {\n try {\n const url = new URL(value);\n return url.protocol === 'https:' && !url.username && !url.password && !url.hash;\n } catch { return false; }\n}, 'OAuth URLs must use HTTPS without userinfo or fragments');\n\nexport const oauthConfigSchema = z.object({\n issuer: oauthHttpsUrl.refine(value => {\n try { return !new URL(value).search; } catch { return false; }\n }, 'OAuth issuer must not contain a query'),\n clientId: z.string().min(1),\n clientSecret: z.string().min(1),\n resource: oauthHttpsUrl,\n scope: z.string().min(1).optional(),\n authMethod: z.enum(['client_secret_basic', 'client_secret_post']).default('client_secret_basic'),\n});\n\nexport type OAuthConfig = z.infer<typeof oauthConfigSchema>;\n\nconst metadataSchema = z.object({\n issuer: z.string(),\n token_endpoint: oauthHttpsUrl,\n token_endpoint_auth_methods_supported: z.array(z.string()).optional(),\n grant_types_supported: z.array(z.string()).optional(),\n});\nconst tokenSchema = z.object({\n access_token: z.string().regex(/^[A-Za-z0-9\\-._~+/]+=*$/),\n token_type: z.string().refine(value => value.toLowerCase() === 'bearer'),\n expires_in: z.number().finite().positive().optional(),\n});\n\nconst TOKEN_TIMEOUT_MS = 10_000;\nconst FAILURE_COOLDOWN_MS = 30_000;\nconst MAX_REFRESH_SKEW_MS = 60_000;\n\nfunction authError(): Error {\n // Never retain an IdP body, a fetch error, or a schema error as a cause:\n // those can contain credentials and are serialized by callers/telemetry.\n return new Error('OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes.');\n}\n\n/** One immutable credential/issuer/resource binding per MCP process client. */\nexport class OAuthClientCredentials {\n private readonly config: OAuthConfig;\n private tokenEndpoint?: string;\n private cached?: { value: string; renewAt: number };\n private pending?: Promise<string>;\n private retryAt = 0;\n\n constructor(config: OAuthConfig) {\n // Also validate programmatic callers; do not expose invalid values in errors.\n const parsed = oauthConfigSchema.safeParse(config);\n if (!parsed.success) throw authError();\n this.config = Object.freeze(parsed.data);\n }\n\n getStatus(): 'configured' | 'ready' | 'unavailable' {\n if (this.cached && Date.now() < this.cached.renewAt) return 'ready';\n if (this.retryAt > 0) return 'unavailable';\n return 'configured';\n }\n\n async getToken(): Promise<string> {\n if (this.cached && Date.now() < this.cached.renewAt) return this.cached.value;\n if (this.pending) return this.pending;\n if (Date.now() < this.retryAt) throw authError();\n\n this.cached = undefined;\n this.pending = this.acquire().catch(() => {\n this.retryAt = Date.now() + FAILURE_COOLDOWN_MS;\n throw authError();\n }).finally(() => { this.pending = undefined; });\n return this.pending;\n }\n\n private async discoverTokenEndpoint(signal: AbortSignal): Promise<string> {\n if (this.tokenEndpoint) return this.tokenEndpoint;\n const issuer = new URL(this.config.issuer);\n const path = issuer.pathname.replace(/\\/$/, '');\n const candidates = [...new Set([\n `${issuer.origin}/.well-known/oauth-authorization-server${path}`,\n `${issuer.origin}/.well-known/openid-configuration${path}`,\n `${issuer.origin}${path}/.well-known/openid-configuration`,\n ])];\n\n for (const url of candidates) {\n // No credentials, cookies, API headers, or redirects during discovery.\n const response = await fetch(url, {\n headers: { Accept: 'application/json' }, redirect: 'error', signal,\n });\n if (response.status === 404 || response.status === 405) continue;\n if (!response.ok) throw authError();\n const metadata = metadataSchema.parse(await response.json());\n if (metadata.issuer !== this.config.issuer) throw authError();\n const methods = metadata.token_endpoint_auth_methods_supported ?? ['client_secret_basic'];\n if (!methods.includes(this.config.authMethod)) throw authError();\n if (metadata.grant_types_supported && !metadata.grant_types_supported.includes('client_credentials')) {\n throw authError();\n }\n this.tokenEndpoint = metadata.token_endpoint;\n return this.tokenEndpoint;\n }\n throw authError();\n }\n\n private async acquire(): Promise<string> {\n // One time budget covers all discovery attempts and token acquisition.\n const signal = AbortSignal.timeout(TOKEN_TIMEOUT_MS);\n const endpoint = await this.discoverTokenEndpoint(signal);\n const body = new URLSearchParams({\n grant_type: 'client_credentials', resource: this.config.resource,\n });\n if (this.config.scope) body.set('scope', this.config.scope);\n const headers: Record<string, string> = {\n Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded',\n };\n if (this.config.authMethod === 'client_secret_post') {\n body.set('client_id', this.config.clientId);\n body.set('client_secret', this.config.clientSecret);\n } else {\n // RFC 6749 2.3.1: form-encode EACH value before constructing Basic auth.\n const encode = (value: string) => new URLSearchParams({ value }).toString().slice(6);\n headers.Authorization = `Basic ${Buffer.from(`${encode(this.config.clientId)}:${encode(this.config.clientSecret)}`).toString('base64')}`;\n }\n\n const startedAt = Date.now();\n const response = await fetch(endpoint, {\n method: 'POST', headers, body: body.toString(), redirect: 'error', signal,\n });\n if (!response.ok) throw authError();\n const token = tokenSchema.parse(await response.json());\n if (token.expires_in !== undefined) {\n const lifetimeMs = token.expires_in * 1000;\n const expiresAt = startedAt + lifetimeMs;\n if (!Number.isFinite(expiresAt) || Date.now() >= expiresAt) throw authError();\n this.cached = {\n value: token.access_token,\n renewAt: expiresAt - Math.min(MAX_REFRESH_SKEW_MS, lifetimeMs * 0.1),\n };\n }\n // With no expires_in, use the token for this call only. No JWT decoding,\n // assumed lifetime, refresh_token grant, disk storage, or background timer.\n this.retryAt = 0;\n return token.access_token;\n }\n}\n","import { TtlCache } from '../utils/cache.js';\nimport type { Config } from '../utils/config.js';\nimport { DatabrainApiError } from '../utils/errors.js';\nimport { logger, registerSecret } from '../utils/logger.js';\nimport type { RequestOptions } from './types.js';\nimport { parseErrorBody } from './error-body.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport { toolContext } from '../utils/tool-context.js';\nimport { OAuthClientCredentials, oauthHttpsUrl } from './oauth-client-credentials.js';\n\nconst MCP_USER_AGENT = `databrain-mcp-server/${MCP_SERVER_VERSION}`;\n\nconst MAX_RETRIES = 3;\nconst REQUEST_TIMEOUT_MS = 30_000;\n\nconst NON_TRANSIENT_PATTERNS = [\n 'out of context',\n 'Definition not found',\n 'not a data question',\n 'Query generation failed',\n 'not found',\n 'not authenticated',\n 'Invalid Service Token',\n 'service token',\n];\n\nexport class DatabrainClient {\n private cache = new TtlCache();\n private runtimeApiToken: string | undefined;\n private readonly oauth?: OAuthClientCredentials;\n private oauthFallback = false;\n\n constructor(private config: Config) {\n registerSecret(config.serviceToken);\n registerSecret(config.apiToken);\n if (config.oauth) {\n if (!oauthHttpsUrl.safeParse(config.apiUrl).success) {\n throw new Error('OAuth requires an HTTPS Databrain API URL without userinfo or fragments.');\n }\n this.oauth = new OAuthClientCredentials(config.oauth);\n // Prevent later mutation of a caller-owned config from moving credentials\n // or the resource bearer to another API/issuer within this client session.\n this.config = Object.freeze({ ...config });\n }\n }\n\n hasApiToken(): boolean {\n return !!(this.runtimeApiToken || this.config.apiToken);\n }\n\n setRuntimeApiToken(token: string): void {\n registerSecret(token);\n this.runtimeApiToken = token;\n logger.info('Runtime API token set for this session');\n }\n\n getServiceToken(): string | undefined {\n return this.config.serviceToken;\n }\n\n getApiUrl(): string {\n return this.config.apiUrl;\n }\n\n getDemoDomain(): string {\n return this.config.demoDomain;\n }\n\n /**\n * Describes which credential is currently used as the bearer for outgoing\n * API calls. Used by `whoami` so the LLM can give the user a truthful answer.\n */\n getActiveAuth():\n | { source: 'service-token'; oauthFallback?: boolean }\n | { source: 'oauth-client-credentials'; status: 'configured' | 'ready' | 'unavailable' }\n | { source: 'api-token' }\n | { source: 'none' } {\n if (this.oauth) {\n if (this.oauthFallback) return { source: 'service-token', oauthFallback: true };\n return { source: 'oauth-client-credentials', status: this.oauth.getStatus() };\n }\n if (this.config.serviceToken) return { source: 'service-token' };\n if (this.runtimeApiToken || this.config.apiToken) return { source: 'api-token' };\n return { source: 'none' };\n }\n\n private async getToken(auth: 'service' | 'api' | 'none'): Promise<string | undefined> {\n if (auth === 'none') return undefined;\n if (auth === 'service') {\n if (this.oauth) {\n try {\n const token = await this.oauth.getToken();\n this.oauthFallback = false;\n return token;\n } catch {\n if (!this.config.serviceToken) {\n throw new DatabrainApiError(0, 'OAUTH_TOKEN_ERROR',\n 'OAuth token acquisition failed. Check the configured issuer, client credentials, resource and scopes.');\n }\n if (!this.oauthFallback) logger.warn('OAuth unavailable; using the configured static service token.');\n this.oauthFallback = true;\n }\n }\n return this.config.serviceToken;\n }\n return this.runtimeApiToken || this.config.apiToken;\n }\n\n async request<T>(options: RequestOptions): Promise<T> {\n return this.requestInternal<T>(options);\n }\n\n private async requestInternal<T>(options: RequestOptions): Promise<T> {\n const usesOAuth = !!this.oauth && options.auth === 'service' && !options.token;\n // An OAuth renewal can switch to a differently scoped static principal.\n // Do not serve/store service response caches across that auth boundary.\n const cacheAllowed = !usesOAuth;\n // Invalidate caches first\n if (options.invalidateCache) {\n for (const prefix of options.invalidateCache) {\n this.cache.invalidateByPrefix(prefix);\n }\n }\n\n // Check cache\n if (cacheAllowed && options.cacheKey && options.cacheTtlMs) {\n const cached = this.cache.get<T>(options.cacheKey);\n if (cached !== undefined) {\n logger.debug(`Cache hit: ${options.cacheKey}`);\n return cached;\n }\n }\n\n let token = options.token ?? await this.getToken(options.auth);\n if (options.auth !== 'none' && !options.token && !token) {\n if (options.auth === 'service') {\n throw new DatabrainApiError(\n 401,\n 'MISSING_SERVICE_TOKEN',\n 'DATABRAIN_SERVICE_TOKEN is required for this operation (org-level token for discovery and data app management). It must be set in the MCP server configuration — do not ask the user for it at runtime.',\n );\n }\n throw new DatabrainApiError(\n 401,\n 'MISSING_API_TOKEN',\n 'A per-data-app API token is required for this embedding operation but none is configured. ' +\n 'Call `create_api_token` to generate one, or `set_api_token` if you already have the value. Do not ask the user for this token — create it via the MCP tools.',\n );\n }\n\n // Build URL\n let url = `${this.config.apiUrl}${options.path}`;\n if (options.query) {\n const params = new URLSearchParams(options.query);\n url += `?${params.toString()}`;\n }\n\n let lastError: Error | undefined;\n\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n if (usesOAuth && attempt > 1) token = await this.getToken(options.auth);\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'User-Agent': MCP_USER_AGENT,\n 'X-Databrain-Client': MCP_USER_AGENT,\n ...options.headers,\n };\n // Tag the call with the active tool's intent so backend telemetry can\n // attribute usage (embedding vs internal analytics, per-tool). Present\n // only when invoked from within a tool handler (see registry wrapper).\n const ctx = toolContext.getStore();\n if (ctx) {\n headers['X-Databrain-MCP-Tool'] = ctx.tool;\n headers['X-Databrain-MCP-Tool-Category'] = ctx.category;\n headers['X-Databrain-MCP-Workflow-Intent'] = ctx.workflowIntent;\n headers['X-Databrain-MCP-Input-Mode'] = ctx.inputMode;\n }\n if (token) {\n // Guest-token endpoints (auth: 'none' with a minted token) read the\n // token from `x-plugin-token` and require a whitelisted Origin.\n // DATABRAIN_DOMAINS includes `usedatabrain.com`, so `mcp.usedatabrain.com`\n // passes the whitelist without needing a per-company config.\n if (options.auth === 'none') {\n headers['x-plugin-token'] = token;\n headers['Origin'] = 'https://mcp.usedatabrain.com';\n } else {\n headers['Authorization'] = `Bearer ${token}`;\n }\n }\n\n const response = await fetch(url, {\n method: options.method,\n headers,\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: AbortSignal.timeout(options.timeoutMs || REQUEST_TIMEOUT_MS),\n ...(usesOAuth ? { redirect: 'error' as const } : {}),\n });\n\n if (!response.ok) {\n // An untrusted API can echo credentials in its error body. Keep\n // OAuth errors body-free in both tool output and telemetry. Never\n // retry/downgrade a backend denial with the static service token.\n if (usesOAuth) {\n throw new DatabrainApiError(response.status, 'OAUTH_API_ERROR',\n `Databrain API request failed with status ${response.status} using configured service credentials.`);\n }\n const errorBody = await response.text();\n logger.warn(`[API Error] ${options.method} ${options.path} status=${response.status} body=${errorBody.slice(0, 500)}`);\n let parsed: unknown = {};\n try {\n parsed = JSON.parse(errorBody);\n } catch {\n // not JSON\n }\n\n const envelope = parseErrorBody(parsed);\n const errorMsg = envelope.message || `API request failed with status ${response.status}`;\n const error = new DatabrainApiError(\n response.status,\n envelope.code || `HTTP_${response.status}`,\n errorMsg,\n parsed,\n envelope.hint,\n envelope.retryable,\n );\n\n // Retry on 429 or 5xx (but not when the error is clearly non-transient)\n // The server's own retryable flag wins over the status heuristic.\n const isTransient =\n envelope.retryable !== undefined\n ? envelope.retryable && (response.status === 429 || response.status >= 500)\n : response.status === 429 ||\n (response.status >= 500 &&\n !NON_TRANSIENT_PATTERNS.some((p) => errorMsg.includes(p)));\n if (isTransient && attempt < MAX_RETRIES) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000);\n logger.warn(`Retrying in ${delay}ms (attempt ${attempt}/${MAX_RETRIES})`, {\n status: response.status,\n path: options.path,\n });\n await new Promise((r) => setTimeout(r, delay));\n lastError = error;\n continue;\n }\n\n throw error;\n }\n\n const text = await response.text();\n let data = text ? (JSON.parse(text) as T) : ({} as T);\n\n // Databrain API wraps most responses in { data: ... } — unwrap automatically\n if (\n !options.skipDataUnwrap &&\n data &&\n typeof data === 'object' &&\n !Array.isArray(data) &&\n 'data' in data\n ) {\n const inner = (data as Record<string, unknown>).data;\n if (inner !== null && inner !== undefined) {\n data = inner as unknown as T;\n }\n }\n\n // Store in cache\n if (cacheAllowed && options.cacheKey && options.cacheTtlMs) {\n this.cache.set(options.cacheKey, data, options.cacheTtlMs);\n }\n\n return data;\n } catch (err) {\n if (err instanceof DatabrainApiError) throw err;\n\n if (attempt < MAX_RETRIES && (err as Error)?.name === 'TimeoutError' && !options.noRetryOnTimeout) {\n const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000);\n logger.warn(`Request timed out, retrying in ${delay}ms`, { path: options.path });\n await new Promise((r) => setTimeout(r, delay));\n lastError = err as Error;\n continue;\n }\n\n throw new DatabrainApiError(\n 0,\n 'NETWORK_ERROR',\n usesOAuth ? 'Failed to connect to Databrain API using configured service credentials.'\n : `Failed to connect to Databrain API: ${(err as Error).message}`,\n );\n }\n }\n\n throw lastError || new DatabrainApiError(0, 'UNKNOWN', 'Request failed after retries');\n }\n}\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/**\n * The template project and the runtime shim are shipped as plain files, not\n * as strings baked into the bundle, so that they stay byte identical to the\n * copies other repos test against. In source they live in\n * src/custom-chart/; tsup copies them to dist/custom-chart/ after a build\n * (see tsup.config.ts onSuccess). This finds them from either place.\n */\nlet cachedDir: string | undefined;\n\nexport function customChartAssetDir(): string {\n if (cachedDir) return cachedDir;\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n here,\n join(here, 'custom-chart'),\n join(here, '..', 'custom-chart'),\n join(here, '..', '..', 'custom-chart'),\n join(here, '..', 'src', 'custom-chart'),\n ];\n for (const candidate of candidates) {\n if (existsSync(join(candidate, 'runtime-shim.js')) && existsSync(join(candidate, 'template', 'manifest.json'))) {\n cachedDir = resolve(candidate);\n return cachedDir;\n }\n }\n throw new Error(\n `Custom chart assets not found near ${here}. Expected runtime-shim.js and template/ (run npm run build).`,\n );\n}\n\nexport function readRuntimeShim(): string {\n return readFileSync(join(customChartAssetDir(), 'runtime-shim.js'), 'utf8');\n}\n\nexport function templateDir(): string {\n return join(customChartAssetDir(), 'template');\n}\n\n/** Every file in the template folder, keyed by its file name. */\nexport function readTemplateFiles(): Record<string, string> {\n const dir = templateDir();\n const files: Record<string, string> = {};\n for (const name of readdirSync(dir).sort()) {\n const path = join(dir, name);\n if (statSync(path).isFile()) files[name] = readFileSync(path, 'utf8');\n }\n return files;\n}\n","import { z } from 'zod';\nimport {\n MAX_MANIFEST_BYTES,\n MAX_OPTIONS,\n MAX_SLOTS,\n type CustomChartManifest,\n type Problem,\n} from './types.js';\n\n/**\n * Manifest v2 validator (brief section 3). It is a strict superset of the\n * plugin's sandbox manifest v1: every v1 manifest validates with\n * schemaVersion 1, and schemaVersion 2 unlocks version, description, the\n * color option type, the number step, the option group, slot descriptions and\n * translations. Unknown keys are refused at every level.\n *\n * The checks run in the plugin parser's order, step for step, and the backend\n * (`serverless/express/src/lib/customCharts/manifest.ts`) runs the same steps,\n * so the three validators produce the same problem list for the same\n * manifest. The cross-repo fixtures in tests/fixtures/custom-chart-manifests\n * pin the first code for every invalid case.\n *\n * 1. object, schemaVersion (stop on failure)\n * 2. top-level unknown and missing keys (stop on failure)\n * 3. id, name, version, description\n * 4. slots: shape and count, then each slot (unknown and missing keys\n * first, then the fields), then duplicate ids\n * 5. options: shape and count, then each option (reserved id, type,\n * unknown and missing keys, then the fields), then duplicate ids\n * 6. translations, only when everything above is clean\n * 7. size, only when everything above is clean\n */\n\nconst ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;\nconst FORBIDDEN_IDS = ['constructor', 'prototype', '__proto__'];\nconst LANGUAGE_PATTERN = /^[a-z]{2}(-[A-Z]{2})?$/;\nconst SEMVER_PATTERN =\n /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[0-9A-Za-z.-]+)?(\\+[0-9A-Za-z.-]+)?$/;\nconst COLOR_PATTERN = /^#[0-9a-fA-F]{6}$/;\nconst FIELD_TYPES = ['string', 'number', 'boolean'];\nconst MAX_TEXT = 256;\nconst MAX_DESCRIPTION = 500;\nconst MAX_GROUP = 64;\nconst MAX_CHOICES = 32;\nconst MAX_STRING_OPTION_LENGTH = 1024;\n\nconst V1_TOP_KEYS = ['schemaVersion', 'id', 'name', 'slots', 'options'];\nconst V2_TOP_EXTRA_KEYS = ['version', 'description', 'translations'];\nconst V1_SLOT_KEYS = ['id', 'label', 'role', 'types', 'required'];\nconst V2_SLOT_EXTRA_KEYS = ['description'];\nconst OPTION_BASE_KEYS = ['id', 'label', 'type', 'default'];\nconst OPTION_TYPE_KEYS: Record<string, string[]> = {\n boolean: [],\n number: ['min', 'max'],\n string: ['maxLength'],\n select: ['choices'],\n color: [],\n};\nconst V2_OPTION_EXTRA_KEYS: Record<string, string[]> = {\n boolean: ['group'],\n number: ['group', 'step'],\n string: ['group'],\n select: ['group'],\n color: ['group'],\n};\nconst TRANSLATION_KEYS = ['name', 'description', 'slots', 'options'];\nconst SLOT_TRANSLATION_KEYS = ['label', 'description'];\nconst OPTION_TRANSLATION_KEYS = ['label', 'group', 'choices'];\n\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n Object.getPrototypeOf(value) === Object.prototype;\n\nexport const isId = (value: unknown): value is string =>\n typeof value === 'string' && ID_PATTERN.test(value) && !FORBIDDEN_IDS.includes(value);\n\nconst isText = (value: unknown, max = MAX_TEXT): value is string =>\n typeof value === 'string' && value.length > 0 && value.length <= max;\n\n/** A translated string may be empty: the plugin renders it as given. */\nconst isTranslationText = (value: unknown): value is string =>\n typeof value === 'string' && value.length <= MAX_TEXT;\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value);\n\nexport const jsonBytes = (value: unknown): number =>\n Buffer.byteLength(JSON.stringify(value), 'utf8');\n\nclass Collector {\n readonly problems: Problem[] = [];\n\n add(code: string, path: string, message: string): void {\n this.problems.push({ code, path, message });\n }\n\n /**\n * Unknown keys report `<scope>.unknownKey`, one per key; a missing required\n * key reports `missingCode(key)`. Returns false when anything was reported,\n * so the caller can stop looking at a shape it cannot trust.\n */\n checkKeys(\n value: Record<string, unknown>,\n path: string,\n scope: string,\n required: readonly string[],\n optional: readonly string[],\n missingCode: (key: string) => string = (key) => `${scope}.${key}`,\n ): boolean {\n const allowed = new Set([...required, ...optional]);\n let ok = true;\n for (const key of Object.keys(value)) {\n if (!allowed.has(key)) {\n this.add(`${scope}.unknownKey`, `${path}/${key}`, `Remove \"${key}\". It is not a known key here.`);\n ok = false;\n }\n }\n for (const key of required) {\n if (!Object.hasOwn(value, key)) {\n this.add(missingCode(key), `${path}/${key}`, `Add the required key \"${key}\".`);\n ok = false;\n }\n }\n return ok;\n }\n}\n\nfunction validateSlot(c: Collector, value: unknown, path: string, v2: boolean): void {\n if (!isRecord(value)) {\n c.add('manifest.slot.invalid', path, 'A slot must be an object.');\n return;\n }\n if (!c.checkKeys(value, path, 'manifest.slot', V1_SLOT_KEYS, v2 ? V2_SLOT_EXTRA_KEYS : [])) return;\n if (!isId(value.id)) {\n c.add('manifest.slot.id', `${path}/id`, 'A slot id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters.');\n }\n if (!isText(value.label)) {\n c.add('manifest.slot.label', `${path}/label`, `A slot label is 1 to ${MAX_TEXT} characters.`);\n }\n const role = value.role;\n if (role !== 'dimension' && role !== 'measure') {\n c.add('manifest.slot.role', `${path}/role`, 'A slot role is \"dimension\" or \"measure\".');\n }\n if (typeof value.required !== 'boolean') {\n c.add('manifest.slot.required', `${path}/required`, 'required is true or false.');\n }\n const types = value.types;\n const typesOk =\n Array.isArray(types) &&\n types.length > 0 &&\n types.length <= 3 &&\n new Set(types).size === types.length &&\n types.every((type) => FIELD_TYPES.includes(type as string));\n if (!typesOk) {\n c.add('manifest.slot.types', `${path}/types`, 'types is a list of 1 to 3 distinct values from \"string\", \"number\" and \"boolean\".');\n } else if (role === 'measure' && (types.length !== 1 || (types as string[])[0] !== 'number')) {\n c.add('manifest.slot.types', `${path}/types`, 'A measure slot accepts only [\"number\"].');\n }\n if (Object.hasOwn(value, 'description') && !isText(value.description)) {\n c.add('manifest.slot.description', `${path}/description`, `A slot description is 1 to ${MAX_TEXT} characters.`);\n }\n}\n\nfunction validateOption(c: Collector, value: unknown, path: string, v2: boolean): void {\n if (!isRecord(value)) {\n c.add('manifest.option.invalid', path, 'An option must be an object.');\n return;\n }\n // Reserved before anything else: the author needs to hear \"reserved\" first.\n const id = value.id;\n if (typeof id === 'string' && (id === 'theme' || id.startsWith('theme.'))) {\n c.add('manifest.option.reserved', `${path}/id`, 'The option id \"theme\" (and any id starting with \"theme.\") is reserved for the host theme.');\n return;\n }\n const type = typeof value.type === 'string' ? value.type : '';\n const typeKeys = Object.hasOwn(OPTION_TYPE_KEYS, type) ? OPTION_TYPE_KEYS[type] : undefined;\n if (!typeKeys || (type === 'color' && !v2)) {\n c.add(\n 'manifest.option.type',\n `${path}/type`,\n v2\n ? 'An option type is one of \"boolean\", \"number\", \"string\", \"select\", \"color\".'\n : 'An option type is one of \"boolean\", \"number\", \"string\", \"select\" (color needs schemaVersion 2).',\n );\n return;\n }\n if (!c.checkKeys(value, path, 'manifest.option', [...OPTION_BASE_KEYS, ...typeKeys], v2 ? V2_OPTION_EXTRA_KEYS[type] : [])) return;\n if (!isId(id)) {\n c.add('manifest.option.id', `${path}/id`, 'An option id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters.');\n }\n if (!isText(value.label)) {\n c.add('manifest.option.label', `${path}/label`, `An option label is 1 to ${MAX_TEXT} characters.`);\n }\n if (Object.hasOwn(value, 'group') && !isText(value.group, MAX_GROUP)) {\n c.add('manifest.option.group', `${path}/group`, `An option group is 1 to ${MAX_GROUP} characters.`);\n }\n switch (type) {\n case 'boolean':\n if (typeof value.default !== 'boolean') {\n c.add('manifest.option.default', `${path}/default`, 'A boolean default is true or false.');\n }\n break;\n case 'number': {\n const rangeOk = isFiniteNumber(value.min) && isFiniteNumber(value.max) && value.min <= value.max;\n if (!rangeOk) {\n c.add('manifest.option.range', `${path}/min`, 'Set finite min and max with min <= max.');\n break;\n }\n const min = value.min as number;\n const max = value.max as number;\n if (!isFiniteNumber(value.default) || value.default < min || value.default > max) {\n c.add('manifest.option.default', `${path}/default`, 'A number default is a finite number between min and max.');\n }\n if (Object.hasOwn(value, 'step') && (!isFiniteNumber(value.step) || value.step <= 0 || value.step > max - min)) {\n c.add('manifest.option.step', `${path}/step`, 'step is a positive number no larger than max - min.');\n }\n break;\n }\n case 'string': {\n const maxLength = value.maxLength;\n if (!isFiniteNumber(maxLength) || !Number.isInteger(maxLength) || maxLength < 1 || maxLength > MAX_STRING_OPTION_LENGTH) {\n c.add('manifest.option.maxLength', `${path}/maxLength`, `A string option needs an integer \"maxLength\" from 1 to ${MAX_STRING_OPTION_LENGTH}.`);\n break;\n }\n if (typeof value.default !== 'string' || value.default.length > maxLength) {\n c.add('manifest.option.default', `${path}/default`, 'A string default is a string no longer than maxLength.');\n }\n break;\n }\n case 'select': {\n const choices = value.choices;\n const choicesOk =\n Array.isArray(choices) &&\n choices.length > 0 &&\n choices.length <= MAX_CHOICES &&\n choices.every(\n (choice) => isRecord(choice) && Object.keys(choice).length === 2 && isId(choice.value) && isText(choice.label),\n ) &&\n new Set(choices.map((choice) => choice.value)).size === choices.length;\n if (!choicesOk) {\n c.add('manifest.option.choices', `${path}/choices`, `choices is a list of 1 to ${MAX_CHOICES} distinct { value, label } pairs; a value follows the id rule.`);\n break;\n }\n if (!choices.some((choice) => choice.value === value.default)) {\n c.add('manifest.option.default', `${path}/default`, 'A select default is the value of one of its choices.');\n }\n break;\n }\n case 'color':\n if (typeof value.default !== 'string' || !COLOR_PATTERN.test(value.default)) {\n c.add('manifest.option.color', `${path}/default`, 'A color default is a 6 digit hex colour like \"#3366cc\".');\n }\n break;\n default:\n break;\n }\n}\n\n/** Runs only on a manifest whose slots and options are already valid. */\nfunction validateTranslations(\n c: Collector,\n value: unknown,\n slots: { id: string }[],\n options: { id: string; type: string; choices?: { value: string }[] }[],\n): void {\n const path = '/translations';\n if (!isRecord(value)) {\n c.add('manifest.translations.invalid', path, 'translations is an object keyed by language code.');\n return;\n }\n for (const [language, entry] of Object.entries(value)) {\n const langPath = `${path}/${language}`;\n if (!LANGUAGE_PATTERN.test(language)) {\n c.add('manifest.translations.language', langPath, `\"${language}\" is not a language code like \"fr\" or \"pt-BR\".`);\n continue;\n }\n if (!isRecord(entry)) {\n c.add('manifest.translations.invalid', langPath, 'A translation is an object with optional name, description, slots and options.');\n continue;\n }\n if (!c.checkKeys(entry, langPath, 'manifest.translations', [], TRANSLATION_KEYS)) continue;\n for (const key of ['name', 'description'] as const) {\n if (Object.hasOwn(entry, key) && !isTranslationText(entry[key])) {\n c.add('manifest.translations.text', `${langPath}/${key}`, `A translated string is at most ${MAX_TEXT} characters.`);\n }\n }\n if (Object.hasOwn(entry, 'slots')) {\n const entrySlots = entry.slots;\n if (!isRecord(entrySlots)) {\n c.add('manifest.translations.invalid', `${langPath}/slots`, 'slots is an object keyed by slot id.');\n } else {\n for (const [slotId, slotEntry] of Object.entries(entrySlots)) {\n const slotPath = `${langPath}/slots/${slotId}`;\n if (!slots.some((slot) => slot.id === slotId)) {\n c.add('manifest.translations.slot', slotPath, `\"${slotId}\" is not a slot id in this manifest.`);\n continue;\n }\n if (!isRecord(slotEntry)) {\n c.add('manifest.translations.invalid', slotPath, 'A slot translation is an object with optional label and description.');\n continue;\n }\n if (!c.checkKeys(slotEntry, slotPath, 'manifest.translations', [], SLOT_TRANSLATION_KEYS)) continue;\n for (const key of ['label', 'description'] as const) {\n if (Object.hasOwn(slotEntry, key) && !isTranslationText(slotEntry[key])) {\n c.add('manifest.translations.text', `${slotPath}/${key}`, `A translated string is at most ${MAX_TEXT} characters.`);\n }\n }\n }\n }\n }\n if (Object.hasOwn(entry, 'options')) {\n const entryOptions = entry.options;\n if (!isRecord(entryOptions)) {\n c.add('manifest.translations.invalid', `${langPath}/options`, 'options is an object keyed by option id.');\n } else {\n for (const [optionId, optionEntry] of Object.entries(entryOptions)) {\n const optionPath = `${langPath}/options/${optionId}`;\n const option = options.find((item) => item.id === optionId);\n if (!option) {\n c.add('manifest.translations.option', optionPath, `\"${optionId}\" is not an option id in this manifest.`);\n continue;\n }\n if (!isRecord(optionEntry)) {\n c.add('manifest.translations.invalid', optionPath, 'An option translation is an object with optional label, group and choices.');\n continue;\n }\n if (!c.checkKeys(optionEntry, optionPath, 'manifest.translations', [], OPTION_TRANSLATION_KEYS)) continue;\n for (const key of ['label', 'group'] as const) {\n if (Object.hasOwn(optionEntry, key) && !isTranslationText(optionEntry[key])) {\n c.add('manifest.translations.text', `${optionPath}/${key}`, `A translated string is at most ${MAX_TEXT} characters.`);\n }\n }\n if (!Object.hasOwn(optionEntry, 'choices')) continue;\n const choices = optionEntry.choices;\n if (!isRecord(choices)) {\n c.add('manifest.translations.invalid', `${optionPath}/choices`, 'choices is an object keyed by choice value.');\n continue;\n }\n for (const [choiceValue, label] of Object.entries(choices)) {\n const choicePath = `${optionPath}/choices/${choiceValue}`;\n if (option.type !== 'select' || !(option.choices ?? []).some((choice) => choice.value === choiceValue)) {\n c.add('manifest.translations.choice', choicePath, `\"${choiceValue}\" is not a choice of option \"${optionId}\".`);\n } else if (!isTranslationText(label)) {\n c.add('manifest.translations.text', choicePath, `A translated string is at most ${MAX_TEXT} characters.`);\n }\n }\n }\n }\n }\n }\n}\n\n/**\n * Validate a manifest value. Returns every problem found, first problem first.\n * An empty array means the manifest is valid.\n */\nexport function validateManifest(value: unknown): Problem[] {\n const c = new Collector();\n if (!isRecord(value)) {\n c.add('manifest.invalid', '', 'manifest.json must be a JSON object.');\n return c.problems;\n }\n\n // 1. schemaVersion\n const schemaVersion = value.schemaVersion;\n if (schemaVersion !== 1 && schemaVersion !== 2) {\n c.add('manifest.schemaVersion.unsupported', '/schemaVersion', 'schemaVersion must be 1 or 2.');\n return c.problems;\n }\n const v2 = schemaVersion === 2;\n\n // 2. unknown and missing top-level keys\n if (!c.checkKeys(value, '', 'manifest', V1_TOP_KEYS, v2 ? V2_TOP_EXTRA_KEYS : [], (key) => `manifest.${key}.required`)) {\n return c.problems;\n }\n\n // 3. id, name and the v2 labels (on v1 the keys were already refused above)\n if (!isId(value.id)) {\n c.add('manifest.id.invalid', '/id', 'id is lowercase, starts with a letter, and uses only a-z, 0-9, _ and -, up to 64 characters. It becomes the chart type \"custom:<id>\".');\n }\n if (!isText(value.name)) {\n c.add('manifest.name.invalid', '/name', `name is 1 to ${MAX_TEXT} characters.`);\n }\n if (Object.hasOwn(value, 'version') && (typeof value.version !== 'string' || !SEMVER_PATTERN.test(value.version))) {\n c.add('manifest.version.invalid', '/version', 'version is a semver string like \"1.2.0\".');\n }\n if (Object.hasOwn(value, 'description') && !isText(value.description, MAX_DESCRIPTION)) {\n c.add('manifest.description.invalid', '/description', `description is 1 to ${MAX_DESCRIPTION} characters.`);\n }\n\n // 4. slots\n const slots = value.slots;\n if (!Array.isArray(slots)) {\n c.add('manifest.slots.invalid', '/slots', 'slots must be an array.');\n } else if (slots.length === 0) {\n c.add('manifest.slots.empty', '/slots', 'Declare at least one slot.');\n } else if (slots.length > MAX_SLOTS) {\n c.add('manifest.slots.tooMany', '/slots', `Declare at most ${MAX_SLOTS} slots.`);\n } else {\n slots.forEach((slot, index) => validateSlot(c, slot, `/slots/${index}`, v2));\n const seen = new Set<string>();\n slots.forEach((slot, index) => {\n if (!isRecord(slot) || typeof slot.id !== 'string') return;\n if (seen.has(slot.id)) {\n c.add('manifest.slot.duplicate', `/slots/${index}/id`, `Slot id \"${slot.id}\" is used more than once.`);\n }\n seen.add(slot.id);\n });\n }\n\n // 5. options\n const options = value.options;\n if (!Array.isArray(options)) {\n c.add('manifest.options.invalid', '/options', 'options must be an array (it may be empty).');\n } else if (options.length > MAX_OPTIONS) {\n c.add('manifest.options.tooMany', '/options', `Declare at most ${MAX_OPTIONS} options.`);\n } else {\n options.forEach((option, index) => validateOption(c, option, `/options/${index}`, v2));\n const seen = new Set<string>();\n options.forEach((option, index) => {\n if (!isRecord(option) || typeof option.id !== 'string') return;\n if (seen.has(option.id)) {\n c.add('manifest.option.duplicate', `/options/${index}/id`, `Option id \"${option.id}\" is used more than once.`);\n }\n seen.add(option.id);\n });\n }\n\n // 6. translations, only on an otherwise clean manifest (v2 only; on v1 the key is unknown)\n if (c.problems.length === 0 && Object.hasOwn(value, 'translations')) {\n validateTranslations(\n c,\n value.translations,\n slots as { id: string }[],\n options as { id: string; type: string; choices?: { value: string }[] }[],\n );\n }\n\n // 7. size, only on an otherwise clean manifest\n if (c.problems.length === 0 && jsonBytes(value) > MAX_MANIFEST_BYTES) {\n c.add('manifest.size', '', `manifest.json must be at most ${MAX_MANIFEST_BYTES} bytes as JSON.`);\n }\n\n return c.problems;\n}\n\n/** Parse a manifest; returns null when validateManifest reports anything. */\nexport function parseManifest(value: unknown): CustomChartManifest | null {\n return validateManifest(value).length === 0 ? (value as CustomChartManifest) : null;\n}\n\n/** Zod view of manifest v2 for tool input schemas. Shape only: validateManifest owns the rules. */\nconst slotSchema = z.object({\n id: z.string(),\n label: z.string(),\n role: z.enum(['dimension', 'measure']),\n types: z.array(z.enum(['string', 'number', 'boolean'])),\n required: z.boolean(),\n description: z.string().optional(),\n});\n\nconst optionSchema = z.object({\n id: z.string(),\n label: z.string(),\n type: z.enum(['boolean', 'number', 'string', 'select', 'color']),\n default: z.union([z.string(), z.number(), z.boolean()]),\n min: z.number().optional(),\n max: z.number().optional(),\n step: z.number().optional(),\n maxLength: z.number().optional(),\n choices: z.array(z.object({ value: z.string(), label: z.string() })).optional(),\n group: z.string().optional(),\n});\n\nexport const manifestSchema = z.object({\n schemaVersion: z.union([z.literal(1), z.literal(2)]),\n id: z.string(),\n name: z.string(),\n version: z.string().optional(),\n description: z.string().optional(),\n slots: z.array(slotSchema),\n options: z.array(optionSchema),\n translations: z.record(z.unknown()).optional(),\n});\n\nexport type ManifestSlotInput = z.infer<typeof slotSchema>;\nexport type ManifestOptionInput = z.infer<typeof optionSchema>;\nexport { slotSchema as manifestSlotSchema, optionSchema as manifestOptionSchema };\n\n/** Deep key sort used by the content hash (brief section 5). Arrays keep their order. */\nexport function sortKeys<T>(value: T): T {\n if (Array.isArray(value)) return value.map((item) => sortKeys(item)) as T;\n if (isRecord(value)) {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value).sort()) out[key] = sortKeys(value[key]);\n return out as T;\n }\n return value;\n}\n","/**\n * Shared shapes for the custom chart package pipeline (DAT-925).\n * Problem is the one error shape used by the manifest validator, the bundle\n * builder, the icon check and the backend API (section 5 of the brief).\n */\nexport interface Problem {\n code: string;\n path: string;\n message: string;\n}\n\nexport type CustomChartFieldType = 'string' | 'number' | 'boolean';\n\nexport interface CustomChartSlot {\n id: string;\n label: string;\n role: 'dimension' | 'measure';\n types: CustomChartFieldType[];\n required: boolean;\n description?: string;\n}\n\nexport type CustomChartOption = { id: string; label: string; group?: string } & (\n | { type: 'boolean'; default: boolean }\n | { type: 'number'; default: number; min: number; max: number; step?: number }\n | { type: 'string'; default: string; maxLength: number }\n | { type: 'select'; default: string; choices: { value: string; label: string }[] }\n | { type: 'color'; default: string }\n);\n\nexport interface CustomChartTranslation {\n name?: string;\n description?: string;\n slots?: Record<string, { label?: string; description?: string }>;\n options?: Record<string, { label?: string; group?: string; choices?: Record<string, string> }>;\n}\n\nexport interface CustomChartManifest {\n schemaVersion: 1 | 2;\n id: string;\n name: string;\n version?: string;\n description?: string;\n slots: CustomChartSlot[];\n options: CustomChartOption[];\n translations?: Record<string, CustomChartTranslation>;\n}\n\n/** The four artifacts a build produces and an upload sends. */\nexport interface CustomChartBundle {\n manifest: CustomChartManifest;\n bundleJs: string;\n bundleCss?: string;\n iconSvg: string;\n}\n\nexport const MAX_MANIFEST_BYTES = 32 * 1024;\nexport const MAX_BUNDLE_JS_BYTES = 2 * 1024 * 1024;\nexport const MAX_BUNDLE_CSS_BYTES = 256 * 1024;\nexport const MAX_ICON_BYTES = 16 * 1024;\nexport const MAX_SLOTS = 16;\nexport const MAX_OPTIONS = 32;\n","import { readTemplateFiles } from './assets.js';\nimport { isId, validateManifest } from './manifest.js';\nimport type { CustomChartOption, CustomChartSlot, Problem } from './types.js';\n\n/**\n * A chart project is the template folder with manifest.json and package.json\n * rewritten for the requested key, name, slots and options. Every other file\n * is copied verbatim so that the guide, the types and the sample chart stay\n * byte identical to what the tests exercise.\n */\nexport interface ScaffoldInput {\n key: string;\n name: string;\n description?: string;\n slots?: CustomChartSlot[];\n options?: CustomChartOption[];\n}\n\nexport interface ScaffoldResult {\n ok: boolean;\n problems: Problem[];\n files: Record<string, string>;\n nextStep: string;\n}\n\nconst DEFAULT_SLOTS: CustomChartSlot[] = [\n { id: 'category', label: 'Category', role: 'dimension', types: ['string'], required: true, description: 'One bar per value.' },\n { id: 'value', label: 'Value', role: 'measure', types: ['number'], required: true },\n { id: 'target', label: 'Target', role: 'measure', types: ['number'], required: false },\n];\n\nexport function scaffoldProject(input: ScaffoldInput): ScaffoldResult {\n const template = readTemplateFiles();\n const templateManifest = JSON.parse(template['manifest.json']) as {\n slots: CustomChartSlot[];\n options: CustomChartOption[];\n };\n const problems: Problem[] = [];\n if (!isId(input.key)) {\n problems.push({\n code: 'manifest.id.invalid',\n path: '/id',\n message: 'key must match ^[a-z][a-z0-9_-]{0,63}$ (lowercase letters, digits, _ and -).',\n });\n }\n const name = typeof input.name === 'string' ? input.name.trim() : '';\n if (name.length === 0 || name.length > 256) {\n problems.push({ code: 'manifest.name.invalid', path: '/name', message: 'name must be 1 to 256 characters.' });\n }\n\n const manifest: Record<string, unknown> = {\n schemaVersion: 2,\n id: input.key,\n name,\n version: '1.0.0',\n ...(input.description ? { description: input.description } : {}),\n slots: input.slots && input.slots.length > 0 ? input.slots : templateManifest.slots ?? DEFAULT_SLOTS,\n options: input.options ?? templateManifest.options,\n };\n problems.push(...validateManifest(manifest));\n\n const packageJson = JSON.parse(template['package.json']) as Record<string, unknown>;\n packageJson.name = input.key;\n\n const files: Record<string, string> = {};\n for (const [file, content] of Object.entries(template)) {\n if (file === 'manifest.json') files[file] = JSON.stringify(manifest, null, 2) + '\\n';\n else if (file === 'package.json') files[file] = JSON.stringify(packageJson, null, 2) + '\\n';\n else files[file] = content;\n }\n\n return {\n ok: problems.length === 0,\n problems,\n files,\n nextStep:\n 'Write these files into a folder, edit chart.ts, then run `databrain custom-chart validate` and ' +\n '`databrain custom-chart build`. Upload with `databrain custom-chart upload` or the custom_chart_upload tool.',\n };\n}\n","import { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { join, resolve, sep } from 'node:path';\nimport { build as esbuild, type Plugin } from 'esbuild';\nimport { readRuntimeShim } from './assets.js';\nimport { sanitizeIcon, validateIcon } from './icon.js';\nimport { sortKeys, validateManifest } from './manifest.js';\nimport {\n MAX_BUNDLE_CSS_BYTES,\n MAX_BUNDLE_JS_BYTES,\n type CustomChartBundle,\n type CustomChartManifest,\n type Problem,\n} from './types.js';\nimport { writeBundleZip } from './zip.js';\n\nconst utf8Bytes = (text: string): number => Buffer.byteLength(text, 'utf8');\n\n/**\n * Content hash exactly as the backend computes it (brief section 5 and\n * FINDINGS be-core 2): sha256 hex over\n * JSON.stringify({ manifest, bundleJs, bundleCss, iconSvg }) in that key\n * order, the manifest keys sorted recursively (arrays keep their order) and\n * bundleCss always present, as \"\" when there is none.\n */\nexport function contentHash(bundle: CustomChartBundle): string {\n const payload = JSON.stringify({\n manifest: sortKeys(bundle.manifest),\n bundleJs: bundle.bundleJs,\n bundleCss: bundle.bundleCss ?? '',\n iconSvg: bundle.iconSvg,\n });\n return createHash('sha256').update(payload, 'utf8').digest('hex');\n}\n\n/** Bytes the upload carries: the four artifacts as UTF-8. */\nexport function bundleSizeBytes(bundle: CustomChartBundle): number {\n return (\n utf8Bytes(JSON.stringify(bundle.manifest)) +\n utf8Bytes(bundle.bundleJs) +\n (bundle.bundleCss ? utf8Bytes(bundle.bundleCss) : 0) +\n utf8Bytes(bundle.iconSvg)\n );\n}\n\nexport interface ValidateBundleInput {\n manifest: unknown;\n bundleJs?: string;\n bundleCss?: string;\n iconSvg?: string;\n}\n\n/**\n * The client side of the upload validation (the server repeats it). Manifest\n * rules first, then the bundle sizes, then the icon.\n */\nexport function validateBundle(input: ValidateBundleInput): Problem[] {\n const problems = validateManifest(input.manifest);\n if (input.bundleJs !== undefined) {\n if (typeof input.bundleJs !== 'string') {\n problems.push({ code: 'bundle.js.type', path: '/bundleJs', message: 'bundleJs must be a string.' });\n } else if (input.bundleJs.trim().length === 0) {\n problems.push({ code: 'bundle.js.empty', path: '/bundleJs', message: 'bundleJs must contain JavaScript.' });\n } else if (utf8Bytes(input.bundleJs) > MAX_BUNDLE_JS_BYTES) {\n problems.push({\n code: 'bundle.js.size',\n path: '/bundleJs',\n message: `bundleJs must be at most ${MAX_BUNDLE_JS_BYTES} bytes (2 MiB) as UTF-8. Drop or shrink a dependency.`,\n });\n }\n }\n if (input.bundleCss !== undefined) {\n if (typeof input.bundleCss !== 'string') {\n problems.push({ code: 'bundle.css.type', path: '/bundleCss', message: 'bundleCss must be a string when present.' });\n } else if (utf8Bytes(input.bundleCss) > MAX_BUNDLE_CSS_BYTES) {\n problems.push({\n code: 'bundle.css.size',\n path: '/bundleCss',\n message: `bundleCss must be at most ${MAX_BUNDLE_CSS_BYTES} bytes (256 KiB).`,\n });\n }\n }\n if (input.iconSvg !== undefined) problems.push(...validateIcon(input.iconSvg, '/iconSvg'));\n return problems;\n}\n\nexport interface ChartProject {\n dir: string;\n manifest: unknown;\n manifestText?: string;\n chartEntry?: string;\n css?: string;\n icon?: string;\n sampleData?: unknown;\n problems: Problem[];\n}\n\n/** Read a chart project folder. Missing or unparsable files become problems. */\nexport function readProject(dirInput: string): ChartProject {\n const dir = resolve(dirInput);\n const problems: Problem[] = [];\n const project: ChartProject = { dir, manifest: undefined, problems };\n\n const manifestPath = join(dir, 'manifest.json');\n if (!existsSync(manifestPath)) {\n problems.push({ code: 'project.manifest.missing', path: '/manifest.json', message: `No manifest.json in ${dir}.` });\n } else {\n project.manifestText = readFileSync(manifestPath, 'utf8');\n try {\n project.manifest = JSON.parse(project.manifestText);\n } catch (err) {\n problems.push({\n code: 'project.manifest.parse',\n path: '/manifest.json',\n message: `manifest.json is not valid JSON: ${(err as Error).message}`,\n });\n }\n }\n\n const entry = ['chart.ts', 'chart.js'].map((name) => join(dir, name)).find((path) => existsSync(path));\n if (!entry) {\n problems.push({ code: 'project.chart.missing', path: '/chart.ts', message: `No chart.ts in ${dir}. It must export render(ctx).` });\n } else {\n project.chartEntry = entry;\n }\n\n const cssPath = join(dir, 'chart.css');\n if (existsSync(cssPath)) project.css = readFileSync(cssPath, 'utf8');\n\n const iconPath = join(dir, 'icon.svg');\n if (!existsSync(iconPath)) {\n problems.push({ code: 'bundle.icon.required', path: '/icon.svg', message: `No icon.svg in ${dir}.` });\n } else {\n project.icon = readFileSync(iconPath, 'utf8');\n }\n\n const samplePath = join(dir, 'sample-data.json');\n if (existsSync(samplePath)) {\n try {\n project.sampleData = JSON.parse(readFileSync(samplePath, 'utf8'));\n } catch (err) {\n problems.push({\n code: 'project.sampleData.parse',\n path: '/sample-data.json',\n message: `sample-data.json is not valid JSON: ${(err as Error).message}`,\n });\n }\n }\n return project;\n}\n\n/** validate = read the folder, check the manifest and the icon. No esbuild. */\nexport function validateProject(dir: string): { ok: boolean; problems: Problem[]; project: ChartProject } {\n const project = readProject(dir);\n const problems = [...project.problems];\n if (project.manifest !== undefined) problems.push(...validateManifest(project.manifest));\n // The build strips the XML declaration and comments before upload, so check that form.\n if (project.icon !== undefined) problems.push(...validateIcon(sanitizeIcon(project.icon)));\n if (project.css !== undefined && utf8Bytes(project.css) > MAX_BUNDLE_CSS_BYTES) {\n problems.push({ code: 'bundle.css.size', path: '/chart.css', message: `chart.css must be at most ${MAX_BUNDLE_CSS_BYTES} bytes.` });\n }\n return { ok: problems.length === 0, problems, project };\n}\n\nexport interface BuildOutputs extends CustomChartBundle {\n manifest: CustomChartManifest;\n contentHash: string;\n sizeBytes: number;\n}\n\nexport interface BuildResult {\n ok: boolean;\n problems: Problem[];\n outputs?: BuildOutputs;\n /** Absolute paths written under dist/, in write order. Empty when write is false. */\n written: string[];\n}\n\nexport const DIST_FILES = ['index.js', 'manifest.json', 'icon.svg', 'index.css', 'bundle.zip'] as const;\n\n/**\n * Every import must resolve to a file inside the chart project. A relative\n * path that climbs out (`../../.config/x.json`) would be inlined into the\n * bundle and uploaded with it; the build refuses it with a problem instead.\n */\nfunction insideProjectOnly(dir: string): Plugin {\n const root = dir.endsWith(sep) ? dir : `${dir}${sep}`;\n return {\n name: 'databrain-inside-project-only',\n setup(build) {\n build.onResolve({ filter: /.*/ }, async (args) => {\n if (args.kind === 'entry-point' || args.pluginData?.databrainChecked) return undefined;\n const resolved = await build.resolve(args.path, {\n kind: args.kind,\n resolveDir: args.resolveDir,\n importer: args.importer,\n pluginData: { databrainChecked: true },\n });\n if (resolved.errors.length > 0) return { errors: resolved.errors };\n if (resolved.external || resolved.path.startsWith(root)) return resolved;\n return {\n errors: [\n {\n text: `\"${args.path}\" is outside the chart project. Only files inside ${dir} can be imported.`,\n },\n ],\n };\n });\n },\n };\n}\n\n/**\n * Compile chart.ts with esbuild to one IIFE, prepend the runtime shim and\n * the slot id list the shim reads, enforce the 2 MiB gate and write dist/.\n * See brief section 4 and contract/runtime-shim.js.\n */\nexport async function buildProject(dir: string, options: { write?: boolean } = {}): Promise<BuildResult> {\n const validated = validateProject(dir);\n const { project } = validated;\n const problems = [...validated.problems];\n if (!project.chartEntry || project.manifest === undefined || problems.length > 0) {\n return { ok: false, problems, written: [] };\n }\n const manifest = project.manifest as CustomChartManifest;\n\n // Bundled for the browser with no externals: every import is resolved\n // inside the project or it is a build error (a node builtin like \"fs\" does\n // not resolve on the browser platform), so dist/index.js never carries a\n // dangling require. The entry must be an ES module that exports render():\n // the shim reads globalThis.__dbnCustomChart.render, and a CommonJS\n // module.exports or a missing export would only fail later, as a blank tile.\n const common = {\n plugins: [insideProjectOnly(project.dir)],\n entryPoints: [project.chartEntry],\n absWorkingDir: project.dir,\n bundle: true as const,\n target: 'es2020',\n platform: 'browser' as const,\n write: false as const,\n legalComments: 'none' as const,\n logLevel: 'silent' as const,\n charset: 'utf8' as const,\n };\n let moduleJs: string;\n try {\n const shape = await esbuild({ ...common, format: 'esm', metafile: true });\n const exported = Object.values(shape.metafile.outputs)[0]?.exports ?? [];\n if (!exported.includes('render')) {\n problems.push({\n code: 'bundle.build.noRender',\n path: '/chart.ts',\n message:\n 'chart.ts must be an ES module that exports a function named render: `export function render(ctx) { ... }`. module.exports and default exports are not read.',\n });\n return { ok: false, problems, written: [] };\n }\n const result = await esbuild({\n ...common,\n format: 'iife',\n globalName: '__dbnCustomChart',\n minify: true,\n });\n moduleJs = result.outputFiles.map((file) => file.text).join('\\n');\n } catch (err) {\n const errors = (err as { errors?: { text: string; location?: { file?: string; line?: number } | null }[] }).errors;\n if (Array.isArray(errors) && errors.length > 0) {\n for (const error of errors) {\n const where = error.location ? `${error.location.file ?? 'chart.ts'}:${error.location.line ?? 0}` : 'chart.ts';\n problems.push({ code: 'bundle.build.failed', path: `/${where}`, message: error.text });\n }\n } else {\n problems.push({ code: 'bundle.build.failed', path: '/chart.ts', message: (err as Error).message });\n }\n return { ok: false, problems, written: [] };\n }\n\n // Shim first (byte identical to contract/runtime-shim.js), then the slot ids\n // so every manifest slot is present on ctx.slots, then the chart module.\n // chart.css is NOT inlined: the host injects bundleCss into the frame\n // (brief section 6.5); dist/index.css carries it for provenance and upload.\n const shim = readRuntimeShim();\n const slotIds = manifest.slots.map((slot) => slot.id);\n const globals = `globalThis.__dbnCustomChartSlots=${JSON.stringify(slotIds)};\\n`;\n const bundleJs = `${shim.trimEnd()}\\n${globals}${moduleJs.trimEnd()}\\nglobalThis.__dbnCustomChart=__dbnCustomChart;\\n`;\n const iconSvg = sanitizeIcon(project.icon as string);\n const bundleCss = project.css;\n\n problems.push(...validateBundle({ manifest, bundleJs, bundleCss, iconSvg }));\n if (problems.length > 0) return { ok: false, problems, written: [] };\n\n const bundle: CustomChartBundle = { manifest, bundleJs, bundleCss, iconSvg };\n const outputs: BuildOutputs = {\n ...bundle,\n contentHash: contentHash(bundle),\n sizeBytes: bundleSizeBytes(bundle),\n };\n\n const written: string[] = [];\n if (options.write !== false) {\n const dist = join(project.dir, 'dist');\n mkdirSync(dist, { recursive: true });\n const manifestJson = JSON.stringify(manifest, null, 2) + '\\n';\n const write = (name: string, content: string | Uint8Array) => {\n const path = join(dist, name);\n writeFileSync(path, content);\n written.push(path);\n };\n write('index.js', bundleJs);\n write('manifest.json', manifestJson);\n write('icon.svg', iconSvg);\n if (bundleCss !== undefined) write('index.css', bundleCss);\n write(\n 'bundle.zip',\n writeBundleZip({\n 'index.js': bundleJs,\n 'manifest.json': manifestJson,\n 'icon.svg': iconSvg,\n ...(bundleCss !== undefined ? { 'index.css': bundleCss } : {}),\n }),\n );\n }\n\n return { ok: true, problems: [], outputs, written };\n}\n\n/** Read a previous build from dist/ (for upload without rebuilding). */\nexport function readDist(dir: string): { bundle?: CustomChartBundle; problems: Problem[] } {\n const dist = join(resolve(dir), 'dist');\n const problems: Problem[] = [];\n const need = ['index.js', 'manifest.json', 'icon.svg'];\n for (const name of need) {\n if (!existsSync(join(dist, name))) {\n problems.push({ code: 'project.dist.missing', path: `/dist/${name}`, message: `dist/${name} is missing. Run databrain custom-chart build first.` });\n }\n }\n if (problems.length > 0) return { problems };\n let manifest: unknown;\n try {\n manifest = JSON.parse(readFileSync(join(dist, 'manifest.json'), 'utf8'));\n } catch (err) {\n return { problems: [{ code: 'project.dist.parse', path: '/dist/manifest.json', message: (err as Error).message }] };\n }\n const manifestProblems = validateManifest(manifest);\n if (manifestProblems.length > 0) return { problems: manifestProblems };\n const cssPath = join(dist, 'index.css');\n return {\n problems,\n bundle: {\n manifest: manifest as CustomChartManifest,\n bundleJs: readFileSync(join(dist, 'index.js'), 'utf8'),\n bundleCss: existsSync(cssPath) ? readFileSync(cssPath, 'utf8') : undefined,\n iconSvg: readFileSync(join(dist, 'icon.svg'), 'utf8'),\n },\n };\n}\n","/**\n * Allow-list SVG sanitiser for icon.svg. A verbatim copy of the backend's\n * `serverless/express/src/utils/helpers/sanitizeSvg.ts` (the one that gates\n * the upload), so the CLI and the validate tool refuse exactly what the\n * server refuses instead of guessing with regexes. Keep the two files in\n * step: a rule added on one side and not the other means \"validate says ok,\n * upload says no\".\n *\n * WHAT IT DOES\n * ------------\n * - Drops the DOCTYPE (entity expansion) and every processing instruction\n * except the XML declaration; drops CDATA and comments.\n * - Keeps only elements on ALLOWED_ELEMENTS. A disallowed element and\n * everything inside it is removed (`<script>`, `<foreignObject>`,\n * `<iframe>`, `<animate>` ... ).\n * - Keeps only attributes on ALLOWED_ATTRIBUTES (plus `data-*` and the\n * `xmlns*` declarations). Every `on*` attribute goes. Any attribute whose\n * value carries `javascript:`, `data:text/html`, or `vbscript:` after\n * entity decoding goes.\n * - `href` / `xlink:href` survive only as a same-document `#id` reference,\n * or, on `<image>` alone, as a `data:image/(png|jpeg|gif|webp);base64`\n * URI. An external `<image href>` is removed with the element.\n * - `<style>` and `style=\"\"` survive only when they contain no `url(`,\n * `@import`, `expression(` or `-moz-binding`. A style that reaches out\n * of the file is dropped entirely rather than partially rewritten.\n *\n * Returns the cleaned markup and a count of what was removed, so the caller\n * can refuse the icon rather than silently rewriting it.\n */\nconst ALLOWED_ELEMENTS: ReadonlySet<string> = new Set([\n 'svg',\n 'g',\n 'defs',\n 'symbol',\n 'use',\n 'title',\n 'desc',\n 'metadata',\n 'path',\n 'rect',\n 'circle',\n 'ellipse',\n 'line',\n 'polyline',\n 'polygon',\n 'text',\n 'tspan',\n 'textpath',\n 'image',\n 'lineargradient',\n 'radialgradient',\n 'stop',\n 'clippath',\n 'mask',\n 'pattern',\n 'marker',\n 'filter',\n 'fegaussianblur',\n 'feoffset',\n 'feblend',\n 'fecolormatrix',\n 'fecomposite',\n 'feflood',\n 'femerge',\n 'femergenode',\n 'fedropshadow',\n 'style',\n 'switch',\n]);\n\nconst ALLOWED_ATTRIBUTES: ReadonlySet<string> = new Set([\n 'id',\n 'class',\n 'style',\n 'href',\n 'xlink:href',\n 'xml:space',\n 'lang',\n 'xml:lang',\n 'version',\n 'baseprofile',\n 'viewbox',\n 'preserveaspectratio',\n 'width',\n 'height',\n 'x',\n 'y',\n 'x1',\n 'y1',\n 'x2',\n 'y2',\n 'cx',\n 'cy',\n 'r',\n 'rx',\n 'ry',\n 'fx',\n 'fy',\n 'fr',\n 'd',\n 'points',\n 'transform',\n 'transform-origin',\n 'fill',\n 'fill-opacity',\n 'fill-rule',\n 'stroke',\n 'stroke-width',\n 'stroke-linecap',\n 'stroke-linejoin',\n 'stroke-miterlimit',\n 'stroke-dasharray',\n 'stroke-dashoffset',\n 'stroke-opacity',\n 'opacity',\n 'color',\n 'display',\n 'visibility',\n 'overflow',\n 'clip-path',\n 'clip-rule',\n 'clippathunits',\n 'mask',\n 'maskunits',\n 'maskcontentunits',\n 'filter',\n 'filterunits',\n 'primitiveunits',\n 'in',\n 'in2',\n 'result',\n 'mode',\n 'operator',\n 'values',\n 'type',\n 'stddeviation',\n 'dx',\n 'dy',\n 'flood-color',\n 'flood-opacity',\n 'offset',\n 'stop-color',\n 'stop-opacity',\n 'gradientunits',\n 'gradienttransform',\n 'spreadmethod',\n 'patternunits',\n 'patterncontentunits',\n 'patterntransform',\n 'markerunits',\n 'markerwidth',\n 'markerheight',\n 'refx',\n 'refy',\n 'orient',\n 'font-family',\n 'font-size',\n 'font-weight',\n 'font-style',\n 'letter-spacing',\n 'text-anchor',\n 'dominant-baseline',\n 'alignment-baseline',\n 'text-decoration',\n 'word-spacing',\n 'paint-order',\n 'vector-effect',\n 'shape-rendering',\n 'text-rendering',\n 'image-rendering',\n 'color-interpolation',\n 'color-interpolation-filters',\n 'enable-background',\n 'startoffset',\n 'method',\n 'spacing',\n 'requiredfeatures',\n 'systemlanguage',\n 'role',\n 'aria-label',\n 'aria-labelledby',\n 'aria-hidden',\n 'focusable',\n 'tabindex',\n]);\n\n/** Pattern over a tag's raw text: name, then attributes, then optional `/`. */\nconst TAG_RE = /^<\\s*(\\/?)\\s*([A-Za-z_][\\w:.-]*)([\\s\\S]*?)(\\/?)\\s*>$/;\n\nconst ATTRIBUTE_RE =\n /([^\\s=/\"']+)(?:\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s\"'>]+)))?/g;\n\nconst decodeEntities = (value: string): string =>\n value\n .replace(/&#x([0-9a-fA-F]+);?/g, (_m, hex: string) =>\n String.fromCodePoint(parseInt(hex, 16)),\n )\n .replace(/&#(\\d+);?/g, (_m, dec: string) =>\n String.fromCodePoint(parseInt(dec, 10)),\n )\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&apos;/g, \"'\")\n .replace(/&amp;/g, '&');\n\n/** Entity-decode, drop control and whitespace characters, lower-case. Then a scheme cannot hide as `java\\tscript:`. */\nconst normaliseForSchemeCheck = (value: string): string =>\n decodeEntities(value)\n .replace(/[\\u0000-\\u0020\\u007f-\\u00a0\\u200b-\\u200f\\u2028\\u2029\\ufeff]/g, '')\n .toLowerCase();\n\nconst DANGEROUS_SCHEME_RE =\n /^(javascript|vbscript|livescript|mocha|data:text\\/html|data:application)/;\n\nconst isDangerousValue = (value: string): boolean => {\n const normalised = normaliseForSchemeCheck(value);\n return (\n DANGEROUS_SCHEME_RE.test(normalised) ||\n normalised.includes('javascript:') ||\n normalised.includes('vbscript:')\n );\n};\n\nconst SAFE_DATA_IMAGE_RE =\n /^data:image\\/(png|jpeg|jpg|gif|webp);base64,[a-z0-9+/=]+$/;\n\nconst isAcceptableHref = (value: string, element: string): boolean => {\n const normalised = normaliseForSchemeCheck(value);\n if (normalised.startsWith('#')) return true;\n return element === 'image' && SAFE_DATA_IMAGE_RE.test(normalised);\n};\n\n/**\n * A style that leaves the document is dropped whole. `url(#id)` is a same\n * document paint server reference (`fill: url(#gradient)`) and is the one\n * `url(` a logo legitimately uses, so it is blanked before the test.\n */\nconst LOCAL_URL_RE = /url\\s*\\(\\s*(['\"]?)#[^)'\"]*\\1\\s*\\)/gi;\nconst STYLE_ESCAPE_RE =\n /url\\s*\\(|@import|expression\\s*\\(|-moz-binding|behavior\\s*:/i;\n\nconst isSafeStyle = (value: string): boolean =>\n !STYLE_ESCAPE_RE.test(decodeEntities(value).replace(LOCAL_URL_RE, ''));\n\nexport type SanitizeSvgResult = {\n svg: string;\n /** Elements, attributes and declarations that were taken out. */\n removed: number;\n /** True when the input did not even look like an SVG document. */\n isSvg: boolean;\n};\n\ntype Token =\n | { kind: 'text'; raw: string }\n | { kind: 'tag'; raw: string }\n | { kind: 'special'; raw: string };\n\n/**\n * Split markup into text, tags, and the specials (comments, CDATA, DOCTYPE,\n * processing instructions) that need their own terminators.\n */\nconst tokenize = (input: string): Token[] => {\n const tokens: Token[] = [];\n let index = 0;\n const length = input.length;\n\n const pushSpecial = (end: number, terminator: string): void => {\n const close = input.indexOf(terminator, end);\n const stop = close === -1 ? length : close + terminator.length;\n tokens.push({ kind: 'special', raw: input.slice(index, stop) });\n index = stop;\n };\n\n while (index < length) {\n const open = input.indexOf('<', index);\n if (open === -1) {\n tokens.push({ kind: 'text', raw: input.slice(index) });\n break;\n }\n if (open > index) {\n tokens.push({ kind: 'text', raw: input.slice(index, open) });\n index = open;\n }\n if (input.startsWith('<!--', index)) {\n pushSpecial(index + 4, '-->');\n continue;\n }\n if (input.startsWith('<![CDATA[', index)) {\n pushSpecial(index + 9, ']]>');\n continue;\n }\n if (input.startsWith('<?', index)) {\n pushSpecial(index + 2, '?>');\n continue;\n }\n if (input.startsWith('<!', index)) {\n // DOCTYPE may carry an internal subset in [ ... ]; find the real end.\n const bracket = input.indexOf('[', index);\n const plainEnd = input.indexOf('>', index);\n if (bracket !== -1 && (plainEnd === -1 || bracket < plainEnd)) {\n const subsetEnd = input.indexOf(']', bracket);\n pushSpecial(subsetEnd === -1 ? index + 2 : subsetEnd, '>');\n } else {\n pushSpecial(index + 2, '>');\n }\n continue;\n }\n // Ordinary tag: ends at the first `>` outside a quoted attribute value.\n let cursor = index + 1;\n let quote: string | null = null;\n while (cursor < length) {\n const ch = input[cursor];\n if (quote) {\n if (ch === quote) quote = null;\n } else if (ch === '\"' || ch === \"'\") {\n quote = ch;\n } else if (ch === '>') {\n break;\n }\n cursor += 1;\n }\n const stop = cursor >= length ? length : cursor + 1;\n tokens.push({ kind: 'tag', raw: input.slice(index, stop) });\n index = stop;\n }\n return tokens;\n};\n\nconst isXmlDeclaration = (raw: string): boolean => /^<\\?xml\\b/i.test(raw);\n\n/**\n * Filter a tag's attributes. Returns the rebuilt attribute string (or `null`\n * when the element itself must go because its only href was hostile) and the\n * number of attributes dropped.\n */\nconst cleanAttributes = (\n element: string,\n rawAttributes: string,\n): { attributes: string; removed: number; dropElement: boolean } => {\n let removed = 0;\n let dropElement = false;\n const kept: string[] = [];\n ATTRIBUTE_RE.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = ATTRIBUTE_RE.exec(rawAttributes)) !== null) {\n const [whole, name, dq, sq, bare] = match;\n if (!name || name === '/') continue;\n const lower = name.toLowerCase();\n const value = dq ?? sq ?? bare ?? '';\n\n if (lower.startsWith('on')) {\n removed += 1;\n continue;\n }\n const isNamespaceDeclaration =\n lower === 'xmlns' || lower.startsWith('xmlns:');\n const isData = lower.startsWith('data-');\n if (!isNamespaceDeclaration && !isData && !ALLOWED_ATTRIBUTES.has(lower)) {\n removed += 1;\n continue;\n }\n if (isDangerousValue(value)) {\n removed += 1;\n continue;\n }\n if (lower === 'href' || lower === 'xlink:href') {\n if (!isAcceptableHref(value, element)) {\n removed += 1;\n // An <image> or <use> whose only reason to exist is an external\n // reference is not a logo any more once that reference is gone.\n if (element === 'image' || element === 'use') dropElement = true;\n continue;\n }\n }\n if (lower === 'style' && !isSafeStyle(value)) {\n removed += 1;\n continue;\n }\n kept.push(whole.trim());\n }\n return {\n attributes: kept.length ? ` ${kept.join(' ')}` : '',\n removed,\n dropElement,\n };\n};\n\n/**\n * Sentinel pushed before a `<style>` open tag so the whole element can be\n * unwound if its content turns out to reach outside the file. NUL bytes cannot\n * occur in well formed XML text, so no real chunk can collide with it, and it\n * is filtered out before the chunks are joined.\n */\nconst STYLE_OPEN_MARKER = '\\u0000style-open\\u0000';\n\nexport const sanitizeSvg = (input: string): SanitizeSvgResult => {\n const tokens = tokenize(input);\n const out: string[] = [];\n let removed = 0;\n let isSvg = false;\n\n // Depth inside a dropped element: while > 0 every token is discarded. The\n // stack tracks which open tags were dropped so the matching close is too.\n let dropDepth = 0;\n const dropStack: string[] = [];\n // Inside <style>: content must be checked as CSS, not copied blind.\n let styleBuffer: string[] | null = null;\n\n for (const token of tokens) {\n if (token.kind === 'special') {\n if (isXmlDeclaration(token.raw) && dropDepth === 0) {\n out.push(token.raw);\n } else {\n removed += 1;\n }\n continue;\n }\n\n if (token.kind === 'text') {\n if (dropDepth > 0) continue;\n if (styleBuffer) {\n styleBuffer.push(token.raw);\n } else {\n out.push(token.raw);\n }\n continue;\n }\n\n const parsed = TAG_RE.exec(token.raw);\n if (!parsed) {\n // Unparseable tag text. Inside a drop it goes anyway; outside, it is\n // not markup we can vouch for, so it goes too.\n removed += 1;\n continue;\n }\n const [, closing, rawName, rawAttributes, selfClosingSlash] = parsed;\n const name = rawName.toLowerCase();\n const isClosing = closing === '/';\n const isSelfClosing = selfClosingSlash === '/';\n\n if (name === 'svg') isSvg = true;\n\n if (dropDepth > 0) {\n if (isClosing) {\n if (dropStack[dropStack.length - 1] === name) {\n dropStack.pop();\n dropDepth -= 1;\n }\n } else if (!isSelfClosing) {\n dropStack.push(name);\n dropDepth += 1;\n }\n continue;\n }\n\n if (isClosing) {\n if (name === 'style' && styleBuffer) {\n const css = styleBuffer.join('');\n styleBuffer = null;\n if (isSafeStyle(css)) {\n out.push(css, token.raw);\n } else {\n // Drop the <style> we already emitted the open tag for.\n const openIndex = out.lastIndexOf(STYLE_OPEN_MARKER);\n if (openIndex !== -1) out.splice(openIndex);\n removed += 1;\n }\n continue;\n }\n if (ALLOWED_ELEMENTS.has(name)) out.push(token.raw);\n continue;\n }\n\n if (!ALLOWED_ELEMENTS.has(name)) {\n removed += 1;\n if (!isSelfClosing) {\n dropStack.push(name);\n dropDepth += 1;\n }\n continue;\n }\n\n const cleaned = cleanAttributes(name, rawAttributes);\n removed += cleaned.removed;\n if (cleaned.dropElement) {\n removed += 1;\n if (!isSelfClosing) {\n dropStack.push(name);\n dropDepth += 1;\n }\n continue;\n }\n\n const rebuilt = `<${rawName}${cleaned.attributes}${\n isSelfClosing ? '/' : ''\n }>`;\n if (name === 'style' && !isSelfClosing) {\n out.push(STYLE_OPEN_MARKER, rebuilt);\n styleBuffer = [];\n continue;\n }\n out.push(rebuilt);\n }\n\n // An unterminated <style> at EOF: keep nothing of it.\n if (styleBuffer) {\n const openIndex = out.lastIndexOf(STYLE_OPEN_MARKER);\n if (openIndex !== -1) out.splice(openIndex);\n removed += 1;\n }\n\n return {\n svg: out.filter((chunk) => chunk !== STYLE_OPEN_MARKER).join(''),\n removed,\n isSvg,\n };\n};\n","import { sanitizeSvg } from './sanitize-svg.js';\nimport { MAX_ICON_BYTES, type Problem } from './types.js';\n\n/**\n * Strict SVG check for icon.svg (brief sections 4 and 5). Nothing is rewritten:\n * an icon either passes every rule or is refused with a problem that says what\n * to remove. The check is the backend's own allow-list sanitiser\n * (`sanitize-svg.ts`, a copy of the server file): the icon is refused when the\n * sanitiser would remove anything, when it carries an `<image>` element, or\n * when it is not one `<svg>` element. The backend runs the same sanitiser on\n * upload and refuses with the same codes: bundle.icon.required,\n * bundle.icon.size, bundle.icon.notSvg, bundle.icon.unsafe. The message names\n * the rule that tripped when one of the common ones did.\n */\nconst UNSAFE = 'bundle.icon.unsafe';\nconst RULES: { pattern: RegExp; message: string }[] = [\n { pattern: /<\\s*script\\b/i, message: 'Remove every <script> element from icon.svg.' },\n { pattern: /<\\s*foreignObject\\b/i, message: 'Remove every <foreignObject> element from icon.svg.' },\n { pattern: /<\\s*image\\b/i, message: 'Remove every <image> element from icon.svg. Draw with paths and shapes.' },\n { pattern: /[\\s/]on[a-z]+\\s*=/i, message: 'Remove every on*= event attribute from icon.svg.' },\n { pattern: /<!--/, message: 'Remove every <!-- comment --> from icon.svg.' },\n { pattern: /<!(?:ENTITY|DOCTYPE)\\b/i, message: 'Remove the DOCTYPE and entity declarations from icon.svg.' },\n { pattern: /<!\\[CDATA\\[/, message: 'Remove every CDATA section from icon.svg.' },\n { pattern: /(?:xlink:)?href\\s*=\\s*[\"']\\s*(?!#)/i, message: 'Every href in icon.svg must start with # (no external or data references).' },\n { pattern: /javascript\\s*:/i, message: 'Remove every javascript: reference from icon.svg.' },\n { pattern: /@import|expression\\s*\\(|-moz-binding/i, message: 'Remove @import, expression() and -moz-binding from icon.svg styles.' },\n { pattern: /url\\s*\\(\\s*['\"]?\\s*(?!#)/i, message: 'Every url() in icon.svg must point at a same-document #id.' },\n];\nconst GENERIC_UNSAFE =\n 'icon.svg must use only plain SVG drawing elements and attributes: no script, event handlers, foreignObject, image, animation, external hrefs, url() styles, comments, DOCTYPE or unknown attributes.';\n\nconst XML_DECLARATION = /^\\s*<\\?xml[^>]*\\?>\\s*/i;\n\n/** One `<svg ...>...</svg>` (or `<svg .../>`) and nothing else, after the XML declaration. */\nexport function isSingleSvgRoot(svg: string): boolean {\n const body = svg.replace(/^/, '').replace(XML_DECLARATION, '').trim();\n return /^<svg[\\s>][\\s\\S]*<\\/svg>$/i.test(body) || /^<svg\\b[^>]*\\/>$/i.test(body);\n}\n\nexport function validateIcon(svg: unknown, path = '/icon.svg'): Problem[] {\n if (typeof svg !== 'string' || svg.trim().length === 0) {\n return [{ code: 'bundle.icon.required', path, message: 'icon.svg is required and must not be empty.' }];\n }\n if (Buffer.byteLength(svg, 'utf8') > MAX_ICON_BYTES) {\n return [{ code: 'bundle.icon.size', path, message: `icon.svg must be at most ${MAX_ICON_BYTES} bytes.` }];\n }\n const cleaned = sanitizeSvg(svg);\n if (!cleaned.isSvg || !isSingleSvgRoot(cleaned.svg)) {\n return [{ code: 'bundle.icon.notSvg', path, message: 'icon.svg must be one <svg> element with nothing after it.' }];\n }\n if (cleaned.removed > 0 || /<\\s*image\\b/i.test(svg)) {\n const rule = RULES.find((candidate) => candidate.pattern.test(svg));\n return [{ code: UNSAFE, path, message: rule ? rule.message : GENERIC_UNSAFE }];\n }\n return [];\n}\n\n/**\n * The form the CLI writes to dist/icon.svg and uploads: BOM, XML declaration\n * and comments removed, trimmed. The server refuses a comment rather than\n * stripping it, so the build strips them here; everything else is left as the\n * author wrote it and must already pass validateIcon.\n */\nexport function sanitizeIcon(svg: string): string {\n return svg\n .replace(/^/, '')\n .replace(XML_DECLARATION, '')\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .trim();\n}\n","import { strFromU8, strToU8, unzipSync, zipSync } from 'fflate';\n\n/**\n * dist/bundle.zip is for humans (drag and drop upload in the app). The API\n * takes JSON. The zip holds exactly the four build outputs at its root.\n */\nexport const BUNDLE_ZIP_FILES = ['index.js', 'manifest.json', 'icon.svg', 'index.css'] as const;\nexport type BundleZipFile = (typeof BUNDLE_ZIP_FILES)[number];\n\nexport type BundleZipInput = Partial<Record<BundleZipFile, string>> &\n Pick<Record<BundleZipFile, string>, 'index.js' | 'manifest.json' | 'icon.svg'>;\n\nexport function writeBundleZip(files: BundleZipInput): Uint8Array {\n const entries: Record<string, Uint8Array> = {};\n for (const name of BUNDLE_ZIP_FILES) {\n const content = files[name];\n if (typeof content === 'string') entries[name] = strToU8(content);\n }\n // Fixed mtime keeps the zip bytes stable for the same inputs.\n return zipSync(entries, { level: 6, mtime: new Date('2000-01-01T00:00:00Z') });\n}\n\nexport function readBundleZip(bytes: Uint8Array): Partial<Record<BundleZipFile, string>> {\n const unzipped = unzipSync(bytes, {\n filter: (file) => (BUNDLE_ZIP_FILES as readonly string[]).includes(file.name),\n });\n const files: Partial<Record<BundleZipFile, string>> = {};\n for (const name of BUNDLE_ZIP_FILES) {\n if (unzipped[name]) files[name] = strFromU8(unzipped[name]);\n }\n return files;\n}\n","import type { DatabrainClient } from '../client/databrain-client.js';\nimport type { CustomChartBundle, Problem } from '../custom-chart/types.js';\n\n/**\n * Service-token custom chart API (brief section 5), mounted by the backend\n * under /api/mcp/custom-charts. Company scope comes from the token; nothing\n * here sends a company or tenant selector.\n */\nexport const CUSTOM_CHARTS_PATH = '/api/mcp/custom-charts';\nconst LIST_CACHE_KEY = 'list_custom_charts';\n\nexport interface CustomChartVersionSummary {\n id: string;\n versionNumber: number;\n contentHash: string;\n sizeBytes?: number;\n createdAt?: string;\n createdBy?: string | null;\n [key: string]: unknown;\n}\n\nexport interface CustomChartSummary {\n id: string;\n key: string;\n name: string;\n description?: string | null;\n status: 'private' | 'published' | string;\n chartType?: string;\n publishedVersionId?: string | null;\n previewVersionId?: string | null;\n publishedVersionNumber?: number | null;\n previewVersionNumber?: number | null;\n versionsCount?: number;\n iconSvg?: string;\n createdBy?: string | null;\n createdAt?: string;\n updatedAt?: string;\n [key: string]: unknown;\n}\n\nexport interface CustomChartDetail extends CustomChartSummary {\n versions?: CustomChartVersionSummary[];\n}\n\nexport interface ValidateResponse {\n ok: boolean;\n problems: Problem[];\n contentHash?: string;\n sizeBytes?: number;\n}\n\nexport interface UploadResponse {\n chart: CustomChartSummary;\n version: CustomChartVersionSummary;\n created: 'chart' | 'version' | 'none';\n}\n\nexport interface UploadBody extends CustomChartBundle {\n name?: string;\n description?: string;\n}\n\nconst encodeKey = (key: string): string => encodeURIComponent(key);\n\nexport class CustomChartsApi {\n constructor(private readonly client: DatabrainClient) {}\n\n async list(): Promise<CustomChartSummary[]> {\n const response = await this.client.request<{ charts?: CustomChartSummary[] } | CustomChartSummary[]>({\n method: 'GET',\n path: CUSTOM_CHARTS_PATH,\n auth: 'service',\n cacheKey: LIST_CACHE_KEY,\n cacheTtlMs: 30_000,\n });\n if (Array.isArray(response)) return response;\n return Array.isArray(response?.charts) ? response.charts : [];\n }\n\n async get(key: string): Promise<CustomChartDetail> {\n const response = await this.client.request<{ chart?: CustomChartDetail } | CustomChartDetail>({\n method: 'GET',\n path: `${CUSTOM_CHARTS_PATH}/${encodeKey(key)}`,\n auth: 'service',\n });\n const wrapped = response as { chart?: CustomChartDetail };\n return wrapped.chart ? wrapped.chart : (response as CustomChartDetail);\n }\n\n /** Never persists. The server repeats every client side check. */\n async validate(body: UploadBody): Promise<ValidateResponse> {\n const response = await this.client.request<ValidateResponse>({\n method: 'POST',\n path: `${CUSTOM_CHARTS_PATH}/validate`,\n auth: 'service',\n body,\n timeoutMs: 60_000,\n });\n return {\n ok: response.ok === true,\n problems: Array.isArray(response.problems) ? response.problems : [],\n contentHash: response.contentHash,\n sizeBytes: response.sizeBytes,\n };\n }\n\n /** Creates the chart (first upload) or a preview version; idempotent on contentHash. */\n async upload(body: UploadBody): Promise<UploadResponse> {\n return this.client.request<UploadResponse>({\n method: 'POST',\n path: CUSTOM_CHARTS_PATH,\n auth: 'service',\n body,\n timeoutMs: 60_000,\n invalidateCache: [LIST_CACHE_KEY],\n });\n }\n\n async publish(key: string, versionId?: string): Promise<{ chart: CustomChartSummary }> {\n return this.client.request<{ chart: CustomChartSummary }>({\n method: 'POST',\n path: `${CUSTOM_CHARTS_PATH}/${encodeKey(key)}/publish`,\n auth: 'service',\n body: versionId ? { versionId } : {},\n invalidateCache: [LIST_CACHE_KEY],\n });\n }\n\n async discardPreview(key: string): Promise<{ chart: CustomChartSummary }> {\n return this.client.request<{ chart: CustomChartSummary }>({\n method: 'POST',\n path: `${CUSTOM_CHARTS_PATH}/${encodeKey(key)}/discard-preview`,\n auth: 'service',\n body: {},\n invalidateCache: [LIST_CACHE_KEY],\n });\n }\n}\n","import type { DatabrainClient } from '../client/databrain-client.js';\nimport { CustomChartsApi } from '../api/custom-charts-api.js';\nimport type { CustomChartSummary } from '../api/custom-charts-api.js';\nimport { DatabrainApiError } from '../utils/errors.js';\nimport type { Problem } from './types.js';\n\nexport {\n CustomChartsApi,\n CUSTOM_CHARTS_PATH,\n type CustomChartDetail,\n type CustomChartSummary,\n type CustomChartVersionSummary,\n type UploadBody,\n type UploadResponse,\n type ValidateResponse,\n} from '../api/custom-charts-api.js';\n\nexport function createCustomChartsApi(client: DatabrainClient): CustomChartsApi {\n return new CustomChartsApi(client);\n}\n\n/** The backend error body is { error: { code, message, problems? } }. Pull the problems out. */\nexport function apiProblems(err: unknown): Problem[] {\n if (!(err instanceof DatabrainApiError)) return [];\n const details = err.details as { error?: { problems?: unknown }; problems?: unknown } | undefined;\n const list = details?.error?.problems ?? details?.problems;\n if (!Array.isArray(list)) return [];\n return list.filter(\n (item): item is Problem =>\n !!item && typeof item === 'object' && typeof (item as Problem).code === 'string' && typeof (item as Problem).message === 'string',\n );\n}\n\nexport interface UploadPlan {\n action: 'create chart' | 'create version' | 'no change';\n key: string;\n existing?: CustomChartSummary;\n nextVersionNumber: number;\n summary: string;\n}\n\n/**\n * What an apply will do, computed from the chart list (brief section 8):\n * \"will create chart\" when the key is new, \"will create version n\" when the\n * chart exists. The content-hash replay (\"none\") is only known to the server.\n */\nexport function planUpload(key: string, existing: CustomChartSummary | undefined, versionsKnown?: number): UploadPlan {\n if (!existing) {\n return { action: 'create chart', key, nextVersionNumber: 1, summary: `will create chart custom:${key} (version 1, status private)` };\n }\n const count = typeof versionsKnown === 'number' ? versionsKnown : typeof existing.versionsCount === 'number' ? existing.versionsCount : undefined;\n const next = count !== undefined ? count + 1 : NaN;\n const label = Number.isFinite(next) ? `version ${next}` : 'the next version';\n return {\n action: 'create version',\n key,\n existing,\n nextVersionNumber: Number.isFinite(next) ? next : 0,\n summary: `will create ${label} of custom:${key} in preview (status stays ${existing.status}; the published version keeps serving until you publish)`,\n };\n}\n"],"mappings":"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","names":["z","isRecord","z","isRecord","existsSync","readFileSync","join","resolve","resolve","join","existsSync","readFileSync","join","resolve","existsSync","readFileSync"]}
@@ -1 +0,0 @@
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 return { publicUrl: publicValue!, issuer: issuerValue!, 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\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'] : 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 'Use ask_question for inline chart or KPI answers. 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 (!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'],\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'],\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 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 // Saved-widget rendering mints a guest token; retain the issuer's embed-write boundary.\n render_chart: ['mcp:dashboards:read', 'mcp:query', 'mcp:embeds:write'],\n refresh_widget: ['mcp:dashboards:read', 'mcp:query', 'mcp:embeds:write'],\n drill_down: ['mcp:dashboards:read', 'mcp:query', 'mcp:embeds:write'],\n change_date_range: ['mcp:dashboards:read', 'mcp:query', 'mcp:embeds:write'],\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};\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_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 { 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']);\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.issuer.replace(/\\/$/, '');\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) 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 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 !== issuer.origin) 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', forbidden);\n }\n const status = 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 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"]}