@berthojoris/mcp-mysql-server 1.16.4 → 1.18.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
@@ -31,9 +31,13 @@ export declare class MySQLMCP {
31
31
  private schemaDesignTools;
32
32
  private securityAuditTools;
33
33
  private indexRecommendationTools;
34
+ private testDataTools;
35
+ private schemaPatternTools;
36
+ private queryVisualizationTools;
37
+ private forecastingTools;
34
38
  private security;
35
39
  private featureConfig;
36
- constructor(permissionsConfig?: string, categoriesConfig?: string, presetName?: string);
40
+ constructor(permissionsConfig?: string, categoriesConfig?: string);
37
41
  private checkToolEnabled;
38
42
  listDatabases(): Promise<{
39
43
  status: string;
@@ -619,17 +623,12 @@ export declare class MySQLMCP {
619
623
  status: string;
620
624
  data: {
621
625
  config: {
622
- preset?: {
623
- name: string;
624
- description: string;
625
- };
626
626
  permissions: string;
627
627
  categories: string;
628
628
  filteringMode: string;
629
629
  enabledLegacy: import("./config/featureConfig").ToolCategory[];
630
630
  enabledDoc: import("./config/featureConfig").DocCategory[];
631
631
  };
632
- preset: import("./config/featureConfig").PermissionPreset | undefined;
633
632
  filteringMode: string;
634
633
  enabledCategories: import("./config/featureConfig").ToolCategory[];
635
634
  categoryStatus: Record<import("./config/featureConfig").ToolCategory, boolean>;
@@ -643,13 +642,9 @@ export declare class MySQLMCP {
643
642
  */
644
643
  isToolEnabled(toolName: string): boolean;
645
644
  /**
646
- * Expose resolved access profile (preset + merged permissions/categories)
645
+ * Expose resolved access profile (resolved permissions/categories)
647
646
  */
648
647
  getAccessProfile(): {
649
- preset?: {
650
- name: string;
651
- description: string;
652
- };
653
648
  permissions: string;
654
649
  categories: string;
655
650
  filteringMode: string;
@@ -1314,7 +1309,9 @@ export declare class MySQLMCP {
1314
1309
  column_name: string;
1315
1310
  pattern_type: string;
1316
1311
  description: string;
1317
- metrics?: Record<string, any>;
1312
+ metrics? /**
1313
+ * Get current schema version
1314
+ */: Record<string, any>;
1318
1315
  recommendations?: string[];
1319
1316
  }>;
1320
1317
  summary: {
@@ -1512,5 +1509,55 @@ export declare class MySQLMCP {
1512
1509
  };
1513
1510
  error?: string;
1514
1511
  }>;
1512
+ generateTestData(params: {
1513
+ table_name: string;
1514
+ row_count: number;
1515
+ batch_size?: number;
1516
+ include_nulls?: boolean;
1517
+ database?: string;
1518
+ }): Promise<{
1519
+ status: string;
1520
+ data?: any;
1521
+ error?: string;
1522
+ }>;
1523
+ analyzeSchemaPatterns(params?: {
1524
+ scope?: "database" | "table";
1525
+ table_name?: string;
1526
+ database?: string;
1527
+ }): Promise<{
1528
+ status: string;
1529
+ data?: any;
1530
+ error?: string;
1531
+ }>;
1532
+ visualizeQuery(params: {
1533
+ query: string;
1534
+ include_explain_json?: boolean;
1535
+ format?: "mermaid" | "json" | "both";
1536
+ }): Promise<{
1537
+ status: string;
1538
+ data?: any;
1539
+ error?: string;
1540
+ }>;
1541
+ predictQueryPerformance(params: {
1542
+ query: string;
1543
+ row_growth_multiplier?: number;
1544
+ per_table_row_growth?: Record<string, number>;
1545
+ include_explain_json?: boolean;
1546
+ }): Promise<{
1547
+ status: string;
1548
+ data?: any;
1549
+ error?: string;
1550
+ }>;
1551
+ forecastDatabaseGrowth(params?: {
1552
+ horizon_days?: number;
1553
+ growth_rate_percent_per_day?: number;
1554
+ growth_rate_percent_per_month?: number;
1555
+ per_table_growth_rate_percent_per_day?: Record<string, number>;
1556
+ database?: string;
1557
+ }): Promise<{
1558
+ status: string;
1559
+ data?: any;
1560
+ error?: string;
1561
+ }>;
1515
1562
  }
1516
1563
  export default MySQLMCP;
package/dist/index.js CHANGED
@@ -32,6 +32,10 @@ const documentationGeneratorTools_1 = require("./tools/documentationGeneratorToo
32
32
  const schemaDesignTools_1 = require("./tools/schemaDesignTools");
33
33
  const securityAuditTools_1 = require("./tools/securityAuditTools");
34
34
  const indexRecommendationTools_1 = require("./tools/indexRecommendationTools");
35
+ const testDataTools_1 = require("./tools/testDataTools");
36
+ const schemaPatternTools_1 = require("./tools/schemaPatternTools");
37
+ const queryVisualizationTools_1 = require("./tools/queryVisualizationTools");
38
+ const forecastingTools_1 = require("./tools/forecastingTools");
35
39
  const securityLayer_1 = __importDefault(require("./security/securityLayer"));
36
40
  const connection_1 = __importDefault(require("./db/connection"));
37
41
  const featureConfig_1 = require("./config/featureConfig");
@@ -40,8 +44,8 @@ const featureConfig_1 = require("./config/featureConfig");
40
44
  * A secure interface for AI models to interact with MySQL databases
41
45
  */
42
46
  class MySQLMCP {
43
- constructor(permissionsConfig, categoriesConfig, presetName) {
44
- this.featureConfig = new featureConfig_1.FeatureConfig(permissionsConfig, categoriesConfig, presetName);
47
+ constructor(permissionsConfig, categoriesConfig) {
48
+ this.featureConfig = new featureConfig_1.FeatureConfig(permissionsConfig, categoriesConfig);
45
49
  this.security = new securityLayer_1.default(this.featureConfig);
46
50
  this.dbTools = new databaseTools_1.DatabaseTools();
47
51
  this.crudTools = new crudTools_1.CrudTools(this.security);
@@ -72,6 +76,10 @@ class MySQLMCP {
72
76
  this.schemaDesignTools = new schemaDesignTools_1.SchemaDesignTools(this.security);
73
77
  this.securityAuditTools = new securityAuditTools_1.SecurityAuditTools();
74
78
  this.indexRecommendationTools = new indexRecommendationTools_1.IndexRecommendationTools(this.security);
79
+ this.testDataTools = new testDataTools_1.TestDataTools(this.security);
80
+ this.schemaPatternTools = new schemaPatternTools_1.SchemaPatternTools(this.security);
81
+ this.queryVisualizationTools = new queryVisualizationTools_1.QueryVisualizationTools(this.security);
82
+ this.forecastingTools = new forecastingTools_1.ForecastingTools(this.security);
75
83
  }
76
84
  // Helper method to check if tool is enabled
77
85
  checkToolEnabled(toolName) {
@@ -572,7 +580,6 @@ class MySQLMCP {
572
580
  status: "success",
573
581
  data: {
574
582
  config: snapshot,
575
- preset: this.featureConfig.getActivePreset(),
576
583
  filteringMode: this.featureConfig.getFilteringMode(),
577
584
  enabledCategories: this.featureConfig.getEnabledCategories(),
578
585
  categoryStatus: this.featureConfig.getCategoryStatus(),
@@ -589,7 +596,7 @@ class MySQLMCP {
589
596
  return this.featureConfig.isToolEnabled(toolName);
590
597
  }
591
598
  /**
592
- * Expose resolved access profile (preset + merged permissions/categories)
599
+ * Expose resolved access profile (resolved permissions/categories)
593
600
  */
594
601
  getAccessProfile() {
595
602
  return this.featureConfig.getConfigSnapshot();
@@ -1155,6 +1162,39 @@ class MySQLMCP {
1155
1162
  return { status: "error", error: check.error };
1156
1163
  return await this.indexRecommendationTools.recommendIndexes(params);
1157
1164
  }
1165
+ // ==========================================
1166
+ // PHASE 3: AI Enhancement Tools (Data Gen + Patterns + Visualization + Forecasting)
1167
+ // ==========================================
1168
+ async generateTestData(params) {
1169
+ const check = this.checkToolEnabled("generateTestData");
1170
+ if (!check.enabled)
1171
+ return { status: "error", error: check.error };
1172
+ return await this.testDataTools.generateTestData(params);
1173
+ }
1174
+ async analyzeSchemaPatterns(params) {
1175
+ const check = this.checkToolEnabled("analyzeSchemaPatterns");
1176
+ if (!check.enabled)
1177
+ return { status: "error", error: check.error };
1178
+ return await this.schemaPatternTools.analyzeSchemaPatterns(params);
1179
+ }
1180
+ async visualizeQuery(params) {
1181
+ const check = this.checkToolEnabled("visualizeQuery");
1182
+ if (!check.enabled)
1183
+ return { status: "error", error: check.error };
1184
+ return await this.queryVisualizationTools.visualizeQuery(params);
1185
+ }
1186
+ async predictQueryPerformance(params) {
1187
+ const check = this.checkToolEnabled("predictQueryPerformance");
1188
+ if (!check.enabled)
1189
+ return { status: "error", error: check.error };
1190
+ return await this.forecastingTools.predictQueryPerformance(params);
1191
+ }
1192
+ async forecastDatabaseGrowth(params) {
1193
+ const check = this.checkToolEnabled("forecastDatabaseGrowth");
1194
+ if (!check.enabled)
1195
+ return { status: "error", error: check.error };
1196
+ return await this.forecastingTools.forecastDatabaseGrowth(params);
1197
+ }
1158
1198
  }
1159
1199
  exports.MySQLMCP = MySQLMCP;
1160
1200
  exports.default = MySQLMCP;
@@ -10,7 +10,6 @@ const index_js_2 = require("./index.js");
10
10
  // Layer 2 (Categories): MCP_CATEGORIES (optional, for fine-grained control)
11
11
  const permissions = process.env.MCP_PERMISSIONS || process.env.MCP_CONFIG || "";
12
12
  const categories = process.env.MCP_CATEGORIES || "";
13
- const preset = process.env.MCP_PRESET || process.env.MCP_PERMISSION_PRESET || "";
14
13
  // Declare the MySQL MCP instance (will be initialized in main())
15
14
  let mysqlMCP;
16
15
  // Define all available tools with their schemas
@@ -2992,6 +2991,141 @@ const TOOLS = [
2992
2991
  },
2993
2992
  },
2994
2993
  },
2994
+ // ==========================================
2995
+ // PHASE 3: AI Enhancement Tools (Data Gen + Patterns + Visualization + Forecasting)
2996
+ // ==========================================
2997
+ {
2998
+ name: "generate_test_data",
2999
+ description: "Generates synthetic test data as SQL INSERT statements for a given table (does not execute). Attempts FK-aware value selection for referential integrity.",
3000
+ inputSchema: {
3001
+ type: "object",
3002
+ properties: {
3003
+ table_name: {
3004
+ type: "string",
3005
+ description: "Target table to generate INSERT statements for",
3006
+ },
3007
+ row_count: {
3008
+ type: "number",
3009
+ description: "Number of rows to generate (max 5000)",
3010
+ },
3011
+ batch_size: {
3012
+ type: "number",
3013
+ description: "Rows per INSERT statement (default 100, max 1000)",
3014
+ },
3015
+ include_nulls: {
3016
+ type: "boolean",
3017
+ description: "Whether generated values may include NULLs when columns are nullable (default true)",
3018
+ },
3019
+ database: {
3020
+ type: "string",
3021
+ description: "Optional: specific database name",
3022
+ },
3023
+ },
3024
+ required: ["table_name", "row_count"],
3025
+ },
3026
+ },
3027
+ {
3028
+ name: "analyze_schema_patterns",
3029
+ description: "Analyzes the schema for common patterns and anti-patterns (missing PKs, wide tables, unindexed FKs, EAV-like tables, etc.) and returns recommendations.",
3030
+ inputSchema: {
3031
+ type: "object",
3032
+ properties: {
3033
+ scope: {
3034
+ type: "string",
3035
+ enum: ["database", "table"],
3036
+ description: "Analysis scope (default: database unless table_name provided)",
3037
+ },
3038
+ table_name: {
3039
+ type: "string",
3040
+ description: "Optional: specific table to analyze (implies table scope)",
3041
+ },
3042
+ database: {
3043
+ type: "string",
3044
+ description: "Optional: specific database name",
3045
+ },
3046
+ },
3047
+ },
3048
+ },
3049
+ {
3050
+ name: "visualize_query",
3051
+ description: "Creates a visual representation of a read-only SQL query as a Mermaid flowchart, based on EXPLAIN FORMAT=JSON and lightweight SQL parsing.",
3052
+ inputSchema: {
3053
+ type: "object",
3054
+ properties: {
3055
+ query: {
3056
+ type: "string",
3057
+ description: "Read-only SQL query to visualize",
3058
+ },
3059
+ include_explain_json: {
3060
+ type: "boolean",
3061
+ description: "Include full EXPLAIN JSON in the response (default true)",
3062
+ },
3063
+ format: {
3064
+ type: "string",
3065
+ enum: ["mermaid", "json", "both"],
3066
+ description: "Output format (default: both)",
3067
+ },
3068
+ },
3069
+ required: ["query"],
3070
+ },
3071
+ },
3072
+ {
3073
+ name: "predict_query_performance",
3074
+ description: "Predicts how EXPLAIN-estimated scan volume/cost could change under table growth assumptions (heuristic).",
3075
+ inputSchema: {
3076
+ type: "object",
3077
+ properties: {
3078
+ query: {
3079
+ type: "string",
3080
+ description: "Read-only SQL query to analyze",
3081
+ },
3082
+ row_growth_multiplier: {
3083
+ type: "number",
3084
+ description: "Default multiplicative growth factor to apply to table row estimates (default 2)",
3085
+ },
3086
+ per_table_row_growth: {
3087
+ type: "object",
3088
+ description: "Optional per-table growth overrides: { tableName: factor }",
3089
+ additionalProperties: { type: "number" },
3090
+ },
3091
+ include_explain_json: {
3092
+ type: "boolean",
3093
+ description: "Include full EXPLAIN JSON in the response (default false)",
3094
+ },
3095
+ },
3096
+ required: ["query"],
3097
+ },
3098
+ },
3099
+ {
3100
+ name: "forecast_database_growth",
3101
+ description: "Forecasts database/table growth based on current INFORMATION_SCHEMA sizes and user-supplied growth rate assumptions.",
3102
+ inputSchema: {
3103
+ type: "object",
3104
+ properties: {
3105
+ horizon_days: {
3106
+ type: "number",
3107
+ description: "Forecast horizon in days (default 30, max 3650)",
3108
+ },
3109
+ growth_rate_percent_per_day: {
3110
+ type: "number",
3111
+ description: "Base daily growth rate percent applied to all tables unless overridden (e.g., 0.5)",
3112
+ },
3113
+ growth_rate_percent_per_month: {
3114
+ type: "number",
3115
+ description: "Base monthly growth rate percent (converted to daily compound rate)",
3116
+ },
3117
+ per_table_growth_rate_percent_per_day: {
3118
+ type: "object",
3119
+ description: "Optional per-table daily growth rate percent overrides: { tableName: percentPerDay }",
3120
+ additionalProperties: { type: "number" },
3121
+ },
3122
+ database: {
3123
+ type: "string",
3124
+ description: "Optional: specific database name",
3125
+ },
3126
+ },
3127
+ },
3128
+ },
2995
3129
  // Smart Data Discovery
2996
3130
  {
2997
3131
  name: "smart_search",
@@ -3179,7 +3313,7 @@ const TOOLS = [
3179
3313
  // Create the MCP server
3180
3314
  const server = new index_js_1.Server({
3181
3315
  name: "mysql-mcp-server",
3182
- version: "1.12.0",
3316
+ version: "1.17.0",
3183
3317
  }, {
3184
3318
  capabilities: {
3185
3319
  tools: {},
@@ -3652,6 +3786,24 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
3652
3786
  case "recommend_indexes":
3653
3787
  result = await mysqlMCP.recommendIndexes((args || {}));
3654
3788
  break;
3789
+ // ==========================================
3790
+ // PHASE 3: AI Enhancement Tools (Data Gen + Patterns + Visualization + Forecasting)
3791
+ // ==========================================
3792
+ case "generate_test_data":
3793
+ result = await mysqlMCP.generateTestData((args || {}));
3794
+ break;
3795
+ case "analyze_schema_patterns":
3796
+ result = await mysqlMCP.analyzeSchemaPatterns((args || {}));
3797
+ break;
3798
+ case "visualize_query":
3799
+ result = await mysqlMCP.visualizeQuery((args || {}));
3800
+ break;
3801
+ case "predict_query_performance":
3802
+ result = await mysqlMCP.predictQueryPerformance((args || {}));
3803
+ break;
3804
+ case "forecast_database_growth":
3805
+ result = await mysqlMCP.forecastDatabaseGrowth((args || {}));
3806
+ break;
3655
3807
  default:
3656
3808
  throw new Error(`Unknown tool: ${name}`);
3657
3809
  }
@@ -3735,15 +3887,9 @@ async function main() {
3735
3887
  await server.connect(transport);
3736
3888
  // Initialize the MySQL MCP instance AFTER transport is connected
3737
3889
  // This ensures the database connection pool is created when the server is ready
3738
- mysqlMCP = new index_js_2.MySQLMCP(permissions, categories, preset);
3890
+ mysqlMCP = new index_js_2.MySQLMCP(permissions, categories);
3739
3891
  // Log the effective filtering configuration to stderr
3740
3892
  const accessProfile = mysqlMCP.getAccessProfile();
3741
- if (accessProfile.preset) {
3742
- console.error(`Preset: ${accessProfile.preset.name} (${accessProfile.preset.description})`);
3743
- }
3744
- else if (preset) {
3745
- console.error(`Preset requested but not recognized: ${preset}`);
3746
- }
3747
3893
  console.error(`Permissions (resolved): ${accessProfile.permissions}`);
3748
3894
  if (accessProfile.categories) {
3749
3895
  console.error(`Categories (resolved): ${accessProfile.categories}`);
@@ -11,9 +11,8 @@ class SecurityLayer {
11
11
  constructor(featureConfig) {
12
12
  this.ajv = new ajv_1.default();
13
13
  this.featureConfig = featureConfig || new featureConfig_js_1.FeatureConfig();
14
- // Initialize masking layer from environment variable
15
- const maskingProfile = process.env.MCP_MASKING_PROFILE || "none";
16
- this.masking = new maskingLayer_js_1.MaskingLayer(maskingProfile);
14
+ // Masking is intentionally not configurable via environment variables.
15
+ this.masking = new maskingLayer_js_1.MaskingLayer("none");
17
16
  // Define dangerous SQL keywords that should ALWAYS be blocked (critical security threats)
18
17
  // These are blocked even with 'execute' permission
19
18
  // Note: Avoid blocking common table/column names like "user" or "password"
@@ -0,0 +1,36 @@
1
+ import { SecurityLayer } from "../security/securityLayer";
2
+ export declare class ForecastingTools {
3
+ private db;
4
+ private security;
5
+ constructor(security: SecurityLayer);
6
+ private validateDatabaseAccess;
7
+ private extractExplainNodes;
8
+ /**
9
+ * Predict how query cost/scan volume might change under table growth assumptions.
10
+ * This is heuristic-based and uses EXPLAIN FORMAT=JSON estimates.
11
+ */
12
+ predictQueryPerformance(params: {
13
+ query: string;
14
+ row_growth_multiplier?: number;
15
+ per_table_row_growth?: Record<string, number>;
16
+ include_explain_json?: boolean;
17
+ }): Promise<{
18
+ status: string;
19
+ data?: any;
20
+ error?: string;
21
+ }>;
22
+ /**
23
+ * Forecast database/table growth based on current sizes and user-supplied growth rate assumptions.
24
+ */
25
+ forecastDatabaseGrowth(params?: {
26
+ horizon_days?: number;
27
+ growth_rate_percent_per_day?: number;
28
+ growth_rate_percent_per_month?: number;
29
+ per_table_growth_rate_percent_per_day?: Record<string, number>;
30
+ database?: string;
31
+ }): Promise<{
32
+ status: string;
33
+ data?: any;
34
+ error?: string;
35
+ }>;
36
+ }