@eamonboyle/mssql-mcp 1.5.0 → 2.0.0

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.
Files changed (125) hide show
  1. package/README.md +379 -361
  2. package/dist/apps/queryResultsApp.d.ts +18 -0
  3. package/dist/apps/queryResultsApp.d.ts.map +1 -0
  4. package/dist/apps/queryResultsApp.js +272 -0
  5. package/dist/apps/queryResultsApp.js.map +1 -0
  6. package/dist/checkNodeVersion.d.ts +2 -0
  7. package/dist/checkNodeVersion.d.ts.map +1 -0
  8. package/dist/checkNodeVersion.js +9 -0
  9. package/dist/checkNodeVersion.js.map +1 -0
  10. package/dist/config.d.ts +58 -7
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +202 -33
  13. package/dist/config.js.map +1 -1
  14. package/dist/confirmationMessage.d.ts +2 -0
  15. package/dist/confirmationMessage.d.ts.map +1 -0
  16. package/dist/confirmationMessage.js +105 -0
  17. package/dist/confirmationMessage.js.map +1 -0
  18. package/dist/db.d.ts +24 -0
  19. package/dist/db.d.ts.map +1 -1
  20. package/dist/db.js +152 -36
  21. package/dist/db.js.map +1 -1
  22. package/dist/filteredRead.js +0 -0
  23. package/dist/httpServer.d.ts +25 -0
  24. package/dist/httpServer.d.ts.map +1 -0
  25. package/dist/httpServer.js +128 -0
  26. package/dist/httpServer.js.map +1 -0
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +47 -366
  30. package/dist/index.js.map +1 -1
  31. package/dist/mcpResults.d.ts +1 -1
  32. package/dist/mcpResults.d.ts.map +1 -1
  33. package/dist/mcpResults.js +2 -2
  34. package/dist/mcpResults.js.map +1 -1
  35. package/dist/nodeVersion.d.ts +14 -0
  36. package/dist/nodeVersion.d.ts.map +1 -0
  37. package/dist/nodeVersion.js +23 -0
  38. package/dist/nodeVersion.js.map +1 -0
  39. package/dist/packageInfo.d.ts +3 -0
  40. package/dist/packageInfo.d.ts.map +1 -0
  41. package/dist/packageInfo.js +5 -0
  42. package/dist/packageInfo.js.map +1 -0
  43. package/dist/promptRegistry.d.ts +1 -1
  44. package/dist/promptRegistry.d.ts.map +1 -1
  45. package/dist/promptRegistry.js +1 -1
  46. package/dist/promptRegistry.js.map +1 -1
  47. package/dist/resourceRegistry.d.ts +9 -1
  48. package/dist/resourceRegistry.d.ts.map +1 -1
  49. package/dist/resourceRegistry.js +9 -2
  50. package/dist/resourceRegistry.js.map +1 -1
  51. package/dist/schema.d.ts.map +1 -1
  52. package/dist/schema.js +16 -20
  53. package/dist/schema.js.map +1 -1
  54. package/dist/server.d.ts +5 -0
  55. package/dist/server.d.ts.map +1 -0
  56. package/dist/server.js +320 -0
  57. package/dist/server.js.map +1 -0
  58. package/dist/serverState.js +2 -4
  59. package/dist/serverState.js.map +1 -1
  60. package/dist/sql.js +0 -0
  61. package/dist/sqlErrors.d.ts +10 -0
  62. package/dist/sqlErrors.d.ts.map +1 -0
  63. package/dist/sqlErrors.js +32 -0
  64. package/dist/sqlErrors.js.map +1 -0
  65. package/dist/toolRegistry.d.ts +1 -1
  66. package/dist/toolRegistry.d.ts.map +1 -1
  67. package/dist/toolRegistry.js +1 -1
  68. package/dist/toolRegistry.js.map +1 -1
  69. package/dist/tools/AnalyzeTableTool.js +2 -4
  70. package/dist/tools/AnalyzeTableTool.js.map +1 -1
  71. package/dist/tools/CreateIndexTool.js +2 -4
  72. package/dist/tools/CreateIndexTool.js.map +1 -1
  73. package/dist/tools/CreateTableTool.js +2 -4
  74. package/dist/tools/CreateTableTool.js.map +1 -1
  75. package/dist/tools/DeleteDataTool.js +2 -4
  76. package/dist/tools/DeleteDataTool.js.map +1 -1
  77. package/dist/tools/DescribeDependenciesTool.js +2 -4
  78. package/dist/tools/DescribeDependenciesTool.js.map +1 -1
  79. package/dist/tools/DescribeObjectTool.js +2 -4
  80. package/dist/tools/DescribeObjectTool.js.map +1 -1
  81. package/dist/tools/DescribeRelationshipsTool.js +2 -4
  82. package/dist/tools/DescribeRelationshipsTool.js.map +1 -1
  83. package/dist/tools/DescribeTableTool.js +2 -4
  84. package/dist/tools/DescribeTableTool.js.map +1 -1
  85. package/dist/tools/DropTableTool.js +2 -4
  86. package/dist/tools/DropTableTool.js.map +1 -1
  87. package/dist/tools/ExplainQueryTool.d.ts.map +1 -1
  88. package/dist/tools/ExplainQueryTool.js +4 -6
  89. package/dist/tools/ExplainQueryTool.js.map +1 -1
  90. package/dist/tools/InsertDataTool.js +2 -4
  91. package/dist/tools/InsertDataTool.js.map +1 -1
  92. package/dist/tools/ListDatabasesTool.d.ts.map +1 -1
  93. package/dist/tools/ListDatabasesTool.js +2 -4
  94. package/dist/tools/ListDatabasesTool.js.map +1 -1
  95. package/dist/tools/ListForeignKeysTool.js +2 -4
  96. package/dist/tools/ListForeignKeysTool.js.map +1 -1
  97. package/dist/tools/ListLargestTablesTool.js +2 -4
  98. package/dist/tools/ListLargestTablesTool.js.map +1 -1
  99. package/dist/tools/ListObjectsTool.js +2 -4
  100. package/dist/tools/ListObjectsTool.js.map +1 -1
  101. package/dist/tools/ListTableTool.js +2 -4
  102. package/dist/tools/ListTableTool.js.map +1 -1
  103. package/dist/tools/PreviewDeleteTool.js +2 -4
  104. package/dist/tools/PreviewDeleteTool.js.map +1 -1
  105. package/dist/tools/PreviewUpdateTool.js +2 -4
  106. package/dist/tools/PreviewUpdateTool.js.map +1 -1
  107. package/dist/tools/ReadDataTool.d.ts +15 -12
  108. package/dist/tools/ReadDataTool.d.ts.map +1 -1
  109. package/dist/tools/ReadDataTool.js +86 -64
  110. package/dist/tools/ReadDataTool.js.map +1 -1
  111. package/dist/tools/SearchDataTool.js +2 -4
  112. package/dist/tools/SearchDataTool.js.map +1 -1
  113. package/dist/tools/SummarizeSchemaTool.js +2 -4
  114. package/dist/tools/SummarizeSchemaTool.js.map +1 -1
  115. package/dist/tools/UpdateDataTool.js +2 -4
  116. package/dist/tools/UpdateDataTool.js.map +1 -1
  117. package/dist/validation.d.ts +26 -3
  118. package/dist/validation.d.ts.map +1 -1
  119. package/dist/validation.js +237 -85
  120. package/dist/validation.js.map +1 -1
  121. package/dist/writePreview.js +0 -0
  122. package/dist/writePreviewGrant.js +0 -0
  123. package/dist/writePreviewGrantStore.js +0 -0
  124. package/dist/writeSafety.js +0 -0
  125. package/package.json +27 -18
@@ -0,0 +1,18 @@
1
+ import type { McpServer } from "@modelcontextprotocol/server";
2
+ /**
3
+ * MCP Apps (io.modelcontextprotocol/ui) view that renders read_data and
4
+ * search_data results as an interactive grid in hosts that support it
5
+ * (Claude, VS Code, Cursor, …). Hosts without MCP Apps ignore the tool
6
+ * metadata and keep using the text/structured result.
7
+ */
8
+ export declare const QUERY_RESULTS_APP_URI = "ui://mssql/query-results.html";
9
+ export declare function buildQueryResultsHtml(): string;
10
+ export declare function registerQueryResultsApp(server: McpServer): void;
11
+ /** Tool `_meta` linking a tool to the query-results view (both key styles). */
12
+ export declare const queryResultsToolMeta: {
13
+ ui: {
14
+ resourceUri: string;
15
+ };
16
+ "ui/resourceUri": string;
17
+ };
18
+ //# sourceMappingURL=queryResultsApp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryResultsApp.d.ts","sourceRoot":"","sources":["../../src/apps/queryResultsApp.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,kCAAkC,CAAC;AAqOrE,wBAAgB,qBAAqB,IAAI,MAAM,CAmB9C;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAuB/D;AAED,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB;;;;;CAGhC,CAAC"}
@@ -0,0 +1,272 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { RESOURCE_MIME_TYPE, registerAppResource, } from "@modelcontextprotocol/ext-apps/server";
4
+ /**
5
+ * MCP Apps (io.modelcontextprotocol/ui) view that renders read_data and
6
+ * search_data results as an interactive grid in hosts that support it
7
+ * (Claude, VS Code, Cursor, …). Hosts without MCP Apps ignore the tool
8
+ * metadata and keep using the text/structured result.
9
+ */
10
+ export const QUERY_RESULTS_APP_URI = "ui://mssql/query-results.html";
11
+ const require = createRequire(import.meta.url);
12
+ /**
13
+ * Loads the self-contained ext-apps View runtime and rewrites its trailing
14
+ * `export{…}` into a local binding so it can be inlined into a single
15
+ * module script (hosts render the view from one HTML document).
16
+ */
17
+ function loadAppRuntime() {
18
+ const bundle = readFileSync(require.resolve("@modelcontextprotocol/ext-apps/app-with-deps"), "utf8");
19
+ const exportStart = bundle.lastIndexOf("export{");
20
+ if (exportStart === -1) {
21
+ throw new Error("Unexpected ext-apps bundle format: missing export list.");
22
+ }
23
+ const exportEnd = bundle.indexOf("}", exportStart);
24
+ const entries = bundle
25
+ .slice(exportStart + "export{".length, exportEnd)
26
+ .split(",")
27
+ .map((entry) => {
28
+ const [local, exported = local] = entry.trim().split(/\s+as\s+/);
29
+ return `${JSON.stringify(exported)}:${local}`;
30
+ });
31
+ return `${bundle.slice(0, exportStart)}const ExtApps={${entries.join(",")}};${bundle.slice(exportEnd + 1)}`;
32
+ }
33
+ const VIEW_STYLES = `
34
+ :root { color-scheme: light dark; }
35
+ * { box-sizing: border-box; }
36
+ body {
37
+ margin: 0;
38
+ font-family: var(--font-sans, system-ui, sans-serif);
39
+ font-size: var(--font-text-sm-size, 13px);
40
+ color: var(--color-text-primary, CanvasText);
41
+ background: var(--color-background-primary, Canvas);
42
+ }
43
+ header {
44
+ display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
45
+ padding: 8px 10px;
46
+ border-bottom: 1px solid var(--color-border-primary, #8884);
47
+ }
48
+ header .summary { flex: 1; color: var(--color-text-secondary, GrayText); }
49
+ .badge {
50
+ padding: 1px 6px; border-radius: 999px; font-size: 11px;
51
+ background: var(--color-background-warning, #f5a62333);
52
+ color: var(--color-text-warning, inherit);
53
+ }
54
+ input[type="search"] {
55
+ font: inherit; padding: 3px 8px; min-width: 160px;
56
+ color: inherit; background: transparent;
57
+ border: 1px solid var(--color-border-primary, #8886);
58
+ border-radius: var(--border-radius-sm, 4px);
59
+ }
60
+ .grid { overflow: auto; max-height: 480px; }
61
+ table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
62
+ th, td {
63
+ padding: 4px 10px; text-align: left; white-space: nowrap;
64
+ border-bottom: 1px solid var(--color-border-secondary, #8882);
65
+ max-width: 360px; overflow: hidden; text-overflow: ellipsis;
66
+ }
67
+ th {
68
+ position: sticky; top: 0; cursor: pointer; user-select: none;
69
+ background: var(--color-background-secondary, Canvas);
70
+ font-weight: 600;
71
+ }
72
+ th[aria-sort="ascending"]::after { content: " ▲"; }
73
+ th[aria-sort="descending"]::after { content: " ▼"; }
74
+ td.null { color: var(--color-text-tertiary, GrayText); font-style: italic; }
75
+ td.num { text-align: right; }
76
+ .empty, .error { padding: 16px 10px; color: var(--color-text-secondary, GrayText); }
77
+ .error { color: var(--color-text-danger, #d33); }
78
+ `;
79
+ const VIEW_SCRIPT = `
80
+ const { App, applyDocumentTheme, applyHostStyleVariables, applyHostFonts } = ExtApps;
81
+ const MAX_RENDERED_ROWS = 1000;
82
+ const root = document.getElementById("root");
83
+ let rows = [];
84
+ let columns = [];
85
+ let meta = {};
86
+ let sort = { column: null, direction: 1 };
87
+ let filter = "";
88
+
89
+ function extractRows(payload) {
90
+ const data = payload && payload.data;
91
+ if (Array.isArray(data)) return data;
92
+ if (data && Array.isArray(data.rows)) return data.rows;
93
+ if (data && Array.isArray(data.data)) return data.data;
94
+ return [];
95
+ }
96
+
97
+ function formatCell(value) {
98
+ if (value === null || value === undefined) return { text: "NULL", cls: "null" };
99
+ if (typeof value === "number" || typeof value === "bigint") return { text: String(value), cls: "num" };
100
+ if (typeof value === "object") return { text: JSON.stringify(value), cls: "" };
101
+ return { text: String(value), cls: "" };
102
+ }
103
+
104
+ function el(tag, attrs, children) {
105
+ const node = document.createElement(tag);
106
+ for (const [key, value] of Object.entries(attrs || {})) {
107
+ if (key === "text") node.textContent = value;
108
+ else if (key.startsWith("on")) node.addEventListener(key.slice(2), value);
109
+ else node.setAttribute(key, value);
110
+ }
111
+ for (const child of children || []) node.append(child);
112
+ return node;
113
+ }
114
+
115
+ function visibleRows() {
116
+ let result = rows;
117
+ if (filter) {
118
+ const needle = filter.toLowerCase();
119
+ result = result.filter((row) =>
120
+ columns.some((column) => String(row[column] ?? "").toLowerCase().includes(needle))
121
+ );
122
+ }
123
+ if (sort.column !== null) {
124
+ const column = sort.column;
125
+ result = [...result].sort((a, b) => {
126
+ const left = a[column];
127
+ const right = b[column];
128
+ if (left === right) return 0;
129
+ if (left === null || left === undefined) return 1;
130
+ if (right === null || right === undefined) return -1;
131
+ return (left < right ? -1 : 1) * sort.direction;
132
+ });
133
+ }
134
+ return result;
135
+ }
136
+
137
+ function render() {
138
+ root.replaceChildren();
139
+ if (meta.error) {
140
+ root.append(el("div", { class: "error", text: meta.error }));
141
+ return;
142
+ }
143
+
144
+ const shown = visibleRows();
145
+ const rendered = shown.slice(0, MAX_RENDERED_ROWS);
146
+ const summaryParts = [rows.length + " row" + (rows.length === 1 ? "" : "s")];
147
+ if (filter) summaryParts.push(shown.length + " matching");
148
+ if (rendered.length < shown.length) summaryParts.push("showing first " + rendered.length);
149
+
150
+ const header = el("header", {}, [
151
+ el("span", { class: "summary", text: summaryParts.join(" · ") }),
152
+ ]);
153
+ if (meta.truncated) {
154
+ header.append(el("span", { class: "badge", text: "Truncated at MAX_ROWS", title: "More rows exist on the server" }));
155
+ }
156
+ header.append(
157
+ el("input", {
158
+ type: "search",
159
+ placeholder: "Filter rows",
160
+ value: filter,
161
+ "aria-label": "Filter rows",
162
+ oninput: (event) => {
163
+ filter = event.target.value;
164
+ render();
165
+ const input = root.querySelector("input[type=search]");
166
+ input.focus();
167
+ input.setSelectionRange(filter.length, filter.length);
168
+ },
169
+ })
170
+ );
171
+ root.append(header);
172
+
173
+ if (rows.length === 0) {
174
+ root.append(el("div", { class: "empty", text: meta.message || "No rows returned." }));
175
+ return;
176
+ }
177
+
178
+ const headRow = el("tr", {}, columns.map((column) => {
179
+ const ariaSort = sort.column === column ? (sort.direction === 1 ? "ascending" : "descending") : "none";
180
+ return el("th", {
181
+ text: column,
182
+ scope: "col",
183
+ "aria-sort": ariaSort,
184
+ onclick: () => {
185
+ sort = sort.column === column ? { column, direction: -sort.direction } : { column, direction: 1 };
186
+ render();
187
+ },
188
+ });
189
+ }));
190
+ const body = el("tbody", {}, rendered.map((row) =>
191
+ el("tr", {}, columns.map((column) => {
192
+ const cell = formatCell(row[column]);
193
+ return el("td", { class: cell.cls, text: cell.text, title: cell.text });
194
+ }))
195
+ ));
196
+ root.append(el("div", { class: "grid" }, [el("table", {}, [el("thead", {}, [headRow]), body])]));
197
+ }
198
+
199
+ function showResult(result) {
200
+ const payload = result && result.structuredContent;
201
+ if (!payload) {
202
+ meta = { error: "This result has no structured content to display." };
203
+ } else if (payload.success === false) {
204
+ meta = { error: payload.message || "The query failed." };
205
+ } else {
206
+ rows = extractRows(payload);
207
+ columns = rows.length > 0 ? Object.keys(rows[0]) : [];
208
+ meta = { truncated: payload.truncated === true, message: payload.message };
209
+ }
210
+ sort = { column: null, direction: 1 };
211
+ filter = "";
212
+ render();
213
+ }
214
+
215
+ function applyHostContext(context) {
216
+ if (!context) return;
217
+ if (context.theme) applyDocumentTheme(context.theme);
218
+ if (context.styles && context.styles.variables) applyHostStyleVariables(context.styles.variables);
219
+ if (context.styles && context.styles.css && context.styles.css.fonts) applyHostFonts(context.styles.css.fonts);
220
+ }
221
+
222
+ const app = new App({ name: "mssql-query-results", version: "1.0.0" }, {}, { autoResize: true });
223
+ app.ontoolresult = showResult;
224
+ app.onhostcontextchanged = applyHostContext;
225
+ root.append(el("div", { class: "empty", text: "Waiting for query results…" }));
226
+ await app.connect();
227
+ applyHostContext(app.getHostContext());
228
+ `;
229
+ let cachedHtml;
230
+ export function buildQueryResultsHtml() {
231
+ cachedHtml ??= `<!doctype html>
232
+ <html lang="en">
233
+ <head>
234
+ <meta charset="utf-8">
235
+ <meta name="viewport" content="width=device-width, initial-scale=1">
236
+ <title>Query results</title>
237
+ <style>${VIEW_STYLES}</style>
238
+ </head>
239
+ <body>
240
+ <div id="root"></div>
241
+ <script type="module">
242
+ ${loadAppRuntime()}
243
+ ${VIEW_SCRIPT}
244
+ </script>
245
+ </body>
246
+ </html>
247
+ `;
248
+ return cachedHtml;
249
+ }
250
+ export function registerQueryResultsApp(server) {
251
+ registerAppResource(server, "query_results_app", QUERY_RESULTS_APP_URI, {
252
+ title: "Query Results Grid",
253
+ description: "Interactive, sortable and filterable grid for read_data and search_data results.",
254
+ mimeType: RESOURCE_MIME_TYPE,
255
+ _meta: { ui: { prefersBorder: true } },
256
+ }, async () => ({
257
+ contents: [
258
+ {
259
+ uri: QUERY_RESULTS_APP_URI,
260
+ mimeType: RESOURCE_MIME_TYPE,
261
+ text: buildQueryResultsHtml(),
262
+ _meta: { ui: { prefersBorder: true } },
263
+ },
264
+ ],
265
+ }));
266
+ }
267
+ /** Tool `_meta` linking a tool to the query-results view (both key styles). */
268
+ export const queryResultsToolMeta = {
269
+ ui: { resourceUri: QUERY_RESULTS_APP_URI },
270
+ "ui/resourceUri": QUERY_RESULTS_APP_URI,
271
+ };
272
+ //# sourceMappingURL=queryResultsApp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryResultsApp.js","sourceRoot":"","sources":["../../src/apps/queryResultsApp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=checkNodeVersion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkNodeVersion.d.ts","sourceRoot":"","sources":["../src/checkNodeVersion.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ // Imported first by index.ts so it runs before any dependency is evaluated.
2
+ // Logs to stderr only: stdout belongs to the stdio JSON-RPC stream.
3
+ import { nodeVersionError } from "./nodeVersion.js";
4
+ const versionError = nodeVersionError();
5
+ if (versionError) {
6
+ console.error(versionError);
7
+ process.exit(1);
8
+ }
9
+ //# sourceMappingURL=checkNodeVersion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkNodeVersion.js","sourceRoot":"","sources":["../src/checkNodeVersion.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,oEAAoE;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,YAAY,GAAG,gBAAgB,EAAE,CAAC;AACxC,IAAI,YAAY,EAAE,CAAC;IACjB,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
package/dist/config.d.ts CHANGED
@@ -1,13 +1,64 @@
1
+ type Environment = NodeJS.ProcessEnv;
1
2
  export type McpTransportMode = "stdio" | "http";
3
+ export declare const SQL_AUTH_TYPES: readonly ["sql", "ntlm", "azure-default", "azure-service-principal", "azure-access-token"];
4
+ export type SqlAuthType = (typeof SQL_AUTH_TYPES)[number];
5
+ export type SqlAuthConfig = {
6
+ type: "sql";
7
+ user: string;
8
+ password: string;
9
+ } | {
10
+ type: "ntlm";
11
+ user: string;
12
+ password: string;
13
+ domain: string;
14
+ } | {
15
+ type: "azure-default";
16
+ clientId?: string;
17
+ } | {
18
+ type: "azure-service-principal";
19
+ clientId: string;
20
+ clientSecret: string;
21
+ tenantId: string;
22
+ } | {
23
+ type: "azure-access-token";
24
+ token: string;
25
+ };
26
+ export interface SqlConnectionConfig {
27
+ serverName: string;
28
+ serverPort?: number;
29
+ auth: SqlAuthConfig;
30
+ encrypt: boolean;
31
+ trustServerCertificate: boolean;
32
+ connectionTimeoutSeconds: number;
33
+ queryTimeoutMs: number;
34
+ }
35
+ export interface EnvironmentConfig extends SqlConnectionConfig {
36
+ databaseName: string;
37
+ databases: string[];
38
+ readOnly: boolean;
39
+ enableDdl: boolean;
40
+ maxRows: number;
41
+ maxWriteRows: number;
42
+ requireWritePreview: boolean;
43
+ mcpTransport: McpTransportMode;
44
+ mcpHttpHost: string;
45
+ mcpHttpPort: number;
46
+ mcpBaseUrl?: string;
47
+ mcpHttpAuthToken?: string;
48
+ mcpHttpAllowUnauthenticated: boolean;
49
+ mcpHttpAllowedHosts: string[];
50
+ }
51
+ export declare function configureRuntimeEnvironment(environment: EnvironmentConfig): void;
52
+ export declare function parseDatabaseList(value: string | undefined): string[];
53
+ export declare function parseSqlAuthConfig(env?: Environment): SqlAuthConfig;
54
+ export declare function parseSqlConnectionConfig(env?: Environment): SqlConnectionConfig;
55
+ export declare function parseEnvironmentConfig(env?: Environment): EnvironmentConfig;
2
56
  export declare function getMaxRows(): number;
3
- export declare function getQueryTimeoutMs(): number;
4
57
  export declare function getDefaultSearchLimit(): number;
5
58
  export declare function clampRowLimit(limit: unknown, fallback?: number): number;
6
- export declare function getMcpTransport(): McpTransportMode;
7
- export declare function getMcpHttpPort(): number;
8
- export declare function getMcpHttpHost(): string;
9
- export declare function getMcpBaseUrl(): string | undefined;
10
- export declare function isDdlEnabled(): boolean;
59
+ export declare function getMcpEndpointUrl(environment: Pick<EnvironmentConfig, "mcpBaseUrl" | "mcpHttpHost" | "mcpHttpPort">): string;
60
+ /** Brackets IPv6 literals (`::1` -> `[::1]`) for use in a URL authority. */
61
+ export declare function formatUrlHost(host: string): string;
11
62
  export declare function getMaxWriteRows(): number;
12
- export declare function isWritePreviewRequired(): boolean;
63
+ export {};
13
64
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA4CA,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhD,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAe,GAAG,MAAM,CAa7E;AAED,wBAAgB,eAAe,IAAI,gBAAgB,CAIlD;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAGlD;AAED,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,KAAK,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC;AAErC,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAC;AAEhD,eAAO,MAAM,cAAc,4FAMjB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChE;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C;IACE,IAAI,EAAE,yBAAyB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACD;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,sBAAsB,EAAE,OAAO,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2BAA2B,EAAE,OAAO,CAAC;IACrC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAID,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,iBAAiB,GAC7B,IAAI,CAKN;AAmHD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CASrE;AAeD,wBAAgB,kBAAkB,CAChC,GAAG,GAAE,WAAyB,GAC7B,aAAa,CAoCf;AAED,wBAAgB,wBAAwB,CACtC,GAAG,GAAE,WAAyB,GAC7B,mBAAmB,CA0BrB;AAED,wBAAgB,sBAAsB,CACpC,GAAG,GAAE,WAAyB,GAC7B,iBAAiB,CAyDnB;AAeD,wBAAgB,UAAU,IAAI,MAAM,CAKnC;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,SAAe,GAAG,MAAM,CAa7E;AAED,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,IAAI,CACf,iBAAiB,EACjB,YAAY,GAAG,aAAa,GAAG,aAAa,CAC7C,GACA,MAAM,CAKR;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,eAAe,IAAI,MAAM,CASxC"}
package/dist/config.js CHANGED
@@ -1,37 +1,215 @@
1
1
  const DEFAULT_MAX_ROWS = 10000;
2
2
  const DEFAULT_QUERY_TIMEOUT_MS = 30000;
3
3
  const DEFAULT_SEARCH_LIMIT = 100;
4
+ const DEFAULT_CONNECTION_TIMEOUT_SECONDS = 30;
4
5
  const DEFAULT_HTTP_PORT = 3333;
5
6
  const DEFAULT_HTTP_HOST = "127.0.0.1";
6
7
  const DEFAULT_MAX_WRITE_ROWS = 100;
7
- function parsePositiveInt(value, fallback, minimum = 1) {
8
- if (!value) {
8
+ export const SQL_AUTH_TYPES = [
9
+ "sql",
10
+ "ntlm",
11
+ "azure-default",
12
+ "azure-service-principal",
13
+ "azure-access-token",
14
+ ];
15
+ let runtimeEnvironment;
16
+ export function configureRuntimeEnvironment(environment) {
17
+ runtimeEnvironment = {
18
+ ...environment,
19
+ databases: [...environment.databases],
20
+ };
21
+ }
22
+ function parseInteger(name, value, fallback, minimum = 1, maximum) {
23
+ if (value === undefined || value.trim() === "") {
9
24
  return fallback;
10
25
  }
11
- const parsed = Number.parseInt(value, 10);
12
- if (!Number.isFinite(parsed) || parsed < minimum) {
13
- return fallback;
26
+ const range = maximum !== undefined
27
+ ? `between ${minimum} and ${maximum}`
28
+ : `of at least ${minimum}`;
29
+ const normalized = value.trim();
30
+ if (!/^\d+$/.test(normalized)) {
31
+ throw new Error(`${name} must be a whole number ${range}.`);
32
+ }
33
+ const parsed = Number(normalized);
34
+ if (!Number.isSafeInteger(parsed) ||
35
+ parsed < minimum ||
36
+ (maximum !== undefined && parsed > maximum)) {
37
+ throw new Error(`${name} must be a whole number ${range}.`);
14
38
  }
15
39
  return parsed;
16
40
  }
17
- function parseBoolean(value, fallback) {
18
- if (!value) {
41
+ function parseRequiredString(name, value) {
42
+ const normalized = value?.trim();
43
+ if (!normalized) {
44
+ throw new Error(`${name} is required and must not be blank.`);
45
+ }
46
+ return normalized;
47
+ }
48
+ function parseBoolean(name, value, fallback) {
49
+ if ((value === undefined || value.trim() === "") && fallback !== undefined) {
19
50
  return fallback;
20
51
  }
21
- const normalized = value.trim().toLowerCase();
52
+ const normalized = value?.trim().toLowerCase();
22
53
  if (normalized === "true") {
23
54
  return true;
24
55
  }
25
56
  if (normalized === "false") {
26
57
  return false;
27
58
  }
28
- return fallback;
59
+ const requirement = fallback === undefined ? "is required and must be" : "must be";
60
+ throw new Error(`${name} ${requirement} either "true" or "false".`);
29
61
  }
30
- export function getMaxRows() {
31
- return parsePositiveInt(process.env.MAX_ROWS, DEFAULT_MAX_ROWS);
62
+ function parseServerPort(value) {
63
+ if (value === undefined || value.trim() === "") {
64
+ return undefined;
65
+ }
66
+ const normalized = value.trim();
67
+ if (!/^\d+$/.test(normalized)) {
68
+ throw new Error("SERVER_PORT must be a valid TCP port between 1 and 65535.");
69
+ }
70
+ const port = Number(normalized);
71
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
72
+ throw new Error("SERVER_PORT must be a valid TCP port between 1 and 65535.");
73
+ }
74
+ return port;
32
75
  }
33
- export function getQueryTimeoutMs() {
34
- return parsePositiveInt(process.env.QUERY_TIMEOUT_MS, DEFAULT_QUERY_TIMEOUT_MS);
76
+ function parseMcpBaseUrl(value) {
77
+ const normalized = value?.trim();
78
+ if (!normalized) {
79
+ return undefined;
80
+ }
81
+ let url;
82
+ try {
83
+ url = new URL(normalized);
84
+ }
85
+ catch {
86
+ throw new Error("MCP_BASE_URL must be a valid absolute HTTP or HTTPS URL.");
87
+ }
88
+ if ((url.protocol !== "http:" && url.protocol !== "https:") ||
89
+ url.username ||
90
+ url.password ||
91
+ url.search ||
92
+ url.hash) {
93
+ throw new Error("MCP_BASE_URL must be a valid absolute HTTP or HTTPS URL.");
94
+ }
95
+ url.pathname = url.pathname.replace(/\/+$/, "");
96
+ return url.toString().replace(/\/$/, "");
97
+ }
98
+ export function parseDatabaseList(value) {
99
+ if (!value?.trim()) {
100
+ return [];
101
+ }
102
+ return value
103
+ .split(",")
104
+ .map((database) => database.trim())
105
+ .filter(Boolean);
106
+ }
107
+ function parseSqlAuthType(value) {
108
+ if (value === undefined || value.trim() === "") {
109
+ return "sql";
110
+ }
111
+ const normalized = value.trim().toLowerCase();
112
+ if (SQL_AUTH_TYPES.includes(normalized)) {
113
+ return normalized;
114
+ }
115
+ throw new Error(`SQL_AUTH_TYPE must be one of: ${SQL_AUTH_TYPES.join(", ")}.`);
116
+ }
117
+ export function parseSqlAuthConfig(env = process.env) {
118
+ const type = parseSqlAuthType(env.SQL_AUTH_TYPE);
119
+ switch (type) {
120
+ case "sql":
121
+ return {
122
+ type,
123
+ user: parseRequiredString("DB_USER", env.DB_USER),
124
+ password: parseRequiredString("DB_PASSWORD", env.DB_PASSWORD),
125
+ };
126
+ case "ntlm":
127
+ return {
128
+ type,
129
+ user: parseRequiredString("DB_USER", env.DB_USER),
130
+ password: parseRequiredString("DB_PASSWORD", env.DB_PASSWORD),
131
+ domain: parseRequiredString("DB_DOMAIN", env.DB_DOMAIN),
132
+ };
133
+ case "azure-default": {
134
+ const clientId = env.AZURE_CLIENT_ID?.trim();
135
+ return clientId ? { type, clientId } : { type };
136
+ }
137
+ case "azure-service-principal":
138
+ return {
139
+ type,
140
+ clientId: parseRequiredString("AZURE_CLIENT_ID", env.AZURE_CLIENT_ID),
141
+ clientSecret: parseRequiredString("AZURE_CLIENT_SECRET", env.AZURE_CLIENT_SECRET),
142
+ tenantId: parseRequiredString("AZURE_TENANT_ID", env.AZURE_TENANT_ID),
143
+ };
144
+ case "azure-access-token":
145
+ return {
146
+ type,
147
+ token: parseRequiredString("AZURE_ACCESS_TOKEN", env.AZURE_ACCESS_TOKEN),
148
+ };
149
+ }
150
+ }
151
+ export function parseSqlConnectionConfig(env = process.env) {
152
+ const auth = parseSqlAuthConfig(env);
153
+ const isEntraAuth = auth.type.startsWith("azure-");
154
+ return {
155
+ serverName: env.SERVER_NAME?.trim() || "localhost",
156
+ serverPort: parseServerPort(env.SERVER_PORT),
157
+ auth,
158
+ // Azure SQL requires TLS and presents publicly trusted certificates, so
159
+ // Entra ID auth types default to encrypting and validating the certificate.
160
+ encrypt: parseBoolean("ENCRYPT", env.ENCRYPT, isEntraAuth),
161
+ trustServerCertificate: parseBoolean("TRUST_SERVER_CERTIFICATE", env.TRUST_SERVER_CERTIFICATE, !isEntraAuth),
162
+ connectionTimeoutSeconds: parseInteger("CONNECTION_TIMEOUT", env.CONNECTION_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT_SECONDS),
163
+ queryTimeoutMs: parseInteger("QUERY_TIMEOUT_MS", env.QUERY_TIMEOUT_MS, DEFAULT_QUERY_TIMEOUT_MS),
164
+ };
165
+ }
166
+ export function parseEnvironmentConfig(env = process.env) {
167
+ const explicitDefault = env.DATABASE_NAME?.trim();
168
+ const configuredDatabases = parseDatabaseList(env.DATABASES);
169
+ const databases = configuredDatabases.length > 0
170
+ ? configuredDatabases
171
+ : explicitDefault
172
+ ? [explicitDefault]
173
+ : [];
174
+ if (databases.length === 0) {
175
+ throw new Error("At least one of DATABASE_NAME or DATABASES is required and must contain a database name.");
176
+ }
177
+ const databaseName = explicitDefault && databases.includes(explicitDefault)
178
+ ? explicitDefault
179
+ : databases[0];
180
+ return {
181
+ ...parseSqlConnectionConfig(env),
182
+ databaseName,
183
+ databases,
184
+ readOnly: parseBoolean("READONLY", env.READONLY, false),
185
+ enableDdl: parseBoolean("ENABLE_DDL", env.ENABLE_DDL, false),
186
+ maxRows: parseInteger("MAX_ROWS", env.MAX_ROWS, DEFAULT_MAX_ROWS),
187
+ maxWriteRows: parseInteger("MAX_WRITE_ROWS", env.MAX_WRITE_ROWS, DEFAULT_MAX_WRITE_ROWS),
188
+ requireWritePreview: parseBoolean("REQUIRE_WRITE_PREVIEW", env.REQUIRE_WRITE_PREVIEW, true),
189
+ mcpTransport: parseMcpTransport(env.MCP_TRANSPORT),
190
+ mcpHttpHost: env.MCP_HTTP_HOST?.trim() || DEFAULT_HTTP_HOST,
191
+ mcpHttpPort: parseInteger("MCP_HTTP_PORT", env.MCP_HTTP_PORT, DEFAULT_HTTP_PORT, 1, 65535),
192
+ mcpBaseUrl: parseMcpBaseUrl(env.MCP_BASE_URL),
193
+ mcpHttpAuthToken: env.MCP_HTTP_AUTH_TOKEN?.trim() || undefined,
194
+ mcpHttpAllowUnauthenticated: parseBoolean("MCP_HTTP_ALLOW_UNAUTHENTICATED", env.MCP_HTTP_ALLOW_UNAUTHENTICATED, false),
195
+ mcpHttpAllowedHosts: parseDatabaseList(env.MCP_HTTP_ALLOWED_HOSTS).map((host) => host.toLowerCase()),
196
+ };
197
+ }
198
+ function parseMcpTransport(value) {
199
+ if (value === undefined || value.trim() === "") {
200
+ return "stdio";
201
+ }
202
+ const normalized = value.trim().toLowerCase();
203
+ if (normalized === "stdio" || normalized === "http") {
204
+ return normalized;
205
+ }
206
+ throw new Error('MCP_TRANSPORT must be either "stdio" or "http".');
207
+ }
208
+ export function getMaxRows() {
209
+ if (runtimeEnvironment) {
210
+ return runtimeEnvironment.maxRows;
211
+ }
212
+ return parseInteger("MAX_ROWS", process.env.MAX_ROWS, DEFAULT_MAX_ROWS);
35
213
  }
36
214
  export function getDefaultSearchLimit() {
37
215
  return Math.min(getMaxRows(), DEFAULT_SEARCH_LIMIT);
@@ -47,28 +225,19 @@ export function clampRowLimit(limit, fallback = getMaxRows()) {
47
225
  }
48
226
  return Math.min(Math.floor(parsed), getMaxRows());
49
227
  }
50
- export function getMcpTransport() {
51
- return process.env.MCP_TRANSPORT?.trim().toLowerCase() === "http"
52
- ? "http"
53
- : "stdio";
54
- }
55
- export function getMcpHttpPort() {
56
- return parsePositiveInt(process.env.MCP_HTTP_PORT, DEFAULT_HTTP_PORT);
228
+ export function getMcpEndpointUrl(environment) {
229
+ const baseUrl = environment.mcpBaseUrl ??
230
+ `http://${formatUrlHost(environment.mcpHttpHost)}:${environment.mcpHttpPort}`;
231
+ return `${baseUrl.replace(/\/+$/, "")}/mcp`;
57
232
  }
58
- export function getMcpHttpHost() {
59
- return process.env.MCP_HTTP_HOST?.trim() || DEFAULT_HTTP_HOST;
60
- }
61
- export function getMcpBaseUrl() {
62
- const value = process.env.MCP_BASE_URL?.trim();
63
- return value ? value : undefined;
64
- }
65
- export function isDdlEnabled() {
66
- return parseBoolean(process.env.ENABLE_DDL, false);
233
+ /** Brackets IPv6 literals (`::1` -> `[::1]`) for use in a URL authority. */
234
+ export function formatUrlHost(host) {
235
+ return host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
67
236
  }
68
237
  export function getMaxWriteRows() {
69
- return parsePositiveInt(process.env.MAX_WRITE_ROWS, DEFAULT_MAX_WRITE_ROWS);
70
- }
71
- export function isWritePreviewRequired() {
72
- return parseBoolean(process.env.REQUIRE_WRITE_PREVIEW, true);
238
+ if (runtimeEnvironment) {
239
+ return runtimeEnvironment.maxWriteRows;
240
+ }
241
+ return parseInteger("MAX_WRITE_ROWS", process.env.MAX_WRITE_ROWS, DEFAULT_MAX_WRITE_ROWS);
73
242
  }
74
243
  //# sourceMappingURL=config.js.map