@berthojoris/mcp-mysql-server 1.13.0 → 1.14.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/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to the MySQL MCP Server will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.14.0] - 2025-12-08
9
+
10
+ ### Added
11
+ - **Data Masking Profiles** - New security feature to mask sensitive data in query responses.
12
+ - Configurable via `MCP_MASKING_PROFILE` environment variable.
13
+ - Profiles: `none` (default), `soft` (secrets), `partial` (secrets + PII), `strict` (all sensitive).
14
+ - Automatically applies to `run_query` and `read_records`.
15
+
8
16
  ## [1.13.0] - 2025-12-07
9
17
 
10
18
  ### Added
package/DOCUMENTATIONS.md CHANGED
@@ -7,7 +7,7 @@ This file contains detailed documentation for all features of the MySQL MCP Serv
7
7
  ## Table of Contents
8
8
 
9
9
  1. [Category Filtering System](#🆕-category-filtering-system) - NEW!
10
- 2. [🔧 Complete Tools Reference](#🔧-complete-tools-reference) - All 120 tools organized by category
10
+ 2. [🔧 Complete Tools Reference](#🔧-complete-tools-reference) - All 124 tools organized by category
11
11
  3. [DDL Operations](#🏗️-ddl-operations)
12
12
  4. [Data Export Tools](#📤-data-export-tools)
13
13
  5. [Data Import Tools](#📥-data-import-tools)
@@ -3759,7 +3759,44 @@ Each bulk operation returns performance metrics:
3759
3759
 
3760
3760
  ---
3761
3761
 
3762
- ## 🤖 OpenAI Codex Integration
3762
+ ## 🎭 Data Masking
3763
+
3764
+ The MySQL MCP server includes a robust data masking layer to protect sensitive information (PII, credentials) in query results. This is useful when sharing database access with AI agents or third parties.
3765
+
3766
+ ### Features
3767
+
3768
+ - **Profile-Based Masking**: Easy configuration profiles (`none`, `soft`, `partial`, `strict`)
3769
+ - **Automatic Detection**: Automatically identifies sensitive columns by name (e.g., `email`, `password`, `ssn`)
3770
+ - **Multiple Strategies**:
3771
+ - **REDACT**: Replaces value with `[REDACTED]`
3772
+ - **PARTIAL**: Partially masks email (`j***@d.com`) and phone/CC (`***1234`)
3773
+ - **HASH**: (Internal placeholder)
3774
+
3775
+ ### Configuration
3776
+
3777
+ Configure the masking profile via the `MCP_MASKING_PROFILE` environment variable:
3778
+
3779
+ ```bash
3780
+ MCP_MASKING_PROFILE=partial
3781
+ ```
3782
+
3783
+ ### Profiles Reference
3784
+
3785
+ | Profile | Description | Credentials (password, key) | PII (email, phone, ssn) |
3786
+ |---------|-------------|-----------------------------|-------------------------|
3787
+ | `none` | No masking (default) | Show | Show |
3788
+ | `soft` | Protect secrets only | **REDACT** | Show |
3789
+ | `partial` | Balanced security | **REDACT** | **PARTIAL** (j***@...) |
3790
+ | `strict` | Maximum security | **REDACT** | **REDACT** |
3791
+
3792
+ ### Behavior
3793
+
3794
+ - Masking applies automatically to `run_query` and `read_records` results.
3795
+ - It filters output **after** the query is run, so WHERE clauses still work on the real data (e.g., you can search by email, but the result will be masked).
3796
+
3797
+ ---
3798
+
3799
+ ## 🤖 OpenAI Codex Integration
3763
3800
 
3764
3801
  OpenAI Codex CLI and VS Code Extension support MCP servers through a shared TOML configuration file. This section provides detailed setup instructions for integrating the MySQL MCP Server with Codex.
3765
3802
 
@@ -4144,7 +4181,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
4144
4181
  | Drift & Migration Assistant (Schema diff + risk summary) | High | High | 4 | ✅ Completed |
4145
4182
  | Safety Sandbox Mode (runQuery dry-run/EXPLAIN-only) | Medium | Low | 5 | ✅ Completed |
4146
4183
  | Anomaly & Slow-Query Watcher | Medium | Medium | 6 | ✅ Completed |
4147
- | Data Masking Profiles for Responses | Medium | Medium | 7 | Planned |
4184
+ | Data Masking Profiles for Responses | Medium | Medium | 7 | Completed |
4148
4185
  | Workflow Macros (e.g., safe_export_table) | Medium | Low | 8 | Planned |
4149
4186
  | Agent-Facing Changelog Feed | Medium | Low | 9 | Planned |
4150
4187
  | Connection Profiles (dev/stage/prod with allow/deny) | High | Low | 10 | Planned |
package/README.md CHANGED
@@ -50,7 +50,7 @@ Add to your AI agent config (`.mcp.json`, `.cursor/mcp.json`, etc.):
50
50
  - [Environment Variables](#environment-variables-configuration)
51
51
  - [Local Development](#local-path-configuration)
52
52
  - [Permission System](#-permission-system)
53
- - [Available Tools (120 total)](#-available-tools)
53
+ - [Available Tools (124 total)](#-available-tools)
54
54
  - [Documentation](#-detailed-documentation)
55
55
  - [Comparison: MCP vs Manual Access](#-mysql-mcp-vs-manual-database-access)
56
56
  - [License](#-license)
@@ -63,13 +63,14 @@ Add to your AI agent config (`.mcp.json`, `.cursor/mcp.json`, etc.):
63
63
  |----------|-------------|
64
64
  | **Full MCP Support** | Works with Claude Code, Cursor, Windsurf, Zed, Cline, Kilo Code, Roo Code, Gemini CLI, OpenAI Codex, and any MCP-compatible AI agent |
65
65
  | **Security First** | Parameterized queries, SQL injection protection, permission-based access control |
66
- | **121 Powerful Tools** | Complete database operations including CRUD, DDL, transactions, stored procedures, backup/restore, migrations |
66
+ | **124 Powerful Tools** | Complete database operations including CRUD, DDL, transactions, stored procedures, backup/restore, migrations |
67
67
  | **Adaptive Presets** | Built-in ReadOnly/Analyst/DBA Lite permission bundles with override merging |
68
68
  | **Schema-Aware RAG Pack** | Compact schema snapshots (tables, PK/FK, row estimates) tailored for embeddings-friendly prompts |
69
69
  | **Category Filtering** | 22 documentation categories for intuitive, fine-grained access control (backward compatible with 10 legacy categories) |
70
70
  | **Transaction Support** | Full ACID transaction management (BEGIN, COMMIT, ROLLBACK) |
71
71
  | **Schema Migrations** | Version control for database schema with up/down migrations |
72
72
  | **Dual Mode** | Run as MCP server OR as REST API |
73
+ | **Data Masking** | Protect PII/Secrets in responses with configurable profiles (soft/partial/strict) |
73
74
  | **TypeScript** | Fully typed with TypeScript definitions |
74
75
 
75
76
  ---
@@ -106,6 +107,7 @@ DB_USER=root
106
107
  DB_PASSWORD=yourpassword
107
108
  DB_NAME=yourdatabase
108
109
  MCP_CONFIG=list,read,utility
110
+ MCP_MASKING_PROFILE=partial
109
111
  ```
110
112
 
111
113
  ### 2. Build Project (If Cloned Locally)
@@ -367,7 +369,8 @@ Alternative approach using environment variables instead of connection string:
367
369
  "DB_PASSWORD": "your_password",
368
370
  "DB_NAME": "your_database",
369
371
  "MCP_PERMISSIONS": "list,read,utility",
370
- "MCP_CATEGORIES": "database_discovery,performance_monitoring"
372
+ "MCP_CATEGORIES": "database_discovery,performance_monitoring",
373
+ "MCP_MASKING_PROFILE": "partial"
371
374
  }
372
375
  }
373
376
  }
@@ -496,12 +499,12 @@ Use these categories for fine-grained control that matches the tool organization
496
499
  | `server_management` | 9 | Process list, explain queries, server info |
497
500
  | `performance_monitoring` | 10 | Metrics, slow queries, health checks |
498
501
  | `cache_management` | 5 | Query cache stats and configuration |
499
- | `query_optimization` | 2 | Analyze queries, get optimization hints |
502
+ | `query_optimization` | 3 | Analyze queries, get optimization hints |
500
503
  | `backup_restore` | 5 | Backup/restore database and tables |
501
504
  | `import_export` | 5 | Import/export JSON, CSV, SQL |
502
505
  | `data_migration` | 5 | Copy, move, clone, sync table data |
503
506
  | `schema_migrations` | 9 | Version control for database schema |
504
- | `analysis` | 3 | AI context optimization and data analysis |
507
+ | `analysis` | 4 | AI context optimization and data analysis |
505
508
 
506
509
  ### Legacy Categories (Backward Compatible)
507
510
 
@@ -531,7 +534,7 @@ Use these categories for fine-grained control that matches the tool organization
531
534
  | **Application Backend** | `database_discovery,crud_operations,bulk_operations,custom_queries,transaction_management` | Full app support |
532
535
  | **Development & Testing** | `database_discovery,crud_operations,bulk_operations,custom_queries,schema_management,utilities,transaction_management` | Development access |
533
536
  | **DBA & DevOps** | `database_discovery,schema_management,table_maintenance,backup_restore,schema_migrations,performance_monitoring` | Admin tasks |
534
- | **Full Access** | *(leave empty)* | All 120 tools enabled |
537
+ | **Full Access** | *(leave empty)* | All 124 tools enabled |
535
538
 
536
539
  #### Using Legacy Categories (Backward Compatible)
537
540
 
@@ -652,11 +655,11 @@ Use both 2nd argument (permissions) and 3rd argument (categories):
652
655
 
653
656
  ## Available Tools
654
657
 
655
- The MCP server provides **120 powerful tools** organized into categories:
658
+ The MCP server provides **124 powerful tools** organized into categories:
656
659
 
657
660
  ### Quick Reference
658
661
 
659
- **121 Tools Available** - Organized into 22 categories
662
+ **124 Tools Available** - Organized into 22 categories
660
663
 
661
664
  | Category | Count | Key Tools |
662
665
  |----------|-------|-----------|
@@ -0,0 +1,37 @@
1
+ export declare enum MaskingStrategy {
2
+ REDACT = "redact",// Replace with [REDACTED]
3
+ PARTIAL = "partial",// Show first/last chars: a***@b.com
4
+ HASH = "hash",// SHA256 hash (simulated for simplicity or real)
5
+ NONE = "none"
6
+ }
7
+ export interface MaskingRule {
8
+ columnPattern: RegExp;
9
+ strategy: MaskingStrategy;
10
+ }
11
+ export declare enum MaskingProfile {
12
+ NONE = "none",
13
+ SOFT = "soft",// Mask only credentials (passwords, secrets)
14
+ PARTIAL = "partial",// Mask credentials + partial mask PII (email, phone)
15
+ STRICT = "strict"
16
+ }
17
+ /**
18
+ * Data Masking Layer
19
+ * Handles identifying and masking sensitive data in query results
20
+ */
21
+ export declare class MaskingLayer {
22
+ private profile;
23
+ private rules;
24
+ constructor(profile?: string);
25
+ private parseProfile;
26
+ private getRulesForProfile;
27
+ /**
28
+ * Check if filtering is active
29
+ */
30
+ isEnabled(): boolean;
31
+ getProfile(): MaskingProfile;
32
+ /**
33
+ * Apply masking to a dataset (array of objects)
34
+ */
35
+ processResults(data: any[]): any[];
36
+ private applyStrategy;
37
+ }
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MaskingLayer = exports.MaskingProfile = exports.MaskingStrategy = void 0;
4
+ var MaskingStrategy;
5
+ (function (MaskingStrategy) {
6
+ MaskingStrategy["REDACT"] = "redact";
7
+ MaskingStrategy["PARTIAL"] = "partial";
8
+ MaskingStrategy["HASH"] = "hash";
9
+ MaskingStrategy["NONE"] = "none"; // No masking
10
+ })(MaskingStrategy || (exports.MaskingStrategy = MaskingStrategy = {}));
11
+ var MaskingProfile;
12
+ (function (MaskingProfile) {
13
+ MaskingProfile["NONE"] = "none";
14
+ MaskingProfile["SOFT"] = "soft";
15
+ MaskingProfile["PARTIAL"] = "partial";
16
+ MaskingProfile["STRICT"] = "strict"; // Redact all PII and credentials
17
+ })(MaskingProfile || (exports.MaskingProfile = MaskingProfile = {}));
18
+ /**
19
+ * Data Masking Layer
20
+ * Handles identifying and masking sensitive data in query results
21
+ */
22
+ class MaskingLayer {
23
+ constructor(profile = "none") {
24
+ this.profile = this.parseProfile(profile);
25
+ this.rules = this.getRulesForProfile(this.profile);
26
+ }
27
+ parseProfile(input) {
28
+ const normalized = input.toLowerCase().trim();
29
+ if (Object.values(MaskingProfile).includes(normalized)) {
30
+ return normalized;
31
+ }
32
+ return MaskingProfile.NONE;
33
+ }
34
+ getRulesForProfile(profile) {
35
+ const credentialsPattern = /^(password|passwd|pwd|secret|token|api_key|auth_key|access_token|refresh_token)$/i;
36
+ const piiPattern = /^(email|phone|mobile|cell|ssn|social_security|credit_card|cc_number|card_number|iban|dob|date_of_birth)$/i;
37
+ switch (profile) {
38
+ case MaskingProfile.NONE:
39
+ return [];
40
+ case MaskingProfile.SOFT:
41
+ return [
42
+ { columnPattern: credentialsPattern, strategy: MaskingStrategy.REDACT }
43
+ ];
44
+ case MaskingProfile.PARTIAL:
45
+ return [
46
+ { columnPattern: credentialsPattern, strategy: MaskingStrategy.REDACT },
47
+ { columnPattern: piiPattern, strategy: MaskingStrategy.PARTIAL }
48
+ ];
49
+ case MaskingProfile.STRICT:
50
+ return [
51
+ { columnPattern: credentialsPattern, strategy: MaskingStrategy.REDACT },
52
+ { columnPattern: piiPattern, strategy: MaskingStrategy.REDACT }
53
+ ];
54
+ default:
55
+ return [];
56
+ }
57
+ }
58
+ /**
59
+ * Check if filtering is active
60
+ */
61
+ isEnabled() {
62
+ return this.profile !== MaskingProfile.NONE;
63
+ }
64
+ getProfile() {
65
+ return this.profile;
66
+ }
67
+ /**
68
+ * Apply masking to a dataset (array of objects)
69
+ */
70
+ processResults(data) {
71
+ if (!this.isEnabled() || !data || data.length === 0) {
72
+ return data;
73
+ }
74
+ // Identify columns to mask based on the first record (optimization)
75
+ const firstRecord = data[0];
76
+ const columns = Object.keys(firstRecord);
77
+ const columnsToMask = [];
78
+ for (const col of columns) {
79
+ for (const rule of this.rules) {
80
+ if (rule.columnPattern.test(col)) {
81
+ columnsToMask.push({ col, strategy: rule.strategy });
82
+ break; // Apply first matching rule
83
+ }
84
+ }
85
+ }
86
+ if (columnsToMask.length === 0) {
87
+ // No sensitive columns found
88
+ return data;
89
+ }
90
+ // Apply masking to all records
91
+ return data.map(record => {
92
+ const maskedRecord = { ...record };
93
+ for (const { col, strategy } of columnsToMask) {
94
+ if (maskedRecord[col] !== null && maskedRecord[col] !== undefined) {
95
+ maskedRecord[col] = this.applyStrategy(maskedRecord[col], strategy);
96
+ }
97
+ }
98
+ return maskedRecord;
99
+ });
100
+ }
101
+ applyStrategy(value, strategy) {
102
+ const strVal = String(value);
103
+ switch (strategy) {
104
+ case MaskingStrategy.REDACT:
105
+ return "[REDACTED]";
106
+ case MaskingStrategy.PARTIAL:
107
+ if (strVal.includes('@')) {
108
+ // Email masking: j***@domain.com
109
+ const [local, domain] = strVal.split('@');
110
+ const maskedLocal = local.length > 2 ? local[0] + '***' + local[local.length - 1] : '***';
111
+ return `${maskedLocal}@${domain}`;
112
+ }
113
+ else if (strVal.length > 4) {
114
+ // Generic partial: show last 4 chars (e.g. phone/cc)
115
+ // or first 1 + last 4
116
+ return '***' + strVal.slice(-4);
117
+ }
118
+ else {
119
+ return "***";
120
+ }
121
+ case MaskingStrategy.HASH:
122
+ // Simple placeholder for hash to avoid crypto dependency if not needed,
123
+ // or use a simple consistent hash if strictly required.
124
+ // For now, let's use a redaction-like placeholder to indicate hashing intent.
125
+ return "[HASHED]";
126
+ default:
127
+ return value;
128
+ }
129
+ }
130
+ }
131
+ exports.MaskingLayer = MaskingLayer;
@@ -1,10 +1,12 @@
1
1
  import { FeatureConfig } from "../config/featureConfig.js";
2
+ import { MaskingLayer } from "./maskingLayer.js";
2
3
  export declare class SecurityLayer {
3
4
  private ajv;
4
5
  private readonly dangerousKeywords;
5
6
  private readonly allowedOperations;
6
7
  private readonly ddlOperations;
7
8
  private featureConfig;
9
+ masking: MaskingLayer;
8
10
  constructor(featureConfig?: FeatureConfig);
9
11
  /**
10
12
  * Check if a query is a read-only information query (SHOW, DESCRIBE, EXPLAIN, etc.)
@@ -6,10 +6,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SecurityLayer = void 0;
7
7
  const ajv_1 = __importDefault(require("ajv"));
8
8
  const featureConfig_js_1 = require("../config/featureConfig.js");
9
+ const maskingLayer_js_1 = require("./maskingLayer.js");
9
10
  class SecurityLayer {
10
11
  constructor(featureConfig) {
11
12
  this.ajv = new ajv_1.default();
12
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);
13
17
  // Define dangerous SQL keywords that should ALWAYS be blocked (critical security threats)
14
18
  // These are blocked even with 'execute' permission
15
19
  // Note: Avoid blocking common table/column names like "user" or "password"
@@ -187,7 +187,7 @@ class CrudTools {
187
187
  const results = await this.db.query(query, paramValidation.sanitizedParams);
188
188
  return {
189
189
  status: "success",
190
- data: results,
190
+ data: this.security.masking.processResults(results),
191
191
  total,
192
192
  };
193
193
  }
@@ -197,7 +197,7 @@ class CrudTools {
197
197
  const results = await this.db.query(query, paramValidation.sanitizedParams);
198
198
  return {
199
199
  status: "success",
200
- data: results,
200
+ data: this.security.masking.processResults(results),
201
201
  total: results.length,
202
202
  };
203
203
  }
@@ -90,9 +90,10 @@ class QueryTools {
90
90
  }
91
91
  // Execute the query with sanitized parameters
92
92
  const results = await this.db.query(finalQuery, paramValidation.sanitizedParams, useCache);
93
+ const maskedResults = this.security.masking.processResults(results);
93
94
  return {
94
95
  status: "success",
95
- data: results,
96
+ data: maskedResults,
96
97
  optimizedQuery,
97
98
  };
98
99
  }
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mysql-mcp",
3
3
  "description": "A Model Context Protocol for MySQL database interaction",
4
- "version": "1.12.0",
4
+ "version": "1.13.0",
5
5
  "tools": [
6
6
  {
7
7
  "name": "list_databases",
@@ -9,7 +9,9 @@
9
9
  "input_schema": {},
10
10
  "output_schema": {
11
11
  "type": "array",
12
- "items": { "type": "string" }
12
+ "items": {
13
+ "type": "string"
14
+ }
13
15
  }
14
16
  },
15
17
  {
@@ -18,7 +20,9 @@
18
20
  "input_schema": {},
19
21
  "output_schema": {
20
22
  "type": "array",
21
- "items": { "type": "string" }
23
+ "items": {
24
+ "type": "string"
25
+ }
22
26
  }
23
27
  },
24
28
  {
@@ -27,9 +31,13 @@
27
31
  "input_schema": {
28
32
  "type": "object",
29
33
  "properties": {
30
- "table_name": { "type": "string" }
34
+ "table_name": {
35
+ "type": "string"
36
+ }
31
37
  },
32
- "required": ["table_name"]
38
+ "required": [
39
+ "table_name"
40
+ ]
33
41
  },
34
42
  "output_schema": {
35
43
  "type": "object",
@@ -39,18 +47,38 @@
39
47
  "items": {
40
48
  "type": "object",
41
49
  "properties": {
42
- "name": { "type": "string" },
43
- "type": { "type": "string" },
44
- "nullable": { "type": "boolean" },
45
- "default": { "type": ["string", "null"] },
46
- "primary_key": { "type": "boolean" }
50
+ "name": {
51
+ "type": "string"
52
+ },
53
+ "type": {
54
+ "type": "string"
55
+ },
56
+ "nullable": {
57
+ "type": "boolean"
58
+ },
59
+ "default": {
60
+ "type": [
61
+ "string",
62
+ "null"
63
+ ]
64
+ },
65
+ "primary_key": {
66
+ "type": "boolean"
67
+ }
47
68
  }
48
69
  }
49
70
  },
50
- "primary_key": { "type": ["string", "null"] },
71
+ "primary_key": {
72
+ "type": [
73
+ "string",
74
+ "null"
75
+ ]
76
+ },
51
77
  "indexes": {
52
78
  "type": "array",
53
- "items": { "type": "string" }
79
+ "items": {
80
+ "type": "string"
81
+ }
54
82
  }
55
83
  }
56
84
  }
@@ -61,17 +89,33 @@
61
89
  "input_schema": {
62
90
  "type": "object",
63
91
  "properties": {
64
- "table_name": { "type": "string" },
65
- "data": { "type": "object" }
92
+ "table_name": {
93
+ "type": "string"
94
+ },
95
+ "data": {
96
+ "type": "object"
97
+ }
66
98
  },
67
- "required": ["table_name", "data"]
99
+ "required": [
100
+ "table_name",
101
+ "data"
102
+ ]
68
103
  },
69
104
  "output_schema": {
70
105
  "type": "object",
71
106
  "properties": {
72
- "success": { "type": "boolean" },
73
- "id": { "type": ["string", "number"] },
74
- "affected_rows": { "type": "number" }
107
+ "success": {
108
+ "type": "boolean"
109
+ },
110
+ "id": {
111
+ "type": [
112
+ "string",
113
+ "number"
114
+ ]
115
+ },
116
+ "affected_rows": {
117
+ "type": "number"
118
+ }
75
119
  }
76
120
  }
77
121
  },
@@ -81,20 +125,42 @@
81
125
  "input_schema": {
82
126
  "type": "object",
83
127
  "properties": {
84
- "table_name": { "type": "string" },
85
- "filters": { "type": "object" },
86
- "limit": { "type": "number" },
87
- "offset": { "type": "number" },
88
- "sort_by": { "type": "string" },
89
- "sort_direction": { "type": "string", "enum": ["ASC", "DESC"] }
128
+ "table_name": {
129
+ "type": "string"
130
+ },
131
+ "filters": {
132
+ "type": "object"
133
+ },
134
+ "limit": {
135
+ "type": "number"
136
+ },
137
+ "offset": {
138
+ "type": "number"
139
+ },
140
+ "sort_by": {
141
+ "type": "string"
142
+ },
143
+ "sort_direction": {
144
+ "type": "string",
145
+ "enum": [
146
+ "ASC",
147
+ "DESC"
148
+ ]
149
+ }
90
150
  },
91
- "required": ["table_name"]
151
+ "required": [
152
+ "table_name"
153
+ ]
92
154
  },
93
155
  "output_schema": {
94
156
  "type": "object",
95
157
  "properties": {
96
- "records": { "type": "array" },
97
- "total": { "type": "number" }
158
+ "records": {
159
+ "type": "array"
160
+ },
161
+ "total": {
162
+ "type": "number"
163
+ }
98
164
  }
99
165
  }
100
166
  },
@@ -104,18 +170,37 @@
104
170
  "input_schema": {
105
171
  "type": "object",
106
172
  "properties": {
107
- "table_name": { "type": "string" },
108
- "id_field": { "type": "string" },
109
- "id": { "type": ["string", "number"] },
110
- "data": { "type": "object" }
173
+ "table_name": {
174
+ "type": "string"
175
+ },
176
+ "id_field": {
177
+ "type": "string"
178
+ },
179
+ "id": {
180
+ "type": [
181
+ "string",
182
+ "number"
183
+ ]
184
+ },
185
+ "data": {
186
+ "type": "object"
187
+ }
111
188
  },
112
- "required": ["table_name", "id", "data"]
189
+ "required": [
190
+ "table_name",
191
+ "id",
192
+ "data"
193
+ ]
113
194
  },
114
195
  "output_schema": {
115
196
  "type": "object",
116
197
  "properties": {
117
- "success": { "type": "boolean" },
118
- "affected_rows": { "type": "number" }
198
+ "success": {
199
+ "type": "boolean"
200
+ },
201
+ "affected_rows": {
202
+ "type": "number"
203
+ }
119
204
  }
120
205
  }
121
206
  },
@@ -125,17 +210,33 @@
125
210
  "input_schema": {
126
211
  "type": "object",
127
212
  "properties": {
128
- "table_name": { "type": "string" },
129
- "id_field": { "type": "string" },
130
- "id": { "type": ["string", "number"] }
213
+ "table_name": {
214
+ "type": "string"
215
+ },
216
+ "id_field": {
217
+ "type": "string"
218
+ },
219
+ "id": {
220
+ "type": [
221
+ "string",
222
+ "number"
223
+ ]
224
+ }
131
225
  },
132
- "required": ["table_name", "id"]
226
+ "required": [
227
+ "table_name",
228
+ "id"
229
+ ]
133
230
  },
134
231
  "output_schema": {
135
232
  "type": "object",
136
233
  "properties": {
137
- "success": { "type": "boolean" },
138
- "affected_rows": { "type": "number" }
234
+ "success": {
235
+ "type": "boolean"
236
+ },
237
+ "affected_rows": {
238
+ "type": "number"
239
+ }
139
240
  }
140
241
  }
141
242
  },
@@ -145,16 +246,26 @@
145
246
  "input_schema": {
146
247
  "type": "object",
147
248
  "properties": {
148
- "query": { "type": "string" },
149
- "params": { "type": "array" }
249
+ "query": {
250
+ "type": "string"
251
+ },
252
+ "params": {
253
+ "type": "array"
254
+ }
150
255
  },
151
- "required": ["query"]
256
+ "required": [
257
+ "query"
258
+ ]
152
259
  },
153
260
  "output_schema": {
154
261
  "type": "object",
155
262
  "properties": {
156
- "results": { "type": "array" },
157
- "fields": { "type": "array" }
263
+ "results": {
264
+ "type": "array"
265
+ },
266
+ "fields": {
267
+ "type": "array"
268
+ }
158
269
  }
159
270
  }
160
271
  },
@@ -164,17 +275,32 @@
164
275
  "input_schema": {
165
276
  "type": "object",
166
277
  "properties": {
167
- "query": { "type": "string" },
168
- "params": { "type": "array" }
278
+ "query": {
279
+ "type": "string"
280
+ },
281
+ "params": {
282
+ "type": "array"
283
+ }
169
284
  },
170
- "required": ["query"]
285
+ "required": [
286
+ "query"
287
+ ]
171
288
  },
172
289
  "output_schema": {
173
290
  "type": "object",
174
291
  "properties": {
175
- "success": { "type": "boolean" },
176
- "affected_rows": { "type": "number" },
177
- "insert_id": { "type": ["number", "null"] }
292
+ "success": {
293
+ "type": "boolean"
294
+ },
295
+ "affected_rows": {
296
+ "type": "number"
297
+ },
298
+ "insert_id": {
299
+ "type": [
300
+ "number",
301
+ "null"
302
+ ]
303
+ }
178
304
  }
179
305
  }
180
306
  },
@@ -185,11 +311,21 @@
185
311
  "output_schema": {
186
312
  "type": "object",
187
313
  "properties": {
188
- "host": { "type": "string" },
189
- "port": { "type": "number" },
190
- "database": { "type": "string" },
191
- "user": { "type": "string" },
192
- "connected": { "type": "boolean" }
314
+ "host": {
315
+ "type": "string"
316
+ },
317
+ "port": {
318
+ "type": "number"
319
+ },
320
+ "database": {
321
+ "type": "string"
322
+ },
323
+ "user": {
324
+ "type": "string"
325
+ },
326
+ "connected": {
327
+ "type": "boolean"
328
+ }
193
329
  }
194
330
  }
195
331
  },
@@ -200,9 +336,15 @@
200
336
  "output_schema": {
201
337
  "type": "object",
202
338
  "properties": {
203
- "success": { "type": "boolean" },
204
- "latency_ms": { "type": "number" },
205
- "message": { "type": "string" }
339
+ "success": {
340
+ "type": "boolean"
341
+ },
342
+ "latency_ms": {
343
+ "type": "number"
344
+ },
345
+ "message": {
346
+ "type": "string"
347
+ }
206
348
  }
207
349
  }
208
350
  },
@@ -212,9 +354,13 @@
212
354
  "input_schema": {
213
355
  "type": "object",
214
356
  "properties": {
215
- "table_name": { "type": "string" }
357
+ "table_name": {
358
+ "type": "string"
359
+ }
216
360
  },
217
- "required": ["table_name"]
361
+ "required": [
362
+ "table_name"
363
+ ]
218
364
  },
219
365
  "output_schema": {
220
366
  "type": "object",
@@ -224,9 +370,15 @@
224
370
  "items": {
225
371
  "type": "object",
226
372
  "properties": {
227
- "table": { "type": "string" },
228
- "column": { "type": "string" },
229
- "referenced_column": { "type": "string" }
373
+ "table": {
374
+ "type": "string"
375
+ },
376
+ "column": {
377
+ "type": "string"
378
+ },
379
+ "referenced_column": {
380
+ "type": "string"
381
+ }
230
382
  }
231
383
  }
232
384
  },
@@ -235,9 +387,15 @@
235
387
  "items": {
236
388
  "type": "object",
237
389
  "properties": {
238
- "table": { "type": "string" },
239
- "column": { "type": "string" },
240
- "referenced_column": { "type": "string" }
390
+ "table": {
391
+ "type": "string"
392
+ },
393
+ "column": {
394
+ "type": "string"
395
+ },
396
+ "referenced_column": {
397
+ "type": "string"
398
+ }
241
399
  }
242
400
  }
243
401
  }
@@ -250,18 +408,36 @@
250
408
  "input_schema": {
251
409
  "type": "object",
252
410
  "properties": {
253
- "database": { "type": "string", "description": "Optional specific database name" },
254
- "max_tables": { "type": "number", "description": "Max tables to include (default 50, max 200)" },
255
- "max_columns": { "type": "number", "description": "Max columns per table (default 12, max 200)" },
256
- "include_relationships": { "type": "boolean", "description": "Whether to include FK relationships (default true)" }
411
+ "database": {
412
+ "type": "string",
413
+ "description": "Optional specific database name"
414
+ },
415
+ "max_tables": {
416
+ "type": "number",
417
+ "description": "Max tables to include (default 50, max 200)"
418
+ },
419
+ "max_columns": {
420
+ "type": "number",
421
+ "description": "Max columns per table (default 12, max 200)"
422
+ },
423
+ "include_relationships": {
424
+ "type": "boolean",
425
+ "description": "Whether to include FK relationships (default true)"
426
+ }
257
427
  }
258
428
  },
259
429
  "output_schema": {
260
430
  "type": "object",
261
431
  "properties": {
262
- "database": { "type": "string" },
263
- "total_tables": { "type": "number" },
264
- "context_text": { "type": "string" }
432
+ "database": {
433
+ "type": "string"
434
+ },
435
+ "total_tables": {
436
+ "type": "number"
437
+ },
438
+ "context_text": {
439
+ "type": "string"
440
+ }
265
441
  }
266
442
  }
267
443
  },
@@ -289,23 +465,77 @@
289
465
  "items": {
290
466
  "type": "object",
291
467
  "properties": {
292
- "table_name": { "type": "string" },
293
- "row_count": { "type": "number" },
294
- "data_size_bytes": { "type": "number" },
295
- "index_size_bytes": { "type": "number" },
296
- "total_size_mb": { "type": "string" }
468
+ "table_name": {
469
+ "type": "string"
470
+ },
471
+ "row_count": {
472
+ "type": "number"
473
+ },
474
+ "data_size_bytes": {
475
+ "type": "number"
476
+ },
477
+ "index_size_bytes": {
478
+ "type": "number"
479
+ },
480
+ "total_size_mb": {
481
+ "type": "string"
482
+ }
297
483
  }
298
484
  }
299
485
  },
300
486
  "summary": {
301
487
  "type": "object",
302
488
  "properties": {
303
- "total_tables": { "type": "number" },
304
- "total_size_mb": { "type": "string" }
489
+ "total_tables": {
490
+ "type": "number"
491
+ },
492
+ "total_size_mb": {
493
+ "type": "string"
494
+ }
495
+ }
496
+ }
497
+ }
498
+ }
499
+ },
500
+ {
501
+ "name": "repair_query",
502
+ "description": "Analyzes a SQL query (and optional error) to suggest repairs or optimizations using EXPLAIN and heuristics.",
503
+ "input_schema": {
504
+ "type": "object",
505
+ "properties": {
506
+ "query": {
507
+ "type": "string",
508
+ "description": "The SQL query to analyze or repair"
509
+ },
510
+ "error_message": {
511
+ "type": "string",
512
+ "description": "Optional error message received when executing the query"
513
+ }
514
+ },
515
+ "required": [
516
+ "query"
517
+ ]
518
+ },
519
+ "output_schema": {
520
+ "type": "object",
521
+ "properties": {
522
+ "status": {
523
+ "type": "string"
524
+ },
525
+ "analysis": {
526
+ "type": "object"
527
+ },
528
+ "fixed_query": {
529
+ "type": "string"
530
+ },
531
+ "suggestions": {
532
+ "type": "array",
533
+ "items": {
534
+ "type": "string"
305
535
  }
306
536
  }
307
537
  }
308
538
  }
309
539
  }
310
540
  ]
311
- }
541
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@berthojoris/mcp-mysql-server",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "Model Context Protocol server for MySQL database integration with dynamic per-project permissions, backup/restore, data import/export, and data migration capabilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",