@berthojoris/mcp-mysql-server 1.4.15 → 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,351 @@
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.FunctionTools = void 0;
7
+ const connection_1 = __importDefault(require("../db/connection"));
8
+ const config_1 = require("../config/config");
9
+ class FunctionTools {
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
+ * List all functions in the current database
46
+ */
47
+ async listFunctions(params) {
48
+ try {
49
+ const dbValidation = this.validateDatabaseAccess(params?.database);
50
+ if (!dbValidation.valid) {
51
+ return { status: 'error', error: dbValidation.error };
52
+ }
53
+ const database = dbValidation.database;
54
+ const query = `
55
+ SELECT
56
+ ROUTINE_NAME as function_name,
57
+ DATA_TYPE as return_type,
58
+ DTD_IDENTIFIER as return_type_full,
59
+ ROUTINE_DEFINITION as definition,
60
+ IS_DETERMINISTIC as is_deterministic,
61
+ SQL_DATA_ACCESS as data_access,
62
+ SECURITY_TYPE as security_type,
63
+ DEFINER as definer,
64
+ CREATED as created,
65
+ LAST_ALTERED as last_altered,
66
+ ROUTINE_COMMENT as comment
67
+ FROM INFORMATION_SCHEMA.ROUTINES
68
+ WHERE ROUTINE_SCHEMA = ? AND ROUTINE_TYPE = 'FUNCTION'
69
+ ORDER BY ROUTINE_NAME
70
+ `;
71
+ const results = await this.db.query(query, [database]);
72
+ return {
73
+ status: 'success',
74
+ data: results,
75
+ queryLog: this.db.getFormattedQueryLogs(1)
76
+ };
77
+ }
78
+ catch (error) {
79
+ return {
80
+ status: 'error',
81
+ error: error.message,
82
+ queryLog: this.db.getFormattedQueryLogs(1)
83
+ };
84
+ }
85
+ }
86
+ /**
87
+ * Get detailed information about a specific function
88
+ */
89
+ async getFunctionInfo(params) {
90
+ try {
91
+ const dbValidation = this.validateDatabaseAccess(params?.database);
92
+ if (!dbValidation.valid) {
93
+ return { status: 'error', error: dbValidation.error };
94
+ }
95
+ const { function_name } = params;
96
+ const database = dbValidation.database;
97
+ // Validate function name
98
+ const identifierValidation = this.security.validateIdentifier(function_name);
99
+ if (!identifierValidation.valid) {
100
+ return { status: 'error', error: identifierValidation.error || 'Invalid function name' };
101
+ }
102
+ // Get function information
103
+ const functionQuery = `
104
+ SELECT
105
+ ROUTINE_NAME as function_name,
106
+ DATA_TYPE as return_type,
107
+ DTD_IDENTIFIER as return_type_full,
108
+ ROUTINE_DEFINITION as definition,
109
+ IS_DETERMINISTIC as is_deterministic,
110
+ SQL_DATA_ACCESS as data_access,
111
+ SECURITY_TYPE as security_type,
112
+ DEFINER as definer,
113
+ CREATED as created,
114
+ LAST_ALTERED as last_altered,
115
+ ROUTINE_COMMENT as comment,
116
+ SQL_MODE as sql_mode,
117
+ CHARACTER_SET_CLIENT as charset,
118
+ COLLATION_CONNECTION as collation,
119
+ DATABASE_COLLATION as db_collation
120
+ FROM INFORMATION_SCHEMA.ROUTINES
121
+ WHERE ROUTINE_SCHEMA = ? AND ROUTINE_NAME = ? AND ROUTINE_TYPE = 'FUNCTION'
122
+ `;
123
+ // Get function parameters
124
+ const parametersQuery = `
125
+ SELECT
126
+ PARAMETER_NAME as name,
127
+ DATA_TYPE as data_type,
128
+ DTD_IDENTIFIER as data_type_full,
129
+ CHARACTER_MAXIMUM_LENGTH as max_length,
130
+ NUMERIC_PRECISION as numeric_precision,
131
+ NUMERIC_SCALE as numeric_scale,
132
+ ORDINAL_POSITION as position
133
+ FROM INFORMATION_SCHEMA.PARAMETERS
134
+ WHERE SPECIFIC_SCHEMA = ? AND SPECIFIC_NAME = ? AND PARAMETER_MODE IS NOT NULL
135
+ ORDER BY ORDINAL_POSITION
136
+ `;
137
+ const [functionInfo, parameters] = await Promise.all([
138
+ this.db.query(functionQuery, [database, function_name]),
139
+ this.db.query(parametersQuery, [database, function_name])
140
+ ]);
141
+ if (functionInfo.length === 0) {
142
+ return {
143
+ status: 'error',
144
+ error: `Function '${function_name}' not found in database '${database}'`,
145
+ queryLog: this.db.getFormattedQueryLogs(2)
146
+ };
147
+ }
148
+ return {
149
+ status: 'success',
150
+ data: {
151
+ ...functionInfo[0],
152
+ parameters: parameters
153
+ },
154
+ queryLog: this.db.getFormattedQueryLogs(2)
155
+ };
156
+ }
157
+ catch (error) {
158
+ return {
159
+ status: 'error',
160
+ error: error.message,
161
+ queryLog: this.db.getFormattedQueryLogs(2)
162
+ };
163
+ }
164
+ }
165
+ /**
166
+ * Create a new function
167
+ */
168
+ async createFunction(params) {
169
+ try {
170
+ const dbValidation = this.validateDatabaseAccess(params?.database);
171
+ if (!dbValidation.valid) {
172
+ return { status: 'error', error: dbValidation.error };
173
+ }
174
+ const { function_name, parameters = [], returns, body, deterministic = false, data_access, security, comment } = params;
175
+ const database = dbValidation.database;
176
+ // Validate function name
177
+ const identifierValidation = this.security.validateIdentifier(function_name);
178
+ if (!identifierValidation.valid) {
179
+ return { status: 'error', error: identifierValidation.error || 'Invalid function name' };
180
+ }
181
+ // Build parameter list
182
+ const parameterList = parameters.map(param => {
183
+ if (!this.security.validateIdentifier(param.name).valid) {
184
+ throw new Error(`Invalid parameter name: ${param.name}`);
185
+ }
186
+ return `\`${param.name}\` ${param.data_type}`;
187
+ }).join(', ');
188
+ // Build CREATE FUNCTION statement
189
+ let createQuery = `CREATE FUNCTION \`${database}\`.\`${function_name}\`(${parameterList})\n`;
190
+ createQuery += `RETURNS ${returns}\n`;
191
+ if (deterministic) {
192
+ createQuery += `DETERMINISTIC\n`;
193
+ }
194
+ else {
195
+ createQuery += `NOT DETERMINISTIC\n`;
196
+ }
197
+ if (data_access) {
198
+ createQuery += `${data_access}\n`;
199
+ }
200
+ if (security) {
201
+ createQuery += `SQL SECURITY ${security}\n`;
202
+ }
203
+ if (comment) {
204
+ createQuery += `COMMENT '${comment.replace(/'/g, "''")}'\n`;
205
+ }
206
+ // Check if body already contains BEGIN/END
207
+ const trimmedBody = body.trim();
208
+ if (trimmedBody.toUpperCase().startsWith('BEGIN') && trimmedBody.toUpperCase().endsWith('END')) {
209
+ createQuery += body;
210
+ }
211
+ else if (trimmedBody.toUpperCase().startsWith('RETURN')) {
212
+ // Simple one-liner function
213
+ createQuery += body;
214
+ }
215
+ else {
216
+ createQuery += `BEGIN\n${body}\nEND`;
217
+ }
218
+ await this.db.query(createQuery);
219
+ return {
220
+ status: 'success',
221
+ data: {
222
+ message: `Function '${function_name}' created successfully`,
223
+ function_name,
224
+ database
225
+ },
226
+ queryLog: this.db.getFormattedQueryLogs(1)
227
+ };
228
+ }
229
+ catch (error) {
230
+ return {
231
+ status: 'error',
232
+ error: error.message,
233
+ queryLog: this.db.getFormattedQueryLogs(1)
234
+ };
235
+ }
236
+ }
237
+ /**
238
+ * Drop a function
239
+ */
240
+ async dropFunction(params) {
241
+ try {
242
+ const dbValidation = this.validateDatabaseAccess(params?.database);
243
+ if (!dbValidation.valid) {
244
+ return { status: 'error', error: dbValidation.error };
245
+ }
246
+ const { function_name, if_exists = false } = params;
247
+ const database = dbValidation.database;
248
+ // Validate function name
249
+ const identifierValidation = this.security.validateIdentifier(function_name);
250
+ if (!identifierValidation.valid) {
251
+ return { status: 'error', error: identifierValidation.error || 'Invalid function name' };
252
+ }
253
+ const dropQuery = `DROP FUNCTION ${if_exists ? 'IF EXISTS' : ''} \`${database}\`.\`${function_name}\``;
254
+ await this.db.query(dropQuery);
255
+ return {
256
+ status: 'success',
257
+ message: `Function '${function_name}' dropped successfully`,
258
+ queryLog: this.db.getFormattedQueryLogs(1)
259
+ };
260
+ }
261
+ catch (error) {
262
+ return {
263
+ status: 'error',
264
+ error: error.message,
265
+ queryLog: this.db.getFormattedQueryLogs(1)
266
+ };
267
+ }
268
+ }
269
+ /**
270
+ * Show the CREATE statement for a function
271
+ */
272
+ async showCreateFunction(params) {
273
+ try {
274
+ const dbValidation = this.validateDatabaseAccess(params?.database);
275
+ if (!dbValidation.valid) {
276
+ return { status: 'error', error: dbValidation.error };
277
+ }
278
+ const { function_name } = params;
279
+ const database = dbValidation.database;
280
+ // Validate function name
281
+ const identifierValidation = this.security.validateIdentifier(function_name);
282
+ if (!identifierValidation.valid) {
283
+ return { status: 'error', error: identifierValidation.error || 'Invalid function name' };
284
+ }
285
+ const query = `SHOW CREATE FUNCTION \`${database}\`.\`${function_name}\``;
286
+ const results = await this.db.query(query);
287
+ if (results.length === 0) {
288
+ return {
289
+ status: 'error',
290
+ error: `Function '${function_name}' not found`,
291
+ queryLog: this.db.getFormattedQueryLogs(1)
292
+ };
293
+ }
294
+ return {
295
+ status: 'success',
296
+ data: results[0],
297
+ queryLog: this.db.getFormattedQueryLogs(1)
298
+ };
299
+ }
300
+ catch (error) {
301
+ return {
302
+ status: 'error',
303
+ error: error.message,
304
+ queryLog: this.db.getFormattedQueryLogs(1)
305
+ };
306
+ }
307
+ }
308
+ /**
309
+ * Execute a function and return its result
310
+ */
311
+ async executeFunction(params) {
312
+ try {
313
+ const dbValidation = this.validateDatabaseAccess(params?.database);
314
+ if (!dbValidation.valid) {
315
+ return { status: 'error', error: dbValidation.error };
316
+ }
317
+ const { function_name, parameters = [] } = params;
318
+ const database = dbValidation.database;
319
+ // Validate function name
320
+ const identifierValidation = this.security.validateIdentifier(function_name);
321
+ if (!identifierValidation.valid) {
322
+ return { status: 'error', error: identifierValidation.error || 'Invalid function name' };
323
+ }
324
+ // Validate parameters
325
+ const paramValidation = this.security.validateParameters(parameters);
326
+ if (!paramValidation.valid) {
327
+ return { status: 'error', error: `Parameter validation failed: ${paramValidation.error}` };
328
+ }
329
+ // Build SELECT query to call the function
330
+ const placeholders = parameters.map(() => '?').join(', ');
331
+ const selectQuery = `SELECT \`${database}\`.\`${function_name}\`(${placeholders}) AS result`;
332
+ const results = await this.db.query(selectQuery, paramValidation.sanitizedParams);
333
+ return {
334
+ status: 'success',
335
+ data: {
336
+ function_name,
337
+ result: results[0]?.result
338
+ },
339
+ queryLog: this.db.getFormattedQueryLogs(1)
340
+ };
341
+ }
342
+ catch (error) {
343
+ return {
344
+ status: 'error',
345
+ error: error.message,
346
+ queryLog: this.db.getFormattedQueryLogs(1)
347
+ };
348
+ }
349
+ }
350
+ }
351
+ exports.FunctionTools = FunctionTools;
@@ -0,0 +1,81 @@
1
+ import { SecurityLayer } from '../security/securityLayer';
2
+ export declare class IndexTools {
3
+ private db;
4
+ private security;
5
+ constructor(security: SecurityLayer);
6
+ /**
7
+ * Validate database access - ensures only the connected database can be accessed
8
+ */
9
+ private validateDatabaseAccess;
10
+ /**
11
+ * List all indexes for a table
12
+ */
13
+ listIndexes(params: {
14
+ table_name: string;
15
+ database?: string;
16
+ }): Promise<{
17
+ status: string;
18
+ data?: any[];
19
+ error?: string;
20
+ queryLog?: string;
21
+ }>;
22
+ /**
23
+ * Get detailed information about a specific index
24
+ */
25
+ getIndexInfo(params: {
26
+ table_name: string;
27
+ index_name: string;
28
+ database?: string;
29
+ }): Promise<{
30
+ status: string;
31
+ data?: any;
32
+ error?: string;
33
+ queryLog?: string;
34
+ }>;
35
+ /**
36
+ * Create a new index
37
+ */
38
+ createIndex(params: {
39
+ table_name: string;
40
+ index_name: string;
41
+ columns: Array<string | {
42
+ column: string;
43
+ length?: number;
44
+ order?: 'ASC' | 'DESC';
45
+ }>;
46
+ unique?: boolean;
47
+ index_type?: 'BTREE' | 'HASH' | 'FULLTEXT' | 'SPATIAL';
48
+ comment?: string;
49
+ database?: string;
50
+ }): Promise<{
51
+ status: string;
52
+ data?: any;
53
+ error?: string;
54
+ queryLog?: string;
55
+ }>;
56
+ /**
57
+ * Drop an index
58
+ */
59
+ dropIndex(params: {
60
+ table_name: string;
61
+ index_name: string;
62
+ database?: string;
63
+ }): Promise<{
64
+ status: string;
65
+ message?: string;
66
+ error?: string;
67
+ queryLog?: string;
68
+ }>;
69
+ /**
70
+ * Analyze index usage and statistics
71
+ */
72
+ analyzeIndex(params: {
73
+ table_name: string;
74
+ database?: string;
75
+ }): Promise<{
76
+ status: string;
77
+ data?: any;
78
+ error?: string;
79
+ queryLog?: string;
80
+ }>;
81
+ }