@berthojoris/mcp-mysql-server 1.5.0 → 1.6.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.
@@ -0,0 +1,371 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MaintenanceTools = void 0;
7
+ const connection_1 = __importDefault(require("../db/connection"));
8
+ const config_1 = require("../config/config");
9
+ class MaintenanceTools {
10
+ constructor(security) {
11
+ this.db = connection_1.default.getInstance();
12
+ this.security = security;
13
+ }
14
+ /**
15
+ * Validate database access - ensures only the connected database can be accessed
16
+ */
17
+ validateDatabaseAccess(requestedDatabase) {
18
+ const connectedDatabase = config_1.dbConfig.database;
19
+ if (!connectedDatabase) {
20
+ return {
21
+ valid: false,
22
+ database: '',
23
+ error: 'No database specified in connection string. Cannot access any database.'
24
+ };
25
+ }
26
+ if (!requestedDatabase) {
27
+ return {
28
+ valid: true,
29
+ database: connectedDatabase
30
+ };
31
+ }
32
+ if (requestedDatabase !== connectedDatabase) {
33
+ return {
34
+ valid: false,
35
+ database: '',
36
+ error: `Access denied. You can only access the connected database '${connectedDatabase}'. Requested database '${requestedDatabase}' is not allowed.`
37
+ };
38
+ }
39
+ return {
40
+ valid: true,
41
+ database: connectedDatabase
42
+ };
43
+ }
44
+ /**
45
+ * Analyze table to update index statistics
46
+ */
47
+ async analyzeTable(params) {
48
+ try {
49
+ const dbValidation = this.validateDatabaseAccess(params?.database);
50
+ if (!dbValidation.valid) {
51
+ return { status: 'error', error: dbValidation.error };
52
+ }
53
+ const { table_name } = params;
54
+ const database = dbValidation.database;
55
+ // Validate table name
56
+ if (!this.security.validateIdentifier(table_name).valid) {
57
+ return { status: 'error', error: 'Invalid table name' };
58
+ }
59
+ const query = `ANALYZE TABLE \`${database}\`.\`${table_name}\``;
60
+ const results = await this.db.query(query);
61
+ return {
62
+ status: 'success',
63
+ data: results[0],
64
+ queryLog: this.db.getFormattedQueryLogs(1)
65
+ };
66
+ }
67
+ catch (error) {
68
+ return {
69
+ status: 'error',
70
+ error: error.message,
71
+ queryLog: this.db.getFormattedQueryLogs(1)
72
+ };
73
+ }
74
+ }
75
+ /**
76
+ * Optimize table to reclaim unused space and defragment
77
+ */
78
+ async optimizeTable(params) {
79
+ try {
80
+ const dbValidation = this.validateDatabaseAccess(params?.database);
81
+ if (!dbValidation.valid) {
82
+ return { status: 'error', error: dbValidation.error };
83
+ }
84
+ const { table_name } = params;
85
+ const database = dbValidation.database;
86
+ // Validate table name
87
+ if (!this.security.validateIdentifier(table_name).valid) {
88
+ return { status: 'error', error: 'Invalid table name' };
89
+ }
90
+ const query = `OPTIMIZE TABLE \`${database}\`.\`${table_name}\``;
91
+ const results = await this.db.query(query);
92
+ return {
93
+ status: 'success',
94
+ data: results[0],
95
+ queryLog: this.db.getFormattedQueryLogs(1)
96
+ };
97
+ }
98
+ catch (error) {
99
+ return {
100
+ status: 'error',
101
+ error: error.message,
102
+ queryLog: this.db.getFormattedQueryLogs(1)
103
+ };
104
+ }
105
+ }
106
+ /**
107
+ * Check table for errors
108
+ */
109
+ async checkTable(params) {
110
+ try {
111
+ const dbValidation = this.validateDatabaseAccess(params?.database);
112
+ if (!dbValidation.valid) {
113
+ return { status: 'error', error: dbValidation.error };
114
+ }
115
+ const { table_name, check_type } = params;
116
+ const database = dbValidation.database;
117
+ // Validate table name
118
+ if (!this.security.validateIdentifier(table_name).valid) {
119
+ return { status: 'error', error: 'Invalid table name' };
120
+ }
121
+ let query = `CHECK TABLE \`${database}\`.\`${table_name}\``;
122
+ if (check_type) {
123
+ query += ` ${check_type}`;
124
+ }
125
+ const results = await this.db.query(query);
126
+ return {
127
+ status: 'success',
128
+ data: results[0],
129
+ queryLog: this.db.getFormattedQueryLogs(1)
130
+ };
131
+ }
132
+ catch (error) {
133
+ return {
134
+ status: 'error',
135
+ error: error.message,
136
+ queryLog: this.db.getFormattedQueryLogs(1)
137
+ };
138
+ }
139
+ }
140
+ /**
141
+ * Repair table (MyISAM, ARCHIVE, CSV only)
142
+ */
143
+ async repairTable(params) {
144
+ try {
145
+ const dbValidation = this.validateDatabaseAccess(params?.database);
146
+ if (!dbValidation.valid) {
147
+ return { status: 'error', error: dbValidation.error };
148
+ }
149
+ const { table_name, quick = false, extended = false, use_frm = false } = params;
150
+ const database = dbValidation.database;
151
+ // Validate table name
152
+ if (!this.security.validateIdentifier(table_name).valid) {
153
+ return { status: 'error', error: 'Invalid table name' };
154
+ }
155
+ let query = `REPAIR TABLE \`${database}\`.\`${table_name}\``;
156
+ const options = [];
157
+ if (quick)
158
+ options.push('QUICK');
159
+ if (extended)
160
+ options.push('EXTENDED');
161
+ if (use_frm)
162
+ options.push('USE_FRM');
163
+ if (options.length > 0) {
164
+ query += ` ${options.join(' ')}`;
165
+ }
166
+ const results = await this.db.query(query);
167
+ return {
168
+ status: 'success',
169
+ data: results[0],
170
+ queryLog: this.db.getFormattedQueryLogs(1)
171
+ };
172
+ }
173
+ catch (error) {
174
+ return {
175
+ status: 'error',
176
+ error: error.message,
177
+ queryLog: this.db.getFormattedQueryLogs(1)
178
+ };
179
+ }
180
+ }
181
+ /**
182
+ * Truncate table (remove all rows quickly)
183
+ */
184
+ async truncateTable(params) {
185
+ try {
186
+ const dbValidation = this.validateDatabaseAccess(params?.database);
187
+ if (!dbValidation.valid) {
188
+ return { status: 'error', error: dbValidation.error };
189
+ }
190
+ const { table_name } = params;
191
+ const database = dbValidation.database;
192
+ // Validate table name
193
+ if (!this.security.validateIdentifier(table_name).valid) {
194
+ return { status: 'error', error: 'Invalid table name' };
195
+ }
196
+ const query = `TRUNCATE TABLE \`${database}\`.\`${table_name}\``;
197
+ await this.db.query(query);
198
+ return {
199
+ status: 'success',
200
+ message: `Table '${table_name}' truncated successfully`,
201
+ queryLog: this.db.getFormattedQueryLogs(1)
202
+ };
203
+ }
204
+ catch (error) {
205
+ return {
206
+ status: 'error',
207
+ error: error.message,
208
+ queryLog: this.db.getFormattedQueryLogs(1)
209
+ };
210
+ }
211
+ }
212
+ /**
213
+ * Get table status and statistics
214
+ */
215
+ async getTableStatus(params) {
216
+ try {
217
+ const dbValidation = this.validateDatabaseAccess(params?.database);
218
+ if (!dbValidation.valid) {
219
+ return { status: 'error', error: dbValidation.error };
220
+ }
221
+ const { table_name } = params;
222
+ const database = dbValidation.database;
223
+ let query = `SHOW TABLE STATUS FROM \`${database}\``;
224
+ if (table_name) {
225
+ if (!this.security.validateIdentifier(table_name).valid) {
226
+ return { status: 'error', error: 'Invalid table name' };
227
+ }
228
+ query += ` LIKE '${table_name}'`;
229
+ }
230
+ const results = await this.db.query(query);
231
+ // Format results for better readability
232
+ const formattedResults = results.map(row => ({
233
+ table_name: row.Name,
234
+ engine: row.Engine,
235
+ version: row.Version,
236
+ row_format: row.Row_format,
237
+ rows: row.Rows,
238
+ avg_row_length: row.Avg_row_length,
239
+ data_length: row.Data_length,
240
+ max_data_length: row.Max_data_length,
241
+ index_length: row.Index_length,
242
+ data_free: row.Data_free,
243
+ auto_increment: row.Auto_increment,
244
+ create_time: row.Create_time,
245
+ update_time: row.Update_time,
246
+ check_time: row.Check_time,
247
+ collation: row.Collation,
248
+ checksum: row.Checksum,
249
+ create_options: row.Create_options,
250
+ comment: row.Comment
251
+ }));
252
+ return {
253
+ status: 'success',
254
+ data: table_name ? formattedResults[0] : formattedResults,
255
+ queryLog: this.db.getFormattedQueryLogs(1)
256
+ };
257
+ }
258
+ catch (error) {
259
+ return {
260
+ status: 'error',
261
+ error: error.message,
262
+ queryLog: this.db.getFormattedQueryLogs(1)
263
+ };
264
+ }
265
+ }
266
+ /**
267
+ * Flush table (close and reopen)
268
+ */
269
+ async flushTable(params) {
270
+ try {
271
+ const dbValidation = this.validateDatabaseAccess(params?.database);
272
+ if (!dbValidation.valid) {
273
+ return { status: 'error', error: dbValidation.error };
274
+ }
275
+ const { table_name, with_read_lock = false } = params;
276
+ const database = dbValidation.database;
277
+ let query;
278
+ if (table_name) {
279
+ if (!this.security.validateIdentifier(table_name).valid) {
280
+ return { status: 'error', error: 'Invalid table name' };
281
+ }
282
+ query = `FLUSH TABLES \`${database}\`.\`${table_name}\``;
283
+ if (with_read_lock) {
284
+ query += ' WITH READ LOCK';
285
+ }
286
+ }
287
+ else {
288
+ query = with_read_lock ? 'FLUSH TABLES WITH READ LOCK' : 'FLUSH TABLES';
289
+ }
290
+ await this.db.query(query);
291
+ return {
292
+ status: 'success',
293
+ message: table_name
294
+ ? `Table '${table_name}' flushed successfully${with_read_lock ? ' with read lock' : ''}`
295
+ : `All tables flushed successfully${with_read_lock ? ' with read lock' : ''}`,
296
+ queryLog: this.db.getFormattedQueryLogs(1)
297
+ };
298
+ }
299
+ catch (error) {
300
+ return {
301
+ status: 'error',
302
+ error: error.message,
303
+ queryLog: this.db.getFormattedQueryLogs(1)
304
+ };
305
+ }
306
+ }
307
+ /**
308
+ * Get table size information
309
+ */
310
+ async getTableSize(params) {
311
+ try {
312
+ const dbValidation = this.validateDatabaseAccess(params?.database);
313
+ if (!dbValidation.valid) {
314
+ return { status: 'error', error: dbValidation.error };
315
+ }
316
+ const { table_name } = params;
317
+ const database = dbValidation.database;
318
+ let query = `
319
+ SELECT
320
+ TABLE_NAME as table_name,
321
+ TABLE_ROWS as row_count,
322
+ DATA_LENGTH as data_size_bytes,
323
+ INDEX_LENGTH as index_size_bytes,
324
+ (DATA_LENGTH + INDEX_LENGTH) as total_size_bytes,
325
+ ROUND((DATA_LENGTH + INDEX_LENGTH) / 1024 / 1024, 2) as total_size_mb,
326
+ DATA_FREE as free_space_bytes,
327
+ ENGINE as engine,
328
+ TABLE_COLLATION as collation
329
+ FROM INFORMATION_SCHEMA.TABLES
330
+ WHERE TABLE_SCHEMA = ?
331
+ `;
332
+ const queryParams = [database];
333
+ if (table_name) {
334
+ if (!this.security.validateIdentifier(table_name).valid) {
335
+ return { status: 'error', error: 'Invalid table name' };
336
+ }
337
+ query += ` AND TABLE_NAME = ?`;
338
+ queryParams.push(table_name);
339
+ }
340
+ query += ` ORDER BY (DATA_LENGTH + INDEX_LENGTH) DESC`;
341
+ const results = await this.db.query(query, queryParams);
342
+ // Calculate totals if getting all tables
343
+ let totalStats = null;
344
+ if (!table_name && results.length > 0) {
345
+ totalStats = {
346
+ total_tables: results.length,
347
+ total_rows: results.reduce((sum, r) => sum + (r.row_count || 0), 0),
348
+ total_data_size_bytes: results.reduce((sum, r) => sum + (r.data_size_bytes || 0), 0),
349
+ total_index_size_bytes: results.reduce((sum, r) => sum + (r.index_size_bytes || 0), 0),
350
+ total_size_mb: results.reduce((sum, r) => sum + (parseFloat(r.total_size_mb) || 0), 0).toFixed(2)
351
+ };
352
+ }
353
+ return {
354
+ status: 'success',
355
+ data: {
356
+ tables: table_name ? results[0] : results,
357
+ ...(totalStats && { summary: totalStats })
358
+ },
359
+ queryLog: this.db.getFormattedQueryLogs(1)
360
+ };
361
+ }
362
+ catch (error) {
363
+ return {
364
+ status: 'error',
365
+ error: error.message,
366
+ queryLog: this.db.getFormattedQueryLogs(1)
367
+ };
368
+ }
369
+ }
370
+ }
371
+ exports.MaintenanceTools = MaintenanceTools;
@@ -0,0 +1,106 @@
1
+ import { SecurityLayer } from '../security/securityLayer';
2
+ export declare class ProcessTools {
3
+ private db;
4
+ private security;
5
+ constructor(security: SecurityLayer);
6
+ /**
7
+ * Show all running processes/connections
8
+ */
9
+ showProcessList(params?: {
10
+ full?: boolean;
11
+ }): Promise<{
12
+ status: string;
13
+ data?: any[];
14
+ error?: string;
15
+ queryLog?: string;
16
+ }>;
17
+ /**
18
+ * Kill a specific process/connection
19
+ */
20
+ killProcess(params: {
21
+ process_id: number;
22
+ type?: 'CONNECTION' | 'QUERY';
23
+ }): Promise<{
24
+ status: string;
25
+ message?: string;
26
+ error?: string;
27
+ queryLog?: string;
28
+ }>;
29
+ /**
30
+ * Show server status variables
31
+ */
32
+ showStatus(params?: {
33
+ like?: string;
34
+ global?: boolean;
35
+ }): Promise<{
36
+ status: string;
37
+ data?: any;
38
+ error?: string;
39
+ queryLog?: string;
40
+ }>;
41
+ /**
42
+ * Show server variables
43
+ */
44
+ showVariables(params?: {
45
+ like?: string;
46
+ global?: boolean;
47
+ }): Promise<{
48
+ status: string;
49
+ data?: any;
50
+ error?: string;
51
+ queryLog?: string;
52
+ }>;
53
+ /**
54
+ * Explain a query (show execution plan)
55
+ */
56
+ explainQuery(params: {
57
+ query: string;
58
+ format?: 'TRADITIONAL' | 'JSON' | 'TREE';
59
+ analyze?: boolean;
60
+ }): Promise<{
61
+ status: string;
62
+ data?: any;
63
+ error?: string;
64
+ queryLog?: string;
65
+ }>;
66
+ /**
67
+ * Show engine status (InnoDB, etc.)
68
+ */
69
+ showEngineStatus(params?: {
70
+ engine?: string;
71
+ }): Promise<{
72
+ status: string;
73
+ data?: any;
74
+ error?: string;
75
+ queryLog?: string;
76
+ }>;
77
+ /**
78
+ * Get server information
79
+ */
80
+ getServerInfo(): Promise<{
81
+ status: string;
82
+ data?: any;
83
+ error?: string;
84
+ queryLog?: string;
85
+ }>;
86
+ /**
87
+ * Show binary logs
88
+ */
89
+ showBinaryLogs(): Promise<{
90
+ status: string;
91
+ data?: any[];
92
+ error?: string;
93
+ queryLog?: string;
94
+ }>;
95
+ /**
96
+ * Show master/replica status
97
+ */
98
+ showReplicationStatus(params?: {
99
+ type?: 'MASTER' | 'REPLICA' | 'SLAVE';
100
+ }): Promise<{
101
+ status: string;
102
+ data?: any;
103
+ error?: string;
104
+ queryLog?: string;
105
+ }>;
106
+ }