@berthojoris/mcp-mysql-server 1.10.0 → 1.10.1

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.
@@ -3,12 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.featureConfig = exports.FeatureConfig = exports.toolCategoryMap = exports.ToolCategory = void 0;
6
+ exports.featureConfig = exports.FeatureConfig = exports.toolDocCategoryMap = exports.toolCategoryMap = exports.DocCategory = exports.ToolCategory = void 0;
7
7
  const dotenv_1 = __importDefault(require("dotenv"));
8
8
  // Load environment variables
9
9
  dotenv_1.default.config();
10
10
  /**
11
- * Available MCP tool categories
11
+ * Available MCP tool categories (Legacy - for backward compatibility)
12
12
  */
13
13
  var ToolCategory;
14
14
  (function (ToolCategory) {
@@ -24,7 +24,36 @@ var ToolCategory;
24
24
  ToolCategory["PROCEDURE"] = "procedure";
25
25
  })(ToolCategory || (exports.ToolCategory = ToolCategory = {}));
26
26
  /**
27
- * Map of tool names to their categories
27
+ * Documentation categories from README (21 categories)
28
+ * More intuitive and matches user mental model
29
+ */
30
+ var DocCategory;
31
+ (function (DocCategory) {
32
+ DocCategory["DATABASE_DISCOVERY"] = "database_discovery";
33
+ DocCategory["CRUD_OPERATIONS"] = "crud_operations";
34
+ DocCategory["BULK_OPERATIONS"] = "bulk_operations";
35
+ DocCategory["CUSTOM_QUERIES"] = "custom_queries";
36
+ DocCategory["SCHEMA_MANAGEMENT"] = "schema_management";
37
+ DocCategory["UTILITIES"] = "utilities";
38
+ DocCategory["TRANSACTION_MANAGEMENT"] = "transaction_management";
39
+ DocCategory["STORED_PROCEDURES"] = "stored_procedures";
40
+ DocCategory["VIEWS_MANAGEMENT"] = "views_management";
41
+ DocCategory["TRIGGERS_MANAGEMENT"] = "triggers_management";
42
+ DocCategory["FUNCTIONS_MANAGEMENT"] = "functions_management";
43
+ DocCategory["INDEX_MANAGEMENT"] = "index_management";
44
+ DocCategory["CONSTRAINT_MANAGEMENT"] = "constraint_management";
45
+ DocCategory["TABLE_MAINTENANCE"] = "table_maintenance";
46
+ DocCategory["SERVER_MANAGEMENT"] = "server_management";
47
+ DocCategory["PERFORMANCE_MONITORING"] = "performance_monitoring";
48
+ DocCategory["CACHE_MANAGEMENT"] = "cache_management";
49
+ DocCategory["QUERY_OPTIMIZATION"] = "query_optimization";
50
+ DocCategory["BACKUP_RESTORE"] = "backup_restore";
51
+ DocCategory["IMPORT_EXPORT"] = "import_export";
52
+ DocCategory["DATA_MIGRATION"] = "data_migration";
53
+ DocCategory["SCHEMA_MIGRATIONS"] = "schema_migrations";
54
+ })(DocCategory || (exports.DocCategory = DocCategory = {}));
55
+ /**
56
+ * Map of tool names to their legacy categories
28
57
  */
29
58
  exports.toolCategoryMap = {
30
59
  // Database tools
@@ -169,124 +198,383 @@ exports.toolCategoryMap = {
169
198
  getDatabaseHealthCheck: ToolCategory.UTILITY,
170
199
  resetPerformanceStats: ToolCategory.UTILITY,
171
200
  };
201
+ /**
202
+ * Map of tool names to their documentation categories (New Enhanced System)
203
+ */
204
+ exports.toolDocCategoryMap = {
205
+ // Database Discovery
206
+ listDatabases: DocCategory.DATABASE_DISCOVERY,
207
+ listTables: DocCategory.DATABASE_DISCOVERY,
208
+ readTableSchema: DocCategory.DATABASE_DISCOVERY,
209
+ getTableRelationships: DocCategory.DATABASE_DISCOVERY,
210
+ // CRUD Operations
211
+ createRecord: DocCategory.CRUD_OPERATIONS,
212
+ readRecords: DocCategory.CRUD_OPERATIONS,
213
+ updateRecord: DocCategory.CRUD_OPERATIONS,
214
+ deleteRecord: DocCategory.CRUD_OPERATIONS,
215
+ // Bulk Operations
216
+ bulkInsert: DocCategory.BULK_OPERATIONS,
217
+ bulkUpdate: DocCategory.BULK_OPERATIONS,
218
+ bulkDelete: DocCategory.BULK_OPERATIONS,
219
+ // Custom Queries
220
+ runQuery: DocCategory.CUSTOM_QUERIES,
221
+ executeSql: DocCategory.CUSTOM_QUERIES,
222
+ // Schema Management (DDL)
223
+ createTable: DocCategory.SCHEMA_MANAGEMENT,
224
+ alterTable: DocCategory.SCHEMA_MANAGEMENT,
225
+ dropTable: DocCategory.SCHEMA_MANAGEMENT,
226
+ executeDdl: DocCategory.SCHEMA_MANAGEMENT,
227
+ // Utilities
228
+ testConnection: DocCategory.UTILITIES,
229
+ describeConnection: DocCategory.UTILITIES,
230
+ exportTableToCSV: DocCategory.UTILITIES,
231
+ exportQueryToCSV: DocCategory.UTILITIES,
232
+ // Transaction Management
233
+ beginTransaction: DocCategory.TRANSACTION_MANAGEMENT,
234
+ commitTransaction: DocCategory.TRANSACTION_MANAGEMENT,
235
+ rollbackTransaction: DocCategory.TRANSACTION_MANAGEMENT,
236
+ getTransactionStatus: DocCategory.TRANSACTION_MANAGEMENT,
237
+ executeInTransaction: DocCategory.TRANSACTION_MANAGEMENT,
238
+ // Stored Procedures
239
+ listStoredProcedures: DocCategory.STORED_PROCEDURES,
240
+ getStoredProcedureInfo: DocCategory.STORED_PROCEDURES,
241
+ executeStoredProcedure: DocCategory.STORED_PROCEDURES,
242
+ createStoredProcedure: DocCategory.STORED_PROCEDURES,
243
+ dropStoredProcedure: DocCategory.STORED_PROCEDURES,
244
+ showCreateProcedure: DocCategory.STORED_PROCEDURES,
245
+ // Views Management
246
+ listViews: DocCategory.VIEWS_MANAGEMENT,
247
+ getViewInfo: DocCategory.VIEWS_MANAGEMENT,
248
+ createView: DocCategory.VIEWS_MANAGEMENT,
249
+ alterView: DocCategory.VIEWS_MANAGEMENT,
250
+ dropView: DocCategory.VIEWS_MANAGEMENT,
251
+ showCreateView: DocCategory.VIEWS_MANAGEMENT,
252
+ // Triggers Management
253
+ listTriggers: DocCategory.TRIGGERS_MANAGEMENT,
254
+ getTriggerInfo: DocCategory.TRIGGERS_MANAGEMENT,
255
+ createTrigger: DocCategory.TRIGGERS_MANAGEMENT,
256
+ dropTrigger: DocCategory.TRIGGERS_MANAGEMENT,
257
+ showCreateTrigger: DocCategory.TRIGGERS_MANAGEMENT,
258
+ // Functions Management
259
+ listFunctions: DocCategory.FUNCTIONS_MANAGEMENT,
260
+ getFunctionInfo: DocCategory.FUNCTIONS_MANAGEMENT,
261
+ createFunction: DocCategory.FUNCTIONS_MANAGEMENT,
262
+ dropFunction: DocCategory.FUNCTIONS_MANAGEMENT,
263
+ showCreateFunction: DocCategory.FUNCTIONS_MANAGEMENT,
264
+ executeFunction: DocCategory.FUNCTIONS_MANAGEMENT,
265
+ // Index Management
266
+ listIndexes: DocCategory.INDEX_MANAGEMENT,
267
+ getIndexInfo: DocCategory.INDEX_MANAGEMENT,
268
+ createIndex: DocCategory.INDEX_MANAGEMENT,
269
+ dropIndex: DocCategory.INDEX_MANAGEMENT,
270
+ analyzeIndex: DocCategory.INDEX_MANAGEMENT,
271
+ // Constraint Management
272
+ listForeignKeys: DocCategory.CONSTRAINT_MANAGEMENT,
273
+ listConstraints: DocCategory.CONSTRAINT_MANAGEMENT,
274
+ addForeignKey: DocCategory.CONSTRAINT_MANAGEMENT,
275
+ dropForeignKey: DocCategory.CONSTRAINT_MANAGEMENT,
276
+ addUniqueConstraint: DocCategory.CONSTRAINT_MANAGEMENT,
277
+ dropConstraint: DocCategory.CONSTRAINT_MANAGEMENT,
278
+ addCheckConstraint: DocCategory.CONSTRAINT_MANAGEMENT,
279
+ // Table Maintenance
280
+ analyzeTable: DocCategory.TABLE_MAINTENANCE,
281
+ optimizeTable: DocCategory.TABLE_MAINTENANCE,
282
+ checkTable: DocCategory.TABLE_MAINTENANCE,
283
+ repairTable: DocCategory.TABLE_MAINTENANCE,
284
+ truncateTable: DocCategory.TABLE_MAINTENANCE,
285
+ getTableStatus: DocCategory.TABLE_MAINTENANCE,
286
+ flushTable: DocCategory.TABLE_MAINTENANCE,
287
+ getTableSize: DocCategory.TABLE_MAINTENANCE,
288
+ // Server Management
289
+ showProcessList: DocCategory.SERVER_MANAGEMENT,
290
+ killProcess: DocCategory.SERVER_MANAGEMENT,
291
+ showStatus: DocCategory.SERVER_MANAGEMENT,
292
+ showVariables: DocCategory.SERVER_MANAGEMENT,
293
+ explainQuery: DocCategory.SERVER_MANAGEMENT,
294
+ showEngineStatus: DocCategory.SERVER_MANAGEMENT,
295
+ getServerInfo: DocCategory.SERVER_MANAGEMENT,
296
+ showBinaryLogs: DocCategory.SERVER_MANAGEMENT,
297
+ showReplicationStatus: DocCategory.SERVER_MANAGEMENT,
298
+ // Performance Monitoring
299
+ getPerformanceMetrics: DocCategory.PERFORMANCE_MONITORING,
300
+ getTopQueriesByTime: DocCategory.PERFORMANCE_MONITORING,
301
+ getTopQueriesByCount: DocCategory.PERFORMANCE_MONITORING,
302
+ getSlowQueries: DocCategory.PERFORMANCE_MONITORING,
303
+ getTableIOStats: DocCategory.PERFORMANCE_MONITORING,
304
+ getIndexUsageStats: DocCategory.PERFORMANCE_MONITORING,
305
+ getUnusedIndexes: DocCategory.PERFORMANCE_MONITORING,
306
+ getConnectionPoolStats: DocCategory.PERFORMANCE_MONITORING,
307
+ getDatabaseHealthCheck: DocCategory.PERFORMANCE_MONITORING,
308
+ resetPerformanceStats: DocCategory.PERFORMANCE_MONITORING,
309
+ // Cache Management
310
+ getCacheStats: DocCategory.CACHE_MANAGEMENT,
311
+ getCacheConfig: DocCategory.CACHE_MANAGEMENT,
312
+ configureCacheSettings: DocCategory.CACHE_MANAGEMENT,
313
+ clearCache: DocCategory.CACHE_MANAGEMENT,
314
+ invalidateCacheForTable: DocCategory.CACHE_MANAGEMENT,
315
+ // Query Optimization
316
+ analyzeQuery: DocCategory.QUERY_OPTIMIZATION,
317
+ getOptimizationHints: DocCategory.QUERY_OPTIMIZATION,
318
+ // Backup & Restore
319
+ backupTable: DocCategory.BACKUP_RESTORE,
320
+ backupDatabase: DocCategory.BACKUP_RESTORE,
321
+ restoreFromSql: DocCategory.BACKUP_RESTORE,
322
+ getCreateTableStatement: DocCategory.BACKUP_RESTORE,
323
+ getDatabaseSchema: DocCategory.BACKUP_RESTORE,
324
+ // Import/Export
325
+ exportTableToJSON: DocCategory.IMPORT_EXPORT,
326
+ exportQueryToJSON: DocCategory.IMPORT_EXPORT,
327
+ exportTableToSql: DocCategory.IMPORT_EXPORT,
328
+ importFromCSV: DocCategory.IMPORT_EXPORT,
329
+ importFromJSON: DocCategory.IMPORT_EXPORT,
330
+ // Data Migration
331
+ copyTableData: DocCategory.DATA_MIGRATION,
332
+ moveTableData: DocCategory.DATA_MIGRATION,
333
+ cloneTable: DocCategory.DATA_MIGRATION,
334
+ compareTableStructure: DocCategory.DATA_MIGRATION,
335
+ syncTableData: DocCategory.DATA_MIGRATION,
336
+ // Schema Migrations
337
+ initMigrationsTable: DocCategory.SCHEMA_MIGRATIONS,
338
+ createMigration: DocCategory.SCHEMA_MIGRATIONS,
339
+ applyMigrations: DocCategory.SCHEMA_MIGRATIONS,
340
+ rollbackMigration: DocCategory.SCHEMA_MIGRATIONS,
341
+ getMigrationStatus: DocCategory.SCHEMA_MIGRATIONS,
342
+ getSchemaVersion: DocCategory.SCHEMA_MIGRATIONS,
343
+ validateMigrations: DocCategory.SCHEMA_MIGRATIONS,
344
+ resetFailedMigration: DocCategory.SCHEMA_MIGRATIONS,
345
+ generateMigrationFromDiff: DocCategory.SCHEMA_MIGRATIONS,
346
+ };
347
+ /**
348
+ * Mapping between legacy categories and documentation categories
349
+ * This allows backward compatibility
350
+ */
351
+ const legacyToDocCategoryMap = {
352
+ list: [
353
+ DocCategory.DATABASE_DISCOVERY,
354
+ DocCategory.STORED_PROCEDURES,
355
+ DocCategory.VIEWS_MANAGEMENT,
356
+ DocCategory.TRIGGERS_MANAGEMENT,
357
+ DocCategory.FUNCTIONS_MANAGEMENT,
358
+ DocCategory.INDEX_MANAGEMENT,
359
+ DocCategory.CONSTRAINT_MANAGEMENT,
360
+ DocCategory.TABLE_MAINTENANCE,
361
+ DocCategory.SERVER_MANAGEMENT,
362
+ DocCategory.SCHEMA_MIGRATIONS,
363
+ ],
364
+ read: [DocCategory.CRUD_OPERATIONS, DocCategory.CUSTOM_QUERIES],
365
+ create: [
366
+ DocCategory.CRUD_OPERATIONS,
367
+ DocCategory.BULK_OPERATIONS,
368
+ DocCategory.IMPORT_EXPORT,
369
+ DocCategory.DATA_MIGRATION,
370
+ ],
371
+ update: [
372
+ DocCategory.CRUD_OPERATIONS,
373
+ DocCategory.BULK_OPERATIONS,
374
+ DocCategory.DATA_MIGRATION,
375
+ ],
376
+ delete: [
377
+ DocCategory.CRUD_OPERATIONS,
378
+ DocCategory.BULK_OPERATIONS,
379
+ DocCategory.DATA_MIGRATION,
380
+ ],
381
+ execute: [DocCategory.CUSTOM_QUERIES, DocCategory.SERVER_MANAGEMENT],
382
+ ddl: [
383
+ DocCategory.SCHEMA_MANAGEMENT,
384
+ DocCategory.VIEWS_MANAGEMENT,
385
+ DocCategory.TRIGGERS_MANAGEMENT,
386
+ DocCategory.INDEX_MANAGEMENT,
387
+ DocCategory.CONSTRAINT_MANAGEMENT,
388
+ DocCategory.TABLE_MAINTENANCE,
389
+ DocCategory.BACKUP_RESTORE,
390
+ DocCategory.DATA_MIGRATION,
391
+ DocCategory.SCHEMA_MIGRATIONS,
392
+ ],
393
+ utility: [
394
+ DocCategory.UTILITIES,
395
+ DocCategory.TABLE_MAINTENANCE,
396
+ DocCategory.PERFORMANCE_MONITORING,
397
+ DocCategory.CACHE_MANAGEMENT,
398
+ DocCategory.QUERY_OPTIMIZATION,
399
+ DocCategory.BACKUP_RESTORE,
400
+ DocCategory.IMPORT_EXPORT,
401
+ ],
402
+ transaction: [DocCategory.TRANSACTION_MANAGEMENT],
403
+ procedure: [DocCategory.STORED_PROCEDURES, DocCategory.FUNCTIONS_MANAGEMENT],
404
+ };
172
405
  /**
173
406
  * Class to manage feature configuration based on runtime or environment variables
407
+ * Supports dual-layer filtering:
408
+ * - Layer 1 (Permissions): Legacy categories (broad control)
409
+ * - Layer 2 (Categories): Documentation categories (fine-grained control, optional)
174
410
  */
175
411
  class FeatureConfig {
176
- constructor(configStr) {
177
- this.originalConfigString = configStr || process.env.MCP_CONFIG || "";
178
- this.enabledCategories = this.parseConfig(configStr);
412
+ constructor(permissionsStr, categoriesStr) {
413
+ // Support both old single-parameter and new dual-parameter signatures
414
+ const permissions = permissionsStr ||
415
+ process.env.MCP_PERMISSIONS ||
416
+ process.env.MCP_CONFIG ||
417
+ "";
418
+ const categories = categoriesStr || process.env.MCP_CATEGORIES || "";
419
+ this.originalPermissionsString = permissions;
420
+ this.originalCategoriesString = categories;
421
+ this.useDualLayer = !!categories.trim();
422
+ const parsed = this.parseConfig(permissions, categories);
423
+ this.enabledLegacyCategories = parsed.legacy;
424
+ this.enabledDocCategories = parsed.doc;
179
425
  }
180
426
  /**
181
- * Parse MCP_CONFIG from provided string or environment variables
427
+ * Parse permissions and categories for dual-layer filtering
428
+ * Layer 1 (permissions): Broad control using legacy categories
429
+ * Layer 2 (categories): Fine-grained control using documentation categories (optional)
182
430
  */
183
- parseConfig(configStr) {
184
- // Priority: 1. Provided config, 2. Environment variable, 3. Enable all
185
- const config = configStr || process.env.MCP_CONFIG || "";
186
- // If config is empty, enable all features
187
- if (!config.trim()) {
188
- return new Set(Object.values(ToolCategory));
431
+ parseConfig(permissionsStr, categoriesStr) {
432
+ // If both are empty, enable all features
433
+ if (!permissionsStr.trim() && !categoriesStr.trim()) {
434
+ return {
435
+ legacy: new Set(Object.values(ToolCategory)),
436
+ doc: new Set(Object.values(DocCategory)),
437
+ };
438
+ }
439
+ // Parse Layer 1: Permissions (legacy categories)
440
+ let legacySet = new Set();
441
+ if (permissionsStr.trim()) {
442
+ const items = permissionsStr
443
+ .split(",")
444
+ .map((c) => c.trim().toLowerCase());
445
+ const validLegacyCategories = items.filter((c) => Object.values(ToolCategory).includes(c));
446
+ legacySet = new Set(validLegacyCategories);
447
+ }
448
+ else {
449
+ // If no permissions specified but categories are, allow all permissions
450
+ legacySet = new Set(Object.values(ToolCategory));
451
+ }
452
+ // Parse Layer 2: Categories (documentation categories)
453
+ let docSet = new Set();
454
+ if (categoriesStr.trim()) {
455
+ // Categories specified - use them for fine-grained filtering
456
+ const items = categoriesStr.split(",").map((c) => c.trim().toLowerCase());
457
+ const validDocCategories = items.filter((c) => Object.values(DocCategory).includes(c));
458
+ docSet = new Set(validDocCategories);
189
459
  }
190
- // Parse comma-separated list
191
- const categories = config.split(",").map((c) => c.trim().toLowerCase());
192
- const validCategories = categories.filter((c) => Object.values(ToolCategory).includes(c));
193
- return new Set(validCategories);
460
+ else {
461
+ // No categories specified - derive from permissions
462
+ legacySet.forEach((legacyCat) => {
463
+ const docCats = legacyToDocCategoryMap[legacyCat] || [];
464
+ docCats.forEach((dc) => docSet.add(dc));
465
+ });
466
+ }
467
+ return {
468
+ legacy: legacySet,
469
+ doc: docSet,
470
+ };
194
471
  }
195
472
  /**
196
473
  * Update configuration at runtime
197
474
  */
198
- setConfig(configStr) {
199
- this.originalConfigString = configStr;
200
- this.enabledCategories = this.parseConfig(configStr);
475
+ setConfig(permissionsStr, categoriesStr) {
476
+ this.originalPermissionsString = permissionsStr;
477
+ this.originalCategoriesString = categoriesStr || "";
478
+ this.useDualLayer = !!(categoriesStr && categoriesStr.trim());
479
+ const parsed = this.parseConfig(permissionsStr, categoriesStr || "");
480
+ this.enabledLegacyCategories = parsed.legacy;
481
+ this.enabledDocCategories = parsed.doc;
201
482
  }
202
483
  /**
203
484
  * Check if a specific tool is enabled
485
+ * Dual-layer logic:
486
+ * - Layer 1 (Permission): Tool must be allowed by its legacy category
487
+ * - Layer 2 (Category): If categories specified, tool must also be in allowed doc category
204
488
  */
205
489
  isToolEnabled(toolName) {
206
- const category = exports.toolCategoryMap[toolName];
207
- // If tool is not in the map, default to disabled
208
- if (!category) {
490
+ const docCategory = exports.toolDocCategoryMap[toolName];
491
+ const legacyCategory = exports.toolCategoryMap[toolName];
492
+ // If tool is not in either map, default to disabled
493
+ if (!docCategory && !legacyCategory) {
209
494
  console.warn(`Unknown tool: ${toolName}`);
210
495
  return false;
211
496
  }
212
- return this.enabledCategories.has(category);
497
+ // Layer 1: Check permission (legacy category)
498
+ const hasPermission = legacyCategory
499
+ ? this.enabledLegacyCategories.has(legacyCategory)
500
+ : false;
501
+ if (!hasPermission) {
502
+ return false;
503
+ }
504
+ // Layer 2: Check category (documentation category) if dual-layer mode
505
+ if (this.useDualLayer) {
506
+ const hasCategory = docCategory
507
+ ? this.enabledDocCategories.has(docCategory)
508
+ : false;
509
+ return hasCategory;
510
+ }
511
+ // Single-layer mode: permission is sufficient
512
+ return true;
213
513
  }
214
514
  /**
215
515
  * Get detailed permission error message for a specific tool
216
516
  */
217
517
  getPermissionError(toolName) {
218
- const category = exports.toolCategoryMap[toolName];
219
- if (!category) {
518
+ const docCategory = exports.toolDocCategoryMap[toolName];
519
+ const legacyCategory = exports.toolCategoryMap[toolName];
520
+ if (!docCategory && !legacyCategory) {
220
521
  return `Unknown tool '${toolName}'. This tool is not recognized by the MCP server.`;
221
522
  }
222
- const isAllPermissions = !this.originalConfigString.trim();
223
- const currentPermissions = isAllPermissions
224
- ? "all"
225
- : this.originalConfigString;
226
- const actionDescriptions = {
227
- [ToolCategory.LIST]: "list databases and tables",
228
- [ToolCategory.READ]: "read data from tables",
229
- [ToolCategory.CREATE]: "create new records",
230
- [ToolCategory.UPDATE]: "update existing records",
231
- [ToolCategory.DELETE]: "delete records",
232
- [ToolCategory.EXECUTE]: "execute custom SQL queries",
233
- [ToolCategory.DDL]: "create, alter, or drop tables (schema changes)",
234
- [ToolCategory.UTILITY]: "use utility functions",
235
- [ToolCategory.TRANSACTION]: "manage database transactions",
236
- [ToolCategory.PROCEDURE]: "manage stored procedures",
237
- };
238
- const toolDescriptions = {
239
- createTable: "create new tables",
240
- alterTable: "modify table structure",
241
- dropTable: "delete tables",
242
- executeDdl: "execute DDL statements",
243
- createRecord: "insert new records",
244
- updateRecord: "update existing records",
245
- deleteRecord: "delete records",
246
- bulkInsert: "insert multiple records in batches",
247
- bulkUpdate: "update multiple records in batches",
248
- bulkDelete: "delete multiple records in batches",
249
- executeSql: "execute custom SQL statements",
250
- runQuery: "run SELECT queries",
251
- beginTransaction: "start database transactions",
252
- commitTransaction: "commit database transactions",
253
- rollbackTransaction: "rollback database transactions",
254
- executeInTransaction: "execute queries within transactions",
255
- createStoredProcedure: "create stored procedures",
256
- dropStoredProcedure: "delete stored procedures",
257
- executeStoredProcedure: "execute stored procedures",
258
- exportTableToCSV: "export table data to CSV",
259
- exportQueryToCSV: "export query results to CSV",
260
- // Backup and restore
261
- backupTable: "backup table to SQL dump",
262
- backupDatabase: "backup database to SQL dump",
263
- restoreFromSql: "restore database from SQL dump",
264
- getCreateTableStatement: "get CREATE TABLE statement",
265
- getDatabaseSchema: "get database schema overview",
266
- // Extended export/import
267
- exportTableToJSON: "export table data to JSON",
268
- exportQueryToJSON: "export query results to JSON",
269
- exportTableToSql: "export table data to SQL INSERT statements",
270
- importFromCSV: "import data from CSV",
271
- importFromJSON: "import data from JSON",
272
- };
273
- const toolDescription = toolDescriptions[toolName] || actionDescriptions[category];
274
- const requiredPermission = category;
275
- return (`Permission denied: Cannot ${toolDescription}. ` +
276
- `This action requires '${requiredPermission}' permission, but your current MCP configuration only allows: ${currentPermissions}. ` +
277
- `To enable this feature, update your MCP server configuration to include '${requiredPermission}' in the permissions list.`);
523
+ const isAllEnabled = !this.originalPermissionsString.trim() &&
524
+ !this.originalCategoriesString.trim();
525
+ if (isAllEnabled) {
526
+ return `Unknown error: All tools should be enabled but '${toolName}' was blocked.`;
527
+ }
528
+ // Build error message based on dual-layer or single-layer mode
529
+ if (this.useDualLayer) {
530
+ const hasPermission = legacyCategory
531
+ ? this.enabledLegacyCategories.has(legacyCategory)
532
+ : false;
533
+ const hasCategory = docCategory
534
+ ? this.enabledDocCategories.has(docCategory)
535
+ : false;
536
+ if (!hasPermission) {
537
+ return (`Permission denied: This tool requires '${legacyCategory}' permission (Layer 1). ` +
538
+ `Your current permissions: ${this.originalPermissionsString || "none"}. ` +
539
+ `Add '${legacyCategory}' to the permissions argument.`);
540
+ }
541
+ if (!hasCategory) {
542
+ return (`Permission denied: This tool requires '${docCategory}' category (Layer 2). ` +
543
+ `Your current categories: ${this.originalCategoriesString || "none"}. ` +
544
+ `Add '${docCategory}' to the categories argument.`);
545
+ }
546
+ }
547
+ else {
548
+ // Single-layer mode
549
+ return (`Permission denied: This tool requires '${legacyCategory}' permission. ` +
550
+ `Your current configuration allows: ${this.originalPermissionsString || "all"}. ` +
551
+ `Add '${legacyCategory}' to enable this tool.`);
552
+ }
553
+ return `Permission denied for tool '${toolName}'.`;
278
554
  }
279
555
  /**
280
- * Check if a category is enabled
556
+ * Check if a legacy category is enabled
281
557
  */
282
558
  isCategoryEnabled(category) {
283
- return this.enabledCategories.has(category);
559
+ return this.enabledLegacyCategories.has(category);
284
560
  }
285
561
  /**
286
- * Get all enabled categories
562
+ * Check if a documentation category is enabled
563
+ */
564
+ isDocCategoryEnabled(category) {
565
+ return this.enabledDocCategories.has(category);
566
+ }
567
+ /**
568
+ * Get all enabled legacy categories
287
569
  */
288
570
  getEnabledCategories() {
289
- return Array.from(this.enabledCategories);
571
+ return Array.from(this.enabledLegacyCategories);
572
+ }
573
+ /**
574
+ * Get all enabled documentation categories
575
+ */
576
+ getEnabledDocCategories() {
577
+ return Array.from(this.enabledDocCategories);
290
578
  }
291
579
  /**
292
580
  * Get all available categories with their status
@@ -294,10 +582,39 @@ class FeatureConfig {
294
582
  getCategoryStatus() {
295
583
  const result = {};
296
584
  for (const category of Object.values(ToolCategory)) {
297
- result[category] = this.enabledCategories.has(category);
585
+ result[category] = this.enabledLegacyCategories.has(category);
586
+ }
587
+ return result;
588
+ }
589
+ /**
590
+ * Get all available documentation categories with their status
591
+ */
592
+ getDocCategoryStatus() {
593
+ const result = {};
594
+ for (const category of Object.values(DocCategory)) {
595
+ result[category] = this.enabledDocCategories.has(category);
298
596
  }
299
597
  return result;
300
598
  }
599
+ /**
600
+ * Check if using dual-layer filtering mode
601
+ */
602
+ isUsingDualLayer() {
603
+ return this.useDualLayer;
604
+ }
605
+ /**
606
+ * Get filtering mode description
607
+ */
608
+ getFilteringMode() {
609
+ if (!this.originalPermissionsString.trim() &&
610
+ !this.originalCategoriesString.trim()) {
611
+ return "No filtering (all tools enabled)";
612
+ }
613
+ if (this.useDualLayer) {
614
+ return "Dual-layer (Permissions + Categories)";
615
+ }
616
+ return "Single-layer (Permissions only)";
617
+ }
301
618
  }
302
619
  exports.FeatureConfig = FeatureConfig;
303
620
  // Export singleton instance
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ export declare class MySQLMCP {
24
24
  private performanceTools;
25
25
  private security;
26
26
  private featureConfig;
27
- constructor(permissionsConfig?: string);
27
+ constructor(permissionsConfig?: string, categoriesConfig?: string);
28
28
  private checkToolEnabled;
29
29
  listDatabases(): Promise<{
30
30
  status: string;
@@ -588,6 +588,12 @@ export declare class MySQLMCP {
588
588
  categoryStatus: Record<import("./config/featureConfig").ToolCategory, boolean>;
589
589
  };
590
590
  };
591
+ /**
592
+ * Check if a specific tool is enabled based on current permissions and categories
593
+ * @param toolName - The tool name in camelCase (e.g., 'listDatabases')
594
+ * @returns boolean indicating if the tool is enabled
595
+ */
596
+ isToolEnabled(toolName: string): boolean;
591
597
  /**
592
598
  * Bulk insert multiple records into the specified table
593
599
  */
package/dist/index.js CHANGED
@@ -31,8 +31,8 @@ const featureConfig_1 = require("./config/featureConfig");
31
31
  * A secure interface for AI models to interact with MySQL databases
32
32
  */
33
33
  class MySQLMCP {
34
- constructor(permissionsConfig) {
35
- this.featureConfig = new featureConfig_1.FeatureConfig(permissionsConfig);
34
+ constructor(permissionsConfig, categoriesConfig) {
35
+ this.featureConfig = new featureConfig_1.FeatureConfig(permissionsConfig, categoriesConfig);
36
36
  this.security = new securityLayer_1.default(this.featureConfig);
37
37
  this.dbTools = new databaseTools_1.DatabaseTools();
38
38
  this.crudTools = new crudTools_1.CrudTools(this.security);
@@ -504,6 +504,14 @@ class MySQLMCP {
504
504
  },
505
505
  };
506
506
  }
507
+ /**
508
+ * Check if a specific tool is enabled based on current permissions and categories
509
+ * @param toolName - The tool name in camelCase (e.g., 'listDatabases')
510
+ * @returns boolean indicating if the tool is enabled
511
+ */
512
+ isToolEnabled(toolName) {
513
+ return this.featureConfig.isToolEnabled(toolName);
514
+ }
507
515
  /**
508
516
  * Bulk insert multiple records into the specified table
509
517
  */
@@ -5,9 +5,11 @@ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
5
5
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
6
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
7
7
  const index_js_2 = require("./index.js");
8
- // Get permissions from environment variable (set by bin/mcp-mysql.js)
9
- // Priority: MCP_PERMISSIONS (from command line) > MCP_CONFIG (from .env) > all permissions
8
+ // Get permissions and categories from environment variables (set by bin/mcp-mysql.js)
9
+ // Layer 1 (Permissions): MCP_PERMISSIONS or MCP_CONFIG (backward compatible)
10
+ // Layer 2 (Categories): MCP_CATEGORIES (optional, for fine-grained control)
10
11
  const permissions = process.env.MCP_PERMISSIONS || process.env.MCP_CONFIG || "";
12
+ const categories = process.env.MCP_CATEGORIES || "";
11
13
  // Declare the MySQL MCP instance (will be initialized in main())
12
14
  let mysqlMCP;
13
15
  // Define all available tools with their schemas
@@ -2691,10 +2693,20 @@ const server = new index_js_1.Server({
2691
2693
  tools: {},
2692
2694
  },
2693
2695
  });
2694
- // Handle list tools request
2696
+ // Handle list tools request - filter tools based on permissions and categories
2695
2697
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
2698
+ // Filter tools to only return those that are enabled based on current config
2699
+ const enabledTools = TOOLS.filter((tool) => {
2700
+ // Convert tool name from snake_case to camelCase for checking
2701
+ // e.g., "list_databases" -> "listDatabases"
2702
+ const toolNameCamelCase = tool.name.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
2703
+ // Check if tool is enabled based on permissions and categories
2704
+ return mysqlMCP.isToolEnabled(toolNameCamelCase);
2705
+ });
2706
+ // Log the filtering results
2707
+ console.error(`Tools available: ${enabledTools.length} of ${TOOLS.length} total tools`);
2696
2708
  return {
2697
- tools: TOOLS,
2709
+ tools: enabledTools,
2698
2710
  };
2699
2711
  });
2700
2712
  // Handle tool call requests
@@ -3182,10 +3194,16 @@ async function main() {
3182
3194
  await server.connect(transport);
3183
3195
  // Initialize the MySQL MCP instance AFTER transport is connected
3184
3196
  // This ensures the database connection pool is created when the server is ready
3185
- mysqlMCP = new index_js_2.MySQLMCP(permissions);
3186
- // Log the effective permissions to stderr
3187
- if (permissions) {
3197
+ mysqlMCP = new index_js_2.MySQLMCP(permissions, categories);
3198
+ // Log the effective filtering configuration to stderr
3199
+ if (permissions && categories) {
3200
+ console.error(`Active permissions (Layer 1): ${permissions}`);
3201
+ console.error(`Active categories (Layer 2): ${categories}`);
3202
+ console.error("Filtering mode: Dual-layer");
3203
+ }
3204
+ else if (permissions) {
3188
3205
  console.error(`Active permissions: ${permissions}`);
3206
+ console.error("Filtering mode: Single-layer");
3189
3207
  }
3190
3208
  else {
3191
3209
  console.error("Active permissions: all (default)");