sql-preview 0.6.2 → 0.6.3
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 +15 -0
- package/dist/mcp-app.html +135 -80
- package/out/server/standalone.js +308 -48
- package/package.json +1 -1
- package/server.json +2 -2
package/Changelog.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.6.3] - 2026-06-03
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added an MCP-accessible credential request flow that returns a short-lived local browser form so agents can ask users to store connection passwords without putting secrets in chat transcripts.
|
|
10
|
+
- Added local interactive Snowflake Browser SSO support for MCP/daemon connection tests, including analyst-style `user` and `authenticator=externalbrowser` aliases.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed unconfigured VS Code workspaces showing a phantom built-in `Workspace Trino` connection beside saved Snowflake profiles.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Simplified the Snowflake connection form by moving token caching and warehouse/database/schema/role defaults into optional settings, with SSO token caching enabled by default.
|
|
19
|
+
|
|
5
20
|
## [0.6.2] - 2026-06-03
|
|
6
21
|
|
|
7
22
|
### Added
|
package/dist/mcp-app.html
CHANGED
|
@@ -74174,6 +74174,7 @@ const DynamicForm = ({
|
|
|
74174
74174
|
submitFeedback
|
|
74175
74175
|
}) => {
|
|
74176
74176
|
const [formData, setFormData] = reactExports.useState({});
|
|
74177
|
+
const [isAdvancedOpen, setIsAdvancedOpen] = reactExports.useState(false);
|
|
74177
74178
|
const lastResetKey = reactExports.useRef(void 0);
|
|
74178
74179
|
const resetKey = reactExports.useMemo(
|
|
74179
74180
|
() => JSON.stringify({
|
|
@@ -74239,12 +74240,104 @@ const DynamicForm = ({
|
|
|
74239
74240
|
onTest(visibleFormData());
|
|
74240
74241
|
}
|
|
74241
74242
|
};
|
|
74243
|
+
const hasMeaningfulInitialValue = (key) => {
|
|
74244
|
+
const value = initialData[key];
|
|
74245
|
+
return value !== void 0 && value !== null && value !== "" && value !== false;
|
|
74246
|
+
};
|
|
74247
|
+
const advancedInitiallyOpen = Object.entries(schema.properties || {}).some(
|
|
74248
|
+
([key, prop]) => {
|
|
74249
|
+
var _a3;
|
|
74250
|
+
return ((_a3 = prop.ui) == null ? void 0 : _a3.advanced) && hasMeaningfulInitialValue(key);
|
|
74251
|
+
}
|
|
74252
|
+
);
|
|
74253
|
+
reactExports.useEffect(() => {
|
|
74254
|
+
setIsAdvancedOpen(advancedInitiallyOpen);
|
|
74255
|
+
}, [advancedInitiallyOpen, resetKey]);
|
|
74242
74256
|
const fieldClass = (key, baseClass) => {
|
|
74243
74257
|
var _a3, _b2;
|
|
74244
74258
|
const normalizedKey = key.replace(/[^a-z0-9_-]/gi, "-").toLowerCase();
|
|
74245
74259
|
const fullWidth = ((_b2 = (_a3 = schema.properties[key]) == null ? void 0 : _a3.ui) == null ? void 0 : _b2.fullWidth) ? " sp-conn-form-field-full" : "";
|
|
74246
74260
|
return `${baseClass} sp-conn-form-field sp-conn-form-field-${normalizedKey}${fullWidth}`;
|
|
74247
74261
|
};
|
|
74262
|
+
const renderPropertyField = ([key, prop]) => {
|
|
74263
|
+
var _a3;
|
|
74264
|
+
const isRequired = (_a3 = schema.required) == null ? void 0 : _a3.includes(key);
|
|
74265
|
+
const value = formData[key];
|
|
74266
|
+
const fieldId = `form-${key}`;
|
|
74267
|
+
const field = (() => {
|
|
74268
|
+
var _a4;
|
|
74269
|
+
if (prop.type === "boolean") {
|
|
74270
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: fieldClass(key, "sp-conn-form-check-row"), children: [
|
|
74271
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
74272
|
+
"input",
|
|
74273
|
+
{
|
|
74274
|
+
type: "checkbox",
|
|
74275
|
+
id: fieldId,
|
|
74276
|
+
checked: !!value,
|
|
74277
|
+
onChange: (e2) => handleChange(key, e2.target.checked)
|
|
74278
|
+
}
|
|
74279
|
+
),
|
|
74280
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: fieldId, className: "sp-conn-form-label", style: { marginBottom: 0 }, children: prop.title || key }),
|
|
74281
|
+
prop.description && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-form-hint", children: prop.description })
|
|
74282
|
+
] });
|
|
74283
|
+
}
|
|
74284
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: fieldClass(key, "sp-conn-form-group"), children: [
|
|
74285
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: fieldId, className: "sp-conn-form-label", children: [
|
|
74286
|
+
prop.title || key,
|
|
74287
|
+
isRequired && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "required", children: "*" })
|
|
74288
|
+
] }),
|
|
74289
|
+
prop.description && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-form-hint", children: prop.description }),
|
|
74290
|
+
Array.isArray(prop.enum) ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
74291
|
+
"select",
|
|
74292
|
+
{
|
|
74293
|
+
id: fieldId,
|
|
74294
|
+
required: isRequired,
|
|
74295
|
+
className: "sp-conn-form-input",
|
|
74296
|
+
value: value || "",
|
|
74297
|
+
onChange: (e2) => handleChange(key, e2.target.value),
|
|
74298
|
+
children: prop.enum.map((option, index2) => {
|
|
74299
|
+
var _a5;
|
|
74300
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: option, children: ((_a5 = prop.enumNames) == null ? void 0 : _a5[index2]) ?? option }, option);
|
|
74301
|
+
})
|
|
74302
|
+
}
|
|
74303
|
+
) : prop.type === "number" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
74304
|
+
"input",
|
|
74305
|
+
{
|
|
74306
|
+
id: fieldId,
|
|
74307
|
+
type: "number",
|
|
74308
|
+
required: isRequired,
|
|
74309
|
+
className: "sp-conn-form-input",
|
|
74310
|
+
value: value !== void 0 ? String(value) : "",
|
|
74311
|
+
onChange: (e2) => handleChange(key, e2.target.value !== "" ? Number(e2.target.value) : void 0)
|
|
74312
|
+
}
|
|
74313
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
74314
|
+
"input",
|
|
74315
|
+
{
|
|
74316
|
+
id: fieldId,
|
|
74317
|
+
type: ((_a4 = prop.ui) == null ? void 0 : _a4.widget) === "password" ? "password" : "text",
|
|
74318
|
+
required: isRequired,
|
|
74319
|
+
className: "sp-conn-form-input",
|
|
74320
|
+
value: value || "",
|
|
74321
|
+
onChange: (e2) => handleChange(key, e2.target.value)
|
|
74322
|
+
}
|
|
74323
|
+
)
|
|
74324
|
+
] });
|
|
74325
|
+
})();
|
|
74326
|
+
const afterField = renderAfterField == null ? void 0 : renderAfterField(key);
|
|
74327
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(React$2.Fragment, { children: [
|
|
74328
|
+
field,
|
|
74329
|
+
afterField
|
|
74330
|
+
] }, key);
|
|
74331
|
+
};
|
|
74332
|
+
const visibleEntries = visiblePropertyEntries();
|
|
74333
|
+
const primaryEntries = visibleEntries.filter(([, prop]) => {
|
|
74334
|
+
var _a3;
|
|
74335
|
+
return !((_a3 = prop.ui) == null ? void 0 : _a3.advanced);
|
|
74336
|
+
});
|
|
74337
|
+
const advancedEntries = visibleEntries.filter(([, prop]) => {
|
|
74338
|
+
var _a3;
|
|
74339
|
+
return (_a3 = prop.ui) == null ? void 0 : _a3.advanced;
|
|
74340
|
+
});
|
|
74248
74341
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, className: "sp-conn-form", role: "form", children: [
|
|
74249
74342
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-form-fields", children: [
|
|
74250
74343
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: fieldClass("name", "sp-conn-form-group"), children: [
|
|
@@ -74264,84 +74357,20 @@ const DynamicForm = ({
|
|
|
74264
74357
|
}
|
|
74265
74358
|
)
|
|
74266
74359
|
] }),
|
|
74267
|
-
|
|
74268
|
-
|
|
74269
|
-
|
|
74270
|
-
|
|
74271
|
-
|
|
74272
|
-
|
|
74273
|
-
|
|
74274
|
-
|
|
74275
|
-
|
|
74276
|
-
|
|
74277
|
-
|
|
74278
|
-
|
|
74279
|
-
|
|
74280
|
-
|
|
74281
|
-
checked: !!value,
|
|
74282
|
-
onChange: (e2) => handleChange(key, e2.target.checked)
|
|
74283
|
-
}
|
|
74284
|
-
),
|
|
74285
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
74286
|
-
"label",
|
|
74287
|
-
{
|
|
74288
|
-
htmlFor: fieldId,
|
|
74289
|
-
className: "sp-conn-form-label",
|
|
74290
|
-
style: { marginBottom: 0 },
|
|
74291
|
-
children: prop.title || key
|
|
74292
|
-
}
|
|
74293
|
-
),
|
|
74294
|
-
prop.description && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-form-hint", children: prop.description })
|
|
74295
|
-
] });
|
|
74296
|
-
}
|
|
74297
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: fieldClass(key, "sp-conn-form-group"), children: [
|
|
74298
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: fieldId, className: "sp-conn-form-label", children: [
|
|
74299
|
-
prop.title || key,
|
|
74300
|
-
isRequired && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "required", children: "*" })
|
|
74301
|
-
] }),
|
|
74302
|
-
prop.description && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sp-conn-form-hint", children: prop.description }),
|
|
74303
|
-
Array.isArray(prop.enum) ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
74304
|
-
"select",
|
|
74305
|
-
{
|
|
74306
|
-
id: fieldId,
|
|
74307
|
-
required: isRequired,
|
|
74308
|
-
className: "sp-conn-form-input",
|
|
74309
|
-
value: value || "",
|
|
74310
|
-
onChange: (e2) => handleChange(key, e2.target.value),
|
|
74311
|
-
children: prop.enum.map((option, index2) => {
|
|
74312
|
-
var _a5;
|
|
74313
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: option, children: ((_a5 = prop.enumNames) == null ? void 0 : _a5[index2]) ?? option }, option);
|
|
74314
|
-
})
|
|
74315
|
-
}
|
|
74316
|
-
) : prop.type === "number" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
74317
|
-
"input",
|
|
74318
|
-
{
|
|
74319
|
-
id: fieldId,
|
|
74320
|
-
type: "number",
|
|
74321
|
-
required: isRequired,
|
|
74322
|
-
className: "sp-conn-form-input",
|
|
74323
|
-
value: value !== void 0 ? String(value) : "",
|
|
74324
|
-
onChange: (e2) => handleChange(key, e2.target.value !== "" ? Number(e2.target.value) : void 0)
|
|
74325
|
-
}
|
|
74326
|
-
) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
74327
|
-
"input",
|
|
74328
|
-
{
|
|
74329
|
-
id: fieldId,
|
|
74330
|
-
type: ((_a4 = prop.ui) == null ? void 0 : _a4.widget) === "password" ? "password" : "text",
|
|
74331
|
-
required: isRequired,
|
|
74332
|
-
className: "sp-conn-form-input",
|
|
74333
|
-
value: value || "",
|
|
74334
|
-
onChange: (e2) => handleChange(key, e2.target.value)
|
|
74335
|
-
}
|
|
74336
|
-
)
|
|
74337
|
-
] });
|
|
74338
|
-
})();
|
|
74339
|
-
const afterField = renderAfterField == null ? void 0 : renderAfterField(key);
|
|
74340
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsxs(React$2.Fragment, { children: [
|
|
74341
|
-
field,
|
|
74342
|
-
afterField
|
|
74343
|
-
] }, key);
|
|
74344
|
-
})
|
|
74360
|
+
primaryEntries.map(renderPropertyField),
|
|
74361
|
+
advancedEntries.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
74362
|
+
"details",
|
|
74363
|
+
{
|
|
74364
|
+
className: "sp-conn-form-advanced",
|
|
74365
|
+
open: isAdvancedOpen,
|
|
74366
|
+
onToggle: (event) => setIsAdvancedOpen(event.currentTarget.open),
|
|
74367
|
+
children: [
|
|
74368
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("summary", { className: "sp-conn-form-advanced-summary", children: "Optional settings" }),
|
|
74369
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-form-advanced-fields", children: advancedEntries.map(renderPropertyField) })
|
|
74370
|
+
]
|
|
74371
|
+
},
|
|
74372
|
+
`advanced-${resetKey}`
|
|
74373
|
+
)
|
|
74345
74374
|
] }),
|
|
74346
74375
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sp-conn-form-actions", children: [
|
|
74347
74376
|
actionStatus && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "sp-conn-form-action-status", children: actionStatus }),
|
|
@@ -74477,7 +74506,7 @@ function schemaWithoutPasswordField(schema) {
|
|
|
74477
74506
|
};
|
|
74478
74507
|
}
|
|
74479
74508
|
function schemaForConnectionMode(schema, connectorId, mode) {
|
|
74480
|
-
if (connectorId !== "snowflake" || mode
|
|
74509
|
+
if (connectorId !== "snowflake" || mode !== "headless") {
|
|
74481
74510
|
return schema;
|
|
74482
74511
|
}
|
|
74483
74512
|
const authModeProperty = schema.properties["authMode"];
|
|
@@ -98867,6 +98896,31 @@ body.vscode-high-contrast .ag-theme-quartz {
|
|
|
98867
98896
|
margin-top: 4px;
|
|
98868
98897
|
}
|
|
98869
98898
|
|
|
98899
|
+
.sp-conn-form-advanced {
|
|
98900
|
+
grid-column: 1 / -1;
|
|
98901
|
+
padding-top: 2px;
|
|
98902
|
+
}
|
|
98903
|
+
|
|
98904
|
+
.sp-conn-form-advanced-summary {
|
|
98905
|
+
cursor: pointer;
|
|
98906
|
+
color: var(--sp-muted-fg);
|
|
98907
|
+
font-size: 12px;
|
|
98908
|
+
font-weight: 600;
|
|
98909
|
+
outline: none;
|
|
98910
|
+
}
|
|
98911
|
+
|
|
98912
|
+
.sp-conn-form-advanced-summary:hover,
|
|
98913
|
+
.sp-conn-form-advanced-summary:focus-visible {
|
|
98914
|
+
color: var(--sp-fg);
|
|
98915
|
+
}
|
|
98916
|
+
|
|
98917
|
+
.sp-conn-form-advanced-fields {
|
|
98918
|
+
display: grid;
|
|
98919
|
+
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
98920
|
+
gap: 12px 16px;
|
|
98921
|
+
margin-top: 10px;
|
|
98922
|
+
}
|
|
98923
|
+
|
|
98870
98924
|
.sp-conn-credential-state {
|
|
98871
98925
|
font-size: 11px;
|
|
98872
98926
|
color: var(--sp-muted-fg);
|
|
@@ -99129,7 +99183,8 @@ body.vscode-high-contrast .ag-theme-quartz {
|
|
|
99129
99183
|
grid-template-columns: minmax(0, 1fr);
|
|
99130
99184
|
}
|
|
99131
99185
|
|
|
99132
|
-
.sp-conn-form-fields
|
|
99186
|
+
.sp-conn-form-fields,
|
|
99187
|
+
.sp-conn-form-advanced-fields {
|
|
99133
99188
|
grid-template-columns: minmax(0, 1fr);
|
|
99134
99189
|
}
|
|
99135
99190
|
|
package/out/server/standalone.js
CHANGED
|
@@ -22327,8 +22327,8 @@ var require_escape_html = __commonJS({
|
|
|
22327
22327
|
"node_modules/escape-html/index.js"(exports, module2) {
|
|
22328
22328
|
"use strict";
|
|
22329
22329
|
var matchHtmlRegExp = /["'&<>]/;
|
|
22330
|
-
module2.exports =
|
|
22331
|
-
function
|
|
22330
|
+
module2.exports = escapeHtml2;
|
|
22331
|
+
function escapeHtml2(string3) {
|
|
22332
22332
|
var str = "" + string3;
|
|
22333
22333
|
var match = matchHtmlRegExp.exec(str);
|
|
22334
22334
|
if (!match) {
|
|
@@ -22453,13 +22453,13 @@ var require_finalhandler = __commonJS({
|
|
|
22453
22453
|
"use strict";
|
|
22454
22454
|
var debug = require_src()("finalhandler");
|
|
22455
22455
|
var encodeUrl = require_encodeurl();
|
|
22456
|
-
var
|
|
22456
|
+
var escapeHtml2 = require_escape_html();
|
|
22457
22457
|
var onFinished = require_on_finished();
|
|
22458
22458
|
var parseUrl2 = require_parseurl();
|
|
22459
22459
|
var statuses = require_statuses();
|
|
22460
22460
|
var isFinished = onFinished.isFinished;
|
|
22461
22461
|
function createHtmlDocument(message) {
|
|
22462
|
-
var body =
|
|
22462
|
+
var body = escapeHtml2(message).replaceAll("\n", "<br>").replaceAll(" ", " ");
|
|
22463
22463
|
return '<!DOCTYPE html>\n<html lang="en">\n<head>\n<meta charset="utf-8">\n<title>Error</title>\n</head>\n<body>\n<pre>' + body + "</pre>\n</body>\n</html>\n";
|
|
22464
22464
|
}
|
|
22465
22465
|
module2.exports = finalhandler;
|
|
@@ -22673,14 +22673,14 @@ var require_etag = __commonJS({
|
|
|
22673
22673
|
"node_modules/etag/index.js"(exports, module2) {
|
|
22674
22674
|
"use strict";
|
|
22675
22675
|
module2.exports = etag;
|
|
22676
|
-
var
|
|
22676
|
+
var crypto7 = require("crypto");
|
|
22677
22677
|
var Stats = require("fs").Stats;
|
|
22678
22678
|
var toString3 = Object.prototype.toString;
|
|
22679
22679
|
function entitytag(entity) {
|
|
22680
22680
|
if (entity.length === 0) {
|
|
22681
22681
|
return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
|
|
22682
22682
|
}
|
|
22683
|
-
var hash =
|
|
22683
|
+
var hash = crypto7.createHash("sha1").update(entity, "utf8").digest("base64").substring(0, 27);
|
|
22684
22684
|
var len = typeof entity === "string" ? Buffer.byteLength(entity, "utf8") : entity.length;
|
|
22685
22685
|
return '"' + len.toString(16) + "-" + hash + '"';
|
|
22686
22686
|
}
|
|
@@ -45141,13 +45141,13 @@ var require_content_disposition = __commonJS({
|
|
|
45141
45141
|
// node_modules/cookie-signature/index.js
|
|
45142
45142
|
var require_cookie_signature = __commonJS({
|
|
45143
45143
|
"node_modules/cookie-signature/index.js"(exports) {
|
|
45144
|
-
var
|
|
45144
|
+
var crypto7 = require("crypto");
|
|
45145
45145
|
exports.sign = function(val, secret) {
|
|
45146
45146
|
if ("string" != typeof val)
|
|
45147
45147
|
throw new TypeError("Cookie value must be provided as a string.");
|
|
45148
45148
|
if (null == secret)
|
|
45149
45149
|
throw new TypeError("Secret key must be provided.");
|
|
45150
|
-
return val + "." +
|
|
45150
|
+
return val + "." + crypto7.createHmac("sha256", secret).update(val).digest("base64").replace(/\=+$/, "");
|
|
45151
45151
|
};
|
|
45152
45152
|
exports.unsign = function(input, secret) {
|
|
45153
45153
|
if ("string" != typeof input)
|
|
@@ -45155,7 +45155,7 @@ var require_cookie_signature = __commonJS({
|
|
|
45155
45155
|
if (null == secret)
|
|
45156
45156
|
throw new TypeError("Secret key must be provided.");
|
|
45157
45157
|
var tentativeValue = input.slice(0, input.lastIndexOf(".")), expectedInput = exports.sign(tentativeValue, secret), expectedBuffer = Buffer.from(expectedInput), inputBuffer = Buffer.from(input);
|
|
45158
|
-
return expectedBuffer.length === inputBuffer.length &&
|
|
45158
|
+
return expectedBuffer.length === inputBuffer.length && crypto7.timingSafeEqual(expectedBuffer, inputBuffer) ? tentativeValue : false;
|
|
45159
45159
|
};
|
|
45160
45160
|
}
|
|
45161
45161
|
});
|
|
@@ -54844,7 +54844,7 @@ var require_send = __commonJS({
|
|
|
54844
54844
|
var createError = require_http_errors();
|
|
54845
54845
|
var debug = require_src()("send");
|
|
54846
54846
|
var encodeUrl = require_encodeurl();
|
|
54847
|
-
var
|
|
54847
|
+
var escapeHtml2 = require_escape_html();
|
|
54848
54848
|
var etag = require_etag();
|
|
54849
54849
|
var fresh = require_fresh();
|
|
54850
54850
|
var fs11 = require("fs");
|
|
@@ -54897,7 +54897,7 @@ var require_send = __commonJS({
|
|
|
54897
54897
|
}
|
|
54898
54898
|
var res = this.res;
|
|
54899
54899
|
var msg = statuses.message[status] || String(status);
|
|
54900
|
-
var doc = createHtmlDocument("Error",
|
|
54900
|
+
var doc = createHtmlDocument("Error", escapeHtml2(msg));
|
|
54901
54901
|
clearHeaders(res);
|
|
54902
54902
|
if (err && err.headers) {
|
|
54903
54903
|
setHeaders(res, err.headers);
|
|
@@ -54997,7 +54997,7 @@ var require_send = __commonJS({
|
|
|
54997
54997
|
return;
|
|
54998
54998
|
}
|
|
54999
54999
|
var loc = encodeUrl(collapseLeadingSlashes(this.path + "/"));
|
|
55000
|
-
var doc = createHtmlDocument("Redirecting", "Redirecting to " +
|
|
55000
|
+
var doc = createHtmlDocument("Redirecting", "Redirecting to " + escapeHtml2(loc));
|
|
55001
55001
|
res.statusCode = 301;
|
|
55002
55002
|
res.setHeader("Content-Type", "text/html; charset=UTF-8");
|
|
55003
55003
|
res.setHeader("Content-Length", Buffer.byteLength(doc));
|
|
@@ -55411,7 +55411,7 @@ var require_response = __commonJS({
|
|
|
55411
55411
|
var createError = require_http_errors();
|
|
55412
55412
|
var deprecate = require_depd()("express");
|
|
55413
55413
|
var encodeUrl = require_encodeurl();
|
|
55414
|
-
var
|
|
55414
|
+
var escapeHtml2 = require_escape_html();
|
|
55415
55415
|
var http4 = require("node:http");
|
|
55416
55416
|
var onFinished = require_on_finished();
|
|
55417
55417
|
var mime = require_mime_types2();
|
|
@@ -55753,7 +55753,7 @@ var require_response = __commonJS({
|
|
|
55753
55753
|
body = statuses.message[status] + ". Redirecting to " + address;
|
|
55754
55754
|
},
|
|
55755
55755
|
html: function() {
|
|
55756
|
-
var u =
|
|
55756
|
+
var u = escapeHtml2(address);
|
|
55757
55757
|
body = "<p>" + statuses.message[status] + ". Redirecting to " + u + "</p>";
|
|
55758
55758
|
},
|
|
55759
55759
|
default: function() {
|
|
@@ -55889,7 +55889,7 @@ var require_serve_static = __commonJS({
|
|
|
55889
55889
|
"node_modules/serve-static/index.js"(exports, module2) {
|
|
55890
55890
|
"use strict";
|
|
55891
55891
|
var encodeUrl = require_encodeurl();
|
|
55892
|
-
var
|
|
55892
|
+
var escapeHtml2 = require_escape_html();
|
|
55893
55893
|
var parseUrl2 = require_parseurl();
|
|
55894
55894
|
var resolve2 = require("path").resolve;
|
|
55895
55895
|
var send = require_send();
|
|
@@ -55975,7 +55975,7 @@ var require_serve_static = __commonJS({
|
|
|
55975
55975
|
originalUrl.path = null;
|
|
55976
55976
|
originalUrl.pathname = collapseLeadingSlashes(originalUrl.pathname + "/");
|
|
55977
55977
|
var loc = encodeUrl(url2.format(originalUrl));
|
|
55978
|
-
var doc = createHtmlDocument("Redirecting", "Redirecting to " +
|
|
55978
|
+
var doc = createHtmlDocument("Redirecting", "Redirecting to " + escapeHtml2(loc));
|
|
55979
55979
|
res.statusCode = 301;
|
|
55980
55980
|
res.setHeader("Content-Type", "text/html; charset=UTF-8");
|
|
55981
55981
|
res.setHeader("Content-Length", Buffer.byteLength(doc));
|
|
@@ -66555,7 +66555,7 @@ var require_form_data = __commonJS({
|
|
|
66555
66555
|
var parseUrl2 = require("url").parse;
|
|
66556
66556
|
var fs11 = require("fs");
|
|
66557
66557
|
var Stream = require("stream").Stream;
|
|
66558
|
-
var
|
|
66558
|
+
var crypto7 = require("crypto");
|
|
66559
66559
|
var mime = require_mime_types5();
|
|
66560
66560
|
var asynckit = require_asynckit();
|
|
66561
66561
|
var setToStringTag = require_es_set_tostringtag();
|
|
@@ -66761,7 +66761,7 @@ var require_form_data = __commonJS({
|
|
|
66761
66761
|
return Buffer.concat([dataBuffer, Buffer.from(this._lastBoundary())]);
|
|
66762
66762
|
};
|
|
66763
66763
|
FormData3.prototype._generateBoundary = function() {
|
|
66764
|
-
this._boundary = "--------------------------" +
|
|
66764
|
+
this._boundary = "--------------------------" + crypto7.randomBytes(12).toString("hex");
|
|
66765
66765
|
};
|
|
66766
66766
|
FormData3.prototype.getLengthSync = function() {
|
|
66767
66767
|
var knownLength = this._overheadLength + this._valueLength;
|
|
@@ -74916,7 +74916,7 @@ var init_src8 = __esm({
|
|
|
74916
74916
|
if (!cfg.account) {
|
|
74917
74917
|
return "account is required";
|
|
74918
74918
|
}
|
|
74919
|
-
if (!cfg
|
|
74919
|
+
if (!this.resolveUsername(cfg)) {
|
|
74920
74920
|
return "username is required";
|
|
74921
74921
|
}
|
|
74922
74922
|
const authMode = this.resolveAuthMode(cfg);
|
|
@@ -74930,14 +74930,20 @@ var init_src8 = __esm({
|
|
|
74930
74930
|
}
|
|
74931
74931
|
// ── Private: build connection options ───────────────────────────────────
|
|
74932
74932
|
resolveAuthMode(cfg) {
|
|
74933
|
+
if (cfg.authenticator?.trim().toLowerCase() === "externalbrowser") {
|
|
74934
|
+
return "externalbrowser";
|
|
74935
|
+
}
|
|
74933
74936
|
return cfg.authMode ?? (cfg.privateKeyPath ? "keypair" : "password");
|
|
74934
74937
|
}
|
|
74938
|
+
resolveUsername(cfg) {
|
|
74939
|
+
return cfg.username || cfg.user;
|
|
74940
|
+
}
|
|
74935
74941
|
buildConnectionOptions(cfg) {
|
|
74936
74942
|
const account = normaliseAccount(cfg.account);
|
|
74937
74943
|
const authMode = this.resolveAuthMode(cfg);
|
|
74938
74944
|
const opts = {
|
|
74939
74945
|
account,
|
|
74940
|
-
username: cfg
|
|
74946
|
+
username: this.resolveUsername(cfg),
|
|
74941
74947
|
warehouse: cfg.warehouse,
|
|
74942
74948
|
database: cfg.database,
|
|
74943
74949
|
schema: cfg.schema,
|
|
@@ -75169,21 +75175,33 @@ var init_descriptor7 = __esm({
|
|
|
75169
75175
|
type: "boolean",
|
|
75170
75176
|
title: "Cache browser SSO token",
|
|
75171
75177
|
description: "Requests Snowflake driver token caching when the account allows ID-token caching",
|
|
75172
|
-
default:
|
|
75173
|
-
ui: { visibleWhen: { field: "authMode", equals: "externalbrowser" } }
|
|
75178
|
+
default: true,
|
|
75179
|
+
ui: { visibleWhen: { field: "authMode", equals: "externalbrowser" }, advanced: true }
|
|
75174
75180
|
},
|
|
75175
75181
|
warehouse: {
|
|
75176
75182
|
type: "string",
|
|
75177
75183
|
title: "Warehouse",
|
|
75178
|
-
description: "Virtual warehouse to use (optional)"
|
|
75184
|
+
description: "Virtual warehouse to use (optional)",
|
|
75185
|
+
ui: { advanced: true }
|
|
75179
75186
|
},
|
|
75180
75187
|
database: {
|
|
75181
75188
|
type: "string",
|
|
75182
75189
|
title: "Database",
|
|
75183
|
-
description: "Default database (optional)"
|
|
75190
|
+
description: "Default database (optional)",
|
|
75191
|
+
ui: { advanced: true }
|
|
75192
|
+
},
|
|
75193
|
+
schema: {
|
|
75194
|
+
type: "string",
|
|
75195
|
+
title: "Schema",
|
|
75196
|
+
description: "Default schema (optional)",
|
|
75197
|
+
ui: { advanced: true }
|
|
75184
75198
|
},
|
|
75185
|
-
|
|
75186
|
-
|
|
75199
|
+
role: {
|
|
75200
|
+
type: "string",
|
|
75201
|
+
title: "Role",
|
|
75202
|
+
description: "Role to assume (optional)",
|
|
75203
|
+
ui: { advanced: true }
|
|
75204
|
+
}
|
|
75187
75205
|
},
|
|
75188
75206
|
required: ["account", "username"]
|
|
75189
75207
|
};
|
|
@@ -76760,7 +76778,16 @@ function isSnowflakeBrowserSsoProfile(value) {
|
|
|
76760
76778
|
return false;
|
|
76761
76779
|
}
|
|
76762
76780
|
const profile = value;
|
|
76763
|
-
return profile["type"] === "snowflake" && profile["authMode"] === "externalbrowser";
|
|
76781
|
+
return profile["type"] === "snowflake" && (profile["authMode"] === "externalbrowser" || typeof profile["authenticator"] === "string" && profile["authenticator"].toLowerCase() === "externalbrowser");
|
|
76782
|
+
}
|
|
76783
|
+
function normalizeConnectionProfileForSave(profile) {
|
|
76784
|
+
const profileToSave = { ...profile };
|
|
76785
|
+
if (isSnowflakeBrowserSsoProfile(profileToSave)) {
|
|
76786
|
+
profileToSave["password"] = "";
|
|
76787
|
+
delete profileToSave["privateKeyPath"];
|
|
76788
|
+
delete profileToSave["privateKeyPassphrase"];
|
|
76789
|
+
}
|
|
76790
|
+
return profileToSave;
|
|
76764
76791
|
}
|
|
76765
76792
|
function stripCommentsAndStrings(sql) {
|
|
76766
76793
|
let result = "";
|
|
@@ -76834,7 +76861,7 @@ function isReadOnlySql(sql) {
|
|
|
76834
76861
|
}
|
|
76835
76862
|
return !WRITE_KEYWORDS.test(normalized);
|
|
76836
76863
|
}
|
|
76837
|
-
var WRITE_KEYWORDS, READ_ONLY_START_KEYWORDS,
|
|
76864
|
+
var WRITE_KEYWORDS, READ_ONLY_START_KEYWORDS, DaemonMcpToolManager;
|
|
76838
76865
|
var init_DaemonMcpToolManager = __esm({
|
|
76839
76866
|
"src/server/DaemonMcpToolManager.ts"() {
|
|
76840
76867
|
"use strict";
|
|
@@ -76844,13 +76871,13 @@ var init_DaemonMcpToolManager = __esm({
|
|
|
76844
76871
|
init_querySplitter();
|
|
76845
76872
|
WRITE_KEYWORDS = /\b(ALTER|CALL|COPY|CREATE|DELETE|DROP|EXEC|EXECUTE|GRANT|INSERT|INSTALL|LOAD|MERGE|REPLACE|REVOKE|SET|TRUNCATE|UPDATE|VACUUM)\b/i;
|
|
76846
76873
|
READ_ONLY_START_KEYWORDS = /* @__PURE__ */ new Set(["SELECT", "SHOW", "DESCRIBE", "EXPLAIN", "WITH"]);
|
|
76847
|
-
SNOWFLAKE_BROWSER_SSO_UNSUPPORTED_MESSAGE = "Snowflake browser SSO is only supported from the local VS Code extension because it requires opening a browser on the user's machine.";
|
|
76848
76874
|
DaemonMcpToolManager = class {
|
|
76849
76875
|
constructor(sessionManager, queryExecutor, connectionManager, connectorRegistry, queryOperationRunner, options = {}) {
|
|
76850
76876
|
this.sessionManager = sessionManager;
|
|
76851
76877
|
this.queryExecutor = queryExecutor;
|
|
76852
76878
|
this.connectionManager = connectionManager;
|
|
76853
76879
|
this.connectorRegistry = connectorRegistry;
|
|
76880
|
+
this.options = options;
|
|
76854
76881
|
this.safeMode = options.safeMode ?? true;
|
|
76855
76882
|
this.queryOperationRunner = queryOperationRunner ?? new QueryOperationRunner(sessionManager, queryExecutor);
|
|
76856
76883
|
}
|
|
@@ -76973,6 +77000,20 @@ var init_DaemonMcpToolManager = __esm({
|
|
|
76973
77000
|
required: ["connectionProfile"]
|
|
76974
77001
|
}
|
|
76975
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
|
+
},
|
|
76976
77017
|
{
|
|
76977
77018
|
name: "test_connection",
|
|
76978
77019
|
description: "Test connectivity for a specific connection profile.",
|
|
@@ -77091,6 +77132,8 @@ var init_DaemonMcpToolManager = __esm({
|
|
|
77091
77132
|
return this.handleListConnections();
|
|
77092
77133
|
case "save_connection":
|
|
77093
77134
|
return this.handleSaveConnection(args);
|
|
77135
|
+
case "request_connection_credentials":
|
|
77136
|
+
return this.handleRequestConnectionCredentials(args);
|
|
77094
77137
|
case "test_connection":
|
|
77095
77138
|
return this.handleTestConnection(args);
|
|
77096
77139
|
case "delete_connection":
|
|
@@ -77262,14 +77305,45 @@ var init_DaemonMcpToolManager = __esm({
|
|
|
77262
77305
|
if (!profile || !profile.id || !profile.name || !profile.type) {
|
|
77263
77306
|
throw new Error("Invalid connection profile: missing id, name, or type");
|
|
77264
77307
|
}
|
|
77265
|
-
|
|
77266
|
-
|
|
77267
|
-
|
|
77268
|
-
await this.connectionManager.saveProfile(
|
|
77308
|
+
const profileToSave = normalizeConnectionProfileForSave(
|
|
77309
|
+
profile
|
|
77310
|
+
);
|
|
77311
|
+
await this.connectionManager.saveProfile(profileToSave);
|
|
77269
77312
|
return {
|
|
77270
77313
|
content: [{ type: "text", text: `Connection '${profile.id}' saved.` }]
|
|
77271
77314
|
};
|
|
77272
77315
|
}
|
|
77316
|
+
async handleRequestConnectionCredentials(args) {
|
|
77317
|
+
const service = this.options.credentialRequestService;
|
|
77318
|
+
if (!service) {
|
|
77319
|
+
throw new Error("Credential request UI is not available for this SQL Preview daemon.");
|
|
77320
|
+
}
|
|
77321
|
+
const connectionId = args?.connectionId;
|
|
77322
|
+
if (!connectionId) {
|
|
77323
|
+
throw new Error("connectionId is required");
|
|
77324
|
+
}
|
|
77325
|
+
const request = await service.create(connectionId);
|
|
77326
|
+
return {
|
|
77327
|
+
structuredContent: {
|
|
77328
|
+
connectionId: request.connectionId,
|
|
77329
|
+
connectionName: request.connectionName,
|
|
77330
|
+
url: request.url,
|
|
77331
|
+
expiresAt: new Date(request.expiresAt).toISOString()
|
|
77332
|
+
},
|
|
77333
|
+
content: [
|
|
77334
|
+
{
|
|
77335
|
+
type: "text",
|
|
77336
|
+
text: `Open this one-time SQL Preview credential form for '${request.connectionName}' and enter the password there; do not paste the password into chat.
|
|
77337
|
+
|
|
77338
|
+
${request.url}
|
|
77339
|
+
|
|
77340
|
+
This link expires at ${new Date(
|
|
77341
|
+
request.expiresAt
|
|
77342
|
+
).toISOString()}.`
|
|
77343
|
+
}
|
|
77344
|
+
]
|
|
77345
|
+
};
|
|
77346
|
+
}
|
|
77273
77347
|
async handleTestConnection(args) {
|
|
77274
77348
|
const typedArgs = args;
|
|
77275
77349
|
let typeToTest;
|
|
@@ -77295,9 +77369,6 @@ var init_DaemonMcpToolManager = __esm({
|
|
|
77295
77369
|
} else {
|
|
77296
77370
|
throw new Error("Must provide either connectionId or (type, connectionProfile)");
|
|
77297
77371
|
}
|
|
77298
|
-
if (typeToTest === "snowflake" && isSnowflakeBrowserSsoProfile(configToTest)) {
|
|
77299
|
-
throw new Error(SNOWFLAKE_BROWSER_SSO_UNSUPPORTED_MESSAGE);
|
|
77300
|
-
}
|
|
77301
77372
|
const result = await this.queryExecutor.testConnection(
|
|
77302
77373
|
typeToTest,
|
|
77303
77374
|
configToTest,
|
|
@@ -77873,6 +77944,79 @@ var init_DaemonQueryExecutor = __esm({
|
|
|
77873
77944
|
}
|
|
77874
77945
|
});
|
|
77875
77946
|
|
|
77947
|
+
// src/server/CredentialRequestService.ts
|
|
77948
|
+
var import_crypto3, DEFAULT_TTL_MS, CredentialRequestService;
|
|
77949
|
+
var init_CredentialRequestService = __esm({
|
|
77950
|
+
"src/server/CredentialRequestService.ts"() {
|
|
77951
|
+
"use strict";
|
|
77952
|
+
import_crypto3 = __toESM(require("crypto"));
|
|
77953
|
+
DEFAULT_TTL_MS = 10 * 60 * 1e3;
|
|
77954
|
+
CredentialRequestService = class {
|
|
77955
|
+
constructor(connectionManager, baseUrl, ttlMs = DEFAULT_TTL_MS) {
|
|
77956
|
+
this.connectionManager = connectionManager;
|
|
77957
|
+
this.baseUrl = baseUrl;
|
|
77958
|
+
this.ttlMs = ttlMs;
|
|
77959
|
+
this.requests = /* @__PURE__ */ new Map();
|
|
77960
|
+
}
|
|
77961
|
+
async create(connectionId) {
|
|
77962
|
+
this.pruneExpired();
|
|
77963
|
+
const profile = await this.connectionManager.getProfile(connectionId);
|
|
77964
|
+
if (!profile) {
|
|
77965
|
+
throw new Error(`Connection profile '${connectionId}' not found`);
|
|
77966
|
+
}
|
|
77967
|
+
const id = import_crypto3.default.randomUUID();
|
|
77968
|
+
const token = import_crypto3.default.randomBytes(24).toString("base64url");
|
|
77969
|
+
const request = {
|
|
77970
|
+
id,
|
|
77971
|
+
token,
|
|
77972
|
+
connectionId,
|
|
77973
|
+
connectionName: profile.name,
|
|
77974
|
+
expiresAt: Date.now() + this.ttlMs,
|
|
77975
|
+
url: `${this.baseUrl}/credential-requests/${encodeURIComponent(id)}?token=${encodeURIComponent(
|
|
77976
|
+
token
|
|
77977
|
+
)}`
|
|
77978
|
+
};
|
|
77979
|
+
this.requests.set(id, request);
|
|
77980
|
+
return request;
|
|
77981
|
+
}
|
|
77982
|
+
get(id, token) {
|
|
77983
|
+
this.pruneExpired();
|
|
77984
|
+
const request = this.requests.get(id);
|
|
77985
|
+
if (!request || request.token !== token) {
|
|
77986
|
+
return void 0;
|
|
77987
|
+
}
|
|
77988
|
+
return request;
|
|
77989
|
+
}
|
|
77990
|
+
async fulfill(id, token, password) {
|
|
77991
|
+
const request = this.get(id, token);
|
|
77992
|
+
if (!request) {
|
|
77993
|
+
throw new Error("Credential request is invalid or expired.");
|
|
77994
|
+
}
|
|
77995
|
+
if (!password) {
|
|
77996
|
+
throw new Error("Password is required.");
|
|
77997
|
+
}
|
|
77998
|
+
const profile = await this.connectionManager.getProfile(request.connectionId);
|
|
77999
|
+
if (!profile) {
|
|
78000
|
+
throw new Error(`Connection profile '${request.connectionId}' not found`);
|
|
78001
|
+
}
|
|
78002
|
+
await this.connectionManager.saveProfile({
|
|
78003
|
+
...profile,
|
|
78004
|
+
password
|
|
78005
|
+
});
|
|
78006
|
+
this.requests.delete(id);
|
|
78007
|
+
return request;
|
|
78008
|
+
}
|
|
78009
|
+
pruneExpired(now = Date.now()) {
|
|
78010
|
+
for (const [id, request] of this.requests.entries()) {
|
|
78011
|
+
if (request.expiresAt <= now) {
|
|
78012
|
+
this.requests.delete(id);
|
|
78013
|
+
}
|
|
78014
|
+
}
|
|
78015
|
+
}
|
|
78016
|
+
};
|
|
78017
|
+
}
|
|
78018
|
+
});
|
|
78019
|
+
|
|
77876
78020
|
// src/server/SessionManager.ts
|
|
77877
78021
|
var import_events2, SessionManager;
|
|
77878
78022
|
var init_SessionManager = __esm({
|
|
@@ -78070,6 +78214,42 @@ function registerDaemonHttpRoutes(context2, mcpHttpSurface) {
|
|
|
78070
78214
|
configPath: context2.resolvedConfig.configPath
|
|
78071
78215
|
});
|
|
78072
78216
|
});
|
|
78217
|
+
app.get("/credential-requests/:id", (req, res) => {
|
|
78218
|
+
setCredentialPageHeaders(res);
|
|
78219
|
+
const id = req.params.id;
|
|
78220
|
+
const token = typeof req.query["token"] === "string" ? req.query["token"] : "";
|
|
78221
|
+
const request = id ? context2.credentialRequestService.get(id, token) : void 0;
|
|
78222
|
+
if (!request) {
|
|
78223
|
+
res.status(404).send(renderCredentialPage({ error: "This credential request is invalid or expired." }));
|
|
78224
|
+
return;
|
|
78225
|
+
}
|
|
78226
|
+
res.send(renderCredentialPage({ request }));
|
|
78227
|
+
});
|
|
78228
|
+
app.post(
|
|
78229
|
+
"/credential-requests/:id",
|
|
78230
|
+
import_express.default.urlencoded({ extended: false }),
|
|
78231
|
+
async (req, res) => {
|
|
78232
|
+
setCredentialPageHeaders(res);
|
|
78233
|
+
const id = req.params.id;
|
|
78234
|
+
const body = req.body;
|
|
78235
|
+
const token = typeof body.token === "string" ? body.token : "";
|
|
78236
|
+
const password = typeof body.password === "string" ? body.password : "";
|
|
78237
|
+
try {
|
|
78238
|
+
if (!id) {
|
|
78239
|
+
throw new Error("Credential request is invalid or expired.");
|
|
78240
|
+
}
|
|
78241
|
+
const request = await context2.credentialRequestService.fulfill(id, token, password);
|
|
78242
|
+
res.send(
|
|
78243
|
+
renderCredentialPage({
|
|
78244
|
+
success: `Password saved for '${request.connectionName}'. You can close this tab and ask your tool to retry the query.`
|
|
78245
|
+
})
|
|
78246
|
+
);
|
|
78247
|
+
} catch (err) {
|
|
78248
|
+
logger.warn("[Daemon] Credential request failed:", err);
|
|
78249
|
+
res.status(400).send(renderCredentialPage({ error: err instanceof Error ? err.message : String(err) }));
|
|
78250
|
+
}
|
|
78251
|
+
}
|
|
78252
|
+
);
|
|
78073
78253
|
app.get("/embedded-app", async (_req, res) => {
|
|
78074
78254
|
serveHtmlFile(res, "mcp-app.html", 'UI bundle not found. Please run "npm run build" first.');
|
|
78075
78255
|
});
|
|
@@ -78253,7 +78433,7 @@ async function handleQueryRoute(context2, req, res, options) {
|
|
|
78253
78433
|
res.status(400).json({ error: options.missingQueryMessage });
|
|
78254
78434
|
return;
|
|
78255
78435
|
}
|
|
78256
|
-
const sessionId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : `${options.sessionPrefix}-${
|
|
78436
|
+
const sessionId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : `${options.sessionPrefix}-${import_crypto4.default.randomUUID()}`;
|
|
78257
78437
|
const controller = new AbortController();
|
|
78258
78438
|
const cleanupDisconnectAbort = attachAbortOnHttpDisconnect(
|
|
78259
78439
|
req,
|
|
@@ -78332,11 +78512,77 @@ function findDistFile(fileName) {
|
|
|
78332
78512
|
];
|
|
78333
78513
|
return candidates.find((candidate) => fs6.existsSync(candidate)) || candidates[0] || fileName;
|
|
78334
78514
|
}
|
|
78335
|
-
|
|
78515
|
+
function setCredentialPageHeaders(res) {
|
|
78516
|
+
res.setHeader("Cache-Control", "no-store");
|
|
78517
|
+
res.setHeader("Referrer-Policy", "no-referrer");
|
|
78518
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
78519
|
+
}
|
|
78520
|
+
function renderCredentialPage(input) {
|
|
78521
|
+
const title = "SQL Preview Credential Request";
|
|
78522
|
+
const message = input.success || input.error || "";
|
|
78523
|
+
const escapedMessage = escapeHtml(message);
|
|
78524
|
+
const request = input.request;
|
|
78525
|
+
const form = request ? `<p>Enter the password for <strong>${escapeHtml(request.connectionName)}</strong>.</p>
|
|
78526
|
+
<p class="hint">The password is sent directly to the local SQL Preview daemon and is not shown in the MCP/chat transcript.</p>
|
|
78527
|
+
<form method="post" autocomplete="off">
|
|
78528
|
+
<input type="hidden" name="token" value="${escapeHtml(request.token)}" />
|
|
78529
|
+
<label for="password">Password</label>
|
|
78530
|
+
<input id="password" name="password" type="password" autocomplete="current-password" autofocus required />
|
|
78531
|
+
<button type="submit">Save password</button>
|
|
78532
|
+
</form>
|
|
78533
|
+
<p class="hint">This request expires at ${escapeHtml(new Date(request.expiresAt).toLocaleString())}.</p>` : "";
|
|
78534
|
+
return `<!doctype html>
|
|
78535
|
+
<html lang="en">
|
|
78536
|
+
<head>
|
|
78537
|
+
<meta charset="utf-8" />
|
|
78538
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
78539
|
+
<title>${title}</title>
|
|
78540
|
+
<style>
|
|
78541
|
+
:root { color-scheme: light dark; font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
78542
|
+
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); }
|
|
78544
|
+
h1 { margin-top: 0; font-size: 1.35rem; }
|
|
78545
|
+
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; }
|
|
78548
|
+
.hint { color: color-mix(in srgb, CanvasText 72%, transparent); }
|
|
78549
|
+
.success { color: #15803d; }
|
|
78550
|
+
.error { color: #b91c1c; }
|
|
78551
|
+
</style>
|
|
78552
|
+
</head>
|
|
78553
|
+
<body>
|
|
78554
|
+
<main>
|
|
78555
|
+
<h1>${title}</h1>
|
|
78556
|
+
${input.success ? `<p class="success">${escapedMessage}</p>` : ""}
|
|
78557
|
+
${input.error ? `<p class="error">${escapedMessage}</p>` : ""}
|
|
78558
|
+
${form}
|
|
78559
|
+
</main>
|
|
78560
|
+
</body>
|
|
78561
|
+
</html>`;
|
|
78562
|
+
}
|
|
78563
|
+
function escapeHtml(value) {
|
|
78564
|
+
return value.replace(/[&<>'"]/g, (char) => {
|
|
78565
|
+
switch (char) {
|
|
78566
|
+
case "&":
|
|
78567
|
+
return "&";
|
|
78568
|
+
case "<":
|
|
78569
|
+
return "<";
|
|
78570
|
+
case ">":
|
|
78571
|
+
return ">";
|
|
78572
|
+
case "'":
|
|
78573
|
+
return "'";
|
|
78574
|
+
case '"':
|
|
78575
|
+
return """;
|
|
78576
|
+
default:
|
|
78577
|
+
return char;
|
|
78578
|
+
}
|
|
78579
|
+
});
|
|
78580
|
+
}
|
|
78581
|
+
var import_crypto4, import_express, fs6, path4;
|
|
78336
78582
|
var init_DaemonHttpRoutes = __esm({
|
|
78337
78583
|
"src/server/surfaces/DaemonHttpRoutes.ts"() {
|
|
78338
78584
|
"use strict";
|
|
78339
|
-
|
|
78585
|
+
import_crypto4 = __toESM(require("crypto"));
|
|
78340
78586
|
import_express = __toESM(require_express2());
|
|
78341
78587
|
fs6 = __toESM(require("fs"));
|
|
78342
78588
|
path4 = __toESM(require("path"));
|
|
@@ -78391,13 +78637,13 @@ function writeFromReadableStream(stream4, writable) {
|
|
|
78391
78637
|
}
|
|
78392
78638
|
return writeFromReadableStreamDefaultReader(stream4.getReader(), writable);
|
|
78393
78639
|
}
|
|
78394
|
-
var import_http22, import_http23, import_stream5,
|
|
78640
|
+
var import_http22, import_http23, import_stream5, import_crypto5, RequestError, toRequestError, GlobalRequest, Request, newHeadersFromIncoming, wrapBodyStream, newRequestFromIncoming, getRequestCache, requestCache, incomingKey, urlKey, headersKey, abortControllerKey, getAbortController, requestPrototype, newRequest, responseCache, getResponseCache, cacheKey, GlobalResponse, Response2, buildOutgoingHttpHeaders, X_ALREADY_SENT, outgoingEnded, incomingDraining, DRAIN_TIMEOUT_MS, MAX_DRAIN_BYTES, drainIncoming, handleRequestError, handleFetchError, handleResponseError, flushHeaders, responseViaCache, isPromise, responseViaResponseObject, getRequestListener;
|
|
78395
78641
|
var init_dist = __esm({
|
|
78396
78642
|
"node_modules/@hono/node-server/dist/index.mjs"() {
|
|
78397
78643
|
import_http22 = require("http2");
|
|
78398
78644
|
import_http23 = require("http2");
|
|
78399
78645
|
import_stream5 = require("stream");
|
|
78400
|
-
|
|
78646
|
+
import_crypto5 = __toESM(require("crypto"), 1);
|
|
78401
78647
|
RequestError = class extends Error {
|
|
78402
78648
|
constructor(message, options) {
|
|
78403
78649
|
super(message, options);
|
|
@@ -78691,7 +78937,7 @@ var init_dist = __esm({
|
|
|
78691
78937
|
};
|
|
78692
78938
|
X_ALREADY_SENT = "x-hono-already-sent";
|
|
78693
78939
|
if (typeof global.crypto === "undefined") {
|
|
78694
|
-
global.crypto =
|
|
78940
|
+
global.crypto = import_crypto5.default;
|
|
78695
78941
|
}
|
|
78696
78942
|
outgoingEnded = Symbol("outgoingEnded");
|
|
78697
78943
|
incomingDraining = Symbol("incomingDraining");
|
|
@@ -126766,11 +127012,11 @@ var init_McpServerFactory = __esm({
|
|
|
126766
127012
|
});
|
|
126767
127013
|
|
|
126768
127014
|
// src/server/surfaces/DaemonMcpHttpSurface.ts
|
|
126769
|
-
var
|
|
127015
|
+
var import_crypto6, DaemonMcpHttpSurface;
|
|
126770
127016
|
var init_DaemonMcpHttpSurface = __esm({
|
|
126771
127017
|
"src/server/surfaces/DaemonMcpHttpSurface.ts"() {
|
|
126772
127018
|
"use strict";
|
|
126773
|
-
|
|
127019
|
+
import_crypto6 = __toESM(require("crypto"));
|
|
126774
127020
|
init_streamableHttp();
|
|
126775
127021
|
init_ConsoleLogger();
|
|
126776
127022
|
init_logRedaction();
|
|
@@ -126850,7 +127096,7 @@ var init_DaemonMcpHttpSurface = __esm({
|
|
|
126850
127096
|
if (typeof headerSessionId === "string" && headerSessionId.length > 0) {
|
|
126851
127097
|
return headerSessionId;
|
|
126852
127098
|
}
|
|
126853
|
-
return `session-${
|
|
127099
|
+
return `session-${import_crypto6.default.randomUUID()}`;
|
|
126854
127100
|
}
|
|
126855
127101
|
async getOrCreateSession(sessionId) {
|
|
126856
127102
|
const existingSession = this.sessions.get(sessionId);
|
|
@@ -131212,7 +131458,8 @@ var init_DaemonSurfaceManager = __esm({
|
|
|
131212
131458
|
refreshActivity: this.options.refreshActivity,
|
|
131213
131459
|
resolvedConfig: this.options.resolvedConfig,
|
|
131214
131460
|
sessionManager: this.options.sessionManager,
|
|
131215
|
-
getSurfaceStatus: () => this.getStatus()
|
|
131461
|
+
getSurfaceStatus: () => this.getStatus(),
|
|
131462
|
+
credentialRequestService: this.options.credentialRequestService
|
|
131216
131463
|
},
|
|
131217
131464
|
this.mcpHttpSurface
|
|
131218
131465
|
);
|
|
@@ -131311,6 +131558,7 @@ var init_Daemon = __esm({
|
|
|
131311
131558
|
init_DaemonMcpToolManager();
|
|
131312
131559
|
init_DaemonQueryExecutor();
|
|
131313
131560
|
init_QueryOperationRunner();
|
|
131561
|
+
init_CredentialRequestService();
|
|
131314
131562
|
init_SessionManager();
|
|
131315
131563
|
init_DaemonSurfaceManager();
|
|
131316
131564
|
Daemon = class {
|
|
@@ -131347,13 +131595,20 @@ var init_Daemon = __esm({
|
|
|
131347
131595
|
this.config.query
|
|
131348
131596
|
);
|
|
131349
131597
|
this.queryOperationRunner = new QueryOperationRunner(this.sessionManager, this.queryExecutor);
|
|
131598
|
+
this.credentialRequestService = new CredentialRequestService(
|
|
131599
|
+
this.connectionManager,
|
|
131600
|
+
this.getHttpBaseUrl()
|
|
131601
|
+
);
|
|
131350
131602
|
this.toolManager = new DaemonMcpToolManager(
|
|
131351
131603
|
this.sessionManager,
|
|
131352
131604
|
this.queryExecutor,
|
|
131353
131605
|
this.connectionManager,
|
|
131354
131606
|
this.connectorRegistry,
|
|
131355
131607
|
this.queryOperationRunner,
|
|
131356
|
-
{
|
|
131608
|
+
{
|
|
131609
|
+
safeMode: this.config.mcp.safeMode,
|
|
131610
|
+
credentialRequestService: this.credentialRequestService
|
|
131611
|
+
}
|
|
131357
131612
|
);
|
|
131358
131613
|
this.surfaceManager = new DaemonSurfaceManager({
|
|
131359
131614
|
app: this.app,
|
|
@@ -131366,7 +131621,8 @@ var init_Daemon = __esm({
|
|
|
131366
131621
|
resolvedConfig: this.resolvedConfig,
|
|
131367
131622
|
sessionManager: this.sessionManager,
|
|
131368
131623
|
socketPath: this.SOCKET_PATH,
|
|
131369
|
-
toolManager: this.toolManager
|
|
131624
|
+
toolManager: this.toolManager,
|
|
131625
|
+
credentialRequestService: this.credentialRequestService
|
|
131370
131626
|
});
|
|
131371
131627
|
this.surfaceManager.registerHttpRoutes();
|
|
131372
131628
|
this.setupLifecycle();
|
|
@@ -131375,6 +131631,10 @@ var init_Daemon = __esm({
|
|
|
131375
131631
|
(e) => logger.warn("[Daemon] Failed to create built-in connections:", e)
|
|
131376
131632
|
);
|
|
131377
131633
|
}
|
|
131634
|
+
getHttpBaseUrl() {
|
|
131635
|
+
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}`;
|
|
131637
|
+
}
|
|
131378
131638
|
/**
|
|
131379
131639
|
* Auto-provisions built-in connections that should always be available.
|
|
131380
131640
|
* DuckDB :memory: allows CSV/Parquet queries without any manual setup.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sql-preview",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"mcpName": "io.github.fadnavismehul/sql-preview",
|
|
5
5
|
"description": "Standalone SQL Preview MCP database server for VS Code, Claude Desktop, Cursor, and other MCP clients.",
|
|
6
6
|
"main": "out/server/standalone.js",
|
package/server.json
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
"source": "github",
|
|
9
9
|
"id": "1025027765"
|
|
10
10
|
},
|
|
11
|
-
"version": "0.6.
|
|
11
|
+
"version": "0.6.3",
|
|
12
12
|
"websiteUrl": "https://github.com/fadnavismehul/sql-preview#readme",
|
|
13
13
|
"packages": [
|
|
14
14
|
{
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
17
17
|
"identifier": "sql-preview",
|
|
18
|
-
"version": "0.6.
|
|
18
|
+
"version": "0.6.3",
|
|
19
19
|
"runtimeHint": "npx",
|
|
20
20
|
"runtimeArguments": [
|
|
21
21
|
{
|