sql-preview 0.6.4 → 0.6.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.
@@ -22581,7 +22581,7 @@ var require_view = __commonJS({
22581
22581
  var debug = require_src()("express:view");
22582
22582
  var path8 = require("node:path");
22583
22583
  var fs11 = require("node:fs");
22584
- var dirname2 = path8.dirname;
22584
+ var dirname3 = path8.dirname;
22585
22585
  var basename = path8.basename;
22586
22586
  var extname = path8.extname;
22587
22587
  var join7 = path8.join;
@@ -22620,7 +22620,7 @@ var require_view = __commonJS({
22620
22620
  for (var i = 0; i < roots.length && !path9; i++) {
22621
22621
  var root = roots[i];
22622
22622
  var loc = resolve2(root, name);
22623
- var dir = dirname2(loc);
22623
+ var dir = dirname3(loc);
22624
22624
  var file = basename(loc);
22625
22625
  path9 = this.resolve(dir, file);
22626
22626
  }
@@ -76257,18 +76257,103 @@ var init_errors = __esm({
76257
76257
  }
76258
76258
  });
76259
76259
 
76260
+ // src/common/tabProjection.ts
76261
+ function beginTabRun(options) {
76262
+ return {
76263
+ status: "loading",
76264
+ columns: [],
76265
+ rows: [],
76266
+ wasTruncated: false,
76267
+ maxRows: options?.maxRows,
76268
+ remoteTabId: void 0,
76269
+ supportsPagination: void 0,
76270
+ error: void 0,
76271
+ errorDetails: void 0,
76272
+ executionTimeMs: void 0,
76273
+ executedAt: void 0
76274
+ };
76275
+ }
76276
+ function applyQueryPage(run, page) {
76277
+ if (page.columns && page.columns.length > 0) {
76278
+ run.columns = page.columns;
76279
+ }
76280
+ if (page.remoteTabId) {
76281
+ run.remoteTabId = page.remoteTabId;
76282
+ }
76283
+ if (page.supportsPagination !== void 0) {
76284
+ run.supportsPagination = page.supportsPagination;
76285
+ }
76286
+ if (page.data && page.data.length > 0) {
76287
+ for (const row of page.data) {
76288
+ if (row !== void 0) {
76289
+ run.rows.push(row);
76290
+ }
76291
+ }
76292
+ }
76293
+ if (run.maxRows !== void 0 && run.rows.length >= run.maxRows) {
76294
+ run.wasTruncated = true;
76295
+ }
76296
+ return run;
76297
+ }
76298
+ function completeTabRun(run, timing) {
76299
+ run.status = "success";
76300
+ if (timing?.startedAt !== void 0) {
76301
+ run.executionTimeMs = Date.now() - timing.startedAt;
76302
+ }
76303
+ run.executedAt = (/* @__PURE__ */ new Date()).toISOString();
76304
+ return run;
76305
+ }
76306
+ function failTabRun(run, error2, options) {
76307
+ run.status = "error";
76308
+ if (options?.aborted) {
76309
+ run.error = "Query cancelled by user";
76310
+ run.errorDetails = void 0;
76311
+ } else {
76312
+ run.error = error2 instanceof Error ? error2.message : String(error2);
76313
+ run.errorDetails = serializeErrorDetails(error2);
76314
+ }
76315
+ run.executedAt = (/* @__PURE__ */ new Date()).toISOString();
76316
+ return run;
76317
+ }
76318
+ var init_tabProjection = __esm({
76319
+ "src/common/tabProjection.ts"() {
76320
+ "use strict";
76321
+ init_errors();
76322
+ }
76323
+ });
76324
+
76260
76325
  // src/server/query/QueryOperationRunner.ts
76261
- var QueryOperationRunner;
76326
+ var _QueryOperationRunner, QueryOperationRunner;
76262
76327
  var init_QueryOperationRunner = __esm({
76263
76328
  "src/server/query/QueryOperationRunner.ts"() {
76264
76329
  "use strict";
76265
- init_errors();
76330
+ init_tabProjection();
76266
76331
  init_ConsoleLogger();
76267
- QueryOperationRunner = class {
76332
+ _QueryOperationRunner = class {
76268
76333
  constructor(sessionManager, queryExecutor) {
76269
76334
  this.sessionManager = sessionManager;
76270
76335
  this.queryExecutor = queryExecutor;
76271
76336
  }
76337
+ /** Updates the row cap live; delegates to the underlying executor. */
76338
+ setMaxRows(value) {
76339
+ this.queryExecutor.setMaxRows(value);
76340
+ }
76341
+ /**
76342
+ * Re-runs `query` outside of tab/session bookkeeping, uncapped by the display row limit, for
76343
+ * CSV export. Mirrors the VS Code extension's ExportService: exports re-execute rather than
76344
+ * reusing the (possibly truncated) rows already stored on the tab.
76345
+ */
76346
+ exportQuery(query, sessionId, abortSignal) {
76347
+ return this.queryExecutor.execute(
76348
+ query,
76349
+ sessionId,
76350
+ void 0,
76351
+ abortSignal,
76352
+ void 0,
76353
+ void 0,
76354
+ _QueryOperationRunner.EXPORT_MAX_ROWS
76355
+ );
76356
+ }
76272
76357
  async run(request) {
76273
76358
  const prepared = this.prepareQueryOperation(request);
76274
76359
  const waitForResult = request.waitForResult ?? true;
@@ -76502,6 +76587,9 @@ var init_QueryOperationRunner = __esm({
76502
76587
  if (!tab) {
76503
76588
  return;
76504
76589
  }
76590
+ const startedAt = Date.now();
76591
+ const run = beginTabRun({ maxRows: this.queryExecutor.maxRows });
76592
+ tab.rows = run.rows;
76505
76593
  try {
76506
76594
  const generator = this.queryExecutor.execute(
76507
76595
  sql,
@@ -76511,51 +76599,55 @@ var init_QueryOperationRunner = __esm({
76511
76599
  connectionProfile,
76512
76600
  authHeader
76513
76601
  );
76514
- let columns = [];
76515
76602
  for await (const page of generator) {
76516
76603
  if (controller.signal.aborted) {
76517
76604
  throw new Error("Query cancelled by user");
76518
76605
  }
76519
- if (page.columns) {
76520
- columns = page.columns;
76521
- tab.columns = columns;
76606
+ applyQueryPage(run, page);
76607
+ tab.columns = run.columns;
76608
+ if (run.supportsPagination !== void 0) {
76609
+ tab.supportsPagination = run.supportsPagination;
76522
76610
  }
76523
76611
  if (page.data && page.data.length > 0) {
76524
- for (const row of page.data) {
76525
- tab.rows.push(row);
76526
- }
76527
76612
  this.sessionManager.updateTab(sessionId, tabId, {
76528
- columns: page.columns ?? tab.columns,
76529
- rows: tab.rows,
76613
+ columns: run.columns,
76614
+ rows: run.rows,
76530
76615
  status: "loading"
76531
76616
  });
76532
76617
  }
76533
- if (page.supportsPagination !== void 0) {
76534
- tab.supportsPagination = page.supportsPagination;
76535
- }
76536
76618
  }
76537
76619
  if (controller.signal.aborted) {
76538
76620
  throw new Error("Query cancelled by user");
76539
76621
  }
76622
+ completeTabRun(run, { startedAt });
76623
+ Object.assign(tab, {
76624
+ executionTimeMs: run.executionTimeMs,
76625
+ wasTruncated: run.wasTruncated,
76626
+ executedAt: run.executedAt
76627
+ });
76540
76628
  this.sessionManager.updateTab(sessionId, tabId, {
76541
76629
  status: "success",
76542
- totalRowsInFirstBatch: tab.rows.length
76630
+ totalRowsInFirstBatch: run.rows.length,
76631
+ executionTimeMs: run.executionTimeMs,
76632
+ wasTruncated: run.wasTruncated,
76633
+ executedAt: run.executedAt
76543
76634
  });
76544
76635
  } catch (err) {
76545
76636
  if (session.abortControllers.get(tabId) !== controller) {
76546
76637
  return;
76547
76638
  }
76548
- const error2 = controller.signal.aborted ? "Query cancelled by user" : err instanceof Error ? err.message : String(err);
76549
- const errorDetails = controller.signal.aborted ? void 0 : serializeErrorDetails(err);
76639
+ failTabRun(run, err, { aborted: controller.signal.aborted });
76550
76640
  Object.assign(tab, {
76551
76641
  status: "error",
76552
- error: error2,
76553
- errorDetails
76642
+ error: run.error,
76643
+ errorDetails: run.errorDetails,
76644
+ executedAt: run.executedAt
76554
76645
  });
76555
76646
  this.sessionManager.updateTab(sessionId, tabId, {
76556
76647
  status: "error",
76557
- error: error2,
76558
- errorDetails
76648
+ error: run.error,
76649
+ errorDetails: run.errorDetails,
76650
+ executedAt: run.executedAt
76559
76651
  });
76560
76652
  }
76561
76653
  }
@@ -76587,6 +76679,9 @@ var init_QueryOperationRunner = __esm({
76587
76679
  columns: tab.columns || [],
76588
76680
  rows: tab.rows || [],
76589
76681
  rowCount,
76682
+ ...tab.wasTruncated !== void 0 ? { wasTruncated: tab.wasTruncated } : {},
76683
+ ...tab.executionTimeMs !== void 0 ? { executionTimeMs: tab.executionTimeMs } : {},
76684
+ ...tab.executedAt !== void 0 ? { executedAt: tab.executedAt } : {},
76590
76685
  ...tab.supportsPagination !== void 0 ? { supportsPagination: tab.supportsPagination } : {}
76591
76686
  };
76592
76687
  }
@@ -76611,6 +76706,8 @@ var init_QueryOperationRunner = __esm({
76611
76706
  return () => abortSignal?.removeEventListener("abort", abortFromCaller);
76612
76707
  }
76613
76708
  };
76709
+ QueryOperationRunner = _QueryOperationRunner;
76710
+ QueryOperationRunner.EXPORT_MAX_ROWS = 5e6;
76614
76711
  }
76615
76712
  });
76616
76713
 
@@ -76772,7 +76869,319 @@ var init_querySplitter = __esm({
76772
76869
  }
76773
76870
  });
76774
76871
 
76872
+ // src/common/toolProfiles.ts
76873
+ function isToolProfileName(value) {
76874
+ return value === "vscode" || value === "headless" || value === "readonly";
76875
+ }
76876
+ var ALL_TOOLS, READ_ONLY_TOOLS, TOOL_PROFILES, WEB_PROXIED_TOOLS;
76877
+ var init_toolProfiles = __esm({
76878
+ "src/common/toolProfiles.ts"() {
76879
+ "use strict";
76880
+ ALL_TOOLS = [
76881
+ "list_connectors",
76882
+ "run_query",
76883
+ "get_tab_info",
76884
+ "list_sessions",
76885
+ "list_connections",
76886
+ "save_connection",
76887
+ "request_connection_credentials",
76888
+ "test_connection",
76889
+ "delete_connection",
76890
+ "list_schemas",
76891
+ "list_tables",
76892
+ "describe_table",
76893
+ "cancel_query",
76894
+ "close_tab"
76895
+ ];
76896
+ READ_ONLY_TOOLS = [
76897
+ "list_connectors",
76898
+ "run_query",
76899
+ "get_tab_info",
76900
+ "list_sessions",
76901
+ "list_connections",
76902
+ "test_connection",
76903
+ "list_schemas",
76904
+ "list_tables",
76905
+ "describe_table",
76906
+ "cancel_query",
76907
+ "close_tab"
76908
+ ];
76909
+ TOOL_PROFILES = {
76910
+ // Human-in-the-loop VS Code surface: full toolset, safe mode from settings.
76911
+ vscode: { name: "vscode", tools: ALL_TOOLS, forceSafeMode: false },
76912
+ // Autonomous agents: starts as the full current toolset (RFC-062); will
76913
+ // diverge toward synchronous/structured tools without breaking clients.
76914
+ headless: { name: "headless", tools: ALL_TOOLS, forceSafeMode: false },
76915
+ // Safe exploration: no profile/credential mutation, SQL gating always on.
76916
+ readonly: { name: "readonly", tools: READ_ONLY_TOOLS, forceSafeMode: true }
76917
+ };
76918
+ WEB_PROXIED_TOOLS = [
76919
+ "list_connectors",
76920
+ "list_connections",
76921
+ "save_connection",
76922
+ "test_connection",
76923
+ "delete_connection"
76924
+ ];
76925
+ }
76926
+ });
76927
+
76928
+ // src/server/tools/toolDefinitions.ts
76929
+ var TOOL_DEFINITIONS;
76930
+ var init_toolDefinitions = __esm({
76931
+ "src/server/tools/toolDefinitions.ts"() {
76932
+ "use strict";
76933
+ TOOL_DEFINITIONS = [
76934
+ {
76935
+ name: "list_connectors",
76936
+ description: "List all supported database connector types and their configuration JSON schemas. Use this to dynamically generate connection setup forms in the UI.",
76937
+ inputSchema: {
76938
+ type: "object",
76939
+ properties: {}
76940
+ }
76941
+ },
76942
+ {
76943
+ name: "run_query",
76944
+ description: "Execute a SQL query for a specific session. Waits for the query to complete and returns the full results including rows and column definitions. The tool result contains structured data (rows + columns) that will be rendered in the SQL Preview data grid UI. If the session does not exist, it will be auto-registered.",
76945
+ inputSchema: {
76946
+ type: "object",
76947
+ properties: {
76948
+ sql: { type: "string", description: "The SQL query to execute" },
76949
+ session: {
76950
+ type: "string",
76951
+ description: "The Session ID to run this query in. Use list_sessions to discover existing sessions, or provide a new ID to auto-create a session."
76952
+ },
76953
+ displayName: {
76954
+ type: "string",
76955
+ description: 'Display name for a new session (only used when auto-registering). Defaults to "MCP Client".'
76956
+ },
76957
+ connectionId: {
76958
+ type: "string",
76959
+ description: "Optional Connection ID to use a specific stored connection. Use list_connections to find available IDs."
76960
+ },
76961
+ connectionProfile: {
76962
+ type: "object",
76963
+ description: "Optional ad-hoc connection profile (including credentials) to use for this query."
76964
+ },
76965
+ authHeader: {
76966
+ type: "string",
76967
+ description: "Optional authorization header for the selected connection."
76968
+ },
76969
+ tabId: {
76970
+ type: "string",
76971
+ description: "Optional Tab ID to use for the result. If provided, the daemon will use this ID instead of generating a new one."
76972
+ },
76973
+ newTab: {
76974
+ type: "boolean",
76975
+ description: "When false, reuse the active tab if one exists. Defaults to true."
76976
+ },
76977
+ waitForResult: {
76978
+ type: "boolean",
76979
+ description: "When false, start the query and return immediately with structured started status. Defaults to true."
76980
+ }
76981
+ },
76982
+ required: ["sql", "session"]
76983
+ },
76984
+ _meta: {
76985
+ ui: {
76986
+ resourceUri: "ui://sql-preview/results-grid"
76987
+ }
76988
+ }
76989
+ },
76990
+ {
76991
+ name: "get_tab_info",
76992
+ description: 'Get information about a result tab in a session. Defaults to a "preview" mode with metadata and a small sample. Use mode="page" to retrieve full pages of rows.',
76993
+ inputSchema: {
76994
+ type: "object",
76995
+ properties: {
76996
+ session: {
76997
+ type: "string",
76998
+ description: "The Session ID. Use list_sessions to discover available sessions."
76999
+ },
77000
+ tabId: {
77001
+ type: "string",
77002
+ description: "The Tab ID to retrieve (optional, defaults to the most recently active tab in the session)"
77003
+ },
77004
+ mode: {
77005
+ type: "string",
77006
+ enum: ["preview", "page"],
77007
+ description: 'Retrieval mode. "preview" (default) returns stats + 10 rows. "page" returns specific rows defined by offset/limit.'
77008
+ },
77009
+ offset: {
77010
+ type: "number",
77011
+ description: 'Row offset for "page" mode (default: 0)'
77012
+ },
77013
+ limit: {
77014
+ type: "number",
77015
+ description: 'Number of rows to return. Default: 100 for "page" mode, 10 for "preview" mode.'
77016
+ }
77017
+ },
77018
+ required: ["session"]
77019
+ }
77020
+ },
77021
+ {
77022
+ name: "list_sessions",
77023
+ description: "List all active sessions managed by the daemon. Use this first to discover existing session IDs before running queries or checking results.",
77024
+ inputSchema: {
77025
+ type: "object",
77026
+ properties: {}
77027
+ }
77028
+ },
77029
+ {
77030
+ name: "list_connections",
77031
+ description: "List available database connections managed by the daemon. Returns connection IDs and metadata (excluding passwords).",
77032
+ inputSchema: {
77033
+ type: "object",
77034
+ properties: {}
77035
+ }
77036
+ },
77037
+ {
77038
+ name: "save_connection",
77039
+ description: "Save or update a connection profile.",
77040
+ inputSchema: {
77041
+ type: "object",
77042
+ properties: {
77043
+ connectionProfile: {
77044
+ type: "object",
77045
+ description: 'The connection profile to save (must include "id", "name", "type").'
77046
+ }
77047
+ },
77048
+ required: ["connectionProfile"]
77049
+ }
77050
+ },
77051
+ {
77052
+ name: "request_connection_credentials",
77053
+ description: "Create a short-lived local browser form where the user can enter credentials for a saved connection without exposing the secret in the MCP transcript. Share only the returned URL with the user; never ask them to paste credentials into chat.",
77054
+ inputSchema: {
77055
+ type: "object",
77056
+ properties: {
77057
+ connectionId: {
77058
+ type: "string",
77059
+ description: "Connection ID that should receive the safely submitted password."
77060
+ }
77061
+ },
77062
+ required: ["connectionId"]
77063
+ }
77064
+ },
77065
+ {
77066
+ name: "test_connection",
77067
+ description: "Test connectivity for a specific connection profile.",
77068
+ inputSchema: {
77069
+ type: "object",
77070
+ properties: {
77071
+ connectionId: {
77072
+ type: "string",
77073
+ description: "Optional Connection ID to test."
77074
+ },
77075
+ type: {
77076
+ type: "string",
77077
+ description: "Connector type (e.g., trino) when testing unsaved profile."
77078
+ },
77079
+ connectionProfile: {
77080
+ type: "object",
77081
+ description: "The configuration object when testing unsaved profile."
77082
+ },
77083
+ authHeader: {
77084
+ type: "string",
77085
+ description: "Optional auth header."
77086
+ }
77087
+ }
77088
+ }
77089
+ },
77090
+ {
77091
+ name: "delete_connection",
77092
+ description: "Delete a connection profile.",
77093
+ inputSchema: {
77094
+ type: "object",
77095
+ properties: {
77096
+ connectionId: {
77097
+ type: "string",
77098
+ description: "The Connection ID to delete."
77099
+ }
77100
+ },
77101
+ required: ["connectionId"]
77102
+ }
77103
+ },
77104
+ {
77105
+ name: "list_schemas",
77106
+ description: "List database schemas (namespaces) available in a stored connection.",
77107
+ inputSchema: {
77108
+ type: "object",
77109
+ properties: {
77110
+ connectionId: { type: "string", description: "Connection ID to inspect." },
77111
+ catalog: { type: "string", description: "Optional catalog filter (e.g. for Trino)." }
77112
+ },
77113
+ required: ["connectionId"]
77114
+ }
77115
+ },
77116
+ {
77117
+ name: "list_tables",
77118
+ description: "List tables and views in a specific schema for a stored connection.",
77119
+ inputSchema: {
77120
+ type: "object",
77121
+ properties: {
77122
+ connectionId: { type: "string", description: "Connection ID to inspect." },
77123
+ schema: { type: "string", description: "Schema name to list tables from." },
77124
+ catalog: { type: "string", description: "Optional catalog (e.g. for Trino/BigQuery)." }
77125
+ },
77126
+ required: ["connectionId", "schema"]
77127
+ }
77128
+ },
77129
+ {
77130
+ name: "describe_table",
77131
+ description: "Return column definitions and metadata for a specific table.",
77132
+ inputSchema: {
77133
+ type: "object",
77134
+ properties: {
77135
+ connectionId: { type: "string", description: "Connection ID to inspect." },
77136
+ table: { type: "string", description: "Table name." },
77137
+ schema: { type: "string", description: "Schema containing the table." },
77138
+ catalog: { type: "string", description: "Optional catalog (e.g. for Trino/BigQuery)." }
77139
+ },
77140
+ required: ["connectionId", "table", "schema"]
77141
+ }
77142
+ },
77143
+ {
77144
+ name: "cancel_query",
77145
+ description: 'Cancel a running query. Use get_tab_info first to check if a query is still in "loading" state before cancelling.',
77146
+ inputSchema: {
77147
+ type: "object",
77148
+ properties: {
77149
+ session: { type: "string", description: "The Session ID" },
77150
+ tabId: { type: "string", description: "The Tab ID to cancel" }
77151
+ },
77152
+ required: ["session", "tabId"]
77153
+ }
77154
+ },
77155
+ {
77156
+ name: "close_tab",
77157
+ description: "Close a tab and remove it from the session.",
77158
+ inputSchema: {
77159
+ type: "object",
77160
+ properties: {
77161
+ session: { type: "string", description: "The Session ID" },
77162
+ tabId: { type: "string", description: "The Tab ID to close" }
77163
+ },
77164
+ required: ["session", "tabId"]
77165
+ }
77166
+ }
77167
+ ];
77168
+ }
77169
+ });
77170
+
76775
77171
  // src/server/DaemonMcpToolManager.ts
77172
+ function resolveProfile(explicit) {
77173
+ if (explicit) {
77174
+ return TOOL_PROFILES[explicit];
77175
+ }
77176
+ const fromEnv = process.env["MCP_PROFILE"];
77177
+ if (fromEnv !== void 0) {
77178
+ if (isToolProfileName(fromEnv)) {
77179
+ return TOOL_PROFILES[fromEnv];
77180
+ }
77181
+ logger.warn(`Ignoring unknown MCP_PROFILE '${fromEnv}'; using 'vscode'.`);
77182
+ }
77183
+ return TOOL_PROFILES.vscode;
77184
+ }
76776
77185
  function isSnowflakeBrowserSsoProfile(value) {
76777
77186
  if (!value || typeof value !== "object") {
76778
77187
  return false;
@@ -76869,6 +77278,8 @@ var init_DaemonMcpToolManager = __esm({
76869
77278
  init_QueryOperationRunner();
76870
77279
  init_QueryToolResultCodecs();
76871
77280
  init_querySplitter();
77281
+ init_toolProfiles();
77282
+ init_toolDefinitions();
76872
77283
  WRITE_KEYWORDS = /\b(ALTER|CALL|COPY|CREATE|DELETE|DROP|EXEC|EXECUTE|GRANT|INSERT|INSTALL|LOAD|MERGE|REPLACE|REVOKE|SET|TRUNCATE|UPDATE|VACUUM)\b/i;
76873
77284
  READ_ONLY_START_KEYWORDS = /* @__PURE__ */ new Set(["SELECT", "SHOW", "DESCRIBE", "EXPLAIN", "WITH"]);
76874
77285
  DaemonMcpToolManager = class {
@@ -76879,246 +77290,34 @@ var init_DaemonMcpToolManager = __esm({
76879
77290
  this.connectorRegistry = connectorRegistry;
76880
77291
  this.options = options;
76881
77292
  this.safeMode = options.safeMode ?? true;
77293
+ this.profile = resolveProfile(options.profile);
77294
+ this.profileTools = new Set(this.profile.tools);
76882
77295
  this.queryOperationRunner = queryOperationRunner ?? new QueryOperationRunner(sessionManager, queryExecutor);
76883
77296
  }
77297
+ get profileName() {
77298
+ return this.profile.name;
77299
+ }
77300
+ get isSafeMode() {
77301
+ return this.profile.forceSafeMode || this.safeMode;
77302
+ }
77303
+ /** Updates the read-only enforcement for agent queries live (e.g. from a settings-panel save). */
77304
+ setSafeMode(value) {
77305
+ this.safeMode = value;
77306
+ }
76884
77307
  getTools() {
76885
- return [
76886
- {
76887
- name: "list_connectors",
76888
- description: "List all supported database connector types and their configuration JSON schemas. Use this to dynamically generate connection setup forms in the UI.",
76889
- inputSchema: {
76890
- type: "object",
76891
- properties: {}
76892
- }
76893
- },
76894
- {
76895
- name: "run_query",
76896
- description: "Execute a SQL query for a specific session. Waits for the query to complete and returns the full results including rows and column definitions. The tool result contains structured data (rows + columns) that will be rendered in the SQL Preview data grid UI. If the session does not exist, it will be auto-registered.",
76897
- inputSchema: {
76898
- type: "object",
76899
- properties: {
76900
- sql: { type: "string", description: "The SQL query to execute" },
76901
- session: {
76902
- type: "string",
76903
- description: "The Session ID to run this query in. Use list_sessions to discover existing sessions, or provide a new ID to auto-create a session."
76904
- },
76905
- displayName: {
76906
- type: "string",
76907
- description: 'Display name for a new session (only used when auto-registering). Defaults to "MCP Client".'
76908
- },
76909
- connectionId: {
76910
- type: "string",
76911
- description: "Optional Connection ID to use a specific stored connection. Use list_connections to find available IDs."
76912
- },
76913
- connectionProfile: {
76914
- type: "object",
76915
- description: "Optional ad-hoc connection profile (including credentials) to use for this query."
76916
- },
76917
- authHeader: {
76918
- type: "string",
76919
- description: "Optional authorization header for the selected connection."
76920
- },
76921
- tabId: {
76922
- type: "string",
76923
- description: "Optional Tab ID to use for the result. If provided, the daemon will use this ID instead of generating a new one."
76924
- },
76925
- newTab: {
76926
- type: "boolean",
76927
- description: "When false, reuse the active tab if one exists. Defaults to true."
76928
- },
76929
- waitForResult: {
76930
- type: "boolean",
76931
- description: "When false, start the query and return immediately with structured started status. Defaults to true."
76932
- }
76933
- },
76934
- required: ["sql", "session"]
76935
- },
76936
- _meta: {
76937
- ui: {
76938
- resourceUri: "ui://sql-preview/results-grid"
76939
- }
76940
- }
76941
- },
76942
- {
76943
- name: "get_tab_info",
76944
- description: 'Get information about a result tab in a session. Defaults to a "preview" mode with metadata and a small sample. Use mode="page" to retrieve full pages of rows.',
76945
- inputSchema: {
76946
- type: "object",
76947
- properties: {
76948
- session: {
76949
- type: "string",
76950
- description: "The Session ID. Use list_sessions to discover available sessions."
76951
- },
76952
- tabId: {
76953
- type: "string",
76954
- description: "The Tab ID to retrieve (optional, defaults to the most recently active tab in the session)"
76955
- },
76956
- mode: {
76957
- type: "string",
76958
- enum: ["preview", "page"],
76959
- description: 'Retrieval mode. "preview" (default) returns stats + 10 rows. "page" returns specific rows defined by offset/limit.'
76960
- },
76961
- offset: {
76962
- type: "number",
76963
- description: 'Row offset for "page" mode (default: 0)'
76964
- },
76965
- limit: {
76966
- type: "number",
76967
- description: 'Number of rows to return. Default: 100 for "page" mode, 10 for "preview" mode.'
76968
- }
76969
- },
76970
- required: ["session"]
76971
- }
76972
- },
76973
- {
76974
- name: "list_sessions",
76975
- description: "List all active sessions managed by the daemon. Use this first to discover existing session IDs before running queries or checking results.",
76976
- inputSchema: {
76977
- type: "object",
76978
- properties: {}
76979
- }
76980
- },
76981
- {
76982
- name: "list_connections",
76983
- description: "List available database connections managed by the daemon. Returns connection IDs and metadata (excluding passwords).",
76984
- inputSchema: {
76985
- type: "object",
76986
- properties: {}
76987
- }
76988
- },
76989
- {
76990
- name: "save_connection",
76991
- description: "Save or update a connection profile.",
76992
- inputSchema: {
76993
- type: "object",
76994
- properties: {
76995
- connectionProfile: {
76996
- type: "object",
76997
- description: 'The connection profile to save (must include "id", "name", "type").'
76998
- }
76999
- },
77000
- required: ["connectionProfile"]
77001
- }
77002
- },
77003
- {
77004
- name: "request_connection_credentials",
77005
- description: "Create a short-lived local browser form where the user can enter credentials for a saved connection without exposing the secret in the MCP transcript. Share only the returned URL with the user; never ask them to paste credentials into chat.",
77006
- inputSchema: {
77007
- type: "object",
77008
- properties: {
77009
- connectionId: {
77010
- type: "string",
77011
- description: "Connection ID that should receive the safely submitted password."
77012
- }
77013
- },
77014
- required: ["connectionId"]
77015
- }
77016
- },
77017
- {
77018
- name: "test_connection",
77019
- description: "Test connectivity for a specific connection profile.",
77020
- inputSchema: {
77021
- type: "object",
77022
- properties: {
77023
- connectionId: {
77024
- type: "string",
77025
- description: "Optional Connection ID to test."
77026
- },
77027
- type: {
77028
- type: "string",
77029
- description: "Connector type (e.g., trino) when testing unsaved profile."
77030
- },
77031
- connectionProfile: {
77032
- type: "object",
77033
- description: "The configuration object when testing unsaved profile."
77034
- },
77035
- authHeader: {
77036
- type: "string",
77037
- description: "Optional auth header."
77038
- }
77039
- }
77040
- }
77041
- },
77042
- {
77043
- name: "delete_connection",
77044
- description: "Delete a connection profile.",
77045
- inputSchema: {
77046
- type: "object",
77047
- properties: {
77048
- connectionId: {
77049
- type: "string",
77050
- description: "The Connection ID to delete."
77051
- }
77052
- },
77053
- required: ["connectionId"]
77054
- }
77055
- },
77056
- {
77057
- name: "list_schemas",
77058
- description: "List database schemas (namespaces) available in a stored connection.",
77059
- inputSchema: {
77060
- type: "object",
77061
- properties: {
77062
- connectionId: { type: "string", description: "Connection ID to inspect." },
77063
- catalog: { type: "string", description: "Optional catalog filter (e.g. for Trino)." }
77064
- },
77065
- required: ["connectionId"]
77066
- }
77067
- },
77068
- {
77069
- name: "list_tables",
77070
- description: "List tables and views in a specific schema for a stored connection.",
77071
- inputSchema: {
77072
- type: "object",
77073
- properties: {
77074
- connectionId: { type: "string", description: "Connection ID to inspect." },
77075
- schema: { type: "string", description: "Schema name to list tables from." },
77076
- catalog: { type: "string", description: "Optional catalog (e.g. for Trino/BigQuery)." }
77077
- },
77078
- required: ["connectionId", "schema"]
77079
- }
77080
- },
77081
- {
77082
- name: "describe_table",
77083
- description: "Return column definitions and metadata for a specific table.",
77084
- inputSchema: {
77085
- type: "object",
77086
- properties: {
77087
- connectionId: { type: "string", description: "Connection ID to inspect." },
77088
- table: { type: "string", description: "Table name." },
77089
- schema: { type: "string", description: "Schema containing the table." },
77090
- catalog: { type: "string", description: "Optional catalog (e.g. for Trino/BigQuery)." }
77091
- },
77092
- required: ["connectionId", "table", "schema"]
77093
- }
77094
- },
77095
- {
77096
- name: "cancel_query",
77097
- description: 'Cancel a running query. Use get_tab_info first to check if a query is still in "loading" state before cancelling.',
77098
- inputSchema: {
77099
- type: "object",
77100
- properties: {
77101
- session: { type: "string", description: "The Session ID" },
77102
- tabId: { type: "string", description: "The Tab ID to cancel" }
77103
- },
77104
- required: ["session", "tabId"]
77105
- }
77106
- },
77107
- {
77108
- name: "close_tab",
77109
- description: "Close a tab and remove it from the session.",
77110
- inputSchema: {
77111
- type: "object",
77112
- properties: {
77113
- session: { type: "string", description: "The Session ID" },
77114
- tabId: { type: "string", description: "The Tab ID to close" }
77115
- },
77116
- required: ["session", "tabId"]
77117
- }
77118
- }
77119
- ];
77308
+ return TOOL_DEFINITIONS.filter((tool) => this.profileTools.has(tool.name)).map(
77309
+ ({ name, description, inputSchema, _meta }) => ({
77310
+ name,
77311
+ description,
77312
+ inputSchema,
77313
+ ..._meta !== void 0 ? { _meta } : {}
77314
+ })
77315
+ );
77120
77316
  }
77121
77317
  async handleToolCall(name, args) {
77318
+ if (!this.profileTools.has(name)) {
77319
+ throw new Error("Unknown tool");
77320
+ }
77122
77321
  switch (name) {
77123
77322
  case "run_query":
77124
77323
  return this.handleRunQuery(args);
@@ -77414,7 +77613,7 @@ This link expires at ${new Date(
77414
77613
  if (!sql) {
77415
77614
  throw new Error("SQL query is required");
77416
77615
  }
77417
- if (this.safeMode && !isReadOnlySql(sql)) {
77616
+ if (this.isSafeMode && !isReadOnlySql(sql)) {
77418
77617
  const message = "MCP safe mode blocked a non-read-only query. Disable sqlPreview.mcpSafeMode to allow write statements.";
77419
77618
  return {
77420
77619
  isError: true,
@@ -77624,6 +77823,7 @@ var init_DaemonQueryExecutor = __esm({
77624
77823
  "use strict";
77625
77824
  init_routing();
77626
77825
  init_errors();
77826
+ init_DaemonConfig();
77627
77827
  init_SubProcessConnectorClient();
77628
77828
  DaemonQueryExecutor = class {
77629
77829
  constructor(connectorRegistry, connectionManager, logger2, driverManager, queryConfig = {}) {
@@ -77632,10 +77832,18 @@ var init_DaemonQueryExecutor = __esm({
77632
77832
  this.logger = logger2;
77633
77833
  this.driverManager = driverManager;
77634
77834
  this.queryConfig = {
77635
- maxRows: queryConfig.maxRows ?? 1e4,
77636
- timeoutMs: queryConfig.timeoutMs ?? 5 * 60 * 1e3
77835
+ maxRows: queryConfig.maxRows ?? DEFAULT_CONFIG.query.maxRows,
77836
+ timeoutMs: queryConfig.timeoutMs ?? DEFAULT_CONFIG.query.timeoutMs
77637
77837
  };
77638
77838
  }
77839
+ /** Row cap applied to every query; results at or above this length were truncated by the connector. */
77840
+ get maxRows() {
77841
+ return this.queryConfig.maxRows;
77842
+ }
77843
+ /** Updates the row cap live, effective for the next query (e.g. from a settings-panel save). */
77844
+ setMaxRows(value) {
77845
+ this.queryConfig.maxRows = value;
77846
+ }
77639
77847
  async getConnectorForProfile(profile) {
77640
77848
  let executablePath;
77641
77849
  let connectorId = profile.type;
@@ -77715,7 +77923,7 @@ var init_DaemonQueryExecutor = __esm({
77715
77923
  /**
77716
77924
  * Orchestrates the query execution.
77717
77925
  */
77718
- async *execute(query, sessionId, connectionId, abortSignal, connectionOverride, authHeaderOverride) {
77926
+ async *execute(query, sessionId, connectionId, abortSignal, connectionOverride, authHeaderOverride, maxRowsOverride) {
77719
77927
  this.logger.info(`Starting query execution for session ${sessionId}`, { query });
77720
77928
  let profile;
77721
77929
  let isAutoRoutedDuckDbFileQuery = false;
@@ -77807,7 +78015,7 @@ var init_DaemonQueryExecutor = __esm({
77807
78015
  this.logger.error("No valid connection profile found.");
77808
78016
  throw new Error("No valid connection profile found.");
77809
78017
  }
77810
- const maxRows = this.queryConfig.maxRows;
78018
+ const maxRows = maxRowsOverride ?? this.queryConfig.maxRows;
77811
78019
  const connectorConfig = {
77812
78020
  ...profile,
77813
78021
  maxRows,
@@ -77952,9 +78160,9 @@ var init_CredentialRequestService = __esm({
77952
78160
  import_crypto3 = __toESM(require("crypto"));
77953
78161
  DEFAULT_TTL_MS = 10 * 60 * 1e3;
77954
78162
  CredentialRequestService = class {
77955
- constructor(connectionManager, baseUrl, ttlMs = DEFAULT_TTL_MS) {
78163
+ constructor(connectionManager, getBaseUrl, ttlMs = DEFAULT_TTL_MS) {
77956
78164
  this.connectionManager = connectionManager;
77957
- this.baseUrl = baseUrl;
78165
+ this.getBaseUrl = getBaseUrl;
77958
78166
  this.ttlMs = ttlMs;
77959
78167
  this.requests = /* @__PURE__ */ new Map();
77960
78168
  }
@@ -77972,7 +78180,7 @@ var init_CredentialRequestService = __esm({
77972
78180
  connectionId,
77973
78181
  connectionName: profile.name,
77974
78182
  expiresAt: Date.now() + this.ttlMs,
77975
- url: `${this.baseUrl}/credential-requests/${encodeURIComponent(id)}?token=${encodeURIComponent(
78183
+ url: `${this.getBaseUrl()}/credential-requests/${encodeURIComponent(id)}?token=${encodeURIComponent(
77976
78184
  token
77977
78185
  )}`
77978
78186
  };
@@ -78182,8 +78390,57 @@ var init_httpAbort = __esm({
78182
78390
  });
78183
78391
 
78184
78392
  // src/server/surfaces/DaemonHttpRoutes.ts
78393
+ function isLoopbackOnly(context2) {
78394
+ return ["127.0.0.1", "localhost", "::1"].includes(context2.resolvedConfig.config.server.host);
78395
+ }
78396
+ function readDaemonVersion() {
78397
+ try {
78398
+ const pkgPath = path4.join(findPackageRoot(__dirname), "package.json");
78399
+ const pkg = JSON.parse(fs6.readFileSync(pkgPath, "utf8"));
78400
+ return pkg.version ?? null;
78401
+ } catch {
78402
+ return null;
78403
+ }
78404
+ }
78405
+ function findPackageRoot(startDir) {
78406
+ let dir = startDir;
78407
+ for (let i = 0; i < 8; i++) {
78408
+ if (fs6.existsSync(path4.join(dir, "package.json"))) {
78409
+ return dir;
78410
+ }
78411
+ const parent = path4.dirname(dir);
78412
+ if (parent === dir) {
78413
+ break;
78414
+ }
78415
+ dir = parent;
78416
+ }
78417
+ return startDir;
78418
+ }
78419
+ function escapeCsvCell(value) {
78420
+ if (value === null || value === void 0) {
78421
+ return "";
78422
+ }
78423
+ let str;
78424
+ if (typeof value === "object") {
78425
+ try {
78426
+ str = JSON.stringify(value);
78427
+ } catch {
78428
+ str = String(value);
78429
+ }
78430
+ } else {
78431
+ str = String(value);
78432
+ }
78433
+ if (/^[=+\-@]/.test(str) && Number.isNaN(Number(str))) {
78434
+ str = "'" + str;
78435
+ }
78436
+ if (/["\n\r,]/.test(str)) {
78437
+ return `"${str.replace(/"/g, '""')}"`;
78438
+ }
78439
+ return str;
78440
+ }
78185
78441
  function registerDaemonHttpRoutes(context2, mcpHttpSurface) {
78186
78442
  const { app } = context2;
78443
+ const daemonVersion = readDaemonVersion();
78187
78444
  app.use((_req, _res, next) => {
78188
78445
  context2.refreshActivity();
78189
78446
  next();
@@ -78193,6 +78450,7 @@ function registerDaemonHttpRoutes(context2, mcpHttpSurface) {
78193
78450
  res.send({
78194
78451
  status: "running",
78195
78452
  service: "sql-preview-daemon",
78453
+ version: daemonVersion,
78196
78454
  readiness: {
78197
78455
  transport: "ready",
78198
78456
  connectors: "lazy",
@@ -78326,7 +78584,8 @@ data: ${Date.now()}
78326
78584
  query: t.query,
78327
78585
  sourceFileUri: t.sourceFileUri,
78328
78586
  wasTruncated: t.wasTruncated,
78329
- totalRowsInFirstBatch: t.totalRowsInFirstBatch
78587
+ totalRowsInFirstBatch: t.totalRowsInFirstBatch,
78588
+ executedAt: t.executedAt
78330
78589
  }))
78331
78590
  );
78332
78591
  } catch (err) {
@@ -78405,6 +78664,163 @@ data: ${Date.now()}
78405
78664
  res.status(500).json({ error: "Internal Server Error" });
78406
78665
  }
78407
78666
  });
78667
+ app.get("/api/v1/sessions/:sid/tabs/:tid/export", async (req, res) => {
78668
+ try {
78669
+ const session = context2.sessionManager.getSession(req.params.sid);
78670
+ if (!session) {
78671
+ res.status(404).json({ error: "Session not found" });
78672
+ return;
78673
+ }
78674
+ const tab = session.tabs.get(req.params.tid);
78675
+ if (!tab) {
78676
+ res.status(404).json({ error: "Tab not found" });
78677
+ return;
78678
+ }
78679
+ if (!tab.query) {
78680
+ res.status(400).json({ error: "Tab has no query to export" });
78681
+ return;
78682
+ }
78683
+ const filename = `${(tab.title || "export").replace(/[^a-z0-9-_]+/gi, "_")}.csv`;
78684
+ res.setHeader("Content-Type", "text/csv; charset=utf-8");
78685
+ res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
78686
+ const controller = new AbortController();
78687
+ const cleanupDisconnectAbort = attachAbortOnHttpDisconnect(
78688
+ req,
78689
+ res,
78690
+ controller,
78691
+ () => logger.warn(
78692
+ `[Daemon] Export for ${req.params.sid}/${req.params.tid} aborted; client disconnected`
78693
+ )
78694
+ );
78695
+ try {
78696
+ let wroteHeader = false;
78697
+ for await (const page of context2.queryOperationRunner.exportQuery(
78698
+ tab.query,
78699
+ req.params.sid,
78700
+ controller.signal
78701
+ )) {
78702
+ if (page.columns && !wroteHeader) {
78703
+ res.write(page.columns.map((c) => escapeCsvCell(c.name)).join(",") + "\r\n");
78704
+ wroteHeader = true;
78705
+ }
78706
+ for (const row of page.data) {
78707
+ res.write(row.map(escapeCsvCell).join(",") + "\r\n");
78708
+ }
78709
+ }
78710
+ } finally {
78711
+ cleanupDisconnectAbort();
78712
+ }
78713
+ res.end();
78714
+ } catch (err) {
78715
+ logger.error("[Daemon] Error in GET /api/v1/sessions/:sid/tabs/:tid/export:", err);
78716
+ if (!res.headersSent) {
78717
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
78718
+ } else {
78719
+ res.end();
78720
+ }
78721
+ }
78722
+ });
78723
+ app.get("/api/v1/settings", (_req, res) => {
78724
+ res.json({
78725
+ maxRowsToDisplay: context2.resolvedConfig.config.query.maxRows,
78726
+ mcpSafeMode: context2.resolvedConfig.config.mcp.safeMode
78727
+ });
78728
+ });
78729
+ app.post("/api/v1/settings", import_express.default.json(), (req, res) => {
78730
+ try {
78731
+ const body = req.body;
78732
+ const patch = {};
78733
+ if (body.maxRowsToDisplay !== void 0) {
78734
+ const maxRows = Number(body.maxRowsToDisplay);
78735
+ if (!Number.isFinite(maxRows) || maxRows < 1) {
78736
+ res.status(400).json({ error: "maxRowsToDisplay must be a positive number" });
78737
+ return;
78738
+ }
78739
+ context2.resolvedConfig.config.query.maxRows = maxRows;
78740
+ context2.queryOperationRunner.setMaxRows(maxRows);
78741
+ patch.query = { maxRows };
78742
+ }
78743
+ if (body.mcpSafeMode !== void 0) {
78744
+ const safeMode = Boolean(body.mcpSafeMode);
78745
+ context2.resolvedConfig.config.mcp.safeMode = safeMode;
78746
+ context2.toolManager.setSafeMode(safeMode);
78747
+ patch.mcp = { safeMode };
78748
+ }
78749
+ if (patch.query || patch.mcp) {
78750
+ persistConfigPatch(context2.resolvedConfig.configPath, patch);
78751
+ }
78752
+ res.json({
78753
+ maxRowsToDisplay: context2.resolvedConfig.config.query.maxRows,
78754
+ mcpSafeMode: context2.resolvedConfig.config.mcp.safeMode
78755
+ });
78756
+ } catch (err) {
78757
+ logger.error("[Daemon] Error in POST /api/v1/settings:", err);
78758
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
78759
+ }
78760
+ });
78761
+ app.post("/api/v1/install-dependency", import_express.default.json(), async (req, res) => {
78762
+ if (!isLoopbackOnly(context2)) {
78763
+ res.status(403).json({
78764
+ error: "Installing dependencies is disabled because this daemon is not bound to loopback (127.0.0.1). Restart it without a non-default --host/SQL_PREVIEW_HOST to enable this."
78765
+ });
78766
+ return;
78767
+ }
78768
+ const dependency = req.body?.dependency;
78769
+ if (typeof dependency !== "string" || !SUPPORTED_OPTIONAL_DEPENDENCIES.has(dependency)) {
78770
+ res.status(400).json({ error: `Unsupported optional dependency: ${String(dependency)}` });
78771
+ return;
78772
+ }
78773
+ const packageRoot = findPackageRoot(__dirname);
78774
+ const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
78775
+ let output = "";
78776
+ try {
78777
+ const exitCode = await new Promise((resolve2, reject) => {
78778
+ const child = (0, import_child_process2.spawn)(npmCommand, ["install", dependency], {
78779
+ cwd: packageRoot,
78780
+ shell: false,
78781
+ windowsHide: true
78782
+ });
78783
+ child.stdout?.on("data", (chunk) => {
78784
+ output += chunk.toString();
78785
+ });
78786
+ child.stderr?.on("data", (chunk) => {
78787
+ output += chunk.toString();
78788
+ });
78789
+ child.on("error", reject);
78790
+ child.on("close", (code) => resolve2(code ?? 1));
78791
+ });
78792
+ if (exitCode !== 0) {
78793
+ logger.error(`[Daemon] npm install ${dependency} exited with code ${exitCode}: ${output}`);
78794
+ res.status(500).json({
78795
+ error: `npm install ${dependency} exited with code ${exitCode}`,
78796
+ output
78797
+ });
78798
+ return;
78799
+ }
78800
+ res.json({
78801
+ status: "installed",
78802
+ dependency,
78803
+ message: `Installed ${dependency}. Restart the SQL Preview daemon to use the new driver.`
78804
+ });
78805
+ } catch (err) {
78806
+ logger.error(`[Daemon] Failed to install ${dependency}:`, err);
78807
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err), output });
78808
+ }
78809
+ });
78810
+ app.post("/api/v1/tools/:name", import_express.default.json(), async (req, res) => {
78811
+ const { name } = req.params;
78812
+ if (!webProxiedTools.has(name)) {
78813
+ res.status(404).json({ error: `Tool '${name}' is not available on the web surface` });
78814
+ return;
78815
+ }
78816
+ try {
78817
+ const result = await context2.toolManager.handleToolCall(name, req.body?.args ?? {});
78818
+ res.json(result);
78819
+ } catch (err) {
78820
+ logger.error(`[Daemon] Error in POST /api/v1/tools/${name}:`, err);
78821
+ res.status(400).json({ error: err instanceof Error ? err.message : String(err) });
78822
+ }
78823
+ });
78408
78824
  app.post("/api/v1/query", import_express.default.json(), async (req, res) => {
78409
78825
  await handleQueryRoute(context2, req, res, {
78410
78826
  displayName: "Web Client",
@@ -78468,6 +78884,24 @@ async function handleQueryRoute(context2, req, res, options) {
78468
78884
  }
78469
78885
  }
78470
78886
  }
78887
+ function persistConfigPatch(configPath, patch) {
78888
+ let onDisk = {};
78889
+ if (fs6.existsSync(configPath)) {
78890
+ try {
78891
+ onDisk = JSON.parse(fs6.readFileSync(configPath, "utf8"));
78892
+ } catch (err) {
78893
+ logger.warn(`[Daemon] Could not parse existing config at ${configPath}, overwriting:`, err);
78894
+ onDisk = {};
78895
+ }
78896
+ }
78897
+ if (patch.query) {
78898
+ onDisk["query"] = { ...onDisk["query"], ...patch.query };
78899
+ }
78900
+ if (patch.mcp) {
78901
+ onDisk["mcp"] = { ...onDisk["mcp"], ...patch.mcp };
78902
+ }
78903
+ fs6.writeFileSync(configPath, JSON.stringify(onDisk, null, 2) + "\n");
78904
+ }
78471
78905
  function sendQueryResult(res, result) {
78472
78906
  if (res.headersSent || res.writableEnded) {
78473
78907
  return;
@@ -78478,7 +78912,10 @@ function sendQueryResult(res, result) {
78478
78912
  data: result.rows,
78479
78913
  rowCount: result.rowCount,
78480
78914
  sessionId: result.sessionId,
78481
- tabId: result.tabId
78915
+ tabId: result.tabId,
78916
+ ...result.wasTruncated !== void 0 ? { wasTruncated: result.wasTruncated } : {},
78917
+ ...result.executionTimeMs !== void 0 ? { executionTimeMs: result.executionTimeMs } : {},
78918
+ ...result.executedAt !== void 0 ? { executedAt: result.executedAt } : {}
78482
78919
  });
78483
78920
  return;
78484
78921
  }
@@ -78540,14 +78977,14 @@ function renderCredentialPage(input) {
78540
78977
  <style>
78541
78978
  :root { color-scheme: light dark; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
78542
78979
  body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: Canvas; color: CanvasText; }
78543
- main { width: min(92vw, 32rem); padding: 2rem; border: 1px solid color-mix(in srgb, CanvasText 18%, transparent); border-radius: 12px; box-shadow: 0 12px 40px color-mix(in srgb, CanvasText 12%, transparent); }
78980
+ main { width: min(92vw, 32rem); padding: 2rem; border: 1px solid color-mix(in srgb, CanvasText 18%, transparent); border-radius: 6px; box-shadow: 0 12px 40px color-mix(in srgb, CanvasText 12%, transparent); }
78544
78981
  h1 { margin-top: 0; font-size: 1.35rem; }
78545
78982
  label { display: block; margin-bottom: 0.4rem; font-weight: 600; }
78546
- input[type="password"] { box-sizing: border-box; width: 100%; padding: 0.75rem; border: 1px solid color-mix(in srgb, CanvasText 30%, transparent); border-radius: 8px; font: inherit; }
78547
- button { margin-top: 1rem; padding: 0.7rem 1rem; border: 0; border-radius: 8px; background: #2563eb; color: white; font: inherit; font-weight: 700; cursor: pointer; }
78983
+ input[type="password"] { box-sizing: border-box; width: 100%; padding: 0.75rem; border: 1px solid color-mix(in srgb, CanvasText 30%, transparent); border-radius: 3px; font: inherit; }
78984
+ button { margin-top: 1rem; padding: 0.7rem 1rem; border: 0; border-radius: 3px; background: #007acc; color: white; font: inherit; font-weight: 700; cursor: pointer; }
78548
78985
  .hint { color: color-mix(in srgb, CanvasText 72%, transparent); }
78549
- .success { color: #15803d; }
78550
- .error { color: #b91c1c; }
78986
+ .success { color: light-dark(#107c10, #4ec9b0); }
78987
+ .error { color: light-dark(#a1260d, #f48771); }
78551
78988
  </style>
78552
78989
  </head>
78553
78990
  <body>
@@ -78578,16 +79015,20 @@ function escapeHtml(value) {
78578
79015
  }
78579
79016
  });
78580
79017
  }
78581
- var import_crypto4, import_express, fs6, path4;
79018
+ var import_crypto4, import_child_process2, import_express, fs6, path4, webProxiedTools, SUPPORTED_OPTIONAL_DEPENDENCIES;
78582
79019
  var init_DaemonHttpRoutes = __esm({
78583
79020
  "src/server/surfaces/DaemonHttpRoutes.ts"() {
78584
79021
  "use strict";
78585
79022
  import_crypto4 = __toESM(require("crypto"));
79023
+ import_child_process2 = require("child_process");
78586
79024
  import_express = __toESM(require_express2());
78587
79025
  fs6 = __toESM(require("fs"));
78588
79026
  path4 = __toESM(require("path"));
78589
79027
  init_ConsoleLogger();
78590
79028
  init_httpAbort();
79029
+ init_toolProfiles();
79030
+ webProxiedTools = new Set(WEB_PROXIED_TOOLS);
79031
+ SUPPORTED_OPTIONAL_DEPENDENCIES = /* @__PURE__ */ new Set(["@duckdb/node-api", "snowflake-sdk"]);
78591
79032
  }
78592
79033
  });
78593
79034
 
@@ -126743,7 +127184,7 @@ var init_telemetry = __esm({
126743
127184
 
126744
127185
  // src/common/guideHub.ts
126745
127186
  function getGuideHubLeadText(state) {
126746
- return "Run queries across all your files and databases and collaborate with your Agents";
127187
+ return "Query your databases and local files right from the editor, and give your AI agents the same access.";
126747
127188
  }
126748
127189
  function getConnectionSummary(state) {
126749
127190
  if (state.connectionCount === 0) {
@@ -127040,7 +127481,17 @@ var init_DaemonMcpHttpSurface = __esm({
127040
127481
  logger.info(`[Daemon] Headers: ${JSON.stringify(redactHeadersForLog(req.headers))}`);
127041
127482
  logger.info(`[Daemon] Query: ${JSON.stringify(req.query)}`);
127042
127483
  try {
127043
- const sessionId = this.resolveSessionId(req);
127484
+ let sessionId = this.resolveSessionId(req);
127485
+ if (req.method === "POST" && !sessionId) {
127486
+ logger.warn("[Daemon] Rejecting POST /mcp request without a sessionId");
127487
+ if (!res.headersSent) {
127488
+ res.status(400).json({ error: "Missing sessionId for POST request" });
127489
+ }
127490
+ return;
127491
+ }
127492
+ if (!sessionId) {
127493
+ sessionId = `session-${import_crypto6.default.randomUUID()}`;
127494
+ }
127044
127495
  const mcpSession = await this.getOrCreateSession(sessionId);
127045
127496
  mcpSession.lastActive = Date.now();
127046
127497
  const transportPromise = mcpSession.transport.handleRequest(req, res);
@@ -127096,7 +127547,7 @@ var init_DaemonMcpHttpSurface = __esm({
127096
127547
  if (typeof headerSessionId === "string" && headerSessionId.length > 0) {
127097
127548
  return headerSessionId;
127098
127549
  }
127099
- return `session-${import_crypto6.default.randomUUID()}`;
127550
+ return void 0;
127100
127551
  }
127101
127552
  async getOrCreateSession(sessionId) {
127102
127553
  const existingSession = this.sessions.get(sessionId);
@@ -131459,7 +131910,8 @@ var init_DaemonSurfaceManager = __esm({
131459
131910
  resolvedConfig: this.options.resolvedConfig,
131460
131911
  sessionManager: this.options.sessionManager,
131461
131912
  getSurfaceStatus: () => this.getStatus(),
131462
- credentialRequestService: this.options.credentialRequestService
131913
+ credentialRequestService: this.options.credentialRequestService,
131914
+ toolManager: this.options.toolManager
131463
131915
  },
131464
131916
  this.mcpHttpSurface
131465
131917
  );
@@ -131502,7 +131954,7 @@ var init_DaemonSurfaceManager = __esm({
131502
131954
  http: {
131503
131955
  status: "ready",
131504
131956
  host: this.options.config.server.host,
131505
- port: this.options.httpPort
131957
+ port: this.options.getHttpPort ? this.options.getHttpPort() : this.options.httpPort
131506
131958
  },
131507
131959
  mcpHttp: {
131508
131960
  path: "/mcp",
@@ -131565,6 +132017,7 @@ var init_Daemon = __esm({
131565
132017
  constructor(configOptions = {}) {
131566
132018
  this.httpServer = null;
131567
132019
  this.isStdioMode = false;
132020
+ this.ownsPidAndSocket = false;
131568
132021
  this.lastActivityTime = Date.now();
131569
132022
  this.uncaughtExceptionHandler = null;
131570
132023
  this.unhandledRejectionHandler = null;
@@ -131597,7 +132050,7 @@ var init_Daemon = __esm({
131597
132050
  this.queryOperationRunner = new QueryOperationRunner(this.sessionManager, this.queryExecutor);
131598
132051
  this.credentialRequestService = new CredentialRequestService(
131599
132052
  this.connectionManager,
131600
- this.getHttpBaseUrl()
132053
+ () => this.getHttpBaseUrl()
131601
132054
  );
131602
132055
  this.toolManager = new DaemonMcpToolManager(
131603
132056
  this.sessionManager,
@@ -131616,6 +132069,15 @@ var init_Daemon = __esm({
131616
132069
  configDir: this.CONFIG_DIR,
131617
132070
  connectorRegistry: this.connectorRegistry,
131618
132071
  httpPort: this.HTTP_PORT,
132072
+ getHttpPort: () => {
132073
+ if (this.httpServer) {
132074
+ const addr = this.httpServer.address();
132075
+ if (addr && typeof addr.port === "number") {
132076
+ return addr.port;
132077
+ }
132078
+ }
132079
+ return this.HTTP_PORT;
132080
+ },
131619
132081
  queryOperationRunner: this.queryOperationRunner,
131620
132082
  refreshActivity: () => this.refreshActivity(),
131621
132083
  resolvedConfig: this.resolvedConfig,
@@ -131633,7 +132095,14 @@ var init_Daemon = __esm({
131633
132095
  }
131634
132096
  getHttpBaseUrl() {
131635
132097
  const host = ["0.0.0.0", "::", ""].includes(this.config.server.host) ? "127.0.0.1" : this.config.server.host;
131636
- return `http://${host}:${this.HTTP_PORT}`;
132098
+ let port = this.HTTP_PORT;
132099
+ if (this.httpServer) {
132100
+ const addr = this.httpServer.address();
132101
+ if (addr && typeof addr.port === "number") {
132102
+ port = addr.port;
132103
+ }
132104
+ }
132105
+ return `http://${host}:${port}`;
131637
132106
  }
131638
132107
  /**
131639
132108
  * Auto-provisions built-in connections that should always be available.
@@ -131796,6 +132265,7 @@ var init_Daemon = __esm({
131796
132265
  }
131797
132266
  }
131798
132267
  fs10.writeFileSync(pidPath, process.pid.toString());
132268
+ this.ownsPidAndSocket = true;
131799
132269
  } catch (e) {
131800
132270
  logger.error("[Daemon] Startup error checking PID:", e);
131801
132271
  process.exit(1);
@@ -131824,6 +132294,19 @@ var init_Daemon = __esm({
131824
132294
  async startStdio() {
131825
132295
  this.isStdioMode = true;
131826
132296
  ConsoleLogger.getInstance().setUseStdErr(true);
132297
+ await new Promise((resolve2, reject) => {
132298
+ this.httpServer = this.app.listen(0, "127.0.0.1", () => {
132299
+ const addr = this.httpServer?.address();
132300
+ const bind2 = addr ? `port ${addr.port}` : "unknown port";
132301
+ logger.info(`[Daemon] Stdio background HTTP listening on http://127.0.0.1:${bind2}`);
132302
+ this.httpServer?.removeAllListeners("error");
132303
+ this.httpServer?.on("error", (err) => {
132304
+ logger.error("[Daemon] Background HTTP server error:", err);
132305
+ });
132306
+ resolve2();
132307
+ });
132308
+ this.httpServer.on("error", reject);
132309
+ });
131827
132310
  await this.surfaceManager.startStdio();
131828
132311
  }
131829
132312
  stop() {
@@ -131859,17 +132342,19 @@ var init_Daemon = __esm({
131859
132342
  clearInterval(this.idleCheckInterval);
131860
132343
  this.idleCheckInterval = void 0;
131861
132344
  }
131862
- const pidPath = path7.join(this.CONFIG_DIR, `server-${this.HTTP_PORT}.pid`);
131863
- if (fs10.existsSync(pidPath)) {
131864
- try {
131865
- fs10.unlinkSync(pidPath);
131866
- } catch {
132345
+ if (this.ownsPidAndSocket) {
132346
+ const pidPath = path7.join(this.CONFIG_DIR, `server-${this.HTTP_PORT}.pid`);
132347
+ if (fs10.existsSync(pidPath)) {
132348
+ try {
132349
+ fs10.unlinkSync(pidPath);
132350
+ } catch {
132351
+ }
131867
132352
  }
131868
- }
131869
- if (fs10.existsSync(this.SOCKET_PATH)) {
131870
- try {
131871
- fs10.unlinkSync(this.SOCKET_PATH);
131872
- } catch {
132353
+ if (fs10.existsSync(this.SOCKET_PATH)) {
132354
+ try {
132355
+ fs10.unlinkSync(this.SOCKET_PATH);
132356
+ } catch {
132357
+ }
131873
132358
  }
131874
132359
  }
131875
132360
  }