@berthojoris/mcp-mysql-server 1.9.2 → 1.10.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.
package/dist/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export declare class MySQLMCP {
21
21
  private backupRestoreTools;
22
22
  private migrationTools;
23
23
  private schemaVersioningTools;
24
+ private performanceTools;
24
25
  private security;
25
26
  private featureConfig;
26
27
  constructor(permissionsConfig?: string);
@@ -1093,5 +1094,80 @@ export declare class MySQLMCP {
1093
1094
  error?: string;
1094
1095
  queryLog?: string;
1095
1096
  }>;
1097
+ getPerformanceMetrics(): Promise<{
1098
+ status: string;
1099
+ data?: any;
1100
+ error?: string;
1101
+ queryLog?: string;
1102
+ }>;
1103
+ getTopQueriesByTime(params?: {
1104
+ limit?: number;
1105
+ }): Promise<{
1106
+ status: string;
1107
+ data?: any[];
1108
+ error?: string;
1109
+ queryLog?: string;
1110
+ }>;
1111
+ getTopQueriesByCount(params?: {
1112
+ limit?: number;
1113
+ }): Promise<{
1114
+ status: string;
1115
+ data?: any[];
1116
+ error?: string;
1117
+ queryLog?: string;
1118
+ }>;
1119
+ getSlowQueries(params?: {
1120
+ limit?: number;
1121
+ threshold_seconds?: number;
1122
+ }): Promise<{
1123
+ status: string;
1124
+ data?: any[];
1125
+ error?: string;
1126
+ queryLog?: string;
1127
+ }>;
1128
+ getTableIOStats(params?: {
1129
+ limit?: number;
1130
+ table_schema?: string;
1131
+ }): Promise<{
1132
+ status: string;
1133
+ data?: any[];
1134
+ error?: string;
1135
+ queryLog?: string;
1136
+ }>;
1137
+ getIndexUsageStats(params?: {
1138
+ limit?: number;
1139
+ table_schema?: string;
1140
+ }): Promise<{
1141
+ status: string;
1142
+ data?: any[];
1143
+ error?: string;
1144
+ queryLog?: string;
1145
+ }>;
1146
+ getUnusedIndexes(params?: {
1147
+ table_schema?: string;
1148
+ }): Promise<{
1149
+ status: string;
1150
+ data?: any[];
1151
+ error?: string;
1152
+ queryLog?: string;
1153
+ }>;
1154
+ getConnectionPoolStats(): Promise<{
1155
+ status: string;
1156
+ data?: any;
1157
+ error?: string;
1158
+ queryLog?: string;
1159
+ }>;
1160
+ getDatabaseHealthCheck(): Promise<{
1161
+ status: string;
1162
+ data?: any;
1163
+ error?: string;
1164
+ queryLog?: string;
1165
+ }>;
1166
+ resetPerformanceStats(): Promise<{
1167
+ status: string;
1168
+ message?: string;
1169
+ error?: string;
1170
+ queryLog?: string;
1171
+ }>;
1096
1172
  }
1097
1173
  export default MySQLMCP;
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ const processTools_1 = require("./tools/processTools");
22
22
  const backupRestoreTools_1 = require("./tools/backupRestoreTools");
23
23
  const migrationTools_1 = require("./tools/migrationTools");
24
24
  const schemaVersioningTools_1 = require("./tools/schemaVersioningTools");
25
+ const performanceTools_1 = require("./tools/performanceTools");
25
26
  const securityLayer_1 = __importDefault(require("./security/securityLayer"));
26
27
  const connection_1 = __importDefault(require("./db/connection"));
27
28
  const featureConfig_1 = require("./config/featureConfig");
@@ -51,6 +52,7 @@ class MySQLMCP {
51
52
  this.backupRestoreTools = new backupRestoreTools_1.BackupRestoreTools(this.security);
52
53
  this.migrationTools = new migrationTools_1.MigrationTools(this.security);
53
54
  this.schemaVersioningTools = new schemaVersioningTools_1.SchemaVersioningTools(this.security);
55
+ this.performanceTools = new performanceTools_1.PerformanceTools(this.security);
54
56
  }
55
57
  // Helper method to check if tool is enabled
56
58
  checkToolEnabled(toolName) {
@@ -506,21 +508,30 @@ class MySQLMCP {
506
508
  * Bulk insert multiple records into the specified table
507
509
  */
508
510
  async bulkInsert(params) {
509
- this.checkToolEnabled("bulk_insert");
511
+ const check = this.checkToolEnabled("bulkInsert");
512
+ if (!check.enabled) {
513
+ return { status: "error", error: check.error };
514
+ }
510
515
  return this.crudTools.bulkInsert(params);
511
516
  }
512
517
  /**
513
518
  * Bulk update multiple records with different conditions and data
514
519
  */
515
520
  async bulkUpdate(params) {
516
- this.checkToolEnabled("bulk_update");
521
+ const check = this.checkToolEnabled("bulkUpdate");
522
+ if (!check.enabled) {
523
+ return { status: "error", error: check.error };
524
+ }
517
525
  return this.crudTools.bulkUpdate(params);
518
526
  }
519
527
  /**
520
528
  * Bulk delete records based on multiple condition sets
521
529
  */
522
530
  async bulkDelete(params) {
523
- this.checkToolEnabled("bulk_delete");
531
+ const check = this.checkToolEnabled("bulkDelete");
532
+ if (!check.enabled) {
533
+ return { status: "error", error: check.error };
534
+ }
524
535
  return this.crudTools.bulkDelete(params);
525
536
  }
526
537
  // Close database connection
@@ -916,6 +927,69 @@ class MySQLMCP {
916
927
  return { status: "error", error: check.error };
917
928
  return await this.processTools.showReplicationStatus(params);
918
929
  }
930
+ // ==========================================
931
+ // Performance Monitoring Tools
932
+ // ==========================================
933
+ async getPerformanceMetrics() {
934
+ const check = this.checkToolEnabled("getPerformanceMetrics");
935
+ if (!check.enabled)
936
+ return { status: "error", error: check.error };
937
+ return await this.performanceTools.getPerformanceMetrics();
938
+ }
939
+ async getTopQueriesByTime(params) {
940
+ const check = this.checkToolEnabled("getTopQueriesByTime");
941
+ if (!check.enabled)
942
+ return { status: "error", error: check.error };
943
+ return await this.performanceTools.getTopQueriesByTime(params);
944
+ }
945
+ async getTopQueriesByCount(params) {
946
+ const check = this.checkToolEnabled("getTopQueriesByCount");
947
+ if (!check.enabled)
948
+ return { status: "error", error: check.error };
949
+ return await this.performanceTools.getTopQueriesByCount(params);
950
+ }
951
+ async getSlowQueries(params) {
952
+ const check = this.checkToolEnabled("getSlowQueries");
953
+ if (!check.enabled)
954
+ return { status: "error", error: check.error };
955
+ return await this.performanceTools.getSlowQueries(params);
956
+ }
957
+ async getTableIOStats(params) {
958
+ const check = this.checkToolEnabled("getTableIOStats");
959
+ if (!check.enabled)
960
+ return { status: "error", error: check.error };
961
+ return await this.performanceTools.getTableIOStats(params);
962
+ }
963
+ async getIndexUsageStats(params) {
964
+ const check = this.checkToolEnabled("getIndexUsageStats");
965
+ if (!check.enabled)
966
+ return { status: "error", error: check.error };
967
+ return await this.performanceTools.getIndexUsageStats(params);
968
+ }
969
+ async getUnusedIndexes(params) {
970
+ const check = this.checkToolEnabled("getUnusedIndexes");
971
+ if (!check.enabled)
972
+ return { status: "error", error: check.error };
973
+ return await this.performanceTools.getUnusedIndexes(params);
974
+ }
975
+ async getConnectionPoolStats() {
976
+ const check = this.checkToolEnabled("getConnectionPoolStats");
977
+ if (!check.enabled)
978
+ return { status: "error", error: check.error };
979
+ return await this.performanceTools.getConnectionPoolStats();
980
+ }
981
+ async getDatabaseHealthCheck() {
982
+ const check = this.checkToolEnabled("getDatabaseHealthCheck");
983
+ if (!check.enabled)
984
+ return { status: "error", error: check.error };
985
+ return await this.performanceTools.getDatabaseHealthCheck();
986
+ }
987
+ async resetPerformanceStats() {
988
+ const check = this.checkToolEnabled("resetPerformanceStats");
989
+ if (!check.enabled)
990
+ return { status: "error", error: check.error };
991
+ return await this.performanceTools.resetPerformanceStats();
992
+ }
919
993
  }
920
994
  exports.MySQLMCP = MySQLMCP;
921
995
  exports.default = MySQLMCP;
@@ -2558,6 +2558,129 @@ const TOOLS = [
2558
2558
  required: ["table1", "table2", "migration_name"],
2559
2559
  },
2560
2560
  },
2561
+ // Performance Monitoring Tools
2562
+ {
2563
+ name: "get_performance_metrics",
2564
+ description: "Get comprehensive performance metrics including query performance, connection stats, buffer pool metrics, and InnoDB statistics.",
2565
+ inputSchema: {
2566
+ type: "object",
2567
+ properties: {},
2568
+ },
2569
+ },
2570
+ {
2571
+ name: "get_top_queries_by_time",
2572
+ description: "Get the top queries ordered by total execution time. Useful for identifying slow queries.",
2573
+ inputSchema: {
2574
+ type: "object",
2575
+ properties: {
2576
+ limit: {
2577
+ type: "number",
2578
+ description: "Maximum number of queries to return (default: 10, max: 100)",
2579
+ },
2580
+ },
2581
+ },
2582
+ },
2583
+ {
2584
+ name: "get_top_queries_by_count",
2585
+ description: "Get the top queries ordered by execution count. Useful for identifying frequently executed queries.",
2586
+ inputSchema: {
2587
+ type: "object",
2588
+ properties: {
2589
+ limit: {
2590
+ type: "number",
2591
+ description: "Maximum number of queries to return (default: 10, max: 100)",
2592
+ },
2593
+ },
2594
+ },
2595
+ },
2596
+ {
2597
+ name: "get_slow_queries",
2598
+ description: "Get queries that exceed a specified execution time threshold.",
2599
+ inputSchema: {
2600
+ type: "object",
2601
+ properties: {
2602
+ limit: {
2603
+ type: "number",
2604
+ description: "Maximum number of queries to return (default: 10, max: 100)",
2605
+ },
2606
+ threshold_seconds: {
2607
+ type: "number",
2608
+ description: "Execution time threshold in seconds (default: 1)",
2609
+ },
2610
+ },
2611
+ },
2612
+ },
2613
+ {
2614
+ name: "get_table_io_stats",
2615
+ description: "Get I/O statistics for tables including read/write operations and timings.",
2616
+ inputSchema: {
2617
+ type: "object",
2618
+ properties: {
2619
+ limit: {
2620
+ type: "number",
2621
+ description: "Maximum number of tables to return (default: 20, max: 100)",
2622
+ },
2623
+ table_schema: {
2624
+ type: "string",
2625
+ description: "Filter by specific database schema",
2626
+ },
2627
+ },
2628
+ },
2629
+ },
2630
+ {
2631
+ name: "get_index_usage_stats",
2632
+ description: "Get index usage statistics showing how often each index is used.",
2633
+ inputSchema: {
2634
+ type: "object",
2635
+ properties: {
2636
+ limit: {
2637
+ type: "number",
2638
+ description: "Maximum number of indexes to return (default: 20, max: 100)",
2639
+ },
2640
+ table_schema: {
2641
+ type: "string",
2642
+ description: "Filter by specific database schema",
2643
+ },
2644
+ },
2645
+ },
2646
+ },
2647
+ {
2648
+ name: "get_unused_indexes",
2649
+ description: "Identify indexes that are not being used by queries. These may be candidates for removal to improve write performance.",
2650
+ inputSchema: {
2651
+ type: "object",
2652
+ properties: {
2653
+ table_schema: {
2654
+ type: "string",
2655
+ description: "Filter by specific database schema",
2656
+ },
2657
+ },
2658
+ },
2659
+ },
2660
+ {
2661
+ name: "get_connection_pool_stats",
2662
+ description: "Get connection pool statistics including current connections, max usage, configuration, and health indicators.",
2663
+ inputSchema: {
2664
+ type: "object",
2665
+ properties: {},
2666
+ },
2667
+ },
2668
+ {
2669
+ name: "get_database_health_check",
2670
+ description: "Perform a comprehensive health check of the database including connection usage, buffer pool efficiency, aborted connections, and slow queries.",
2671
+ inputSchema: {
2672
+ type: "object",
2673
+ properties: {},
2674
+ },
2675
+ },
2676
+ {
2677
+ name: "reset_performance_stats",
2678
+ description: "Reset performance schema statistics. This clears query digest statistics, table I/O stats, and index usage stats. Requires 'utility' permission.",
2679
+ inputSchema: {
2680
+ type: "object",
2681
+ properties: {},
2682
+ },
2683
+ },
2561
2684
  ];
2562
2685
  // Create the MCP server
2563
2686
  const server = new index_js_1.Server({
@@ -2924,6 +3047,37 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
2924
3047
  case "generate_migration_from_diff":
2925
3048
  result = await mysqlMCP.generateMigrationFromDiff((args || {}));
2926
3049
  break;
3050
+ // Performance Monitoring Tools
3051
+ case "get_performance_metrics":
3052
+ result = await mysqlMCP.getPerformanceMetrics();
3053
+ break;
3054
+ case "get_top_queries_by_time":
3055
+ result = await mysqlMCP.getTopQueriesByTime((args || {}));
3056
+ break;
3057
+ case "get_top_queries_by_count":
3058
+ result = await mysqlMCP.getTopQueriesByCount((args || {}));
3059
+ break;
3060
+ case "get_slow_queries":
3061
+ result = await mysqlMCP.getSlowQueries((args || {}));
3062
+ break;
3063
+ case "get_table_io_stats":
3064
+ result = await mysqlMCP.getTableIOStats((args || {}));
3065
+ break;
3066
+ case "get_index_usage_stats":
3067
+ result = await mysqlMCP.getIndexUsageStats((args || {}));
3068
+ break;
3069
+ case "get_unused_indexes":
3070
+ result = await mysqlMCP.getUnusedIndexes((args || {}));
3071
+ break;
3072
+ case "get_connection_pool_stats":
3073
+ result = await mysqlMCP.getConnectionPoolStats();
3074
+ break;
3075
+ case "get_database_health_check":
3076
+ result = await mysqlMCP.getDatabaseHealthCheck();
3077
+ break;
3078
+ case "reset_performance_stats":
3079
+ result = await mysqlMCP.resetPerformanceStats();
3080
+ break;
2927
3081
  default:
2928
3082
  throw new Error(`Unknown tool: ${name}`);
2929
3083
  }
@@ -6,6 +6,10 @@ export declare class SecurityLayer {
6
6
  private readonly ddlOperations;
7
7
  private featureConfig;
8
8
  constructor(featureConfig?: FeatureConfig);
9
+ /**
10
+ * Check if a query is a read-only information query (SHOW, DESCRIBE, EXPLAIN, etc.)
11
+ */
12
+ private isInformationQuery;
9
13
  /**
10
14
  * Validate input against a JSON schema
11
15
  */
@@ -39,7 +43,7 @@ export declare class SecurityLayer {
39
43
  sanitizedParams?: any[];
40
44
  };
41
45
  /**
42
- * Check if a query is a read-only SELECT query
46
+ * Check if a query is a read-only SELECT query or information query (SHOW, DESCRIBE, etc.)
43
47
  */
44
48
  isReadOnlyQuery(query: string): boolean;
45
49
  /**
@@ -34,6 +34,14 @@ class SecurityLayer {
34
34
  // Define DDL operations that require special permission
35
35
  this.ddlOperations = ["CREATE", "ALTER", "DROP", "TRUNCATE", "RENAME"];
36
36
  }
37
+ /**
38
+ * Check if a query is a read-only information query (SHOW, DESCRIBE, EXPLAIN, etc.)
39
+ */
40
+ isInformationQuery(query) {
41
+ const trimmedQuery = query.trim().toUpperCase();
42
+ const readOnlyCommands = ["SHOW", "DESCRIBE", "DESC", "EXPLAIN", "HELP"];
43
+ return readOnlyCommands.some((cmd) => trimmedQuery.startsWith(cmd));
44
+ }
37
45
  /**
38
46
  * Validate input against a JSON schema
39
47
  */
@@ -132,6 +140,10 @@ class SecurityLayer {
132
140
  }
133
141
  // Remove trailing semicolon for analysis
134
142
  const cleanQuery = trimmedQuery.replace(/;$/, "");
143
+ // Check if it's an information query (SHOW, DESCRIBE, EXPLAIN, etc.) - these are always allowed
144
+ if (this.isInformationQuery(trimmedQuery)) {
145
+ return { valid: true, queryType: "INFORMATION" };
146
+ }
135
147
  // Determine query type - check basic operations first
136
148
  let queryType = "";
137
149
  for (const operation of this.allowedOperations) {
@@ -257,9 +269,14 @@ class SecurityLayer {
257
269
  return { valid: true, sanitizedParams };
258
270
  }
259
271
  /**
260
- * Check if a query is a read-only SELECT query
272
+ * Check if a query is a read-only SELECT query or information query (SHOW, DESCRIBE, etc.)
261
273
  */
262
274
  isReadOnlyQuery(query) {
275
+ // Check if it's an information query first (SHOW, DESCRIBE, EXPLAIN, etc.)
276
+ if (this.isInformationQuery(query)) {
277
+ return true;
278
+ }
279
+ // Check if it's a SELECT query
263
280
  const validation = this.validateQuery(query);
264
281
  return validation.valid && validation.queryType === "SELECT";
265
282
  }
@@ -0,0 +1,111 @@
1
+ import { SecurityLayer } from '../security/securityLayer';
2
+ export declare class PerformanceTools {
3
+ private db;
4
+ private security;
5
+ constructor(security: SecurityLayer);
6
+ /**
7
+ * Get comprehensive performance metrics
8
+ */
9
+ getPerformanceMetrics(): Promise<{
10
+ status: string;
11
+ data?: any;
12
+ error?: string;
13
+ queryLog?: string;
14
+ }>;
15
+ /**
16
+ * Get top queries by execution time
17
+ */
18
+ getTopQueriesByTime(params?: {
19
+ limit?: number;
20
+ }): Promise<{
21
+ status: string;
22
+ data?: any[];
23
+ error?: string;
24
+ queryLog?: string;
25
+ }>;
26
+ /**
27
+ * Get top queries by execution count
28
+ */
29
+ getTopQueriesByCount(params?: {
30
+ limit?: number;
31
+ }): Promise<{
32
+ status: string;
33
+ data?: any[];
34
+ error?: string;
35
+ queryLog?: string;
36
+ }>;
37
+ /**
38
+ * Get slow queries
39
+ */
40
+ getSlowQueries(params?: {
41
+ limit?: number;
42
+ threshold_seconds?: number;
43
+ }): Promise<{
44
+ status: string;
45
+ data?: any[];
46
+ error?: string;
47
+ queryLog?: string;
48
+ }>;
49
+ /**
50
+ * Get table I/O statistics
51
+ */
52
+ getTableIOStats(params?: {
53
+ limit?: number;
54
+ table_schema?: string;
55
+ }): Promise<{
56
+ status: string;
57
+ data?: any[];
58
+ error?: string;
59
+ queryLog?: string;
60
+ }>;
61
+ /**
62
+ * Get index usage statistics
63
+ */
64
+ getIndexUsageStats(params?: {
65
+ limit?: number;
66
+ table_schema?: string;
67
+ }): Promise<{
68
+ status: string;
69
+ data?: any[];
70
+ error?: string;
71
+ queryLog?: string;
72
+ }>;
73
+ /**
74
+ * Get unused indexes
75
+ */
76
+ getUnusedIndexes(params?: {
77
+ table_schema?: string;
78
+ }): Promise<{
79
+ status: string;
80
+ data?: any[];
81
+ error?: string;
82
+ queryLog?: string;
83
+ }>;
84
+ /**
85
+ * Get connection pool statistics
86
+ */
87
+ getConnectionPoolStats(): Promise<{
88
+ status: string;
89
+ data?: any;
90
+ error?: string;
91
+ queryLog?: string;
92
+ }>;
93
+ /**
94
+ * Get database health check
95
+ */
96
+ getDatabaseHealthCheck(): Promise<{
97
+ status: string;
98
+ data?: any;
99
+ error?: string;
100
+ queryLog?: string;
101
+ }>;
102
+ /**
103
+ * Reset performance schema statistics
104
+ */
105
+ resetPerformanceStats(): Promise<{
106
+ status: string;
107
+ message?: string;
108
+ error?: string;
109
+ queryLog?: string;
110
+ }>;
111
+ }