mssql-mcp 2.0.2 → 2.0.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.
Files changed (3) hide show
  1. package/README.md +16 -2
  2. package/dist/index.js +135 -413
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # MS SQL Server MCP Server v2.0.2
1
+ # MS SQL Server MCP Server v2.0.3
2
2
 
3
3
  šŸš€ **Smart Trust-Based Model Context Protocol (MCP) server** for Microsoft SQL Server with intelligent auto-connection and AI-friendly design.
4
4
 
@@ -96,6 +96,7 @@ All tools auto-connect using environment variables.
96
96
  - āœ… **Query Caching**: 5-minute TTL for SELECT queries
97
97
  - āœ… **Performance Monitoring**: Execution time tracking
98
98
  - āœ… **Latest MCP SDK**: v1.20.2 with 2025 protocol
99
+ - āœ… **Clean Logging**: Minimal console output, MCP protocol compatible
99
100
 
100
101
  ## šŸ” Troubleshooting
101
102
 
@@ -108,6 +109,19 @@ All tools auto-connect using environment variables.
108
109
  - Only server operations are blocked
109
110
  - Database operations should work normally
110
111
 
112
+ ## šŸ“‹ Version History
113
+
114
+ ### v2.0.3 - Latest
115
+ - āœ… Updated documentation with modern styling
116
+ - āœ… Improved troubleshooting section
117
+ - āœ… Enhanced feature descriptions
118
+
119
+ ### v2.0.2 - Performance & Compatibility
120
+ - āœ… Simplified logging for MCP protocol compatibility
121
+ - āœ… All console output moved to stderr
122
+ - āœ… Clean, professional log messages
123
+ - āœ… Version bump to 2.0.2
124
+
111
125
  ## šŸ“„ License
112
126
 
113
127
  MIT License
@@ -119,4 +133,4 @@ MIT License
119
133
 
120
134
  ---
121
135
 
122
- **šŸŽ‰ v2.0.2: Smart auto-connection with complete SQL support**
136
+ **šŸŽ‰ v2.0.3: Enhanced documentation and user experience**
package/dist/index.js CHANGED
@@ -17,152 +17,24 @@ const ConfigSchema = z.object({
17
17
  connectionTimeout: z.number().int().min(1000).max(60000).optional().default(30000),
18
18
  requestTimeout: z.number().int().min(1000).max(300000).optional().default(30000),
19
19
  });
20
- // Enhanced error class for better error handling with MCP compliance
21
- class MCPServerError extends Error {
22
- code;
23
- details;
24
- category;
25
- constructor(message, code, details, category = 'SYSTEM') {
26
- super(message);
27
- this.code = code;
28
- this.details = details;
29
- this.category = category;
30
- this.name = 'MCPServerError';
31
- }
32
- toCallToolResult() {
33
- return {
34
- content: [{
35
- type: "text",
36
- text: `Error: ${this.message} (Code: ${this.code})`
37
- }],
38
- isError: true,
39
- _meta: {
40
- errorCategory: this.category,
41
- errorCode: this.code,
42
- timestamp: new Date().toISOString(),
43
- details: this.details
44
- }
45
- };
46
- }
47
- }
48
- // Query result cache for performance optimization
49
- class QueryCache {
50
- cache = new Map();
51
- defaultTTL = 300000; // 5 minutes
52
- set(key, data, ttl = this.defaultTTL) {
53
- this.cache.set(key, { data, timestamp: Date.now(), ttl });
54
- }
55
- get(key) {
56
- const item = this.cache.get(key);
57
- if (!item)
58
- return null;
59
- if (Date.now() - item.timestamp > item.ttl) {
60
- this.cache.delete(key);
61
- return null;
62
- }
63
- return item.data;
64
- }
65
- clear() {
66
- this.cache.clear();
67
- }
68
- cleanup() {
69
- const now = Date.now();
70
- for (const [key, item] of this.cache.entries()) {
71
- if (now - item.timestamp > item.ttl) {
72
- this.cache.delete(key);
73
- }
74
- }
75
- }
76
- }
77
- // No rate limiting - trust-based approach
78
- class RateLimiter {
79
- constructor() {
80
- // Silent - no console output for MCP protocol
81
- }
82
- checkLimit(clientId, operation) {
83
- // Always allow - user has provided credentials and is trusted
84
- return true;
85
- }
86
- }
87
20
  class MSSQLMCPServer {
88
21
  server;
89
22
  pool = null;
90
23
  config = null;
91
- queryCache = new QueryCache();
92
- rateLimiter = new RateLimiter();
93
- clientId = `client_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
94
24
  constructor() {
95
25
  this.server = new McpServer({
96
26
  name: "mssql-mcp-server",
97
- version: "2.0.2",
98
- instructions: "šŸš€ UNRESTRICTED SQL SERVER MCP SERVER v2.0.2 - TRUST-BASED!\n\n✨ FOR AI ASSISTANTS: COMPLETE FREEDOM!\n• All SQL operations allowed (SELECT, INSERT, UPDATE, DELETE, DROP, CREATE, etc.)\n• No rate limiting - unlimited operations\n• Auto-connects automatically with environment variables\n• Just use any tool directly (execute_query, get_schema, etc.)\n\nšŸ”§ SETUP: Set DB_SERVER, DB_DATABASE, DB_USER, DB_PASSWORD environment variables\n\nšŸ›”ļø MINIMAL SECURITY: Only blocks server-level operations (SHUTDOWN, XP_CMDSHELL)\n⚔ FEATURES: Query caching, performance monitoring, dry-run validation",
27
+ version: "2.0.3",
99
28
  });
100
29
  this.setupTools();
101
30
  this.setupResources();
102
- // Set up periodic cache cleanup (every 5 minutes)
103
- setInterval(() => {
104
- this.queryCache.cleanup();
105
- }, 300000);
106
- }
107
- // Enhanced error handling method with MCP compliance
108
- handleToolError(error, toolName) {
109
- if (error instanceof MCPServerError) {
110
- console.error(`MCP Error [${error.code}] in ${toolName}: ${error.message}`, error.details);
111
- return error.toCallToolResult();
112
- }
113
- const errorMessage = error instanceof Error ? error.message : String(error);
114
- console.error(`Tool error in ${toolName}:`, errorMessage);
115
- return {
116
- content: [{
117
- type: "text",
118
- text: `Error: ${errorMessage}`
119
- }],
120
- isError: true,
121
- _meta: {
122
- errorCategory: 'EXECUTION',
123
- toolName,
124
- timestamp: new Date().toISOString()
125
- }
126
- };
127
- }
128
- // Helper method to generate cache keys
129
- generateCacheKey(operation, params) {
130
- return `${operation}_${JSON.stringify(params)}`;
131
- }
132
- // Trust-based SQL validation - only block truly dangerous system operations
133
- validateSQLSecurity(query, context = { operation: 'unknown' }) {
134
- // User has already provided database credentials and connection
135
- // Only block operations that could compromise the database server itself
136
- const criticalSystemPatterns = [
137
- { pattern: /\b(SHUTDOWN|KILL)\b/i, category: 'SERVER_CONTROL' },
138
- { pattern: /\b(XP_CMDSHELL|SP_OACREATE|SP_OAMETHOD)\b/i, category: 'SYSTEM_EXEC' },
139
- { pattern: /\b(RECONFIGURE|DISK\s+INIT)\b/i, category: 'SERVER_CONFIG' }
140
- ];
141
- for (const { pattern, category } of criticalSystemPatterns) {
142
- if (pattern.test(query)) {
143
- throw new MCPServerError(`System-level operation not allowed: ${category}`, "SYSTEM_OPERATION_BLOCKED", {
144
- pattern: pattern.source,
145
- category,
146
- query: query.substring(0, 100) + (query.length > 100 ? '...' : ''),
147
- context,
148
- note: "This operation could affect the database server itself"
149
- }, 'SECURITY');
150
- }
151
- }
152
31
  }
153
32
  setupTools() {
154
- // Tool: Connect to database with enhanced security validation and MCP annotations
155
- this.server.tool("connect_database", "Connect to MS SQL Server database with security validation using environment variables", {}, {
156
- title: "Connect to Database",
157
- description: "Establishes secure connection to MS SQL Server using environment variables only",
158
- annotations: {
159
- destructiveHint: false,
160
- idempotentHint: true,
161
- readOnlyHint: true
162
- }
163
- }, async (extra) => {
33
+ // Tool: Connect to database with enhanced security validation (only uses environment variables)
34
+ this.server.tool("connect_database", "Connect to MS SQL Server database with security validation (uses only environment variables for security)", {
35
+ // No parameters - only environment variables will be used for security
36
+ }, async (args) => {
164
37
  try {
165
- // No rate limiting - user is trusted
166
38
  // SECURITY: Only use environment variables, ignore all user parameters
167
39
  const config = ConfigSchema.parse({
168
40
  server: process.env.DB_SERVER,
@@ -175,124 +47,43 @@ class MSSQLMCPServer {
175
47
  requestTimeout: process.env.DB_REQUEST_TIMEOUT ? parseInt(process.env.DB_REQUEST_TIMEOUT) : 30000,
176
48
  });
177
49
  if (!config.server) {
178
- throw new MCPServerError("Server is required. Set DB_SERVER environment variable.", "SERVER_REQUIRED", undefined, 'VALIDATION');
50
+ throw new Error("Server is required. Provide it as parameter or set DB_SERVER environment variable.");
179
51
  }
180
- const startTime = Date.now();
181
52
  await this.connect(config);
182
- const connectionTime = Date.now() - startTime;
183
53
  return {
184
- content: [{
54
+ content: [
55
+ {
185
56
  type: "text",
186
- text: `āœ… Successfully connected to SQL Server: ${config.server}${config.database ? ` (Database: ${config.database})` : ""} in ${connectionTime}ms`
187
- }],
188
- _meta: {
189
- connectionTime: `${connectionTime}ms`,
190
- server: config.server,
191
- database: config.database,
192
- port: config.port,
193
- sslEnabled: !config.trustServerCertificate,
194
- timestamp: new Date().toISOString()
195
- }
57
+ text: `āœ… Successfully connected to SQL Server: ${config.server}${config.database ? ` (Database: ${config.database})` : ""}`,
58
+ },
59
+ ],
196
60
  };
197
61
  }
198
62
  catch (error) {
199
- return this.handleToolError(error, 'connect_database');
63
+ const errorMessage = error instanceof Error ? error.message : String(error);
64
+ console.error("āŒ Database connection failed:", errorMessage);
65
+ return {
66
+ content: [
67
+ {
68
+ type: "text",
69
+ text: `āŒ Failed to connect: ${errorMessage}`,
70
+ },
71
+ ],
72
+ isError: true,
73
+ };
200
74
  }
201
75
  });
202
- // Tool: Execute SQL query with enhanced security, caching, and MCP annotations
203
- this.server.tool("execute_query", "Execute SQL queries directly - auto-connects if needed. No manual connection required when environment variables are set.", {
204
- query: z.string().min(1, "Query cannot be empty").describe("SQL query to execute (parameters recommended for user input)"),
205
- parameters: z.record(z.any()).optional().describe("Query parameters for security (key-value pairs)"),
206
- useCache: z.boolean().optional().default(true).describe("Enable result caching for SELECT queries"),
207
- dryRun: z.boolean().optional().default(false).describe("Validate query without execution"),
208
- }, {
209
- title: "Execute SQL Query",
210
- description: "Executes SQL queries with comprehensive security validation and performance optimization",
211
- annotations: {
212
- destructiveHint: false,
213
- idempotentHint: true,
214
- readOnlyHint: true
215
- }
216
- }, async ({ query, parameters, useCache, dryRun }, extra) => {
76
+ // Tool: Execute SQL query with enhanced security
77
+ this.server.tool("execute_query", "Execute a SQL query against the connected database with security validation", {
78
+ query: z.string().min(1, "Query cannot be empty").describe("SQL query to execute"),
79
+ parameters: z.record(z.any()).optional().describe("Query parameters (key-value pairs) - always use parameters for user input"),
80
+ }, async ({ query, parameters }) => {
217
81
  try {
218
- // No rate limiting - user is trusted
219
- // Auto-connection logic for better AI experience
220
82
  if (!this.pool) {
221
- console.error("Auto-connecting...");
222
- try {
223
- // Try to auto-connect using environment variables
224
- const config = ConfigSchema.parse({
225
- server: process.env.DB_SERVER,
226
- database: process.env.DB_DATABASE,
227
- user: process.env.DB_USER,
228
- password: process.env.DB_PASSWORD,
229
- port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 1433,
230
- trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'true',
231
- connectionTimeout: process.env.DB_CONNECTION_TIMEOUT ? parseInt(process.env.DB_CONNECTION_TIMEOUT) : 30000,
232
- requestTimeout: process.env.DB_REQUEST_TIMEOUT ? parseInt(process.env.DB_REQUEST_TIMEOUT) : 30000,
233
- });
234
- if (!config.server) {
235
- throw new MCPServerError("āŒ Auto-connection failed: DB_SERVER environment variable not set. Please set database connection environment variables or use connect_database tool first.", "AUTO_CONNECTION_FAILED", {
236
- requiredEnvVars: ['DB_SERVER', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD'],
237
- suggestion: "Use connect_database tool manually or set environment variables"
238
- }, 'CONNECTION');
239
- }
240
- await this.connect(config);
241
- console.error("Auto-connected");
242
- }
243
- catch (autoConnectError) {
244
- throw new MCPServerError("āŒ No database connection available and auto-connection failed. Please use connect_database tool first or check your environment variables.", "NO_CONNECTION_AUTO_FAILED", {
245
- autoConnectError: autoConnectError instanceof Error ? autoConnectError.message : String(autoConnectError),
246
- suggestion: "1. Use connect_database tool first, OR\n2. Set environment variables: DB_SERVER, DB_DATABASE, DB_USER, DB_PASSWORD"
247
- }, 'CONNECTION');
248
- }
249
- }
250
- // Enhanced security validation with context
251
- this.validateSQLSecurity(query, { operation: 'execute_query', userProvided: true });
252
- // Check cache for SELECT queries
253
- const queryType = query.trim().toUpperCase().split(/\s+/)[0];
254
- const isSelectQuery = queryType === 'SELECT';
255
- let cacheKey = null;
256
- if (isSelectQuery && useCache && !dryRun) {
257
- cacheKey = this.generateCacheKey('execute_query', { query, parameters });
258
- const cachedResult = this.queryCache.get(cacheKey);
259
- if (cachedResult) {
260
- return {
261
- content: [{
262
- type: "text",
263
- text: JSON.stringify({
264
- ...cachedResult,
265
- fromCache: true,
266
- executionTime: "0ms (cached)",
267
- cacheHit: true
268
- }, null, 2)
269
- }],
270
- _meta: {
271
- cached: true,
272
- cacheKey,
273
- timestamp: new Date().toISOString()
274
- }
275
- };
276
- }
277
- }
278
- // Dry run validation
279
- if (dryRun) {
280
- return {
281
- content: [{
282
- type: "text",
283
- text: `āœ… Query validation passed:\n• Query type: ${queryType}\n• Parameters: ${parameters ? Object.keys(parameters).length : 0}\n• Estimated complexity: ${query.length > 200 ? 'High' : 'Low'}\n• Security: āœ“ Passed\n• Ready for execution`
284
- }],
285
- _meta: {
286
- dryRun: true,
287
- queryType,
288
- parameterCount: parameters ? Object.keys(parameters).length : 0,
289
- validationPassed: true,
290
- timestamp: new Date().toISOString()
291
- }
292
- };
83
+ throw new Error("No database connection. Please connect first using connect_database tool.");
293
84
  }
294
85
  const request = this.pool.request();
295
- // Add parameters with type safety
86
+ // Add parameters if provided (recommended for security)
296
87
  if (parameters) {
297
88
  for (const [key, value] of Object.entries(parameters)) {
298
89
  request.input(key, value);
@@ -301,190 +92,131 @@ class MSSQLMCPServer {
301
92
  const startTime = Date.now();
302
93
  const result = await request.query(query);
303
94
  const executionTime = Date.now() - startTime;
304
- const response = {
305
- recordset: result.recordset,
306
- rowsAffected: result.rowsAffected,
307
- output: result.output,
308
- executionTime: `${executionTime}ms`,
309
- parametersUsed: parameters ? Object.keys(parameters).length : 0,
310
- fromCache: false,
311
- queryType,
312
- rowCount: result.recordset.length
313
- };
314
- // Cache SELECT query results
315
- if (isSelectQuery && useCache && cacheKey) {
316
- this.queryCache.set(cacheKey, response);
317
- }
318
95
  return {
319
- content: [{
96
+ content: [
97
+ {
320
98
  type: "text",
321
- text: JSON.stringify(response, null, 2)
322
- }],
323
- _meta: {
324
- executionTime: `${executionTime}ms`,
325
- queryType,
326
- rowCount: result.recordset.length,
327
- parametersUsed: parameters ? Object.keys(parameters).length : 0,
328
- cached: false,
329
- timestamp: new Date().toISOString()
330
- }
99
+ text: JSON.stringify({
100
+ recordset: result.recordset,
101
+ rowsAffected: result.rowsAffected,
102
+ output: result.output,
103
+ executionTime: `${executionTime}ms`,
104
+ parametersUsed: parameters ? Object.keys(parameters).length : 0,
105
+ }, null, 2),
106
+ },
107
+ ],
331
108
  };
332
109
  }
333
110
  catch (error) {
334
- return this.handleToolError(error, 'execute_query');
111
+ const errorMessage = error instanceof Error ? error.message : String(error);
112
+ console.error("āŒ Query execution failed:", errorMessage);
113
+ return {
114
+ content: [
115
+ {
116
+ type: "text",
117
+ text: `āŒ Query execution failed: ${errorMessage}`,
118
+ },
119
+ ],
120
+ isError: true,
121
+ };
335
122
  }
336
123
  });
337
- // Tool: Get database schema with enhanced filtering and MCP annotations
338
- this.server.tool("get_schema", "Explore database schema - auto-connects if needed. No manual connection required when environment variables are set.", {
339
- objectType: z.enum(["tables", "views", "procedures", "functions", "all"]).optional().default("tables").describe("Type of objects to list"),
340
- schemaName: z.string().optional().describe("Filter by specific schema name"),
341
- includeMetadata: z.boolean().optional().default(true).describe("Include detailed metadata"),
342
- }, {
343
- title: "Get Database Schema",
344
- description: "Retrieves comprehensive database schema information with filtering options",
345
- annotations: {
346
- destructiveHint: false,
347
- idempotentHint: true,
348
- readOnlyHint: true
349
- }
350
- }, async ({ objectType, schemaName, includeMetadata }, extra) => {
124
+ // Tool: Get database schema
125
+ this.server.tool("get_schema", "Get database schema information (tables, columns, etc.)", {
126
+ objectType: z.enum(["tables", "views", "procedures", "functions", "all"]).optional().default("tables"),
127
+ schemaName: z.string().optional().describe("Specific schema name to filter"),
128
+ }, async ({ objectType, schemaName }) => {
351
129
  try {
352
- // Auto-connection logic for seamless AI experience
353
130
  if (!this.pool) {
354
- try {
355
- const config = ConfigSchema.parse({
356
- server: process.env.DB_SERVER,
357
- database: process.env.DB_DATABASE,
358
- user: process.env.DB_USER,
359
- password: process.env.DB_PASSWORD,
360
- port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 1433,
361
- trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'true',
362
- connectionTimeout: process.env.DB_CONNECTION_TIMEOUT ? parseInt(process.env.DB_CONNECTION_TIMEOUT) : 30000,
363
- requestTimeout: process.env.DB_REQUEST_TIMEOUT ? parseInt(process.env.DB_REQUEST_TIMEOUT) : 30000,
364
- });
365
- if (config.server) {
366
- await this.connect(config);
367
- }
368
- else {
369
- throw new MCPServerError("Database connection required. Set environment variables or use connect_database tool.", "NO_CONNECTION", { requiredEnvVars: ['DB_SERVER', 'DB_DATABASE', 'DB_USER', 'DB_PASSWORD'] }, 'CONNECTION');
370
- }
371
- }
372
- catch (error) {
373
- throw new MCPServerError("Database connection required. Use connect_database tool or set environment variables.", "NO_CONNECTION_AUTO_FAILED", undefined, 'CONNECTION');
374
- }
131
+ throw new Error("No database connection. Please connect first.");
375
132
  }
376
133
  let query = "";
377
- const startTime = Date.now();
378
134
  if (objectType === "tables" || objectType === "all") {
379
- query += includeMetadata ? `
380
- SELECT
381
- TABLE_SCHEMA,
382
- TABLE_NAME,
383
- TABLE_TYPE,
384
- 'table' as OBJECT_TYPE,
385
- (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS c
386
- WHERE c.TABLE_NAME = t.TABLE_NAME AND c.TABLE_SCHEMA = t.TABLE_SCHEMA) as COLUMN_COUNT
387
- FROM INFORMATION_SCHEMA.TABLES t
388
- ${schemaName ? `WHERE TABLE_SCHEMA = @schemaName` : ""}
389
- ` : `
390
- SELECT
135
+ query += `
136
+ SELECT
391
137
  TABLE_SCHEMA,
392
138
  TABLE_NAME,
393
139
  TABLE_TYPE,
394
140
  'table' as OBJECT_TYPE
395
141
  FROM INFORMATION_SCHEMA.TABLES
396
- ${schemaName ? `WHERE TABLE_SCHEMA = @schemaName` : ""}
142
+ ${schemaName ? `WHERE TABLE_SCHEMA = '${schemaName}'` : ""}
397
143
  `;
398
144
  }
399
145
  if (objectType === "views" || objectType === "all") {
400
146
  if (query)
401
147
  query += " UNION ALL ";
402
148
  query += `
403
- SELECT
149
+ SELECT
404
150
  TABLE_SCHEMA,
405
151
  TABLE_NAME,
406
152
  'VIEW' as TABLE_TYPE,
407
153
  'view' as OBJECT_TYPE
408
- ${includeMetadata ? ", (SELECT COUNT(*) FROM INFORMATION_SCHEMA.VIEW_COLUMNS v WHERE v.TABLE_NAME = TABLE_NAME AND v.TABLE_SCHEMA = TABLE_SCHEMA) as COLUMN_COUNT" : ""}
409
154
  FROM INFORMATION_SCHEMA.VIEWS
410
- ${schemaName ? `WHERE TABLE_SCHEMA = @schemaName` : ""}
155
+ ${schemaName ? `WHERE TABLE_SCHEMA = '${schemaName}'` : ""}
411
156
  `;
412
157
  }
413
158
  if (objectType === "procedures" || objectType === "all") {
414
159
  if (query)
415
160
  query += " UNION ALL ";
416
161
  query += `
417
- SELECT
162
+ SELECT
418
163
  ROUTINE_SCHEMA as TABLE_SCHEMA,
419
164
  ROUTINE_NAME as TABLE_NAME,
420
165
  'PROCEDURE' as TABLE_TYPE,
421
166
  'procedure' as OBJECT_TYPE
422
- ${includeMetadata ? ", (SELECT COUNT(*) FROM INFORMATION_SCHEMA.PARAMETERS p WHERE p.SPECIFIC_NAME = ROUTINE_NAME AND p.SPECIFIC_SCHEMA = ROUTINE_SCHEMA) as PARAMETER_COUNT" : ""}
423
167
  FROM INFORMATION_SCHEMA.ROUTINES
424
168
  WHERE ROUTINE_TYPE = 'PROCEDURE'
425
- ${schemaName ? `AND ROUTINE_SCHEMA = @schemaName` : ""}
169
+ ${schemaName ? `AND ROUTINE_SCHEMA = '${schemaName}'` : ""}
426
170
  `;
427
171
  }
428
172
  if (objectType === "functions" || objectType === "all") {
429
173
  if (query)
430
174
  query += " UNION ALL ";
431
175
  query += `
432
- SELECT
176
+ SELECT
433
177
  ROUTINE_SCHEMA as TABLE_SCHEMA,
434
178
  ROUTINE_NAME as TABLE_NAME,
435
179
  'FUNCTION' as TABLE_TYPE,
436
180
  'function' as OBJECT_TYPE
437
- ${includeMetadata ? ", (SELECT COUNT(*) FROM INFORMATION_SCHEMA.PARAMETERS p WHERE p.SPECIFIC_NAME = ROUTINE_NAME AND p.SPECIFIC_SCHEMA = ROUTINE_SCHEMA) as PARAMETER_COUNT" : ""}
438
181
  FROM INFORMATION_SCHEMA.ROUTINES
439
182
  WHERE ROUTINE_TYPE = 'FUNCTION'
440
- ${schemaName ? `AND ROUTINE_SCHEMA = @schemaName` : ""}
183
+ ${schemaName ? `AND ROUTINE_SCHEMA = '${schemaName}'` : ""}
441
184
  `;
442
185
  }
443
186
  query += " ORDER BY TABLE_SCHEMA, TABLE_NAME";
444
- const request = this.pool.request();
445
- if (schemaName) {
446
- request.input('schemaName', schemaName);
447
- }
448
- const result = await request.query(query);
449
- const executionTime = Date.now() - startTime;
187
+ const result = await this.pool.request().query(query);
450
188
  return {
451
- content: [{
189
+ content: [
190
+ {
452
191
  type: "text",
453
- text: JSON.stringify({
454
- objects: result.recordset,
455
- metadata: {
456
- objectType,
457
- schemaName: schemaName || 'all',
458
- count: result.recordset.length,
459
- executionTime: `${executionTime}ms`,
460
- includeMetadata
461
- }
462
- }, null, 2)
463
- }],
464
- _meta: {
465
- objectType,
466
- count: result.recordset.length,
467
- executionTime: `${executionTime}ms`,
468
- schemaFiltered: !!schemaName,
469
- timestamp: new Date().toISOString()
470
- }
192
+ text: JSON.stringify(result.recordset, null, 2),
193
+ },
194
+ ],
471
195
  };
472
196
  }
473
197
  catch (error) {
474
- return this.handleToolError(error, 'get_schema');
198
+ return {
199
+ content: [
200
+ {
201
+ type: "text",
202
+ text: `Schema query failed: ${error instanceof Error ? error.message : String(error)}`,
203
+ },
204
+ ],
205
+ isError: true,
206
+ };
475
207
  }
476
208
  });
477
209
  // Tool: Get table structure
478
- this.server.tool("describe_table", "Get table structure - auto-connects if needed. No manual connection required when environment variables are set.", {
210
+ this.server.tool("describe_table", "Get detailed structure of a specific table", {
479
211
  tableName: z.string().describe("Name of the table"),
480
212
  schemaName: z.string().optional().default("dbo").describe("Schema name"),
481
213
  }, async ({ tableName, schemaName }) => {
482
214
  try {
483
215
  if (!this.pool) {
484
- throw new MCPServerError("No database connection. Please connect first.", "NO_CONNECTION");
216
+ throw new Error("No database connection. Please connect first.");
485
217
  }
486
218
  const query = `
487
- SELECT
219
+ SELECT
488
220
  COLUMN_NAME,
489
221
  DATA_TYPE,
490
222
  CHARACTER_MAXIMUM_LENGTH,
@@ -494,7 +226,7 @@ class MSSQLMCPServer {
494
226
  COLUMN_DEFAULT,
495
227
  ORDINAL_POSITION
496
228
  FROM INFORMATION_SCHEMA.COLUMNS
497
- WHERE TABLE_NAME = @tableName
229
+ WHERE TABLE_NAME = @tableName
498
230
  AND TABLE_SCHEMA = @schemaName
499
231
  ORDER BY ORDINAL_POSITION
500
232
  `;
@@ -512,7 +244,15 @@ class MSSQLMCPServer {
512
244
  };
513
245
  }
514
246
  catch (error) {
515
- return this.handleToolError(error);
247
+ return {
248
+ content: [
249
+ {
250
+ type: "text",
251
+ text: `Table description failed: ${error instanceof Error ? error.message : String(error)}`,
252
+ },
253
+ ],
254
+ isError: true,
255
+ };
516
256
  }
517
257
  });
518
258
  // Tool: Get enhanced connection status
@@ -526,12 +266,6 @@ class MSSQLMCPServer {
526
266
  connectionTime: isConnected ? new Date().toISOString() : null,
527
267
  securityFeatures: {
528
268
  sqlInjectionProtection: "Enabled",
529
- rateLimiting: "Enabled",
530
- caching: "Enabled"
531
- },
532
- cacheStats: {
533
- cacheSize: this.queryCache['cache'].size,
534
- clientInfo: this.clientId
535
269
  },
536
270
  poolInfo: this.pool ? {
537
271
  size: this.pool.size,
@@ -556,19 +290,26 @@ class MSSQLMCPServer {
556
290
  await this.pool.close();
557
291
  this.pool = null;
558
292
  this.config = null;
559
- this.queryCache.clear(); // Clear cache on disconnect
560
293
  }
561
294
  return {
562
295
  content: [
563
296
  {
564
297
  type: "text",
565
- text: "Successfully disconnected from database and cleared cache",
298
+ text: "Successfully disconnected from database",
566
299
  },
567
300
  ],
568
301
  };
569
302
  }
570
303
  catch (error) {
571
- return this.handleToolError(error);
304
+ return {
305
+ content: [
306
+ {
307
+ type: "text",
308
+ text: `Disconnect failed: ${error instanceof Error ? error.message : String(error)}`,
309
+ },
310
+ ],
311
+ isError: true,
312
+ };
572
313
  }
573
314
  });
574
315
  // Tool: Get table data with enhanced security and validation
@@ -583,15 +324,15 @@ class MSSQLMCPServer {
583
324
  }, async ({ tableName, schemaName, limit, offset, whereClause, orderBy, parameters }) => {
584
325
  try {
585
326
  if (!this.pool) {
586
- throw new MCPServerError("No database connection. Please connect first.", "NO_CONNECTION");
327
+ throw new Error("No database connection. Please connect first.");
587
328
  }
588
329
  // Security: Validate table and schema names to prevent SQL injection
589
330
  const tableNamePattern = /^[a-zA-Z0-9_]+$/;
590
331
  if (!tableNamePattern.test(tableName)) {
591
- throw new MCPServerError("Invalid table name. Only letters, numbers, and underscores are allowed.", "INVALID_TABLE_NAME");
332
+ throw new Error("Invalid table name. Only letters, numbers, and underscores are allowed.");
592
333
  }
593
334
  if (!tableNamePattern.test(schemaName)) {
594
- throw new MCPServerError("Invalid schema name. Only letters, numbers, and underscores are allowed.", "INVALID_SCHEMA_NAME");
335
+ throw new Error("Invalid schema name. Only letters, numbers, and underscores are allowed.");
595
336
  }
596
337
  // Build query using parameterized approach
597
338
  let query = `SELECT * FROM [${schemaName}].[${tableName}]`;
@@ -607,9 +348,10 @@ class MSSQLMCPServer {
607
348
  }
608
349
  if (orderBy) {
609
350
  // Validate ORDER BY clause for basic security
351
+ // Allow dotted identifiers, bracketed identifiers, commas, spaces and optional ASC/DESC per column
610
352
  const orderByPattern = /^([\[\]a-zA-Z0-9_.]+(\s+(ASC|DESC))?)(\s*,\s*[\[\]a-zA-Z0-9_.]+(\s+(ASC|DESC))?)*$/i;
611
353
  if (!orderByPattern.test(orderBy)) {
612
- throw new MCPServerError("Invalid ORDER BY clause. Only column names, commas, spaces, ASC, and DESC are allowed.", "INVALID_ORDER_BY");
354
+ throw new Error("Invalid ORDER BY clause. Only column names, commas, spaces, ASC, and DESC are allowed.");
613
355
  }
614
356
  query += ` ORDER BY ${orderBy}`;
615
357
  }
@@ -642,7 +384,17 @@ class MSSQLMCPServer {
642
384
  };
643
385
  }
644
386
  catch (error) {
645
- return this.handleToolError(error);
387
+ const errorMessage = error instanceof Error ? error.message : String(error);
388
+ console.error("āŒ Get table data failed:", errorMessage);
389
+ return {
390
+ content: [
391
+ {
392
+ type: "text",
393
+ text: `āŒ Get table data failed: ${errorMessage}`,
394
+ },
395
+ ],
396
+ isError: true,
397
+ };
646
398
  }
647
399
  });
648
400
  // Tool: Execute stored procedure
@@ -653,7 +405,7 @@ class MSSQLMCPServer {
653
405
  }, async ({ procedureName, schemaName, parameters }) => {
654
406
  try {
655
407
  if (!this.pool) {
656
- throw new MCPServerError("No database connection. Please connect first.", "NO_CONNECTION");
408
+ throw new Error("No database connection. Please connect first.");
657
409
  }
658
410
  const request = this.pool.request();
659
411
  // Add parameters if provided
@@ -678,17 +430,25 @@ class MSSQLMCPServer {
678
430
  };
679
431
  }
680
432
  catch (error) {
681
- return this.handleToolError(error);
433
+ return {
434
+ content: [
435
+ {
436
+ type: "text",
437
+ text: `Procedure execution failed: ${error instanceof Error ? error.message : String(error)}`,
438
+ },
439
+ ],
440
+ isError: true,
441
+ };
682
442
  }
683
443
  });
684
444
  // Tool: Get database list
685
445
  this.server.tool("list_databases", "List all databases on the connected SQL Server instance", {}, async () => {
686
446
  try {
687
447
  if (!this.pool) {
688
- throw new MCPServerError("No database connection. Please connect first.", "NO_CONNECTION");
448
+ throw new Error("No database connection. Please connect first.");
689
449
  }
690
450
  const query = `
691
- SELECT
451
+ SELECT
692
452
  name,
693
453
  database_id,
694
454
  create_date,
@@ -713,30 +473,16 @@ class MSSQLMCPServer {
713
473
  };
714
474
  }
715
475
  catch (error) {
716
- return this.handleToolError(error);
717
- }
718
- });
719
- // Tool: Clear query cache
720
- this.server.tool("clear_cache", "Clear the query result cache", {}, async () => {
721
- try {
722
- const cacheSize = this.queryCache['cache'].size;
723
- this.queryCache.clear();
724
476
  return {
725
477
  content: [
726
478
  {
727
479
  type: "text",
728
- text: JSON.stringify({
729
- message: "Query cache cleared successfully",
730
- clearedEntries: cacheSize,
731
- timestamp: new Date().toISOString()
732
- }, null, 2),
480
+ text: `List databases failed: ${error instanceof Error ? error.message : String(error)}`,
733
481
  },
734
482
  ],
483
+ isError: true,
735
484
  };
736
485
  }
737
- catch (error) {
738
- return this.handleToolError(error);
739
- }
740
486
  });
741
487
  }
742
488
  setupResources() {
@@ -749,14 +495,6 @@ class MSSQLMCPServer {
749
495
  database: this.config.database,
750
496
  port: this.config.port,
751
497
  } : null,
752
- cacheStats: {
753
- cacheSize: this.queryCache['cache'].size,
754
- lastCleanup: new Date().toISOString()
755
- },
756
- clientInfo: {
757
- clientId: this.clientId,
758
- rateLimitsEnabled: true
759
- }
760
498
  };
761
499
  return {
762
500
  contents: [
@@ -768,22 +506,6 @@ class MSSQLMCPServer {
768
506
  ],
769
507
  };
770
508
  });
771
- // Dynamic resource for query results cache status
772
- this.server.resource("cache-status", "mssql://cache/status", async () => {
773
- const status = {
774
- cacheSize: this.queryCache['cache'].size,
775
- rateLimiting: "DISABLED - Trust-based approach",
776
- clientId: this.clientId,
777
- timestamp: new Date().toISOString()
778
- };
779
- return {
780
- contents: [{
781
- uri: "mssql://cache/status",
782
- text: JSON.stringify(status, null, 2),
783
- mimeType: "application/json"
784
- }]
785
- };
786
- });
787
509
  }
788
510
  async connect(config) {
789
511
  try {
@@ -871,7 +593,7 @@ class MSSQLMCPServer {
871
593
  console.error('Unhandled Rejection:', reason);
872
594
  shutdown('unhandledRejection');
873
595
  });
874
- console.error("MSSQL MCP Server v2.0.2 starting...");
596
+ console.error("MSSQL MCP Server v2.0.3 starting...");
875
597
  await this.server.connect(transport);
876
598
  console.error("Server ready");
877
599
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mssql-mcp",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "MCP Server for MS SQL Server integration with Claude Desktop, Cursor, Windsurf and VS Code",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -45,7 +45,7 @@
45
45
  "author": "BYMCS <hello@bymcs.com>",
46
46
  "license": "MIT",
47
47
  "dependencies": {
48
- "@modelcontextprotocol/sdk": "^1.20.2",
48
+ "@modelcontextprotocol/sdk": "^1.17.1",
49
49
  "dotenv": "^16.3.1",
50
50
  "mssql": "^11.0.1",
51
51
  "zod": "^3.22.4"