mssql-mcp 2.3.2 → 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/dist/src/constants.d.ts +1 -1
- package/dist/src/constants.js +1 -1
- package/dist/tests/unit/config.test.d.ts +1 -0
- package/dist/tests/unit/config.test.js +180 -0
- package/dist/tests/unit/errors.test.d.ts +1 -0
- package/dist/tests/unit/errors.test.js +52 -0
- package/dist/tests/unit/format.test.d.ts +1 -0
- package/dist/tests/unit/format.test.js +106 -0
- package/dist/tests/unit/query-builders.test.js +24 -0
- package/dist/tests/unit/validators.test.js +12 -0
- package/package.json +4 -4
package/dist/src/constants.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const SERVER_NAME = "mssql-mcp-server";
|
|
2
|
-
export declare const SERVER_VERSION = "2.3.
|
|
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;
|
package/dist/src/constants.js
CHANGED
|
@@ -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
|
+
});
|
|
@@ -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
|
+
});
|
|
@@ -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.
|
|
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": "^
|
|
53
|
+
"mssql": "^12.5.5",
|
|
54
54
|
"zod": "^3.25.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@types/mssql": "^
|
|
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",
|