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.
- package/Changelog.md +30 -1
- package/README.md +3 -3
- package/dist/mcp-app.html +929 -226
- package/out/server/standalone.js +777 -292
- package/package.json +1 -1
- package/server.json +2 -2
package/out/server/standalone.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
76330
|
+
init_tabProjection();
|
|
76266
76331
|
init_ConsoleLogger();
|
|
76267
|
-
|
|
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
|
-
|
|
76520
|
-
|
|
76521
|
-
|
|
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:
|
|
76529
|
-
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:
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
76888
|
-
description
|
|
76889
|
-
inputSchema
|
|
76890
|
-
|
|
76891
|
-
|
|
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.
|
|
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 ??
|
|
77636
|
-
timeoutMs: queryConfig.timeoutMs ??
|
|
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,
|
|
78163
|
+
constructor(connectionManager, getBaseUrl, ttlMs = DEFAULT_TTL_MS) {
|
|
77956
78164
|
this.connectionManager = connectionManager;
|
|
77957
|
-
this.
|
|
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.
|
|
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:
|
|
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:
|
|
78547
|
-
button { margin-top: 1rem; padding: 0.7rem 1rem; border: 0; border-radius:
|
|
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: #
|
|
78550
|
-
.error { color: #
|
|
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 "
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
131863
|
-
|
|
131864
|
-
|
|
131865
|
-
|
|
131866
|
-
|
|
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
|
-
|
|
131870
|
-
|
|
131871
|
-
|
|
131872
|
-
|
|
132353
|
+
if (fs10.existsSync(this.SOCKET_PATH)) {
|
|
132354
|
+
try {
|
|
132355
|
+
fs10.unlinkSync(this.SOCKET_PATH);
|
|
132356
|
+
} catch {
|
|
132357
|
+
}
|
|
131873
132358
|
}
|
|
131874
132359
|
}
|
|
131875
132360
|
}
|