@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 +8 -0
- package/DOCUMENTATIONS.md +40 -3
- package/README.md +11 -8
- package/dist/security/maskingLayer.d.ts +37 -0
- package/dist/security/maskingLayer.js +131 -0
- package/dist/security/securityLayer.d.ts +2 -0
- package/dist/security/securityLayer.js +4 -0
- package/dist/tools/crudTools.js +2 -2
- package/dist/tools/queryTools.js +2 -1
- package/manifest.json +312 -82
- package/package.json +1 -1
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
|
|
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
|
-
##
|
|
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 |
|
|
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 (
|
|
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
|
-
| **
|
|
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` |
|
|
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` |
|
|
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
|
|
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 **
|
|
658
|
+
The MCP server provides **124 powerful tools** organized into categories:
|
|
656
659
|
|
|
657
660
|
### Quick Reference
|
|
658
661
|
|
|
659
|
-
**
|
|
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"
|
package/dist/tools/crudTools.js
CHANGED
|
@@ -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
|
}
|
package/dist/tools/queryTools.js
CHANGED
|
@@ -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:
|
|
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.
|
|
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": {
|
|
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": {
|
|
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": {
|
|
34
|
+
"table_name": {
|
|
35
|
+
"type": "string"
|
|
36
|
+
}
|
|
31
37
|
},
|
|
32
|
-
"required": [
|
|
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": {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
|
|
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": {
|
|
71
|
+
"primary_key": {
|
|
72
|
+
"type": [
|
|
73
|
+
"string",
|
|
74
|
+
"null"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
51
77
|
"indexes": {
|
|
52
78
|
"type": "array",
|
|
53
|
-
"items": {
|
|
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": {
|
|
65
|
-
|
|
92
|
+
"table_name": {
|
|
93
|
+
"type": "string"
|
|
94
|
+
},
|
|
95
|
+
"data": {
|
|
96
|
+
"type": "object"
|
|
97
|
+
}
|
|
66
98
|
},
|
|
67
|
-
"required": [
|
|
99
|
+
"required": [
|
|
100
|
+
"table_name",
|
|
101
|
+
"data"
|
|
102
|
+
]
|
|
68
103
|
},
|
|
69
104
|
"output_schema": {
|
|
70
105
|
"type": "object",
|
|
71
106
|
"properties": {
|
|
72
|
-
"success": {
|
|
73
|
-
|
|
74
|
-
|
|
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": {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
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": [
|
|
151
|
+
"required": [
|
|
152
|
+
"table_name"
|
|
153
|
+
]
|
|
92
154
|
},
|
|
93
155
|
"output_schema": {
|
|
94
156
|
"type": "object",
|
|
95
157
|
"properties": {
|
|
96
|
-
"records": {
|
|
97
|
-
|
|
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": {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
"
|
|
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": [
|
|
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": {
|
|
118
|
-
|
|
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": {
|
|
129
|
-
|
|
130
|
-
|
|
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": [
|
|
226
|
+
"required": [
|
|
227
|
+
"table_name",
|
|
228
|
+
"id"
|
|
229
|
+
]
|
|
133
230
|
},
|
|
134
231
|
"output_schema": {
|
|
135
232
|
"type": "object",
|
|
136
233
|
"properties": {
|
|
137
|
-
"success": {
|
|
138
|
-
|
|
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": {
|
|
149
|
-
|
|
249
|
+
"query": {
|
|
250
|
+
"type": "string"
|
|
251
|
+
},
|
|
252
|
+
"params": {
|
|
253
|
+
"type": "array"
|
|
254
|
+
}
|
|
150
255
|
},
|
|
151
|
-
"required": [
|
|
256
|
+
"required": [
|
|
257
|
+
"query"
|
|
258
|
+
]
|
|
152
259
|
},
|
|
153
260
|
"output_schema": {
|
|
154
261
|
"type": "object",
|
|
155
262
|
"properties": {
|
|
156
|
-
"results": {
|
|
157
|
-
|
|
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": {
|
|
168
|
-
|
|
278
|
+
"query": {
|
|
279
|
+
"type": "string"
|
|
280
|
+
},
|
|
281
|
+
"params": {
|
|
282
|
+
"type": "array"
|
|
283
|
+
}
|
|
169
284
|
},
|
|
170
|
-
"required": [
|
|
285
|
+
"required": [
|
|
286
|
+
"query"
|
|
287
|
+
]
|
|
171
288
|
},
|
|
172
289
|
"output_schema": {
|
|
173
290
|
"type": "object",
|
|
174
291
|
"properties": {
|
|
175
|
-
"success": {
|
|
176
|
-
|
|
177
|
-
|
|
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": {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
"
|
|
192
|
-
|
|
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": {
|
|
204
|
-
|
|
205
|
-
|
|
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": {
|
|
357
|
+
"table_name": {
|
|
358
|
+
"type": "string"
|
|
359
|
+
}
|
|
216
360
|
},
|
|
217
|
-
"required": [
|
|
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": {
|
|
228
|
-
|
|
229
|
-
|
|
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": {
|
|
239
|
-
|
|
240
|
-
|
|
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": {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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": {
|
|
263
|
-
|
|
264
|
-
|
|
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": {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
"
|
|
296
|
-
|
|
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": {
|
|
304
|
-
|
|
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.
|
|
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",
|