mssql-mcp 2.1.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -89
  3. package/dist/src/config.d.ts +39 -0
  4. package/dist/src/config.js +37 -0
  5. package/dist/src/constants.d.ts +15 -0
  6. package/dist/src/constants.js +15 -0
  7. package/dist/src/db/connection.d.ts +8 -0
  8. package/dist/src/db/connection.js +80 -0
  9. package/dist/src/db/query-builders.d.ts +3 -0
  10. package/dist/src/db/query-builders.js +58 -0
  11. package/dist/src/db/validators.d.ts +5 -0
  12. package/dist/src/db/validators.js +25 -0
  13. package/dist/src/index.js +26 -0
  14. package/dist/src/resources/connection.d.ts +2 -0
  15. package/dist/src/resources/connection.js +19 -0
  16. package/dist/src/resources/metadata.d.ts +2 -0
  17. package/dist/src/resources/metadata.js +58 -0
  18. package/dist/src/schemas/outputs.d.ts +153 -0
  19. package/dist/src/schemas/outputs.js +54 -0
  20. package/dist/src/server.d.ts +2 -0
  21. package/dist/src/server.js +27 -0
  22. package/dist/src/tools/connect.d.ts +2 -0
  23. package/dist/src/tools/connect.js +45 -0
  24. package/dist/src/tools/databases.d.ts +2 -0
  25. package/dist/src/tools/databases.js +53 -0
  26. package/dist/src/tools/procedure.d.ts +2 -0
  27. package/dist/src/tools/procedure.js +106 -0
  28. package/dist/src/tools/query.d.ts +2 -0
  29. package/dist/src/tools/query.js +92 -0
  30. package/dist/src/tools/schema.d.ts +2 -0
  31. package/dist/src/tools/schema.js +96 -0
  32. package/dist/src/tools/status.d.ts +2 -0
  33. package/dist/src/tools/status.js +17 -0
  34. package/dist/src/tools/table.d.ts +2 -0
  35. package/dist/src/tools/table.js +261 -0
  36. package/dist/src/transports/http.d.ts +3 -0
  37. package/dist/src/transports/http.js +54 -0
  38. package/dist/src/transports/stdio.d.ts +2 -0
  39. package/dist/src/transports/stdio.js +23 -0
  40. package/dist/src/types.d.ts +37 -0
  41. package/dist/src/types.js +1 -0
  42. package/dist/src/utils/errors.d.ts +19 -0
  43. package/dist/src/utils/errors.js +29 -0
  44. package/dist/src/utils/format.d.ts +6 -0
  45. package/dist/src/utils/format.js +27 -0
  46. package/dist/src/utils/markdown.d.ts +3 -0
  47. package/dist/src/utils/markdown.js +33 -0
  48. package/dist/src/utils/pagination.d.ts +3 -0
  49. package/dist/src/utils/pagination.js +18 -0
  50. package/dist/tests/unit/markdown.test.d.ts +1 -0
  51. package/dist/tests/unit/markdown.test.js +70 -0
  52. package/dist/tests/unit/query-builders.test.d.ts +1 -0
  53. package/dist/tests/unit/query-builders.test.js +63 -0
  54. package/dist/tests/unit/tool-contracts.test.d.ts +1 -0
  55. package/dist/tests/unit/tool-contracts.test.js +62 -0
  56. package/dist/tests/unit/validators.test.d.ts +1 -0
  57. package/dist/tests/unit/validators.test.js +51 -0
  58. package/package.json +10 -6
  59. package/dist/index.js +0 -648
  60. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
package/dist/index.js DELETED
@@ -1,648 +0,0 @@
1
- #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import sql from "mssql";
5
- import { z } from "zod";
6
- import dotenv from "dotenv";
7
- // Load environment variables
8
- dotenv.config();
9
- // Database connection configuration schema with strict validation
10
- const ConfigSchema = z.object({
11
- server: z.string().min(1, "Server address is required"),
12
- database: z.string().optional(),
13
- user: z.string().optional(),
14
- password: z.string().optional(),
15
- port: z.number().int().min(1).max(65535).optional().default(1433),
16
- encrypt: z.boolean().optional().default(true),
17
- trustServerCertificate: z.boolean().optional().default(false),
18
- connectionTimeout: z.number().int().min(1000).max(60000).optional().default(30000),
19
- requestTimeout: z.number().int().min(1000).max(300000).optional().default(30000),
20
- });
21
- class MSSQLMCPServer {
22
- server;
23
- pool = null;
24
- config = null;
25
- constructor() {
26
- this.server = new McpServer({
27
- name: "mssql-mcp-server",
28
- version: "2.1.1",
29
- });
30
- this.setupTools();
31
- this.setupResources();
32
- }
33
- setupTools() {
34
- // Tool: Connect to database with enhanced security validation (only uses environment variables)
35
- this.server.registerTool("connect_database", {
36
- title: "Connect Database",
37
- description: "Connect to MS SQL Server database with security validation (uses only environment variables for security)",
38
- inputSchema: {
39
- // No parameters - only environment variables will be used for security
40
- },
41
- }, async () => {
42
- try {
43
- // SECURITY: Only use environment variables, ignore all user parameters
44
- const config = ConfigSchema.parse({
45
- server: process.env.DB_SERVER,
46
- database: process.env.DB_DATABASE,
47
- user: process.env.DB_USER,
48
- password: process.env.DB_PASSWORD,
49
- port: process.env.DB_PORT ? parseInt(process.env.DB_PORT) : 1433,
50
- encrypt: process.env.DB_ENCRYPT !== 'false', // Default true for Azure SQL compatibility
51
- trustServerCertificate: process.env.DB_TRUST_SERVER_CERTIFICATE === 'true',
52
- connectionTimeout: process.env.DB_CONNECTION_TIMEOUT ? parseInt(process.env.DB_CONNECTION_TIMEOUT) : 30000,
53
- requestTimeout: process.env.DB_REQUEST_TIMEOUT ? parseInt(process.env.DB_REQUEST_TIMEOUT) : 30000,
54
- });
55
- if (!config.server) {
56
- throw new Error("Server is required. Provide it as parameter or set DB_SERVER environment variable.");
57
- }
58
- await this.connect(config);
59
- return {
60
- content: [
61
- {
62
- type: "text",
63
- text: `✅ Successfully connected to SQL Server: ${config.server}${config.database ? ` (Database: ${config.database})` : ""}`,
64
- },
65
- ],
66
- };
67
- }
68
- catch (error) {
69
- const errorMessage = error instanceof Error ? error.message : String(error);
70
- console.error("❌ Database connection failed:", errorMessage);
71
- return {
72
- content: [
73
- {
74
- type: "text",
75
- text: `❌ Failed to connect: ${errorMessage}`,
76
- },
77
- ],
78
- isError: true,
79
- };
80
- }
81
- });
82
- // Tool: Execute SQL query with enhanced security
83
- this.server.registerTool("execute_query", {
84
- title: "Execute Query",
85
- description: "Execute a SQL query against the connected database with security validation",
86
- inputSchema: {
87
- query: z.string().min(1, "Query cannot be empty").describe("SQL query to execute"),
88
- parameters: z.record(z.any()).optional().describe("Query parameters (key-value pairs) - always use parameters for user input"),
89
- },
90
- }, async ({ query, parameters }) => {
91
- try {
92
- if (!this.pool) {
93
- throw new Error("No database connection. Please connect first using connect_database tool.");
94
- }
95
- const request = this.pool.request();
96
- // Add parameters if provided (recommended for security)
97
- if (parameters) {
98
- for (const [key, value] of Object.entries(parameters)) {
99
- request.input(key, value);
100
- }
101
- }
102
- const startTime = Date.now();
103
- const result = await request.query(query);
104
- const executionTime = Date.now() - startTime;
105
- return {
106
- content: [
107
- {
108
- type: "text",
109
- text: JSON.stringify({
110
- recordset: result.recordset,
111
- rowsAffected: result.rowsAffected,
112
- output: result.output,
113
- executionTime: `${executionTime}ms`,
114
- parametersUsed: parameters ? Object.keys(parameters).length : 0,
115
- }, null, 2),
116
- },
117
- ],
118
- };
119
- }
120
- catch (error) {
121
- const errorMessage = error instanceof Error ? error.message : String(error);
122
- console.error("❌ Query execution failed:", errorMessage);
123
- return {
124
- content: [
125
- {
126
- type: "text",
127
- text: `❌ Query execution failed: ${errorMessage}`,
128
- },
129
- ],
130
- isError: true,
131
- };
132
- }
133
- });
134
- // Tool: Get database schema
135
- this.server.registerTool("get_schema", {
136
- title: "Get Schema",
137
- description: "Get database schema information (tables, columns, etc.)",
138
- inputSchema: {
139
- objectType: z.enum(["tables", "views", "procedures", "functions", "all"]).optional().default("tables"),
140
- schemaName: z.string().optional().describe("Specific schema name to filter"),
141
- },
142
- }, async ({ objectType, schemaName }) => {
143
- try {
144
- if (!this.pool) {
145
- throw new Error("No database connection. Please connect first.");
146
- }
147
- let query = "";
148
- if (objectType === "tables" || objectType === "all") {
149
- query += `
150
- SELECT
151
- TABLE_SCHEMA,
152
- TABLE_NAME,
153
- TABLE_TYPE,
154
- 'table' as OBJECT_TYPE
155
- FROM INFORMATION_SCHEMA.TABLES
156
- ${schemaName ? `WHERE TABLE_SCHEMA = '${schemaName}'` : ""}
157
- `;
158
- }
159
- if (objectType === "views" || objectType === "all") {
160
- if (query)
161
- query += " UNION ALL ";
162
- query += `
163
- SELECT
164
- TABLE_SCHEMA,
165
- TABLE_NAME,
166
- 'VIEW' as TABLE_TYPE,
167
- 'view' as OBJECT_TYPE
168
- FROM INFORMATION_SCHEMA.VIEWS
169
- ${schemaName ? `WHERE TABLE_SCHEMA = '${schemaName}'` : ""}
170
- `;
171
- }
172
- if (objectType === "procedures" || objectType === "all") {
173
- if (query)
174
- query += " UNION ALL ";
175
- query += `
176
- SELECT
177
- ROUTINE_SCHEMA as TABLE_SCHEMA,
178
- ROUTINE_NAME as TABLE_NAME,
179
- 'PROCEDURE' as TABLE_TYPE,
180
- 'procedure' as OBJECT_TYPE
181
- FROM INFORMATION_SCHEMA.ROUTINES
182
- WHERE ROUTINE_TYPE = 'PROCEDURE'
183
- ${schemaName ? `AND ROUTINE_SCHEMA = '${schemaName}'` : ""}
184
- `;
185
- }
186
- if (objectType === "functions" || objectType === "all") {
187
- if (query)
188
- query += " UNION ALL ";
189
- query += `
190
- SELECT
191
- ROUTINE_SCHEMA as TABLE_SCHEMA,
192
- ROUTINE_NAME as TABLE_NAME,
193
- 'FUNCTION' as TABLE_TYPE,
194
- 'function' as OBJECT_TYPE
195
- FROM INFORMATION_SCHEMA.ROUTINES
196
- WHERE ROUTINE_TYPE = 'FUNCTION'
197
- ${schemaName ? `AND ROUTINE_SCHEMA = '${schemaName}'` : ""}
198
- `;
199
- }
200
- query += " ORDER BY TABLE_SCHEMA, TABLE_NAME";
201
- const result = await this.pool.request().query(query);
202
- return {
203
- content: [
204
- {
205
- type: "text",
206
- text: JSON.stringify(result.recordset, null, 2),
207
- },
208
- ],
209
- };
210
- }
211
- catch (error) {
212
- return {
213
- content: [
214
- {
215
- type: "text",
216
- text: `Schema query failed: ${error instanceof Error ? error.message : String(error)}`,
217
- },
218
- ],
219
- isError: true,
220
- };
221
- }
222
- });
223
- // Tool: Get table structure
224
- this.server.registerTool("describe_table", {
225
- title: "Describe Table",
226
- description: "Get detailed structure of a specific table",
227
- inputSchema: {
228
- tableName: z.string().describe("Name of the table"),
229
- schemaName: z.string().optional().default("dbo").describe("Schema name"),
230
- },
231
- }, async ({ tableName, schemaName }) => {
232
- try {
233
- if (!this.pool) {
234
- throw new Error("No database connection. Please connect first.");
235
- }
236
- const query = `
237
- SELECT
238
- COLUMN_NAME,
239
- DATA_TYPE,
240
- CHARACTER_MAXIMUM_LENGTH,
241
- NUMERIC_PRECISION,
242
- NUMERIC_SCALE,
243
- IS_NULLABLE,
244
- COLUMN_DEFAULT,
245
- ORDINAL_POSITION
246
- FROM INFORMATION_SCHEMA.COLUMNS
247
- WHERE TABLE_NAME = @tableName
248
- AND TABLE_SCHEMA = @schemaName
249
- ORDER BY ORDINAL_POSITION
250
- `;
251
- const result = await this.pool.request()
252
- .input('tableName', sql.VarChar, tableName)
253
- .input('schemaName', sql.VarChar, schemaName)
254
- .query(query);
255
- return {
256
- content: [
257
- {
258
- type: "text",
259
- text: JSON.stringify(result.recordset, null, 2),
260
- },
261
- ],
262
- };
263
- }
264
- catch (error) {
265
- return {
266
- content: [
267
- {
268
- type: "text",
269
- text: `Table description failed: ${error instanceof Error ? error.message : String(error)}`,
270
- },
271
- ],
272
- isError: true,
273
- };
274
- }
275
- });
276
- // Tool: Get enhanced connection status
277
- this.server.registerTool("connection_status", {
278
- title: "Connection Status",
279
- description: "Check current database connection status with detailed information",
280
- inputSchema: {},
281
- }, async () => {
282
- const isConnected = this.pool?.connected || false;
283
- const status = {
284
- connected: isConnected,
285
- server: this.config?.server || "Not configured",
286
- database: this.config?.database || "Not specified",
287
- port: this.config?.port || "Not specified",
288
- connectionTime: isConnected ? new Date().toISOString() : null,
289
- securityFeatures: {
290
- sqlInjectionProtection: "Enabled",
291
- },
292
- poolInfo: this.pool ? {
293
- size: this.pool.size,
294
- available: this.pool.available,
295
- pending: this.pool.pending,
296
- borrowed: this.pool.borrowed,
297
- } : null,
298
- };
299
- return {
300
- content: [
301
- {
302
- type: "text",
303
- text: JSON.stringify(status, null, 2),
304
- },
305
- ],
306
- };
307
- });
308
- // Tool: Disconnect from database
309
- this.server.registerTool("disconnect_database", {
310
- title: "Disconnect Database",
311
- description: "Disconnect from the current database",
312
- inputSchema: {},
313
- }, async () => {
314
- try {
315
- if (this.pool) {
316
- await this.pool.close();
317
- this.pool = null;
318
- this.config = null;
319
- }
320
- return {
321
- content: [
322
- {
323
- type: "text",
324
- text: "Successfully disconnected from database",
325
- },
326
- ],
327
- };
328
- }
329
- catch (error) {
330
- return {
331
- content: [
332
- {
333
- type: "text",
334
- text: `Disconnect failed: ${error instanceof Error ? error.message : String(error)}`,
335
- },
336
- ],
337
- isError: true,
338
- };
339
- }
340
- });
341
- // Tool: Get table data with enhanced security and validation
342
- this.server.registerTool("get_table_data", {
343
- title: "Get Table Data",
344
- description: "Get data from a specific table with optional filtering, pagination and input validation",
345
- inputSchema: {
346
- tableName: z.string().min(1).regex(/^[a-zA-Z0-9_]+$/, "Table name can only contain letters, numbers, and underscores").describe("Name of the table"),
347
- schemaName: z.string().regex(/^[a-zA-Z0-9_]+$/, "Schema name can only contain letters, numbers, and underscores").optional().default("dbo").describe("Schema name"),
348
- limit: z.number().int().min(1).max(10000).optional().default(100).describe("Maximum number of rows to return (1-10000)"),
349
- offset: z.number().int().min(0).optional().default(0).describe("Number of rows to skip"),
350
- whereClause: z.string().optional().describe("WHERE clause (without the WHERE keyword) - use parameters for values"),
351
- orderBy: z.string().optional().describe("ORDER BY clause (without the ORDER BY keyword)"),
352
- parameters: z.record(z.any()).optional().describe("Parameters for WHERE clause"),
353
- },
354
- }, async ({ tableName, schemaName, limit, offset, whereClause, orderBy, parameters }) => {
355
- try {
356
- if (!this.pool) {
357
- throw new Error("No database connection. Please connect first.");
358
- }
359
- // Security: Validate table and schema names to prevent SQL injection
360
- const tableNamePattern = /^[a-zA-Z0-9_]+$/;
361
- if (!tableNamePattern.test(tableName)) {
362
- throw new Error("Invalid table name. Only letters, numbers, and underscores are allowed.");
363
- }
364
- if (!tableNamePattern.test(schemaName)) {
365
- throw new Error("Invalid schema name. Only letters, numbers, and underscores are allowed.");
366
- }
367
- // Build query using parameterized approach
368
- let query = `SELECT * FROM [${schemaName}].[${tableName}]`;
369
- const request = this.pool.request();
370
- if (whereClause) {
371
- // Add parameters for WHERE clause if provided
372
- if (parameters) {
373
- for (const [key, value] of Object.entries(parameters)) {
374
- request.input(key, value);
375
- }
376
- }
377
- query += ` WHERE ${whereClause}`;
378
- }
379
- if (orderBy) {
380
- // Validate ORDER BY clause for basic security
381
- // Allow dotted identifiers, bracketed identifiers, commas, spaces and optional ASC/DESC per column
382
- const orderByPattern = /^([\[\]a-zA-Z0-9_.]+(\s+(ASC|DESC))?)((\s*,\s*)[\[\]a-zA-Z0-9_.]+(\s+(ASC|DESC))?)*$/i;
383
- if (!orderByPattern.test(orderBy)) {
384
- throw new Error("Invalid ORDER BY clause. Only column names, commas, spaces, ASC, and DESC are allowed.");
385
- }
386
- query += ` ORDER BY ${orderBy}`;
387
- }
388
- else {
389
- // Default ordering for pagination
390
- query += ` ORDER BY (SELECT NULL)`;
391
- }
392
- query += ` OFFSET ${offset} ROWS FETCH NEXT ${limit} ROWS ONLY`;
393
- const startTime = Date.now();
394
- const result = await request.query(query);
395
- const executionTime = Date.now() - startTime;
396
- return {
397
- content: [
398
- {
399
- type: "text",
400
- text: JSON.stringify({
401
- data: result.recordset,
402
- metadata: {
403
- rowCount: result.recordset.length,
404
- offset: offset,
405
- limit: limit,
406
- executionTime: `${executionTime}ms`,
407
- table: `${schemaName}.${tableName}`,
408
- hasWhereClause: !!whereClause,
409
- parametersUsed: parameters ? Object.keys(parameters).length : 0,
410
- }
411
- }, null, 2),
412
- },
413
- ],
414
- };
415
- }
416
- catch (error) {
417
- const errorMessage = error instanceof Error ? error.message : String(error);
418
- console.error("❌ Get table data failed:", errorMessage);
419
- return {
420
- content: [
421
- {
422
- type: "text",
423
- text: `❌ Get table data failed: ${errorMessage}`,
424
- },
425
- ],
426
- isError: true,
427
- };
428
- }
429
- });
430
- // Tool: Execute stored procedure
431
- this.server.registerTool("execute_procedure", {
432
- title: "Execute Procedure",
433
- description: "Execute a stored procedure with parameters",
434
- inputSchema: {
435
- procedureName: z.string().describe("Name of the stored procedure"),
436
- schemaName: z.string().optional().default("dbo").describe("Schema name"),
437
- parameters: z.record(z.any()).optional().describe("Procedure parameters (key-value pairs)"),
438
- },
439
- }, async ({ procedureName, schemaName, parameters }) => {
440
- try {
441
- if (!this.pool) {
442
- throw new Error("No database connection. Please connect first.");
443
- }
444
- const request = this.pool.request();
445
- // Add parameters if provided
446
- if (parameters) {
447
- for (const [key, value] of Object.entries(parameters)) {
448
- request.input(key, value);
449
- }
450
- }
451
- const result = await request.execute(`[${schemaName}].[${procedureName}]`);
452
- return {
453
- content: [
454
- {
455
- type: "text",
456
- text: JSON.stringify({
457
- recordsets: result.recordsets,
458
- rowsAffected: result.rowsAffected,
459
- output: result.output,
460
- returnValue: result.returnValue,
461
- }, null, 2),
462
- },
463
- ],
464
- };
465
- }
466
- catch (error) {
467
- return {
468
- content: [
469
- {
470
- type: "text",
471
- text: `Procedure execution failed: ${error instanceof Error ? error.message : String(error)}`,
472
- },
473
- ],
474
- isError: true,
475
- };
476
- }
477
- });
478
- // Tool: Get database list
479
- this.server.registerTool("list_databases", {
480
- title: "List Databases",
481
- description: "List all databases on the connected SQL Server instance",
482
- inputSchema: {},
483
- }, async () => {
484
- try {
485
- if (!this.pool) {
486
- throw new Error("No database connection. Please connect first.");
487
- }
488
- const query = `
489
- SELECT
490
- name,
491
- database_id,
492
- create_date,
493
- collation_name,
494
- state_desc,
495
- user_access_desc,
496
- is_read_only,
497
- is_auto_close_on,
498
- is_auto_shrink_on,
499
- recovery_model_desc
500
- FROM sys.databases
501
- ORDER BY name
502
- `;
503
- const result = await this.pool.request().query(query);
504
- return {
505
- content: [
506
- {
507
- type: "text",
508
- text: JSON.stringify(result.recordset, null, 2),
509
- },
510
- ],
511
- };
512
- }
513
- catch (error) {
514
- return {
515
- content: [
516
- {
517
- type: "text",
518
- text: `List databases failed: ${error instanceof Error ? error.message : String(error)}`,
519
- },
520
- ],
521
- isError: true,
522
- };
523
- }
524
- });
525
- }
526
- setupResources() {
527
- // Resource: Current connection info
528
- this.server.registerResource("connection-info", "mssql://connection/info", {
529
- title: "Connection Info",
530
- description: "Current MSSQL connection information",
531
- mimeType: "application/json",
532
- }, async () => {
533
- const info = {
534
- connected: this.pool?.connected || false,
535
- config: this.config ? {
536
- server: this.config.server,
537
- database: this.config.database,
538
- port: this.config.port,
539
- } : null,
540
- };
541
- return {
542
- contents: [
543
- {
544
- uri: "mssql://connection/info",
545
- text: JSON.stringify(info, null, 2),
546
- mimeType: "application/json",
547
- },
548
- ],
549
- };
550
- });
551
- }
552
- async connect(config) {
553
- try {
554
- // Close existing connection if any
555
- if (this.pool) {
556
- console.error("Closing existing connection...");
557
- await this.pool.close();
558
- }
559
- console.error(`Connecting to ${config.server}:${config.port}`);
560
- // Create new connection pool with enhanced security settings
561
- this.pool = new sql.ConnectionPool({
562
- server: config.server,
563
- database: config.database,
564
- user: config.user,
565
- password: config.password,
566
- port: config.port,
567
- options: {
568
- encrypt: config.encrypt,
569
- trustServerCertificate: config.trustServerCertificate,
570
- enableArithAbort: true,
571
- },
572
- connectionTimeout: config.connectionTimeout,
573
- requestTimeout: config.requestTimeout,
574
- // Connection pool settings for better resource management
575
- pool: {
576
- max: 10,
577
- min: 0,
578
- idleTimeoutMillis: 30000,
579
- },
580
- });
581
- // Set up event handlers for better monitoring
582
- this.pool.on('connect', () => {
583
- console.error('Database connected');
584
- });
585
- this.pool.on('error', (err) => {
586
- console.error('Database error:', err);
587
- });
588
- await this.pool.connect();
589
- this.config = config;
590
- console.error(`Connected to ${config.server}${config.database ? `/${config.database}` : ''}`);
591
- }
592
- catch (error) {
593
- console.error("Connection failed:", error);
594
- if (this.pool) {
595
- try {
596
- await this.pool.close();
597
- }
598
- catch (closeError) {
599
- console.error("Error closing failed connection:", closeError);
600
- }
601
- this.pool = null;
602
- }
603
- this.config = null;
604
- throw error;
605
- }
606
- }
607
- async run() {
608
- const transport = new StdioServerTransport();
609
- // Enhanced graceful shutdown handling
610
- const shutdown = async (signal) => {
611
- console.error(`\nShutting down (${signal})...`);
612
- try {
613
- if (this.pool) {
614
- console.error("Closing database connection...");
615
- await this.pool.close();
616
- console.error("Database connection closed");
617
- }
618
- }
619
- catch (error) {
620
- console.error("Shutdown error:", error);
621
- }
622
- console.error("Server stopped");
623
- process.exit(0);
624
- };
625
- // Handle various shutdown signals
626
- process.on('SIGINT', () => shutdown('SIGINT'));
627
- process.on('SIGTERM', () => shutdown('SIGTERM'));
628
- process.on('SIGUSR2', () => shutdown('SIGUSR2')); // For nodemon
629
- // Handle uncaught exceptions
630
- process.on('uncaughtException', (error) => {
631
- console.error('Uncaught Exception:', error);
632
- shutdown('uncaughtException');
633
- });
634
- process.on('unhandledRejection', (reason, promise) => {
635
- console.error('Unhandled Rejection:', reason);
636
- shutdown('unhandledRejection');
637
- });
638
- console.error("MSSQL MCP Server v2.1.1 starting...");
639
- await this.server.connect(transport);
640
- console.error("Server ready");
641
- }
642
- }
643
- // Start the server
644
- const server = new MSSQLMCPServer();
645
- server.run().catch((error) => {
646
- console.error("Server failed to start:", error);
647
- process.exit(1);
648
- });
File without changes