@databrainhq/mcp-server 0.3.0-rc.5 → 0.3.0-rc.7

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.
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  oauthConfigSchema,
4
4
  oauthHttpsUrl
5
- } from "./chunk-PRE4CGOV.js";
5
+ } from "./chunk-ETXH5HTT.js";
6
6
 
7
7
  // src/utils/config.ts
8
8
  import { z, ZodError } from "zod";
@@ -72,4 +72,4 @@ export {
72
72
  DEFAULT_POSTHOG_HOST,
73
73
  loadConfig
74
74
  };
75
- //# sourceMappingURL=chunk-V64ZQINV.js.map
75
+ //# sourceMappingURL=chunk-VSSGMPSL.js.map
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  registerSecret,
13
13
  scaffoldProject,
14
14
  validateProject
15
- } from "./chunk-PRE4CGOV.js";
15
+ } from "./chunk-ETXH5HTT.js";
16
16
 
17
17
  // src/cli/index.ts
18
18
  import { existsSync, mkdirSync, readdirSync, writeFileSync } from "fs";
package/dist/index.js CHANGED
@@ -3,17 +3,17 @@ import {
3
3
  DEFAULT_POSTHOG_API_KEY,
4
4
  DEFAULT_POSTHOG_HOST,
5
5
  loadConfig
6
- } from "./chunk-V64ZQINV.js";
6
+ } from "./chunk-VSSGMPSL.js";
7
7
  import {
8
8
  registerAllPrompts,
9
9
  registerAllResources,
10
10
  registerAllTools
11
- } from "./chunk-5IQ7MVUH.js";
11
+ } from "./chunk-JSYG7ULR.js";
12
12
  import {
13
13
  DatabrainClient,
14
14
  MCP_SERVER_VERSION,
15
15
  logger
16
- } from "./chunk-PRE4CGOV.js";
16
+ } from "./chunk-ETXH5HTT.js";
17
17
 
18
18
  // src/index.ts
19
19
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -59,6 +59,8 @@ FIRST classify the request before calling tools \u2014 this avoids wasted turns:
59
59
 
60
60
  Production safety: OAuth may not be available yet. In service-token mode, writes are actor=system. For create_datamart and create_or_update_metric_from_query, dry_run is the default. Apply requires confirm="APPLY_TO_PRODUCTION" after the user reviews the plan. Widget validation and mutation tools must verify the resolved embed is tenant-scoped for the requested clientId; if not, they must refuse to touch it as a likely production/data-app dashboard. Do not apply from a vague request.
61
61
 
62
+ Semantic models: search_semantic_model, get_semantic_entity, propose_semantic_change, apply_semantic_change, eval_semantic_model and semantic_readiness use the configured service credential and datamartId from list_datamarts. For a v2 model, use these tools instead of update_semantic_layer. Search and inspect first. Propose a change, review its errors and plan, then evaluate its returned source. Apply that exact source with its expected_content_hash only after the user supplies confirm="APPLY_TO_PRODUCTION" and dry_run=false. Never supply confirmation yourself. Apply changes the live model for every consumer. A conflict requires a new proposal and review. dry_run defaults to true and never writes.
63
+
62
64
  Custom chart types (chart type custom:<key>): custom_chart_scaffold returns a chart project the agent writes to disk, custom_chart_validate checks a manifest and bundle without persisting, custom_chart_upload creates the chart or a preview version only with mode="apply" and confirm="APPLY_TO_PRODUCTION" (dry_run shows the plan first), and publish_custom_chart makes a version live for every builder only with the same confirmation because every dashboard using that key re-renders. Build the bundle with \`databrain custom-chart build\` (the CLI in this package), never by hand.
63
65
 
64
66
  Infrastructure creation is intentionally narrow in MCP: datasource and workspace creation still happen outside this workflow; sync_datasource and dry-run/apply create_datamart are available. The semantic layer is populated and maintained via MCP tools (get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status). When the semantic layer is empty, use these tools \u2014 do NOT send the user to the UI for semantic metadata.
@@ -115,7 +117,7 @@ function banner(msg) {
115
117
  async function main() {
116
118
  try {
117
119
  if (process.argv.includes("--http")) {
118
- const { startHosted } = await import("./main-Q6XT4O46.js");
120
+ const { startHosted } = await import("./main-AZ26BAEM.js");
119
121
  await startHosted();
120
122
  return;
121
123
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/utils/analytics.ts"],"sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './utils/config.js';\nimport { createServer } from './server.js';\nimport { setupAnalytics } from './utils/analytics.js';\nimport { logger } from './utils/logger.js';\nimport { MCP_SERVER_VERSION } from './version.js';\n\nfunction banner(msg: string): void {\n process.stderr.write(`[databrain] ${msg}\\n`);\n}\n\nasync function main(): Promise<void> {\n try {\n if (process.argv.includes('--http')) {\n const { startHosted } = await import('./hosted/main.js');\n await startHosted();\n return;\n }\n const config = loadConfig();\n const server = createServer(config);\n const posthog = setupAnalytics(server, config);\n const transport = new StdioServerTransport();\n\n let shuttingDown = false;\n const flushAnalytics = async (): Promise<void> => {\n if (shuttingDown || !posthog) return;\n shuttingDown = true;\n await posthog.shutdown();\n };\n // StdioServerTransport never observes stdin ending, so the MCP client\n // disconnecting drains the event loop and exits the process without ever\n // calling transport/server onclose. `beforeExit` is what actually fires\n // in that path; posthog-node's flush timers are unref'd so they don't\n // keep the process alive on their own.\n process.on('beforeExit', () => void flushAnalytics());\n process.on('SIGINT', () => void flushAnalytics().then(() => process.exit(0)));\n process.on('SIGTERM', () => void flushAnalytics().then(() => process.exit(0)));\n\n await server.connect(transport);\n banner(`v${MCP_SERVER_VERSION} ready`);\n } catch (error) {\n logger.error('Failed to start server', error);\n process.exit(1);\n }\n}\n\nmain();\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { DatabrainClient } from './client/databrain-client.js';\nimport type { Config } from './utils/config.js';\nimport { registerAllTools } from './tools/registry.js';\nimport { registerAllResources } from './resources/registry.js';\nimport { registerAllPrompts } from './prompts/registry.js';\nimport { MCP_SERVER_VERSION } from './version.js';\n\nexport function createServer(config: Config): McpServer {\n const client = new DatabrainClient(config);\n const auth = client.getActiveAuth();\n let operatorBlurb: string;\n if (auth.source === 'oauth-client-credentials') {\n operatorBlurb =\n 'OPERATOR IDENTITY: OAuth client-credentials mode is configured for org-level calls. Tokens identify a service application, not a human operator. The backend must validate the token and map its issuer/subject to an authorized principal. Configuration alone does not prove backend acceptance. Do NOT ask the user for credentials or tokens.\\n\\n';\n } else if (auth.source === 'service-token') {\n operatorBlurb =\n 'OPERATOR IDENTITY: service-token mode. Calls are authenticated at org level — actions in the audit log are tagged actor=system, not a specific user. Per-user identity binding will be added when the OAuth-based login flow ships. Do NOT ask the user for credentials or tokens.\\n\\n';\n } else if (auth.source === 'api-token') {\n operatorBlurb =\n 'OPERATOR IDENTITY: API-token-only mode. A per-data-app API token is configured, but DATABRAIN_SERVICE_TOKEN is not. Embedding operations can use the configured API token, but org-level setup tools require DATABRAIN_SERVICE_TOKEN in the MCP config.\\n\\n';\n } else {\n operatorBlurb =\n 'OPERATOR IDENTITY: no usable token is configured. The MCP server normally refuses this config at startup. Ask the user to add DATABRAIN_SERVICE_TOKEN or DATABRAIN_API_TOKEN to the MCP config before proceeding.\\n\\n';\n }\n\n const serviceTokenStatus = config.oauth\n ? 'OAuth client credentials are configured for service-level operations. A configured DATABRAIN_SERVICE_TOKEN is used only if token acquisition fails.'\n : config.serviceToken\n ? 'This MCP server has DATABRAIN_SERVICE_TOKEN configured; never ask the user for it.'\n : 'DATABRAIN_SERVICE_TOKEN is not configured. Ask the user to add it to the MCP config only if org-level setup is needed.';\n const apiTokenStatus = client.hasApiToken()\n ? 'This MCP server has a per-data-app API token configured.'\n : config.oauth\n ? 'Created/rotated via create_api_token or rotate_api_token using configured OAuth service credentials, once the backend supports them.'\n : 'Created/rotated via create_api_token or rotate_api_token during setup when DATABRAIN_SERVICE_TOKEN is available.';\n\n const server = new McpServer(\n {\n name: 'databrain',\n version: MCP_SERVER_VERSION,\n description:\n 'Databrain MCP Server — build customer-facing embedded analytics AND run internal analytics on your own data. Supports embed setup, semantic-layer maintenance, natural-language querying, SQL→metric creation (embedded production metrics and unpublished internal workspace metrics), theming, filters, permissions, and guest token generation.',\n },\n {\n instructions:\n operatorBlurb +\n 'Databrain uses two token types — never confuse them:\\n' +\n `• SERVICE TOKEN (DATABRAIN_SERVICE_TOKEN): org-level, pre-configured in MCP server config. Used for: list_datasources, discover_datasource_schema, sync_datasource, list_datamarts, create_datamart, list_workspaces, list_data_apps, list_tenants, create_data_app, list_api_tokens, create_api_token, rotate_api_token, list_dashboards (workspace scope), ask_question, get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status, custom_chart_validate (server check), custom_chart_upload, list_custom_charts, publish_custom_chart. ${serviceTokenStatus}\\n` +\n `• API TOKEN (per data app): scoped to one data app, used for embedding operations (list_embeds, get_embed_details, create_embed, update_embed, generate_guest_token), data-app scoped dashboard listing, widget CRUD/metric creation through guest-token APIs, and list_scheduled_reports. ${apiTokenStatus}\\n\\n` +\n 'FIRST classify the request before calling tools — this avoids wasted turns:\\n' +\n '• (A) EMBEDDING: putting Databrain dashboards/charts in front of YOUR customers (data apps, embeds, guest tokens, per-customer dashboard widgets). Needs an API token and usually a clientId.\\n' +\n '• (B) INTERNAL ANALYTICS: exploring your own data or migrating SQL into your own Databrain metrics for internal use. Workspace-scoped, uses the service token, NO clientId. Creating an unpublished workspace metric (create_workspace_metric) lives here — it is NOT embedding.\\n\\n' +\n '5 core use cases — match the user\\'s scenario to the right prompt:\\n' +\n '1. Embed existing dashboard → prompt: embed-existing-dashboard (list_data_apps → create_api_token → list_dashboards → create_embed → generate_guest_token → generate_embed_code)\\n' +\n '2. Blank dashboard per client → prompt: embed-blank-dashboard (list_data_apps → list_datamarts → create_embed with clientId per client → generate_guest_token → generate_embed_code)\\n' +\n '3. Explore & query data → prompt: query-data (list_datamarts → get_semantic_layer → if empty, auto-generate descriptions with update_semantic_layer or use populate-semantic-layer prompt → ask_question)\\n' +\n '4. SQL → embedded production metric on a customer dashboard (EMBEDDING, requires clientId) → prompt: sql-to-metric-migration (validate_metric_spec → create_or_update_metric_from_query dry_run → apply only with explicit production confirmation)\\n' +\n '5. SQL → unpublished workspace metric (INTERNAL ANALYTICS, no clientId, not attached to any dashboard) → prompt: sql-to-workspace-metric (discover_datasource_schema → create_workspace_metric dry_run → apply with explicit confirmation)\\n\\n' +\n 'Production safety: OAuth may not be available yet. In service-token mode, writes are actor=system. For create_datamart and create_or_update_metric_from_query, dry_run is the default. Apply requires confirm=\"APPLY_TO_PRODUCTION\" after the user reviews the plan. Widget validation and mutation tools must verify the resolved embed is tenant-scoped for the requested clientId; if not, they must refuse to touch it as a likely production/data-app dashboard. Do not apply from a vague request.\\n\\n' +\n 'Custom chart types (chart type custom:<key>): custom_chart_scaffold returns a chart project the agent writes to disk, custom_chart_validate checks a manifest and bundle without persisting, custom_chart_upload creates the chart or a preview version only with mode=\"apply\" and confirm=\"APPLY_TO_PRODUCTION\" (dry_run shows the plan first), and publish_custom_chart makes a version live for every builder only with the same confirmation because every dashboard using that key re-renders. Build the bundle with `databrain custom-chart build` (the CLI in this package), never by hand.\\n\\n' +\n 'Infrastructure creation is intentionally narrow in MCP: datasource and workspace creation still happen outside this workflow; sync_datasource and dry-run/apply create_datamart are available. The semantic layer is populated and maintained via MCP tools (get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status). When the semantic layer is empty, use these tools — do NOT send the user to the UI for semantic metadata.\\n\\n' +\n 'After embed setup, customize with: brand-my-embed prompt for theming. Use update_embed (body passthrough) for filters, localization, and permissions.\\n' +\n 'Use generate_guest_token for frontend auth and generate_embed_code for framework snippets.\\n' +\n 'For advanced embed configuration (UI toggles, filters, i18n), use update_embed with a body passthrough.\\n' +\n 'To diagnose embed issues, use get_embed_details and reason about the response.\\n' +\n 'Do NOT ask the user for any tokens — manage them via MCP tools.\\n\\n' +\n 'IMPORTANT — accessSettings defaults:\\n' +\n 'When calling create_embed, only pass accessSettings: { datamartName: \"...\" } plus any flags the user explicitly wants enabled. For multi-tenant dashboard embeds, also pass clientId. The tool auto-fills all required isAllow* fields with secure defaults (all false). Do NOT guess or enumerate accessSettings fields.\\n\\n' +\n 'IMPORTANT — create_data_app does NOT accept workspaceName. Only pass { name: \"...\" }.\\n\\n' +\n 'IMPORTANT — Be interactive. Present results from each tool call and ask the user before proceeding to the next step. Never batch multiple mutating operations without user confirmation.\\n' +\n 'Trigger keywords: embed, dashboard, analytics, Databrain, guest token, theme, filters, permissions, datasource, datamart, workspace, metric creation.',\n },\n );\n\n registerAllTools(server, client);\n registerAllResources(server);\n registerAllPrompts(server);\n\n return server;\n}\n","import { createHash } from 'node:crypto';\nimport { PostHog } from 'posthog-node';\nimport { instrument } from '@posthog/mcp';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { Config } from './config.js';\nimport { DEFAULT_POSTHOG_API_KEY, DEFAULT_POSTHOG_HOST } from './config.js';\nimport { logger } from './logger.js';\n\n// Derives a stable per-install distinct_id from the active auth token without\n// ever sending the token itself to PostHog.\nfunction distinctIdFromToken(token: string | undefined): string | null {\n if (!token) return null;\n return createHash('sha256').update(token).digest('hex').slice(0, 32);\n}\n\n/**\n * Wires PostHog MCP analytics (tool calls, initialize, exceptions) into the\n * server. Always on — not user-configurable. Returns the PostHog client so\n * the caller can flush/shutdown it on exit, or null when no auth token is\n * configured yet (nothing to derive a distinct_id from).\n */\nexport function setupAnalytics(server: McpServer, config: Config): PostHog | null {\n const distinctId = distinctIdFromToken(config.serviceToken || config.apiToken);\n if (!distinctId) {\n return null;\n }\n\n const posthog = new PostHog(config.posthogApiKey || DEFAULT_POSTHOG_API_KEY, {\n host: config.posthogHost || DEFAULT_POSTHOG_HOST,\n });\n\n instrument(server, posthog, {\n logger: (msg) => logger.debug(`[posthog] ${msg}`),\n identify: { distinctId },\n });\n\n return posthog;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/utils/analytics.ts"],"sourcesContent":["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { loadConfig } from './utils/config.js';\nimport { createServer } from './server.js';\nimport { setupAnalytics } from './utils/analytics.js';\nimport { logger } from './utils/logger.js';\nimport { MCP_SERVER_VERSION } from './version.js';\n\nfunction banner(msg: string): void {\n process.stderr.write(`[databrain] ${msg}\\n`);\n}\n\nasync function main(): Promise<void> {\n try {\n if (process.argv.includes('--http')) {\n const { startHosted } = await import('./hosted/main.js');\n await startHosted();\n return;\n }\n const config = loadConfig();\n const server = createServer(config);\n const posthog = setupAnalytics(server, config);\n const transport = new StdioServerTransport();\n\n let shuttingDown = false;\n const flushAnalytics = async (): Promise<void> => {\n if (shuttingDown || !posthog) return;\n shuttingDown = true;\n await posthog.shutdown();\n };\n // StdioServerTransport never observes stdin ending, so the MCP client\n // disconnecting drains the event loop and exits the process without ever\n // calling transport/server onclose. `beforeExit` is what actually fires\n // in that path; posthog-node's flush timers are unref'd so they don't\n // keep the process alive on their own.\n process.on('beforeExit', () => void flushAnalytics());\n process.on('SIGINT', () => void flushAnalytics().then(() => process.exit(0)));\n process.on('SIGTERM', () => void flushAnalytics().then(() => process.exit(0)));\n\n await server.connect(transport);\n banner(`v${MCP_SERVER_VERSION} ready`);\n } catch (error) {\n logger.error('Failed to start server', error);\n process.exit(1);\n }\n}\n\nmain();\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { DatabrainClient } from './client/databrain-client.js';\nimport type { Config } from './utils/config.js';\nimport { registerAllTools } from './tools/registry.js';\nimport { registerAllResources } from './resources/registry.js';\nimport { registerAllPrompts } from './prompts/registry.js';\nimport { MCP_SERVER_VERSION } from './version.js';\n\nexport function createServer(config: Config): McpServer {\n const client = new DatabrainClient(config);\n const auth = client.getActiveAuth();\n let operatorBlurb: string;\n if (auth.source === 'oauth-client-credentials') {\n operatorBlurb =\n 'OPERATOR IDENTITY: OAuth client-credentials mode is configured for org-level calls. Tokens identify a service application, not a human operator. The backend must validate the token and map its issuer/subject to an authorized principal. Configuration alone does not prove backend acceptance. Do NOT ask the user for credentials or tokens.\\n\\n';\n } else if (auth.source === 'service-token') {\n operatorBlurb =\n 'OPERATOR IDENTITY: service-token mode. Calls are authenticated at org level — actions in the audit log are tagged actor=system, not a specific user. Per-user identity binding will be added when the OAuth-based login flow ships. Do NOT ask the user for credentials or tokens.\\n\\n';\n } else if (auth.source === 'api-token') {\n operatorBlurb =\n 'OPERATOR IDENTITY: API-token-only mode. A per-data-app API token is configured, but DATABRAIN_SERVICE_TOKEN is not. Embedding operations can use the configured API token, but org-level setup tools require DATABRAIN_SERVICE_TOKEN in the MCP config.\\n\\n';\n } else {\n operatorBlurb =\n 'OPERATOR IDENTITY: no usable token is configured. The MCP server normally refuses this config at startup. Ask the user to add DATABRAIN_SERVICE_TOKEN or DATABRAIN_API_TOKEN to the MCP config before proceeding.\\n\\n';\n }\n\n const serviceTokenStatus = config.oauth\n ? 'OAuth client credentials are configured for service-level operations. A configured DATABRAIN_SERVICE_TOKEN is used only if token acquisition fails.'\n : config.serviceToken\n ? 'This MCP server has DATABRAIN_SERVICE_TOKEN configured; never ask the user for it.'\n : 'DATABRAIN_SERVICE_TOKEN is not configured. Ask the user to add it to the MCP config only if org-level setup is needed.';\n const apiTokenStatus = client.hasApiToken()\n ? 'This MCP server has a per-data-app API token configured.'\n : config.oauth\n ? 'Created/rotated via create_api_token or rotate_api_token using configured OAuth service credentials, once the backend supports them.'\n : 'Created/rotated via create_api_token or rotate_api_token during setup when DATABRAIN_SERVICE_TOKEN is available.';\n\n const server = new McpServer(\n {\n name: 'databrain',\n version: MCP_SERVER_VERSION,\n description:\n 'Databrain MCP Server — build customer-facing embedded analytics AND run internal analytics on your own data. Supports embed setup, semantic-layer maintenance, natural-language querying, SQL→metric creation (embedded production metrics and unpublished internal workspace metrics), theming, filters, permissions, and guest token generation.',\n },\n {\n instructions:\n operatorBlurb +\n 'Databrain uses two token types — never confuse them:\\n' +\n `• SERVICE TOKEN (DATABRAIN_SERVICE_TOKEN): org-level, pre-configured in MCP server config. Used for: list_datasources, discover_datasource_schema, sync_datasource, list_datamarts, create_datamart, list_workspaces, list_data_apps, list_tenants, create_data_app, list_api_tokens, create_api_token, rotate_api_token, list_dashboards (workspace scope), ask_question, get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status, custom_chart_validate (server check), custom_chart_upload, list_custom_charts, publish_custom_chart. ${serviceTokenStatus}\\n` +\n `• API TOKEN (per data app): scoped to one data app, used for embedding operations (list_embeds, get_embed_details, create_embed, update_embed, generate_guest_token), data-app scoped dashboard listing, widget CRUD/metric creation through guest-token APIs, and list_scheduled_reports. ${apiTokenStatus}\\n\\n` +\n 'FIRST classify the request before calling tools — this avoids wasted turns:\\n' +\n '• (A) EMBEDDING: putting Databrain dashboards/charts in front of YOUR customers (data apps, embeds, guest tokens, per-customer dashboard widgets). Needs an API token and usually a clientId.\\n' +\n '• (B) INTERNAL ANALYTICS: exploring your own data or migrating SQL into your own Databrain metrics for internal use. Workspace-scoped, uses the service token, NO clientId. Creating an unpublished workspace metric (create_workspace_metric) lives here — it is NOT embedding.\\n\\n' +\n '5 core use cases — match the user\\'s scenario to the right prompt:\\n' +\n '1. Embed existing dashboard → prompt: embed-existing-dashboard (list_data_apps → create_api_token → list_dashboards → create_embed → generate_guest_token → generate_embed_code)\\n' +\n '2. Blank dashboard per client → prompt: embed-blank-dashboard (list_data_apps → list_datamarts → create_embed with clientId per client → generate_guest_token → generate_embed_code)\\n' +\n '3. Explore & query data → prompt: query-data (list_datamarts → get_semantic_layer → if empty, auto-generate descriptions with update_semantic_layer or use populate-semantic-layer prompt → ask_question)\\n' +\n '4. SQL → embedded production metric on a customer dashboard (EMBEDDING, requires clientId) → prompt: sql-to-metric-migration (validate_metric_spec → create_or_update_metric_from_query dry_run → apply only with explicit production confirmation)\\n' +\n '5. SQL → unpublished workspace metric (INTERNAL ANALYTICS, no clientId, not attached to any dashboard) → prompt: sql-to-workspace-metric (discover_datasource_schema → create_workspace_metric dry_run → apply with explicit confirmation)\\n\\n' +\n 'Production safety: OAuth may not be available yet. In service-token mode, writes are actor=system. For create_datamart and create_or_update_metric_from_query, dry_run is the default. Apply requires confirm=\"APPLY_TO_PRODUCTION\" after the user reviews the plan. Widget validation and mutation tools must verify the resolved embed is tenant-scoped for the requested clientId; if not, they must refuse to touch it as a likely production/data-app dashboard. Do not apply from a vague request.\\n\\n' +\n 'Semantic models: search_semantic_model, get_semantic_entity, propose_semantic_change, apply_semantic_change, eval_semantic_model and semantic_readiness use the configured service credential and datamartId from list_datamarts. For a v2 model, use these tools instead of update_semantic_layer. Search and inspect first. Propose a change, review its errors and plan, then evaluate its returned source. Apply that exact source with its expected_content_hash only after the user supplies confirm=\"APPLY_TO_PRODUCTION\" and dry_run=false. Never supply confirmation yourself. Apply changes the live model for every consumer. A conflict requires a new proposal and review. dry_run defaults to true and never writes.\\n\\n' +\n 'Custom chart types (chart type custom:<key>): custom_chart_scaffold returns a chart project the agent writes to disk, custom_chart_validate checks a manifest and bundle without persisting, custom_chart_upload creates the chart or a preview version only with mode=\"apply\" and confirm=\"APPLY_TO_PRODUCTION\" (dry_run shows the plan first), and publish_custom_chart makes a version live for every builder only with the same confirmation because every dashboard using that key re-renders. Build the bundle with `databrain custom-chart build` (the CLI in this package), never by hand.\\n\\n' +\n 'Infrastructure creation is intentionally narrow in MCP: datasource and workspace creation still happen outside this workflow; sync_datasource and dry-run/apply create_datamart are available. The semantic layer is populated and maintained via MCP tools (get_semantic_layer, update_semantic_layer, start_semantic_layer_generation, get_semantic_layer_generation_status). When the semantic layer is empty, use these tools — do NOT send the user to the UI for semantic metadata.\\n\\n' +\n 'After embed setup, customize with: brand-my-embed prompt for theming. Use update_embed (body passthrough) for filters, localization, and permissions.\\n' +\n 'Use generate_guest_token for frontend auth and generate_embed_code for framework snippets.\\n' +\n 'For advanced embed configuration (UI toggles, filters, i18n), use update_embed with a body passthrough.\\n' +\n 'To diagnose embed issues, use get_embed_details and reason about the response.\\n' +\n 'Do NOT ask the user for any tokens — manage them via MCP tools.\\n\\n' +\n 'IMPORTANT — accessSettings defaults:\\n' +\n 'When calling create_embed, only pass accessSettings: { datamartName: \"...\" } plus any flags the user explicitly wants enabled. For multi-tenant dashboard embeds, also pass clientId. The tool auto-fills all required isAllow* fields with secure defaults (all false). Do NOT guess or enumerate accessSettings fields.\\n\\n' +\n 'IMPORTANT — create_data_app does NOT accept workspaceName. Only pass { name: \"...\" }.\\n\\n' +\n 'IMPORTANT — Be interactive. Present results from each tool call and ask the user before proceeding to the next step. Never batch multiple mutating operations without user confirmation.\\n' +\n 'Trigger keywords: embed, dashboard, analytics, Databrain, guest token, theme, filters, permissions, datasource, datamart, workspace, metric creation.',\n },\n );\n\n registerAllTools(server, client);\n registerAllResources(server);\n registerAllPrompts(server);\n\n return server;\n}\n","import { createHash } from 'node:crypto';\nimport { PostHog } from 'posthog-node';\nimport { instrument } from '@posthog/mcp';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { Config } from './config.js';\nimport { DEFAULT_POSTHOG_API_KEY, DEFAULT_POSTHOG_HOST } from './config.js';\nimport { logger } from './logger.js';\n\n// Derives a stable per-install distinct_id from the active auth token without\n// ever sending the token itself to PostHog.\nfunction distinctIdFromToken(token: string | undefined): string | null {\n if (!token) return null;\n return createHash('sha256').update(token).digest('hex').slice(0, 32);\n}\n\n/**\n * Wires PostHog MCP analytics (tool calls, initialize, exceptions) into the\n * server. Always on — not user-configurable. Returns the PostHog client so\n * the caller can flush/shutdown it on exit, or null when no auth token is\n * configured yet (nothing to derive a distinct_id from).\n */\nexport function setupAnalytics(server: McpServer, config: Config): PostHog | null {\n const distinctId = distinctIdFromToken(config.serviceToken || config.apiToken);\n if (!distinctId) {\n return null;\n }\n\n const posthog = new PostHog(config.posthogApiKey || DEFAULT_POSTHOG_API_KEY, {\n host: config.posthogHost || DEFAULT_POSTHOG_HOST,\n });\n\n instrument(server, posthog, {\n logger: (msg) => logger.debug(`[posthog] ${msg}`),\n identify: { distinctId },\n });\n\n return posthog;\n}\n"],"mappings":"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","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  registerAllPrompts,
4
4
  registerAllResources,
5
5
  registerAllTools
6
- } from "./chunk-5IQ7MVUH.js";
6
+ } from "./chunk-JSYG7ULR.js";
7
7
  import {
8
8
  DatabrainApiError,
9
9
  DatabrainClient,
@@ -11,7 +11,7 @@ import {
11
11
  toolContext,
12
12
  toolError,
13
13
  toolSuccess
14
- } from "./chunk-PRE4CGOV.js";
14
+ } from "./chunk-ETXH5HTT.js";
15
15
  import {
16
16
  FilePatStore,
17
17
  HOSTED_SCOPES,
@@ -95,6 +95,12 @@ var TOOL_SCOPES = {
95
95
  update_semantic_layer: ["mcp:datamarts:write"],
96
96
  start_semantic_layer_generation: ["mcp:datamarts:write"],
97
97
  get_semantic_layer_generation_status: ["mcp:datamarts:read"],
98
+ search_semantic_model: ["mcp:datamarts:read"],
99
+ get_semantic_entity: ["mcp:datamarts:read"],
100
+ propose_semantic_change: ["mcp:datamarts:read"],
101
+ apply_semantic_change: ["mcp:datamarts:write"],
102
+ eval_semantic_model: ["mcp:datamarts:read", "mcp:query"],
103
+ semantic_readiness: ["mcp:datamarts:read"],
98
104
  list_widgets: ["mcp:dashboards:read"],
99
105
  validate_metric_spec: ["mcp:dashboards:read", "mcp:query"],
100
106
  create_or_update_metric_from_query: ["mcp:dashboards:write", "mcp:query"],
@@ -468,4 +474,4 @@ async function startHosted() {
468
474
  export {
469
475
  startHosted
470
476
  };
471
- //# sourceMappingURL=main-Q6XT4O46.js.map
477
+ //# sourceMappingURL=main-AZ26BAEM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hosted/main.ts","../src/hosted/config.ts","../src/hosted/http.ts","../src/hosted/server.ts","../src/hosted/authorization.ts","../src/hosted/provider.ts"],"sourcesContent":["import { isAbsolute } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { loadHostedConfig } from './config.js';\nimport { FilePatStore } from './auth.js';\nimport { createHostedHttpServer } from './http.js';\nimport type { HostedAdapter } from './types.js';\nimport { createProviderAdapter } from './provider.js';\n\nexport async function startHosted(): Promise<void> {\n const config = loadHostedConfig();\n let adapter: HostedAdapter;\n if (config.testOnly) {\n const file = process.env.DATABRAIN_HOSTED_TEST_FIXTURES;\n if (!file || !isAbsolute(file)) throw new Error('Hosted test mode requires an absolute fixture store path.');\n const { createTestOnlyAdapter } = await import('./test-only.js');\n adapter = createTestOnlyAdapter(file);\n } else {\n const file = process.env.DATABRAIN_HOSTED_ADAPTER_MODULE;\n if (!file) {\n adapter = createProviderAdapter(config);\n process.stderr.write('[databrain] OAuth verification configured; backend data operations and PAT membership require a delegated adapter.\\n');\n } else {\n if (!isAbsolute(file)) throw new Error('DATABRAIN_HOSTED_ADAPTER_MODULE must be an absolute path.');\n try { adapter = (await import(pathToFileURL(file).href)).adapter; }\n catch { throw new Error('Could not load the trusted hosted adapter.'); }\n }\n if (!adapter || ['verifyOAuth', 'resolvePatGrant', 'executeRequest'].some(key =>\n typeof adapter[key as keyof HostedAdapter] !== 'function')) throw new Error('Invalid trusted hosted adapter.');\n }\n const patPath = process.env.DATABRAIN_HOSTED_PAT_STORE;\n if (patPath && !isAbsolute(patPath)) throw new Error('Hosted PAT store must be an absolute path.');\n const server = createHostedHttpServer(config, adapter, patPath ? new FilePatStore(patPath) : undefined);\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(config.port, 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 const publicUrl = validUrl(env.DATABRAIN_HOSTED_PUBLIC_URL, 'DATABRAIN_HOSTED_PUBLIC_URL');\n validUrl(env.DATABRAIN_HOSTED_ISSUER, 'DATABRAIN_HOSTED_ISSUER');\n if (publicUrl.pathname !== '/mcp') throw new Error('DATABRAIN_HOSTED_PUBLIC_URL must end in /mcp.');\n const port = Number(env.DATABRAIN_HOSTED_PORT || '3144');\n // 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: env.DATABRAIN_HOSTED_PUBLIC_URL!, issuer: env.DATABRAIN_HOSTED_ISSUER!, 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 } from './server.js';\nimport { createBearerVerifier } from './auth.js';\nimport { authorizeTool, assertBoundArguments, requiredScopes } from './authorization.js';\nimport { HOSTED_SCOPES, HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedConfig, PatStore } from './types.js';\n\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 if (body?.method === 'tools/call' && typeof body.params?.name === 'string') {\n try {\n authorizeTool(body.params.name, principal);\n assertBoundArguments(body.params.arguments, principal);\n } catch (error) {\n if (error instanceof HostedAuthError && error.code === 'insufficient_scope') {\n res.setHeader('WWW-Authenticate', `Bearer error=\"insufficient_scope\", resource_metadata=\"${metadataUrl}\", scope=\"${requiredScopes(body.params.name).join(' ')}\"`);\n }\n throw error;\n }\n }\n const mcp = createHostedToolServer(principal, adapter);\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });\n let closed = false;\n const close = () => { if (!closed) { closed = true; void mcp.close(); } };\n res.once('close', close);\n try {\n await mcp.connect(transport);\n await transport.handleRequest(req, res, body);\n } finally { if (res.writableEnded) close(); }\n } finally { active--; }\n } catch (error) {\n if (res.headersSent) { res.end(); return; }\n const status = error instanceof HostedAuthError ? error.status : 503;\n if (status === 401) res.setHeader('WWW-Authenticate', `Bearer error=\"invalid_token\", resource_metadata=\"${metadataUrl}\"`);\n reply(res, status, { error: error instanceof HostedAuthError ? error.code : 'temporarily_unavailable' });\n }\n });\n server.requestTimeout = 30_000;\n server.headersTimeout = 10_000;\n server.keepAliveTimeout = 5_000;\n return server;\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { instrument } from '@posthog/mcp';\nimport { PostHog } from 'posthog-node';\nimport { DatabrainClient } from '../client/databrain-client.js';\nimport type { RequestOptions } from '../client/types.js';\nimport { registerAllTools } from '../tools/registry.js';\nimport { registerAllResources } from '../resources/registry.js';\nimport { registerAllPrompts } from '../prompts/registry.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport { DatabrainApiError, toolError, toolSuccess } from '../utils/errors.js';\nimport { toolContext } from '../utils/tool-context.js';\nimport { assertBoundArguments, authorizeTool } from './authorization.js';\nimport { HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedPrincipal } from './types.js';\n\n// Stdio's instrumentation changes the wire schema (required `context`). Use that\n// same implementation, with capture disabled until hosted telemetry has an owner.\n// No bearer, request headers, tool arguments or results are sent to analytics.\nconst schemaInstrumentation = new PostHog('hosted-contract-only', { disabled: true });\n\n/** A request-local client: shared APIs/tools run unchanged, through a delegated executor. */\nclass HostedClient extends DatabrainClient {\n constructor(private readonly adapter: HostedAdapter, private readonly principal: HostedPrincipal) {\n super({ apiUrl: 'https://api.usedatabrain.com', demoDomain: 'https://demo.usedatabrain.com' });\n }\n // A generated API credential must never replace the authorizing hosted principal.\n override setRuntimeApiToken(_token: string): void {}\n\n override async request<T>(options: RequestOptions): Promise<T> {\n try {\n const name = toolContext.getStore()?.tool;\n if (!name) throw new HostedAuthError(403, 'tool_not_authorized');\n authorizeTool(name, this.principal);\n assertBoundArguments(options.body, this.principal);\n assertBoundArguments(options.query, this.principal);\n const raw = await this.adapter.executeRequest(structuredClone(options), structuredClone(this.principal));\n if (!options.skipDataUnwrap && raw && typeof raw === 'object' && !Array.isArray(raw) &&\n 'data' in raw && raw.data !== null && raw.data !== undefined) return raw.data as T;\n return raw as T;\n } catch (error) {\n // The adapter may encounter secret-bearing remote bodies. Do not pass them to tools/logs.\n const status = error instanceof HostedAuthError ? error.status : 503;\n throw new DatabrainApiError(status, 'HOSTED_REQUEST_DENIED',\n 'The authorized backend could not complete this request. Check workspace access and hosted integration.');\n }\n }\n}\n\nexport function createHostedToolServer(principal: HostedPrincipal, adapter: HostedAdapter): McpServer {\n const bound = structuredClone(principal);\n const server = new McpServer({ name: 'databrain', version: MCP_SERVER_VERSION }, {\n instructions: 'Hosted Databrain: each request is authenticated as a user in one authorized workspace. ' +\n 'Use whoami to inspect the verified identity and scopes. Never ask for a service token or put credentials in tool arguments. ' +\n 'Workspace and tenant access come from the bearer principal. Existing dry-run and production-confirmation rules still apply.',\n });\n\n // The existing shared registry already wraps server.tool for intent telemetry. Layer\n // hosted policy underneath it; keep every schema, description, annotation and handler.\n const register = server.tool.bind(server) as (...args: any[]) => unknown;\n server.tool = ((...args: any[]) => {\n const name = args[0] as string;\n const index = args.length - 1;\n const handler = args[index] as (...values: any[]) => unknown;\n const shape = args[2] as Record<string, unknown>;\n args[index] = async (...values: any[]) => {\n try {\n authorizeTool(name, bound);\n assertBoundArguments(values[0], bound);\n if (name === 'whoami') {\n return toolSuccess(JSON.stringify({\n status: 'authenticated', activeBearer: bound.credentialType,\n actorKind: bound.actorKind, userId: bound.userId, companyId: bound.companyId,\n workspaceId: bound.workspaceId, workspaceName: bound.workspaceName,\n oauthClientId: bound.clientId, scopes: bound.scopes,\n }));\n }\n // Required selectors retain their public schema; matching inputs are always replaced\n // by the trusted value. Optional selectors are supplied when that tool declares them.\n if (shape && typeof shape === 'object' && values[0] && typeof values[0] === 'object') {\n for (const key of ['workspaceName', 'workspaceId'] as const) {\n if (Object.hasOwn(shape, key) && bound[key] !== undefined) values[0][key] = bound[key];\n }\n }\n return await handler(...values);\n } catch {\n return toolError('Hosted authorization denied this tool call. Check the granted scopes, workspace and tenant access.');\n }\n };\n return register(...args);\n }) as McpServer['tool'];\n\n registerAllTools(server, new HostedClient(adapter, bound));\n registerAllResources(server);\n registerAllPrompts(server);\n instrument(server, schemaInstrumentation, { beforeSend: () => null });\n return server;\n}\n","import { HostedAuthError } from './types.js';\nimport type { HostedPrincipal, HostedScope } from './types.js';\n\n/** Explicit exhaustive policy: a newly registered tool is denied until it gets a policy. */\nexport const TOOL_SCOPES: Readonly<Record<string, readonly HostedScope[]>> = {\n whoami: [],\n list_datasources: ['mcp:datamarts:read'], discover_datasource_schema: ['mcp:datamarts:read'],\n list_datamarts: ['mcp:datamarts:read'], list_workspaces: ['mcp:datamarts:read'],\n sync_datasource: ['mcp:datamarts:write'], create_datamart: ['mcp:datamarts:write'],\n list_data_apps: ['mcp:dashboards:read'], list_tenants: ['mcp:dashboards:read'],\n create_data_app: ['mcp:embeds:write'], list_api_tokens: ['mcp:embeds:write'],\n create_api_token: ['mcp:embeds:write'], rotate_api_token: ['mcp:embeds:write'],\n create_embed: ['mcp:embeds:write'], list_embeds: ['mcp:dashboards:read'],\n get_embed_details: ['mcp:dashboards:read'], update_embed: ['mcp:embeds:write'],\n customize_embed_theme: ['mcp:embeds:write'], get_demo_link: ['mcp:embeds:write'],\n generate_guest_token: ['mcp:embeds:write'], generate_embed_code: ['mcp:dashboards:read'],\n list_dashboards: ['mcp:dashboards:read'], create_dashboard: ['mcp:dashboards:write'],\n get_dashboard: ['mcp:dashboards:read'], generate_dashboard_tiles: ['mcp:dashboards:write', 'mcp:query'],\n ask_question: ['mcp:query'], get_semantic_layer: ['mcp:datamarts:read'],\n update_semantic_layer: ['mcp:datamarts:write'], start_semantic_layer_generation: ['mcp:datamarts:write'],\n get_semantic_layer_generation_status: ['mcp:datamarts:read'],\n list_widgets: ['mcp:dashboards:read'], validate_metric_spec: ['mcp:dashboards:read', 'mcp:query'],\n create_or_update_metric_from_query: ['mcp:dashboards:write', 'mcp:query'],\n create_workspace_metric: ['mcp:dashboards:write', 'mcp:query'],\n add_widget: ['mcp:dashboards:write'], update_widget: ['mcp:dashboards:write'],\n remove_widget: ['mcp:dashboards:write'], generate_widget: ['mcp:dashboards:write', 'mcp:query'],\n list_scheduled_reports: ['mcp:dashboards:read'],\n 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};\n\nexport function requiredScopes(name: string): readonly HostedScope[] {\n if (!Object.hasOwn(TOOL_SCOPES, name)) throw new HostedAuthError(403, 'tool_not_authorized');\n return TOOL_SCOPES[name];\n}\n\nexport function authorizeTool(name: string, principal: HostedPrincipal): void {\n if (principal.expiresAt !== undefined && principal.expiresAt <= Date.now() / 1000) throw new HostedAuthError(401, 'invalid_token');\n if (requiredScopes(name).some(scope => !principal.scopes.includes(scope))) throw new HostedAuthError(403, 'insufficient_scope');\n}\n\n/** Reject conflicting selectors even in raw body passthroughs before a handler can use them. */\nexport function assertBoundArguments(value: unknown, principal: HostedPrincipal): void {\n if (!value || typeof value !== 'object') return;\n for (const [key, child] of Object.entries(value)) {\n if (['workspaceId', 'workspaceName', 'companyId', 'userId'].includes(key) &&\n child !== principal[key as 'workspaceId' | 'workspaceName' | 'companyId' | 'userId']) {\n throw new HostedAuthError(403, 'principal_mismatch');\n }\n if (['scope', 'scopes', 'authUser', 'actorKind', 'credentialType', 'oauthClientId', 'operatorEmail',\n 'session_variables', 'isAllClient', 'isAdmin', 'serviceToken', 'apiToken', 'authorization'].includes(key)) {\n throw new HostedAuthError(403, 'identity_argument_forbidden');\n }\n if (['clientId', 'tenantId'].includes(key) &&\n (typeof child !== 'string' || !principal.tenantIds?.includes(child))) {\n throw new HostedAuthError(403, 'tenant_not_authorized');\n }\n assertBoundArguments(child, principal);\n }\n}\n","import { z } from 'zod';\nimport { principalSchema } from './auth.js';\nimport { HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedConfig } from './types.js';\n\n/** BE-3's documented principal endpoint is the trust boundary for opaque OAuth tokens. */\nexport function createProviderAdapter(config: HostedConfig): HostedAdapter {\n const issuer = new URL(config.issuer);\n if (issuer.protocol !== 'https:' || issuer.username || issuer.password || issuer.search || issuer.hash) {\n throw new Error('Invalid hosted authorization server configuration.');\n }\n const endpoint = `${config.issuer.replace(/\\/$/, '')}/oauth/principal`;\n const responseSchema = principalSchema.extend({ issuer: z.literal(config.issuer) }).strip();\n return {\n async verifyOAuth(token, resource) {\n try {\n // Only the explicitly configured issuer ever receives the token, for verification.\n // No redirects, arbitrary token URLs, cache, service-token fallback or REST replay.\n const response = await fetch(endpoint, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n redirect: 'error', signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) throw new HostedAuthError(response.status >= 500 ? 503 : 401, 'invalid_token');\n const { issuer: _issuer, ...principal } = responseSchema.parse(await response.json());\n if (principal.resource !== resource || principal.credentialType !== 'oauth') throw new HostedAuthError(401, 'invalid_token');\n return principal;\n } catch (error) {\n if (error instanceof HostedAuthError) throw error;\n throw new HostedAuthError(401, 'invalid_token');\n }\n },\n async resolvePatGrant() { throw new HostedAuthError(401, 'pat_membership_adapter_required'); },\n async executeRequest() { throw new HostedAuthError(503, 'delegated_backend_adapter_required'); },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/hosted/main.ts","../src/hosted/config.ts","../src/hosted/http.ts","../src/hosted/server.ts","../src/hosted/authorization.ts","../src/hosted/provider.ts"],"sourcesContent":["import { isAbsolute } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { loadHostedConfig } from './config.js';\nimport { FilePatStore } from './auth.js';\nimport { createHostedHttpServer } from './http.js';\nimport type { HostedAdapter } from './types.js';\nimport { createProviderAdapter } from './provider.js';\n\nexport async function startHosted(): Promise<void> {\n const config = loadHostedConfig();\n let adapter: HostedAdapter;\n if (config.testOnly) {\n const file = process.env.DATABRAIN_HOSTED_TEST_FIXTURES;\n if (!file || !isAbsolute(file)) throw new Error('Hosted test mode requires an absolute fixture store path.');\n const { createTestOnlyAdapter } = await import('./test-only.js');\n adapter = createTestOnlyAdapter(file);\n } else {\n const file = process.env.DATABRAIN_HOSTED_ADAPTER_MODULE;\n if (!file) {\n adapter = createProviderAdapter(config);\n process.stderr.write('[databrain] OAuth verification configured; backend data operations and PAT membership require a delegated adapter.\\n');\n } else {\n if (!isAbsolute(file)) throw new Error('DATABRAIN_HOSTED_ADAPTER_MODULE must be an absolute path.');\n try { adapter = (await import(pathToFileURL(file).href)).adapter; }\n catch { throw new Error('Could not load the trusted hosted adapter.'); }\n }\n if (!adapter || ['verifyOAuth', 'resolvePatGrant', 'executeRequest'].some(key =>\n typeof adapter[key as keyof HostedAdapter] !== 'function')) throw new Error('Invalid trusted hosted adapter.');\n }\n const patPath = process.env.DATABRAIN_HOSTED_PAT_STORE;\n if (patPath && !isAbsolute(patPath)) throw new Error('Hosted PAT store must be an absolute path.');\n const server = createHostedHttpServer(config, adapter, patPath ? new FilePatStore(patPath) : undefined);\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject);\n server.listen(config.port, 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 const publicUrl = validUrl(env.DATABRAIN_HOSTED_PUBLIC_URL, 'DATABRAIN_HOSTED_PUBLIC_URL');\n validUrl(env.DATABRAIN_HOSTED_ISSUER, 'DATABRAIN_HOSTED_ISSUER');\n if (publicUrl.pathname !== '/mcp') throw new Error('DATABRAIN_HOSTED_PUBLIC_URL must end in /mcp.');\n const port = Number(env.DATABRAIN_HOSTED_PORT || '3144');\n // 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: env.DATABRAIN_HOSTED_PUBLIC_URL!, issuer: env.DATABRAIN_HOSTED_ISSUER!, 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 } from './server.js';\nimport { createBearerVerifier } from './auth.js';\nimport { authorizeTool, assertBoundArguments, requiredScopes } from './authorization.js';\nimport { HOSTED_SCOPES, HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedConfig, PatStore } from './types.js';\n\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 if (body?.method === 'tools/call' && typeof body.params?.name === 'string') {\n try {\n authorizeTool(body.params.name, principal);\n assertBoundArguments(body.params.arguments, principal);\n } catch (error) {\n if (error instanceof HostedAuthError && error.code === 'insufficient_scope') {\n res.setHeader('WWW-Authenticate', `Bearer error=\"insufficient_scope\", resource_metadata=\"${metadataUrl}\", scope=\"${requiredScopes(body.params.name).join(' ')}\"`);\n }\n throw error;\n }\n }\n const mcp = createHostedToolServer(principal, adapter);\n const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });\n let closed = false;\n const close = () => { if (!closed) { closed = true; void mcp.close(); } };\n res.once('close', close);\n try {\n await mcp.connect(transport);\n await transport.handleRequest(req, res, body);\n } finally { if (res.writableEnded) close(); }\n } finally { active--; }\n } catch (error) {\n if (res.headersSent) { res.end(); return; }\n const status = error instanceof HostedAuthError ? error.status : 503;\n if (status === 401) res.setHeader('WWW-Authenticate', `Bearer error=\"invalid_token\", resource_metadata=\"${metadataUrl}\"`);\n reply(res, status, { error: error instanceof HostedAuthError ? error.code : 'temporarily_unavailable' });\n }\n });\n server.requestTimeout = 30_000;\n server.headersTimeout = 10_000;\n server.keepAliveTimeout = 5_000;\n return server;\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { instrument } from '@posthog/mcp';\nimport { PostHog } from 'posthog-node';\nimport { DatabrainClient } from '../client/databrain-client.js';\nimport type { RequestOptions } from '../client/types.js';\nimport { registerAllTools } from '../tools/registry.js';\nimport { registerAllResources } from '../resources/registry.js';\nimport { registerAllPrompts } from '../prompts/registry.js';\nimport { MCP_SERVER_VERSION } from '../version.js';\nimport { DatabrainApiError, toolError, toolSuccess } from '../utils/errors.js';\nimport { toolContext } from '../utils/tool-context.js';\nimport { assertBoundArguments, authorizeTool } from './authorization.js';\nimport { HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedPrincipal } from './types.js';\n\n// Stdio's instrumentation changes the wire schema (required `context`). Use that\n// same implementation, with capture disabled until hosted telemetry has an owner.\n// No bearer, request headers, tool arguments or results are sent to analytics.\nconst schemaInstrumentation = new PostHog('hosted-contract-only', { disabled: true });\n\n/** A request-local client: shared APIs/tools run unchanged, through a delegated executor. */\nclass HostedClient extends DatabrainClient {\n constructor(private readonly adapter: HostedAdapter, private readonly principal: HostedPrincipal) {\n super({ apiUrl: 'https://api.usedatabrain.com', demoDomain: 'https://demo.usedatabrain.com' });\n }\n // A generated API credential must never replace the authorizing hosted principal.\n override setRuntimeApiToken(_token: string): void {}\n\n override async request<T>(options: RequestOptions): Promise<T> {\n try {\n const name = toolContext.getStore()?.tool;\n if (!name) throw new HostedAuthError(403, 'tool_not_authorized');\n authorizeTool(name, this.principal);\n assertBoundArguments(options.body, this.principal);\n assertBoundArguments(options.query, this.principal);\n const raw = await this.adapter.executeRequest(structuredClone(options), structuredClone(this.principal));\n if (!options.skipDataUnwrap && raw && typeof raw === 'object' && !Array.isArray(raw) &&\n 'data' in raw && raw.data !== null && raw.data !== undefined) return raw.data as T;\n return raw as T;\n } catch (error) {\n // The adapter may encounter secret-bearing remote bodies. Do not pass them to tools/logs.\n const status = error instanceof HostedAuthError ? error.status : 503;\n throw new DatabrainApiError(status, 'HOSTED_REQUEST_DENIED',\n 'The authorized backend could not complete this request. Check workspace access and hosted integration.');\n }\n }\n}\n\nexport function createHostedToolServer(principal: HostedPrincipal, adapter: HostedAdapter): McpServer {\n const bound = structuredClone(principal);\n const server = new McpServer({ name: 'databrain', version: MCP_SERVER_VERSION }, {\n instructions: 'Hosted Databrain: each request is authenticated as a user in one authorized workspace. ' +\n 'Use whoami to inspect the verified identity and scopes. Never ask for a service token or put credentials in tool arguments. ' +\n 'Workspace and tenant access come from the bearer principal. Existing dry-run and production-confirmation rules still apply.',\n });\n\n // The existing shared registry already wraps server.tool for intent telemetry. Layer\n // hosted policy underneath it; keep every schema, description, annotation and handler.\n const register = server.tool.bind(server) as (...args: any[]) => unknown;\n server.tool = ((...args: any[]) => {\n const name = args[0] as string;\n const index = args.length - 1;\n const handler = args[index] as (...values: any[]) => unknown;\n const shape = args[2] as Record<string, unknown>;\n args[index] = async (...values: any[]) => {\n try {\n authorizeTool(name, bound);\n assertBoundArguments(values[0], bound);\n if (name === 'whoami') {\n return toolSuccess(JSON.stringify({\n status: 'authenticated', activeBearer: bound.credentialType,\n actorKind: bound.actorKind, userId: bound.userId, companyId: bound.companyId,\n workspaceId: bound.workspaceId, workspaceName: bound.workspaceName,\n oauthClientId: bound.clientId, scopes: bound.scopes,\n }));\n }\n // Required selectors retain their public schema; matching inputs are always replaced\n // by the trusted value. Optional selectors are supplied when that tool declares them.\n if (shape && typeof shape === 'object' && values[0] && typeof values[0] === 'object') {\n for (const key of ['workspaceName', 'workspaceId'] as const) {\n if (Object.hasOwn(shape, key) && bound[key] !== undefined) values[0][key] = bound[key];\n }\n }\n return await handler(...values);\n } catch {\n return toolError('Hosted authorization denied this tool call. Check the granted scopes, workspace and tenant access.');\n }\n };\n return register(...args);\n }) as McpServer['tool'];\n\n registerAllTools(server, new HostedClient(adapter, bound));\n registerAllResources(server);\n registerAllPrompts(server);\n instrument(server, schemaInstrumentation, { beforeSend: () => null });\n return server;\n}\n","import { HostedAuthError } from './types.js';\nimport type { HostedPrincipal, HostedScope } from './types.js';\n\n/** Explicit exhaustive policy: a newly registered tool is denied until it gets a policy. */\nexport const TOOL_SCOPES: Readonly<Record<string, readonly HostedScope[]>> = {\n whoami: [],\n list_datasources: ['mcp:datamarts:read'], discover_datasource_schema: ['mcp:datamarts:read'],\n list_datamarts: ['mcp:datamarts:read'], list_workspaces: ['mcp:datamarts:read'],\n sync_datasource: ['mcp:datamarts:write'], create_datamart: ['mcp:datamarts:write'],\n list_data_apps: ['mcp:dashboards:read'], list_tenants: ['mcp:dashboards:read'],\n create_data_app: ['mcp:embeds:write'], list_api_tokens: ['mcp:embeds:write'],\n create_api_token: ['mcp:embeds:write'], rotate_api_token: ['mcp:embeds:write'],\n create_embed: ['mcp:embeds:write'], list_embeds: ['mcp:dashboards:read'],\n get_embed_details: ['mcp:dashboards:read'], update_embed: ['mcp:embeds:write'],\n customize_embed_theme: ['mcp:embeds:write'], get_demo_link: ['mcp:embeds:write'],\n generate_guest_token: ['mcp:embeds:write'], generate_embed_code: ['mcp:dashboards:read'],\n list_dashboards: ['mcp:dashboards:read'], create_dashboard: ['mcp:dashboards:write'],\n get_dashboard: ['mcp:dashboards:read'], generate_dashboard_tiles: ['mcp:dashboards:write', 'mcp:query'],\n ask_question: ['mcp:query'], get_semantic_layer: ['mcp:datamarts:read'],\n update_semantic_layer: ['mcp:datamarts:write'], start_semantic_layer_generation: ['mcp:datamarts:write'],\n get_semantic_layer_generation_status: ['mcp:datamarts:read'],\n 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'], semantic_readiness: ['mcp:datamarts:read'],\n list_widgets: ['mcp:dashboards:read'], validate_metric_spec: ['mcp:dashboards:read', 'mcp:query'],\n create_or_update_metric_from_query: ['mcp:dashboards:write', 'mcp:query'],\n create_workspace_metric: ['mcp:dashboards:write', 'mcp:query'],\n add_widget: ['mcp:dashboards:write'], update_widget: ['mcp:dashboards:write'],\n remove_widget: ['mcp:dashboards:write'], generate_widget: ['mcp:dashboards:write', 'mcp:query'],\n list_scheduled_reports: ['mcp:dashboards:read'],\n 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};\n\nexport function requiredScopes(name: string): readonly HostedScope[] {\n if (!Object.hasOwn(TOOL_SCOPES, name)) throw new HostedAuthError(403, 'tool_not_authorized');\n return TOOL_SCOPES[name];\n}\n\nexport function authorizeTool(name: string, principal: HostedPrincipal): void {\n if (principal.expiresAt !== undefined && principal.expiresAt <= Date.now() / 1000) throw new HostedAuthError(401, 'invalid_token');\n if (requiredScopes(name).some(scope => !principal.scopes.includes(scope))) throw new HostedAuthError(403, 'insufficient_scope');\n}\n\n/** Reject conflicting selectors even in raw body passthroughs before a handler can use them. */\nexport function assertBoundArguments(value: unknown, principal: HostedPrincipal): void {\n if (!value || typeof value !== 'object') return;\n for (const [key, child] of Object.entries(value)) {\n if (['workspaceId', 'workspaceName', 'companyId', 'userId'].includes(key) &&\n child !== principal[key as 'workspaceId' | 'workspaceName' | 'companyId' | 'userId']) {\n throw new HostedAuthError(403, 'principal_mismatch');\n }\n if (['scope', 'scopes', 'authUser', 'actorKind', 'credentialType', 'oauthClientId', 'operatorEmail',\n 'session_variables', 'isAllClient', 'isAdmin', 'serviceToken', 'apiToken', 'authorization'].includes(key)) {\n throw new HostedAuthError(403, 'identity_argument_forbidden');\n }\n if (['clientId', 'tenantId'].includes(key) &&\n (typeof child !== 'string' || !principal.tenantIds?.includes(child))) {\n throw new HostedAuthError(403, 'tenant_not_authorized');\n }\n assertBoundArguments(child, principal);\n }\n}\n","import { z } from 'zod';\nimport { principalSchema } from './auth.js';\nimport { HostedAuthError } from './types.js';\nimport type { HostedAdapter, HostedConfig } from './types.js';\n\n/** BE-3's documented principal endpoint is the trust boundary for opaque OAuth tokens. */\nexport function createProviderAdapter(config: HostedConfig): HostedAdapter {\n const issuer = new URL(config.issuer);\n if (issuer.protocol !== 'https:' || issuer.username || issuer.password || issuer.search || issuer.hash) {\n throw new Error('Invalid hosted authorization server configuration.');\n }\n const endpoint = `${config.issuer.replace(/\\/$/, '')}/oauth/principal`;\n const responseSchema = principalSchema.extend({ issuer: z.literal(config.issuer) }).strip();\n return {\n async verifyOAuth(token, resource) {\n try {\n // Only the explicitly configured issuer ever receives the token, for verification.\n // No redirects, arbitrary token URLs, cache, service-token fallback or REST replay.\n const response = await fetch(endpoint, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },\n redirect: 'error', signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) throw new HostedAuthError(response.status >= 500 ? 503 : 401, 'invalid_token');\n const { issuer: _issuer, ...principal } = responseSchema.parse(await response.json());\n if (principal.resource !== resource || principal.credentialType !== 'oauth') throw new HostedAuthError(401, 'invalid_token');\n return principal;\n } catch (error) {\n if (error instanceof HostedAuthError) throw error;\n throw new HostedAuthError(401, 'invalid_token');\n }\n },\n async resolvePatGrant() { throw new HostedAuthError(401, 'pat_membership_adapter_required'); },\n async executeRequest() { throw new HostedAuthError(503, 'delegated_backend_adapter_required'); },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databrainhq/mcp-server",
3
- "version": "0.3.0-rc.5",
3
+ "version": "0.3.0-rc.7",
4
4
  "description": "MCP server for Databrain embedded analytics - connect databases, create datamarts, configure embeds, and generate frontend code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -40,6 +40,7 @@
40
40
  "esbuild": "0.27.3",
41
41
  "fflate": "0.8.3",
42
42
  "posthog-node": "^5.21.2",
43
+ "yaml": "^2.9.1",
43
44
  "zod": "^3.24.0"
44
45
  },
45
46
  "devDependencies": {