mssql-mcp 2.3.1 → 2.3.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # MS SQL Server MCP Server v2.3.1
1
+ # MS SQL Server MCP Server v2.3.2
2
2
 
3
3
  🚀 **Model Context Protocol (MCP) server** for Microsoft SQL Server - compatible with Claude Desktop, Cursor, Windsurf and VS Code.
4
4
 
@@ -1,5 +1,5 @@
1
1
  export declare const SERVER_NAME = "mssql-mcp-server";
2
- export declare const SERVER_VERSION = "2.3.1";
2
+ export declare const SERVER_VERSION = "2.3.3";
3
3
  export declare const DEFAULT_PORT = 1433;
4
4
  export declare const DEFAULT_ENCRYPT = true;
5
5
  export declare const DEFAULT_TRUST_SERVER_CERTIFICATE = false;
@@ -1,5 +1,5 @@
1
1
  export const SERVER_NAME = "mssql-mcp-server";
2
- export const SERVER_VERSION = "2.3.1";
2
+ export const SERVER_VERSION = "2.3.3";
3
3
  export const DEFAULT_PORT = 1433;
4
4
  export const DEFAULT_ENCRYPT = true;
5
5
  export const DEFAULT_TRUST_SERVER_CERTIFICATE = false;
@@ -19,6 +19,7 @@ export async function connectPool(config) {
19
19
  encrypt: config.encrypt,
20
20
  trustServerCertificate: config.trustServerCertificate,
21
21
  enableArithAbort: true,
22
+ useUTC: false,
22
23
  },
23
24
  connectionTimeout: config.connectionTimeout,
24
25
  requestTimeout: config.requestTimeout,
@@ -1,10 +1,9 @@
1
1
  import { z } from "zod";
2
2
  import { requirePool } from "../db/connection.js";
3
- import { toActionableError, toolError, toolSuccess } from "../utils/errors.js";
3
+ import { toActionableError, toolError, toolSuccess, toolSuccessMarkdown } from "../utils/errors.js";
4
4
  import { formatJson } from "../utils/format.js";
5
5
  import { formatMarkdownTable } from "../utils/markdown.js";
6
6
  import { buildPaginationMeta, clampLimit } from "../utils/pagination.js";
7
- import { PaginationSchema, DatabaseInfoSchema } from "../schemas/outputs.js";
8
7
  export function registerDatabasesTools(server) {
9
8
  server.registerTool("mssql_list_databases", {
10
9
  title: "List Databases",
@@ -20,7 +19,6 @@ export function registerDatabasesTools(server) {
20
19
  .describe("Output format: 'json' for structured data, 'markdown' for human-readable table"),
21
20
  },
22
21
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
23
- outputSchema: { databases: z.array(DatabaseInfoSchema), pagination: PaginationSchema },
24
22
  }, async ({ limit: rawLimit, offset, response_format }) => {
25
23
  try {
26
24
  const pool = requirePool();
@@ -40,7 +38,7 @@ export function registerDatabasesTools(server) {
40
38
  const rows = page;
41
39
  let text = formatMarkdownTable(rows, "Databases");
42
40
  text += `\n\n*Showing ${page.length} of ${allRows.length} databases*`;
43
- return toolSuccess(text, structured);
41
+ return toolSuccessMarkdown(text);
44
42
  }
45
43
  return toolSuccess(formatJson(structured), structured);
46
44
  }
@@ -1,15 +1,9 @@
1
1
  import { z } from "zod";
2
2
  import { requirePool } from "../db/connection.js";
3
3
  import { validateIdentifier, bracketIdentifier } from "../db/validators.js";
4
- import { toActionableError, toolError, toolSuccess } from "../utils/errors.js";
4
+ import { toActionableError, toolError, toolSuccess, toolSuccessMarkdown } from "../utils/errors.js";
5
5
  import { formatJson } from "../utils/format.js";
6
6
  import { formatMarkdownMultiRecordsets } from "../utils/markdown.js";
7
- const ProcedureOutputSchema = {
8
- recordsets: z.array(z.array(z.record(z.unknown()))),
9
- rows_affected: z.array(z.number()),
10
- output: z.record(z.unknown()),
11
- return_value: z.unknown(),
12
- };
13
7
  export function registerProcedureTools(server) {
14
8
  server.registerTool("mssql_execute_stored_procedure", {
15
9
  title: "Execute Stored Procedure",
@@ -31,7 +25,6 @@ export function registerProcedureTools(server) {
31
25
  .describe("Output format: 'json' for structured data, 'markdown' for human-readable tables"),
32
26
  },
33
27
  annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: true },
34
- outputSchema: ProcedureOutputSchema,
35
28
  }, async ({ procedureName, schemaName, parameters, response_format }) => {
36
29
  try {
37
30
  const pool = requirePool();
@@ -52,7 +45,7 @@ export function registerProcedureTools(server) {
52
45
  return_value: result.returnValue,
53
46
  };
54
47
  if (response_format === "markdown") {
55
- return toolSuccess(formatMarkdownMultiRecordsets(result.recordsets, qualifiedName), structured);
48
+ return toolSuccessMarkdown(formatMarkdownMultiRecordsets(result.recordsets, qualifiedName));
56
49
  }
57
50
  return toolSuccess(formatJson(structured), structured);
58
51
  }
@@ -95,7 +88,7 @@ export function registerProcedureTools(server) {
95
88
  returnValue: result.returnValue,
96
89
  };
97
90
  if (response_format === "markdown") {
98
- return toolSuccess(formatMarkdownMultiRecordsets(result.recordsets));
91
+ return toolSuccessMarkdown(formatMarkdownMultiRecordsets(result.recordsets));
99
92
  }
100
93
  return toolSuccess(formatJson(structured), structured);
101
94
  }
@@ -1,17 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { requirePool } from "../db/connection.js";
3
- import { toActionableError, toolError, toolSuccess } from "../utils/errors.js";
3
+ import { toActionableError, toolError, toolSuccess, toolSuccessMarkdown } from "../utils/errors.js";
4
4
  import { formatJson, truncatePayload } from "../utils/format.js";
5
5
  import { formatMarkdownTable } from "../utils/markdown.js";
6
- const QueryOutputSchema = {
7
- recordset: z.array(z.record(z.unknown())),
8
- rows_affected: z.array(z.number()),
9
- output: z.record(z.unknown()),
10
- execution_time_ms: z.number(),
11
- parameters_used: z.number(),
12
- truncated: z.boolean(),
13
- truncation_message: z.string().optional(),
14
- };
15
6
  async function runSqlQueryCore(query, parameters, response_format, logLabel) {
16
7
  try {
17
8
  const pool = requirePool();
@@ -41,7 +32,7 @@ async function runSqlQueryCore(query, parameters, response_format, logLabel) {
41
32
  if (truncated)
42
33
  text += `\n\n> ⚠️ ${truncation_message}`;
43
34
  text += `\n\n*Rows affected: ${(result.rowsAffected ?? []).join(", ")} · ${elapsed}ms*`;
44
- return toolSuccess(text, structured);
35
+ return toolSuccessMarkdown(text);
45
36
  }
46
37
  return toolSuccess(formatJson(structured), structured);
47
38
  }
@@ -72,7 +63,6 @@ export function registerQueryTools(server) {
72
63
  .describe("Output format: 'json' for structured data, 'markdown' for human-readable table"),
73
64
  },
74
65
  annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: true },
75
- outputSchema: QueryOutputSchema,
76
66
  }, ({ query, parameters, response_format }) => runSqlQueryCore(query, parameters, response_format, "mssql_run_sql_query"));
77
67
  // Backward-compatible alias
78
68
  server.registerTool("mssql_execute_query", {
@@ -2,11 +2,10 @@ import { z } from "zod";
2
2
  import sql from "mssql";
3
3
  import { requirePool } from "../db/connection.js";
4
4
  import { buildSchemaObjectsQuery } from "../db/query-builders.js";
5
- import { toActionableError, toolError, toolSuccess } from "../utils/errors.js";
5
+ import { toActionableError, toolError, toolSuccess, toolSuccessMarkdown } from "../utils/errors.js";
6
6
  import { formatJson } from "../utils/format.js";
7
7
  import { formatMarkdownTable } from "../utils/markdown.js";
8
8
  import { buildPaginationMeta, clampLimit } from "../utils/pagination.js";
9
- import { PaginationSchema, SchemaObjectSchema } from "../schemas/outputs.js";
10
9
  export function registerSchemaTools(server) {
11
10
  server.registerTool("mssql_list_schema_objects", {
12
11
  title: "List Schema Objects",
@@ -32,7 +31,6 @@ export function registerSchemaTools(server) {
32
31
  .describe("Output format: 'json' for structured data, 'markdown' for human-readable table"),
33
32
  },
34
33
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
35
- outputSchema: { objects: z.array(SchemaObjectSchema), pagination: PaginationSchema },
36
34
  }, async ({ objectType, schemaName, limit: rawLimit, offset, response_format }) => {
37
35
  try {
38
36
  const pool = requirePool();
@@ -51,7 +49,7 @@ export function registerSchemaTools(server) {
51
49
  const rows = page;
52
50
  let text = formatMarkdownTable(rows, `Schema Objects (${objectType})`);
53
51
  text += `\n\n*Showing ${page.length} of ${allRows.length} · offset ${offset}*`;
54
- return toolSuccess(text, structured);
52
+ return toolSuccessMarkdown(text);
55
53
  }
56
54
  return toolSuccess(formatJson(structured), structured);
57
55
  }
@@ -85,7 +83,7 @@ export function registerSchemaTools(server) {
85
83
  const result = await request.query(query);
86
84
  const rows = result.recordset ?? [];
87
85
  if (response_format === "markdown") {
88
- return toolSuccess(formatMarkdownTable(rows));
86
+ return toolSuccessMarkdown(formatMarkdownTable(rows));
89
87
  }
90
88
  return toolSuccess(formatJson(rows));
91
89
  }
@@ -3,11 +3,10 @@ import sql from "mssql";
3
3
  import { requirePool } from "../db/connection.js";
4
4
  import { validateIdentifier, validateOrderBy, SAFE_IDENTIFIER_RE } from "../db/validators.js";
5
5
  import { buildSelectQuery, buildSelectWithWhereQuery } from "../db/query-builders.js";
6
- import { toActionableError, toolError, toolSuccess } from "../utils/errors.js";
6
+ import { toActionableError, toolError, toolSuccess, toolSuccessMarkdown } from "../utils/errors.js";
7
7
  import { formatJson, truncatePayload } from "../utils/format.js";
8
8
  import { formatMarkdownTable } from "../utils/markdown.js";
9
9
  import { buildPaginationMeta, clampLimit } from "../utils/pagination.js";
10
- import { PaginationSchema, ColumnInfoSchema } from "../schemas/outputs.js";
11
10
  export function registerTableTools(server) {
12
11
  server.registerTool("mssql_describe_table_columns", {
13
12
  title: "Describe Table Columns",
@@ -23,7 +22,6 @@ export function registerTableTools(server) {
23
22
  .describe("Output format: 'json' for structured data, 'markdown' for human-readable table"),
24
23
  },
25
24
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
26
- outputSchema: { columns: z.array(ColumnInfoSchema), table: z.string() },
27
25
  }, async ({ tableName, schemaName, response_format }) => {
28
26
  try {
29
27
  const pool = requirePool();
@@ -47,7 +45,7 @@ export function registerTableTools(server) {
47
45
  };
48
46
  if (response_format === "markdown") {
49
47
  const rows = result.recordset;
50
- return toolSuccess(formatMarkdownTable(rows, `Columns: ${schemaName}.${tableName}`), structured);
48
+ return toolSuccessMarkdown(formatMarkdownTable(rows, `Columns: ${schemaName}.${tableName}`));
51
49
  }
52
50
  return toolSuccess(formatJson(structured), structured);
53
51
  }
@@ -86,7 +84,7 @@ export function registerTableTools(server) {
86
84
  `);
87
85
  const rows = result.recordset ?? [];
88
86
  if (response_format === "markdown") {
89
- return toolSuccess(formatMarkdownTable(rows));
87
+ return toolSuccessMarkdown(formatMarkdownTable(rows));
90
88
  }
91
89
  return toolSuccess(formatJson(rows));
92
90
  }
@@ -140,14 +138,6 @@ export function registerTableTools(server) {
140
138
  .describe("Output format: 'json' for structured data, 'markdown' for human-readable table"),
141
139
  },
142
140
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
143
- outputSchema: {
144
- data: z.array(z.record(z.unknown())),
145
- pagination: PaginationSchema,
146
- table: z.string(),
147
- execution_time_ms: z.number(),
148
- truncated: z.boolean().optional(),
149
- truncation_message: z.string().optional(),
150
- },
151
141
  }, async ({ tableName, schemaName, columns, limit: rawLimit, offset, whereClause, orderBy, parameters, response_format }) => {
152
142
  try {
153
143
  const pool = requirePool();
@@ -185,7 +175,7 @@ export function registerTableTools(server) {
185
175
  if (truncated)
186
176
  text += `\n\n> ⚠️ ${truncation_message}`;
187
177
  text += `\n\n*${rows.length} rows · offset ${offset} · ${elapsed}ms*`;
188
- return toolSuccess(text, structured);
178
+ return toolSuccessMarkdown(text);
189
179
  }
190
180
  return toolSuccess(formatJson(structured), structured);
191
181
  }
@@ -250,7 +240,7 @@ export function registerTableTools(server) {
250
240
  structured.truncation_message = truncation_message;
251
241
  }
252
242
  if (response_format === "markdown") {
253
- return toolSuccess(formatMarkdownTable(data));
243
+ return toolSuccessMarkdown(formatMarkdownTable(data));
254
244
  }
255
245
  return toolSuccess(formatJson(structured), structured);
256
246
  }
@@ -10,6 +10,12 @@ export declare function toolError(message: string): {
10
10
  }[];
11
11
  isError: true;
12
12
  };
13
+ export declare function toolSuccessMarkdown(text: string): {
14
+ content: {
15
+ type: "text";
16
+ text: string;
17
+ }[];
18
+ };
13
19
  export declare function toolSuccess(text: string, structuredContent?: Record<string, unknown>): {
14
20
  content: Array<{
15
21
  type: "text";
@@ -1,3 +1,4 @@
1
+ import { normalizeOutput } from "./format.js";
1
2
  export class McpToolError extends Error {
2
3
  cause;
3
4
  constructor(message, cause) {
@@ -18,12 +19,17 @@ export function toolError(message) {
18
19
  isError: true,
19
20
  };
20
21
  }
22
+ export function toolSuccessMarkdown(text) {
23
+ return {
24
+ content: [{ type: "text", text }],
25
+ };
26
+ }
21
27
  export function toolSuccess(text, structuredContent) {
22
28
  const result = {
23
29
  content: [{ type: "text", text }],
24
30
  };
25
31
  if (structuredContent !== undefined) {
26
- result.structuredContent = structuredContent;
32
+ result.structuredContent = normalizeOutput(structuredContent);
27
33
  }
28
34
  return result;
29
35
  }
@@ -1,4 +1,12 @@
1
1
  export declare function formatJson(data: unknown): string;
2
+ export declare function formatDisplayValue(value: unknown, column?: {
3
+ type?: unknown;
4
+ scale?: number;
5
+ }): string;
6
+ export declare function normalizeOutput(value: unknown, column?: {
7
+ type?: unknown;
8
+ scale?: number;
9
+ }): unknown;
2
10
  export declare function truncatePayload(data: unknown[], maxBytes?: number): {
3
11
  data: unknown[];
4
12
  truncated: boolean;
@@ -1,18 +1,127 @@
1
1
  import { MAX_TEXT_PAYLOAD_BYTES } from "../constants.js";
2
+ function pad(value, width) {
3
+ return String(value).padStart(width, "0");
4
+ }
5
+ function getRecordsetColumns(value) {
6
+ if (!Array.isArray(value)) {
7
+ return undefined;
8
+ }
9
+ const columns = value.columns;
10
+ if (!columns || typeof columns !== "object" || Array.isArray(columns)) {
11
+ return undefined;
12
+ }
13
+ return columns;
14
+ }
15
+ function getTypeName(column) {
16
+ const type = column?.type;
17
+ if (typeof type === "function") {
18
+ return type.name || undefined;
19
+ }
20
+ if (type && typeof type === "object" && "name" in type) {
21
+ const name = type.name;
22
+ return typeof name === "string" ? name : undefined;
23
+ }
24
+ return undefined;
25
+ }
26
+ function getExtraFraction(date) {
27
+ const raw = typeof date.nanosecondsDelta === "number"
28
+ ? date.nanosecondsDelta
29
+ : typeof date.nanosecondDelta === "number"
30
+ ? date.nanosecondDelta
31
+ : 0;
32
+ if (!Number.isFinite(raw)) {
33
+ return 0;
34
+ }
35
+ return Math.max(0, Math.min(9999, Math.round(raw * 10_000_000)));
36
+ }
37
+ function buildFraction(date, milliseconds, scale) {
38
+ const base = `${pad(milliseconds, 3)}${pad(getExtraFraction(date), 4)}`;
39
+ const safeScale = scale === undefined ? undefined : Math.max(0, Math.min(7, scale));
40
+ if (safeScale === 0) {
41
+ return "";
42
+ }
43
+ if (safeScale !== undefined) {
44
+ return base.slice(0, safeScale);
45
+ }
46
+ return base.replace(/0+$/, "");
47
+ }
48
+ function formatDateParts(date, useUtcClock) {
49
+ return {
50
+ year: useUtcClock ? date.getUTCFullYear() : date.getFullYear(),
51
+ month: (useUtcClock ? date.getUTCMonth() : date.getMonth()) + 1,
52
+ day: useUtcClock ? date.getUTCDate() : date.getDate(),
53
+ hours: useUtcClock ? date.getUTCHours() : date.getHours(),
54
+ minutes: useUtcClock ? date.getUTCMinutes() : date.getMinutes(),
55
+ seconds: useUtcClock ? date.getUTCSeconds() : date.getSeconds(),
56
+ milliseconds: useUtcClock ? date.getUTCMilliseconds() : date.getMilliseconds(),
57
+ };
58
+ }
59
+ function formatDateValue(date, column) {
60
+ const typeName = getTypeName(column)?.toLowerCase();
61
+ const useUtcClock = typeName === "datetimeoffset";
62
+ const parts = formatDateParts(date, useUtcClock);
63
+ const fraction = buildFraction(date, parts.milliseconds, column?.scale);
64
+ const dateText = `${pad(parts.year, 4)}-${pad(parts.month, 2)}-${pad(parts.day, 2)}`;
65
+ const timeText = `${pad(parts.hours, 2)}:${pad(parts.minutes, 2)}:${pad(parts.seconds, 2)}`;
66
+ if (typeName === "date") {
67
+ return dateText;
68
+ }
69
+ if (typeName === "time") {
70
+ return fraction ? `${timeText}.${fraction}` : timeText;
71
+ }
72
+ return fraction ? `${dateText} ${timeText}.${fraction}` : `${dateText} ${timeText}`;
73
+ }
74
+ function isPlainObject(value) {
75
+ return typeof value === "object"
76
+ && value !== null
77
+ && !Array.isArray(value)
78
+ && !Buffer.isBuffer(value)
79
+ && !(value instanceof Date);
80
+ }
81
+ function normalizeRecordRow(row, columns) {
82
+ return Object.fromEntries(Object.entries(row).map(([key, value]) => [key, normalizeOutput(value, columns[key])]));
83
+ }
2
84
  export function formatJson(data) {
3
- return JSON.stringify(data, null, 2);
85
+ return JSON.stringify(normalizeOutput(data), null, 2);
86
+ }
87
+ export function formatDisplayValue(value, column) {
88
+ const normalized = normalizeOutput(value, column);
89
+ if (normalized == null) {
90
+ return "";
91
+ }
92
+ if (typeof normalized === "object") {
93
+ return JSON.stringify(normalized);
94
+ }
95
+ return String(normalized);
96
+ }
97
+ export function normalizeOutput(value, column) {
98
+ if (value instanceof Date) {
99
+ return formatDateValue(value, column);
100
+ }
101
+ const columns = getRecordsetColumns(value);
102
+ if (Array.isArray(value)) {
103
+ if (columns) {
104
+ return value.map((row) => isPlainObject(row) ? normalizeRecordRow(row, columns) : normalizeOutput(row));
105
+ }
106
+ return value.map((entry) => normalizeOutput(entry));
107
+ }
108
+ if (isPlainObject(value)) {
109
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, normalizeOutput(entry)]));
110
+ }
111
+ return value;
4
112
  }
5
113
  export function truncatePayload(data, maxBytes = MAX_TEXT_PAYLOAD_BYTES) {
6
- const full = JSON.stringify(data);
114
+ const normalizedData = normalizeOutput(data);
115
+ const full = JSON.stringify(normalizedData);
7
116
  if (Buffer.byteLength(full, "utf8") <= maxBytes) {
8
- return { data, truncated: false };
117
+ return { data: normalizedData, truncated: false };
9
118
  }
10
119
  // Binary search for safe row count
11
120
  let lo = 0;
12
- let hi = data.length;
121
+ let hi = normalizedData.length;
13
122
  while (lo < hi) {
14
123
  const mid = Math.floor((lo + hi + 1) / 2);
15
- if (Buffer.byteLength(JSON.stringify(data.slice(0, mid)), "utf8") <= maxBytes) {
124
+ if (Buffer.byteLength(JSON.stringify(normalizedData.slice(0, mid)), "utf8") <= maxBytes) {
16
125
  lo = mid;
17
126
  }
18
127
  else {
@@ -20,8 +129,8 @@ export function truncatePayload(data, maxBytes = MAX_TEXT_PAYLOAD_BYTES) {
20
129
  }
21
130
  }
22
131
  return {
23
- data: data.slice(0, lo),
132
+ data: normalizedData.slice(0, lo),
24
133
  truncated: true,
25
- truncation_message: `Result truncated to ${lo} of ${data.length} rows to stay within ${maxBytes} byte limit.`,
134
+ truncation_message: `Result truncated to ${lo} of ${normalizedData.length} rows to stay within ${maxBytes} byte limit.`,
26
135
  };
27
136
  }
@@ -1,5 +1,6 @@
1
- function escapeCell(value) {
2
- return String(value ?? "")
1
+ import { formatDisplayValue } from "./format.js";
2
+ function escapeCell(value, column) {
3
+ return formatDisplayValue(value, column)
3
4
  .replace(/\r?\n|\r/g, " ")
4
5
  .replace(/\|/g, "\\|");
5
6
  }
@@ -7,17 +8,18 @@ export function formatMarkdownTable(rows, title) {
7
8
  if (rows.length === 0) {
8
9
  return title ? `**${title}**\n\nNo results found.` : "No results found.";
9
10
  }
11
+ const columns = (rows.columns) ?? undefined;
10
12
  const headers = Object.keys(rows[0]);
11
13
  const header = `| ${headers.join(" | ")} |`;
12
14
  const sep = `| ${headers.map(() => "---").join(" | ")} |`;
13
15
  const body = rows
14
- .map((row) => `| ${headers.map((h) => escapeCell(row[h])).join(" | ")} |`)
16
+ .map((row) => `| ${headers.map((h) => escapeCell(row[h], columns?.[h])).join(" | ")} |`)
15
17
  .join("\n");
16
18
  const table = [header, sep, body].join("\n");
17
19
  return title ? `**${title}**\n\n${table}` : table;
18
20
  }
19
21
  export function formatMarkdownList(obj, title) {
20
- const lines = Object.entries(obj).map(([k, v]) => `- **${k}**: ${typeof v === "object" ? JSON.stringify(v) : String(v ?? "")}`);
22
+ const lines = Object.entries(obj).map(([k, v]) => `- **${k}**: ${formatDisplayValue(v)}`);
21
23
  const list = lines.join("\n");
22
24
  return title ? `**${title}**\n\n${list}` : list;
23
25
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,180 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { test } from "node:test";
3
+ import { ConfigSchema, loadHttpConfig, loadConfigFromEnv } from "../../src/config.js";
4
+ // --- ConfigSchema ---
5
+ test("ConfigSchema - applies default port 1433", () => {
6
+ const result = ConfigSchema.parse({ server: "localhost" });
7
+ assert.equal(result.port, 1433);
8
+ });
9
+ test("ConfigSchema - applies default encrypt true", () => {
10
+ const result = ConfigSchema.parse({ server: "localhost" });
11
+ assert.equal(result.encrypt, true);
12
+ });
13
+ test("ConfigSchema - applies default trustServerCertificate false", () => {
14
+ const result = ConfigSchema.parse({ server: "localhost" });
15
+ assert.equal(result.trustServerCertificate, false);
16
+ });
17
+ test("ConfigSchema - applies default connectionTimeout 30000", () => {
18
+ const result = ConfigSchema.parse({ server: "localhost" });
19
+ assert.equal(result.connectionTimeout, 30000);
20
+ });
21
+ test("ConfigSchema - applies default requestTimeout 30000", () => {
22
+ const result = ConfigSchema.parse({ server: "localhost" });
23
+ assert.equal(result.requestTimeout, 30000);
24
+ });
25
+ test("ConfigSchema - accepts overridden port", () => {
26
+ const result = ConfigSchema.parse({ server: "myserver", port: 1434 });
27
+ assert.equal(result.port, 1434);
28
+ });
29
+ test("ConfigSchema - accepts encrypt false", () => {
30
+ const result = ConfigSchema.parse({ server: "myserver", encrypt: false });
31
+ assert.equal(result.encrypt, false);
32
+ });
33
+ test("ConfigSchema - accepts trustServerCertificate true", () => {
34
+ const result = ConfigSchema.parse({ server: "myserver", trustServerCertificate: true });
35
+ assert.equal(result.trustServerCertificate, true);
36
+ });
37
+ test("ConfigSchema - throws on empty server string", () => {
38
+ assert.throws(() => ConfigSchema.parse({ server: "" }), /Server address is required/);
39
+ });
40
+ test("ConfigSchema - throws on missing server", () => {
41
+ assert.throws(() => ConfigSchema.parse({}));
42
+ });
43
+ test("ConfigSchema - throws on port out of range", () => {
44
+ assert.throws(() => ConfigSchema.parse({ server: "localhost", port: 0 }));
45
+ assert.throws(() => ConfigSchema.parse({ server: "localhost", port: 65536 }));
46
+ });
47
+ // --- loadHttpConfig ---
48
+ test("loadHttpConfig - returns default host 127.0.0.1 when env not set", () => {
49
+ const savedHost = process.env.MCP_HOST;
50
+ const savedPort = process.env.MCP_PORT;
51
+ delete process.env.MCP_HOST;
52
+ delete process.env.MCP_PORT;
53
+ try {
54
+ const config = loadHttpConfig();
55
+ assert.equal(config.host, "127.0.0.1");
56
+ }
57
+ finally {
58
+ if (savedHost !== undefined)
59
+ process.env.MCP_HOST = savedHost;
60
+ if (savedPort !== undefined)
61
+ process.env.MCP_PORT = savedPort;
62
+ }
63
+ });
64
+ test("loadHttpConfig - returns default port 3001 when env not set", () => {
65
+ const savedHost = process.env.MCP_HOST;
66
+ const savedPort = process.env.MCP_PORT;
67
+ delete process.env.MCP_HOST;
68
+ delete process.env.MCP_PORT;
69
+ try {
70
+ const config = loadHttpConfig();
71
+ assert.equal(config.port, 3001);
72
+ }
73
+ finally {
74
+ if (savedHost !== undefined)
75
+ process.env.MCP_HOST = savedHost;
76
+ if (savedPort !== undefined)
77
+ process.env.MCP_PORT = savedPort;
78
+ }
79
+ });
80
+ test("loadHttpConfig - reads host from MCP_HOST", () => {
81
+ const savedHost = process.env.MCP_HOST;
82
+ process.env.MCP_HOST = "0.0.0.0";
83
+ try {
84
+ const config = loadHttpConfig();
85
+ assert.equal(config.host, "0.0.0.0");
86
+ }
87
+ finally {
88
+ if (savedHost !== undefined)
89
+ process.env.MCP_HOST = savedHost;
90
+ else
91
+ delete process.env.MCP_HOST;
92
+ }
93
+ });
94
+ test("loadHttpConfig - reads port from MCP_PORT", () => {
95
+ const savedPort = process.env.MCP_PORT;
96
+ process.env.MCP_PORT = "8080";
97
+ try {
98
+ const config = loadHttpConfig();
99
+ assert.equal(config.port, 8080);
100
+ }
101
+ finally {
102
+ if (savedPort !== undefined)
103
+ process.env.MCP_PORT = savedPort;
104
+ else
105
+ delete process.env.MCP_PORT;
106
+ }
107
+ });
108
+ // --- loadConfigFromEnv ---
109
+ test("loadConfigFromEnv - throws when DB_SERVER not set", () => {
110
+ const saved = process.env.DB_SERVER;
111
+ delete process.env.DB_SERVER;
112
+ try {
113
+ assert.throws(() => loadConfigFromEnv());
114
+ }
115
+ finally {
116
+ if (saved !== undefined)
117
+ process.env.DB_SERVER = saved;
118
+ }
119
+ });
120
+ test("loadConfigFromEnv - reads DB_SERVER and applies defaults", () => {
121
+ const savedServer = process.env.DB_SERVER;
122
+ const savedEncrypt = process.env.DB_ENCRYPT;
123
+ process.env.DB_SERVER = "myserver";
124
+ delete process.env.DB_ENCRYPT;
125
+ try {
126
+ const config = loadConfigFromEnv();
127
+ assert.equal(config.server, "myserver");
128
+ assert.equal(config.port, 1433);
129
+ assert.equal(config.encrypt, true);
130
+ assert.equal(config.trustServerCertificate, false);
131
+ }
132
+ finally {
133
+ if (savedServer !== undefined)
134
+ process.env.DB_SERVER = savedServer;
135
+ else
136
+ delete process.env.DB_SERVER;
137
+ if (savedEncrypt !== undefined)
138
+ process.env.DB_ENCRYPT = savedEncrypt;
139
+ }
140
+ });
141
+ test("loadConfigFromEnv - DB_ENCRYPT=false disables encryption", () => {
142
+ const savedServer = process.env.DB_SERVER;
143
+ const savedEncrypt = process.env.DB_ENCRYPT;
144
+ process.env.DB_SERVER = "myserver";
145
+ process.env.DB_ENCRYPT = "false";
146
+ try {
147
+ const config = loadConfigFromEnv();
148
+ assert.equal(config.encrypt, false);
149
+ }
150
+ finally {
151
+ if (savedServer !== undefined)
152
+ process.env.DB_SERVER = savedServer;
153
+ else
154
+ delete process.env.DB_SERVER;
155
+ if (savedEncrypt !== undefined)
156
+ process.env.DB_ENCRYPT = savedEncrypt;
157
+ else
158
+ delete process.env.DB_ENCRYPT;
159
+ }
160
+ });
161
+ test("loadConfigFromEnv - DB_TRUST_SERVER_CERTIFICATE=true enables trust", () => {
162
+ const savedServer = process.env.DB_SERVER;
163
+ const savedTrust = process.env.DB_TRUST_SERVER_CERTIFICATE;
164
+ process.env.DB_SERVER = "myserver";
165
+ process.env.DB_TRUST_SERVER_CERTIFICATE = "true";
166
+ try {
167
+ const config = loadConfigFromEnv();
168
+ assert.equal(config.trustServerCertificate, true);
169
+ }
170
+ finally {
171
+ if (savedServer !== undefined)
172
+ process.env.DB_SERVER = savedServer;
173
+ else
174
+ delete process.env.DB_SERVER;
175
+ if (savedTrust !== undefined)
176
+ process.env.DB_TRUST_SERVER_CERTIFICATE = savedTrust;
177
+ else
178
+ delete process.env.DB_TRUST_SERVER_CERTIFICATE;
179
+ }
180
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,52 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { test } from "node:test";
3
+ import { McpToolError, toActionableError, toolError } from "../../src/utils/errors.js";
4
+ test("toolError - wraps message with error emoji", () => {
5
+ const result = toolError("connection failed");
6
+ assert.equal(result.content[0].type, "text");
7
+ assert.ok(result.content[0].text.includes("❌"));
8
+ assert.ok(result.content[0].text.includes("connection failed"));
9
+ });
10
+ test("toolError - sets isError flag to true", () => {
11
+ const result = toolError("oops");
12
+ assert.equal(result.isError, true);
13
+ });
14
+ test("toolError - content type is always 'text'", () => {
15
+ const result = toolError("msg");
16
+ assert.equal(result.content.length, 1);
17
+ assert.equal(result.content[0].type, "text");
18
+ });
19
+ test("toActionableError - extracts message from Error instance", () => {
20
+ const err = new Error("connection refused");
21
+ assert.equal(toActionableError(err), "connection refused");
22
+ });
23
+ test("toActionableError - converts plain string to string", () => {
24
+ assert.equal(toActionableError("plain string"), "plain string");
25
+ });
26
+ test("toActionableError - converts number to string", () => {
27
+ assert.equal(toActionableError(42), "42");
28
+ });
29
+ test("toActionableError - converts null to string", () => {
30
+ assert.equal(toActionableError(null), "null");
31
+ });
32
+ test("McpToolError - has correct name", () => {
33
+ const err = new McpToolError("test error");
34
+ assert.equal(err.name, "McpToolError");
35
+ });
36
+ test("McpToolError - is instanceof Error", () => {
37
+ const err = new McpToolError("test error");
38
+ assert.ok(err instanceof Error);
39
+ });
40
+ test("McpToolError - stores message", () => {
41
+ const err = new McpToolError("something failed");
42
+ assert.equal(err.message, "something failed");
43
+ });
44
+ test("McpToolError - stores cause when provided", () => {
45
+ const cause = new Error("original cause");
46
+ const err = new McpToolError("wrapper", cause);
47
+ assert.equal(err.cause, cause);
48
+ });
49
+ test("McpToolError - cause is undefined when not provided", () => {
50
+ const err = new McpToolError("no cause");
51
+ assert.equal(err.cause, undefined);
52
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,106 @@
1
+ import { strict as assert } from "node:assert";
2
+ import { test } from "node:test";
3
+ import { formatDisplayValue, normalizeOutput } from "../../src/utils/format.js";
4
+ // Type stubs — getTypeName() uses function.name lowercased,
5
+ // or falls back to object.name property.
6
+ const DateType = { name: "Date" };
7
+ const TimeType = { name: "Time" };
8
+ function pad(value, width) {
9
+ return String(value).padStart(width, "0");
10
+ }
11
+ function formatLocalDate(d) {
12
+ return `${pad(d.getFullYear(), 4)}-${pad(d.getMonth() + 1, 2)}-${pad(d.getDate(), 2)}`;
13
+ }
14
+ function formatLocalTime(d) {
15
+ return `${pad(d.getHours(), 2)}:${pad(d.getMinutes(), 2)}:${pad(d.getSeconds(), 2)}`;
16
+ }
17
+ function formatLocalDateTime(d) {
18
+ return `${formatLocalDate(d)} ${formatLocalTime(d)}`;
19
+ }
20
+ // --- formatDisplayValue ---
21
+ test("formatDisplayValue - null returns empty string", () => {
22
+ assert.equal(formatDisplayValue(null), "");
23
+ });
24
+ test("formatDisplayValue - undefined returns empty string", () => {
25
+ assert.equal(formatDisplayValue(undefined), "");
26
+ });
27
+ test("formatDisplayValue - string returns itself", () => {
28
+ assert.equal(formatDisplayValue("hello"), "hello");
29
+ });
30
+ test("formatDisplayValue - number returns string representation", () => {
31
+ assert.equal(formatDisplayValue(42), "42");
32
+ assert.equal(formatDisplayValue(3.14), "3.14");
33
+ });
34
+ test("formatDisplayValue - boolean returns string representation", () => {
35
+ assert.equal(formatDisplayValue(true), "true");
36
+ assert.equal(formatDisplayValue(false), "false");
37
+ });
38
+ test("formatDisplayValue - object returns JSON string", () => {
39
+ assert.equal(formatDisplayValue({ a: 1 }), JSON.stringify({ a: 1 }));
40
+ });
41
+ // --- normalizeOutput: SQL date types ---
42
+ test("normalizeOutput - 'date' type returns date-only string", () => {
43
+ const d = new Date(2026, 3, 1, 10, 51, 23, 600);
44
+ const result = normalizeOutput(d, { type: DateType });
45
+ assert.equal(result, formatLocalDate(d));
46
+ });
47
+ test("normalizeOutput - 'time' type with scale=0 returns time without fraction", () => {
48
+ const d = new Date(2026, 3, 1, 10, 51, 23, 0);
49
+ const result = normalizeOutput(d, { type: TimeType, scale: 0 });
50
+ assert.equal(result, "10:51:23");
51
+ });
52
+ test("normalizeOutput - 'time' type with scale=3 includes milliseconds", () => {
53
+ const d = new Date(2026, 3, 1, 10, 51, 23, 600);
54
+ const result = normalizeOutput(d, { type: TimeType, scale: 3 });
55
+ assert.equal(result, "10:51:23.600");
56
+ });
57
+ test("normalizeOutput - datetime with scale=0 returns no fraction", () => {
58
+ const d = new Date(2026, 3, 1, 10, 51, 23, 600);
59
+ const result = normalizeOutput(d, { scale: 0 });
60
+ assert.equal(result, `${formatLocalDate(d)} ${formatLocalTime(d)}`);
61
+ });
62
+ test("normalizeOutput - datetime with scale=3 returns 3-digit fraction", () => {
63
+ const d = new Date(2026, 3, 1, 10, 51, 23, 600);
64
+ const result = normalizeOutput(d, { scale: 3 });
65
+ assert.equal(result, `${formatLocalDateTime(d)}.600`);
66
+ });
67
+ test("normalizeOutput - datetime with no column metadata trims trailing zeros", () => {
68
+ // 600ms → base "6000000" → trimmed to "6"
69
+ const d = new Date(2026, 3, 1, 10, 51, 23, 600);
70
+ const result = normalizeOutput(d);
71
+ assert.ok(result.startsWith(formatLocalDate(d)));
72
+ assert.ok(!result.endsWith("0"), `Trailing zeros not trimmed: ${result}`);
73
+ });
74
+ test("normalizeOutput - datetime with zero milliseconds and no scale returns no fraction", () => {
75
+ // 0ms → base "0000000" → trimmed to "" → no fraction
76
+ const d = new Date(2026, 3, 1, 10, 51, 23, 0);
77
+ const result = normalizeOutput(d);
78
+ assert.equal(result, `${formatLocalDateTime(d)}`);
79
+ });
80
+ // --- normalizeOutput: primitives & structures ---
81
+ test("normalizeOutput - passes through number", () => {
82
+ assert.equal(normalizeOutput(42), 42);
83
+ });
84
+ test("normalizeOutput - passes through string", () => {
85
+ assert.equal(normalizeOutput("hello"), "hello");
86
+ });
87
+ test("normalizeOutput - passes through null", () => {
88
+ assert.equal(normalizeOutput(null), null);
89
+ });
90
+ test("normalizeOutput - passes through boolean", () => {
91
+ assert.equal(normalizeOutput(true), true);
92
+ });
93
+ test("normalizeOutput - normalizes Date inside plain object", () => {
94
+ const d = new Date(2026, 3, 1, 10, 0, 0, 0);
95
+ const result = normalizeOutput({ created: d });
96
+ assert.equal(result.created, `${formatLocalDateTime(d)}`);
97
+ });
98
+ test("normalizeOutput - normalizes array of primitives unchanged", () => {
99
+ const result = normalizeOutput([1, "two", null]);
100
+ assert.deepEqual(result, [1, "two", null]);
101
+ });
102
+ test("normalizeOutput - normalizes Date values inside array", () => {
103
+ const d = new Date(2026, 3, 1, 10, 0, 0, 0);
104
+ const result = normalizeOutput([d]);
105
+ assert.equal(result[0], `${formatLocalDateTime(d)}`);
106
+ });
@@ -1,6 +1,16 @@
1
1
  import { strict as assert } from "node:assert";
2
2
  import { test } from "node:test";
3
3
  import { formatMarkdownTable, formatMarkdownList, formatMarkdownMultiRecordsets, } from "../../src/utils/markdown.js";
4
+ function DateTimeOffset() { }
5
+ function pad(value, width) {
6
+ return String(value).padStart(width, "0");
7
+ }
8
+ function formatUtcDateTime(date) {
9
+ return [
10
+ `${pad(date.getUTCFullYear(), 4)}-${pad(date.getUTCMonth() + 1, 2)}-${pad(date.getUTCDate(), 2)}`,
11
+ `${pad(date.getUTCHours(), 2)}:${pad(date.getUTCMinutes(), 2)}:${pad(date.getUTCSeconds(), 2)}.${pad(date.getUTCMilliseconds(), 3)}6938`,
12
+ ].join(" ");
13
+ }
4
14
  test("formatMarkdownTable - renders header and rows", () => {
5
15
  const rows = [
6
16
  { name: "Users", schema: "dbo" },
@@ -48,6 +58,16 @@ test("formatMarkdownTable - CRLF in cell values are replaced with space", () =>
48
58
  const result = formatMarkdownTable(rows);
49
59
  assert.ok(result.includes("line1 line2"));
50
60
  });
61
+ test("formatMarkdownTable - datetimeoffset values render without JS timezone string", () => {
62
+ const offsetTime = new Date(Date.UTC(2026, 3, 1, 10, 51, 23, 600));
63
+ Object.defineProperty(offsetTime, "nanosecondsDelta", { value: 0.0006938 });
64
+ const rows = Object.assign([{ offset_time: offsetTime }], {
65
+ columns: { offset_time: { type: DateTimeOffset, scale: 7 } },
66
+ });
67
+ const result = formatMarkdownTable(rows);
68
+ assert.ok(result.includes(formatUtcDateTime(offsetTime)));
69
+ assert.ok(!result.includes("GMT"));
70
+ });
51
71
  test("formatMarkdownList - renders key-value pairs", () => {
52
72
  const result = formatMarkdownList({ status: "connected", server: "localhost" });
53
73
  assert.ok(result.includes("**status**: connected"));
@@ -61,3 +61,27 @@ test("buildSchemaObjectsQuery - without schema filter has no @schemaName", () =>
61
61
  const q = buildSchemaObjectsQuery("tables");
62
62
  assert.ok(!q.includes("@schemaName"));
63
63
  });
64
+ test("buildSchemaObjectsQuery - procedures only", () => {
65
+ const q = buildSchemaObjectsQuery("procedures");
66
+ assert.ok(q.includes("ROUTINE_TYPE = 'PROCEDURE'"));
67
+ assert.ok(!q.includes("INFORMATION_SCHEMA.TABLES"));
68
+ assert.ok(!q.includes("INFORMATION_SCHEMA.VIEWS"));
69
+ assert.ok(!q.includes("ROUTINE_TYPE = 'FUNCTION'"));
70
+ });
71
+ test("buildSchemaObjectsQuery - functions only", () => {
72
+ const q = buildSchemaObjectsQuery("functions");
73
+ assert.ok(q.includes("ROUTINE_TYPE = 'FUNCTION'"));
74
+ assert.ok(!q.includes("INFORMATION_SCHEMA.TABLES"));
75
+ assert.ok(!q.includes("ROUTINE_TYPE = 'PROCEDURE'"));
76
+ });
77
+ test("buildSelectWithWhereQuery - with column projection", () => {
78
+ const q = buildSelectWithWhereQuery("dbo", "Orders", ["Id", "Status"], "Status = @status", null, 0, 10);
79
+ assert.ok(q.includes("[Id], [Status]"));
80
+ assert.ok(q.includes("WHERE Status = @status"));
81
+ assert.ok(!q.includes("SELECT *"));
82
+ });
83
+ test("buildSelectWithWhereQuery - with orderBy", () => {
84
+ const q = buildSelectWithWhereQuery("dbo", "Orders", null, "Total > @min", "Total DESC", 0, 20);
85
+ assert.ok(q.includes("ORDER BY Total DESC"));
86
+ assert.ok(q.includes("WHERE Total > @min"));
87
+ });
@@ -1,7 +1,18 @@
1
1
  import { strict as assert } from "node:assert";
2
2
  import { test } from "node:test";
3
+ import { toolSuccess, toolSuccessMarkdown } from "../../src/utils/errors.js";
3
4
  import { buildPaginationMeta, clampLimit } from "../../src/utils/pagination.js";
4
- import { truncatePayload } from "../../src/utils/format.js";
5
+ import { formatJson, truncatePayload } from "../../src/utils/format.js";
6
+ function DateTime2() { }
7
+ function pad(value, width) {
8
+ return String(value).padStart(width, "0");
9
+ }
10
+ function formatLocalDateTime(date) {
11
+ return [
12
+ `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1, 2)}-${pad(date.getDate(), 2)}`,
13
+ `${pad(date.getHours(), 2)}:${pad(date.getMinutes(), 2)}:${pad(date.getSeconds(), 2)}.${pad(date.getMilliseconds(), 3)}6938`,
14
+ ].join(" ");
15
+ }
5
16
  test("clampLimit - defaults to DEFAULT_PAGE_SIZE (20)", () => {
6
17
  assert.equal(clampLimit(undefined), 20);
7
18
  });
@@ -60,3 +71,25 @@ test("truncatePayload - empty array not truncated", () => {
60
71
  assert.equal(result.truncated, false);
61
72
  assert.equal(result.data.length, 0);
62
73
  });
74
+ test("formatJson - recordset datetime values use SQL-like formatting", () => {
75
+ const serverTime = new Date(2026, 3, 1, 10, 51, 23, 600);
76
+ Object.defineProperty(serverTime, "nanosecondsDelta", { value: 0.0006938 });
77
+ const rows = Object.assign([{ server_time: serverTime }], {
78
+ columns: { server_time: { type: DateTime2, scale: 7 } },
79
+ });
80
+ const result = formatJson({ recordset: rows });
81
+ assert.ok(result.includes(formatLocalDateTime(serverTime)));
82
+ assert.ok(!result.includes("GMT"));
83
+ assert.ok(!result.includes("T10:51:23"));
84
+ });
85
+ test("toolSuccessMarkdown - returns text content without structuredContent", () => {
86
+ const result = toolSuccessMarkdown("| col |\n| --- |\n| value |");
87
+ assert.equal(result.content[0].type, "text");
88
+ assert.ok(result.content[0].text.includes("| col |"));
89
+ assert.equal(result.structuredContent, undefined);
90
+ });
91
+ test("toolSuccess - keeps structuredContent for json-style responses", () => {
92
+ const result = toolSuccess("{\n \"ok\": true\n}", { ok: true });
93
+ assert.equal(result.content[0].type, "text");
94
+ assert.equal(result.structuredContent?.ok, true);
95
+ });
@@ -49,3 +49,15 @@ test("validateOrderBy - invalid clauses", () => {
49
49
  assert.throws(() => validateOrderBy("Name UNION SELECT"), /Invalid ORDER BY/);
50
50
  assert.throws(() => validateOrderBy("Name--comment"), /Invalid ORDER BY/);
51
51
  });
52
+ test("isValidIdentifier - 128-char identifier is valid (at limit)", () => {
53
+ const name = "a" + "x".repeat(127); // 1 + 127 = 128 chars
54
+ assert.ok(isValidIdentifier(name));
55
+ });
56
+ test("isValidIdentifier - 129-char identifier is invalid (over limit)", () => {
57
+ const name = "a" + "x".repeat(128); // 1 + 128 = 129 chars
58
+ assert.ok(!isValidIdentifier(name));
59
+ });
60
+ test("isValidIdentifier - single char identifier is valid", () => {
61
+ assert.ok(isValidIdentifier("a"));
62
+ assert.ok(isValidIdentifier("_"));
63
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mssql-mcp",
3
- "version": "2.3.1",
3
+ "version": "2.3.3",
4
4
  "description": "MCP Server for MS SQL Server integration with Claude Desktop, Cursor, Windsurf and VS Code",
5
5
  "main": "dist/src/index.js",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "clean": "rimraf dist",
32
32
  "prepublishOnly": "npm run clean && npm run build",
33
33
  "typecheck": "tsc --noEmit",
34
- "test": "node --test dist/tests/unit/validators.test.js dist/tests/unit/query-builders.test.js dist/tests/unit/tool-contracts.test.js dist/tests/unit/markdown.test.js",
34
+ "test": "node --test dist/tests/unit/validators.test.js dist/tests/unit/query-builders.test.js dist/tests/unit/tool-contracts.test.js dist/tests/unit/markdown.test.js dist/tests/unit/errors.test.js dist/tests/unit/config.test.js dist/tests/unit/format.test.js",
35
35
  "ci": "npm run typecheck && npm run build && npm test"
36
36
  },
37
37
  "keywords": [
@@ -50,11 +50,11 @@
50
50
  "dependencies": {
51
51
  "@modelcontextprotocol/sdk": "1.28.0",
52
52
  "dotenv": "^16.3.1",
53
- "mssql": "^11.0.1",
53
+ "mssql": "^12.5.5",
54
54
  "zod": "^3.25.0"
55
55
  },
56
56
  "devDependencies": {
57
- "@types/mssql": "^9.1.7",
57
+ "@types/mssql": "^12.3.0",
58
58
  "@types/node": "^20.0.0",
59
59
  "cross-env": "^7.0.3",
60
60
  "rimraf": "^5.0.0",