@berthojoris/mcp-mysql-server 1.13.0 β 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/DOCUMENTATIONS.md +81 -16
- package/README.md +10 -21
- package/dist/config/featureConfig.js +1 -13
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/mcp-server.js +33 -0
- 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/macroTools.d.ts +20 -0
- package/dist/tools/macroTools.js +91 -0
- package/dist/tools/queryTools.js +2 -1
- package/manifest.json +312 -82
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ 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.1] - 2025-12-08
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Workflow Macros** - New `safe_export_table` tool that combines data export with mandatory masking.
|
|
12
|
+
- Allows safe export of sensitive data by enforcing masking before the data leaves the database.
|
|
13
|
+
- Supports configurable masking profiles (strict (default), partial, soft).
|
|
14
|
+
|
|
15
|
+
## [1.14.0] - 2025-12-08
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **Data Masking Profiles** - New security feature to mask sensitive data in query responses.
|
|
19
|
+
- Configurable via `MCP_MASKING_PROFILE` environment variable.
|
|
20
|
+
- Profiles: `none` (default), `soft` (secrets), `partial` (secrets + PII), `strict` (all sensitive).
|
|
21
|
+
- Automatically applies to `run_query` and `read_records`.
|
|
22
|
+
|
|
8
23
|
## [1.13.0] - 2025-12-07
|
|
9
24
|
|
|
10
25
|
### 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)
|
|
@@ -26,19 +26,6 @@ This file contains detailed documentation for all features of the MySQL MCP Serv
|
|
|
26
26
|
18. [Performance Monitoring](#π-performance-monitoring)
|
|
27
27
|
19. [Usage Examples](#π-usage-examples)
|
|
28
28
|
20. [Query Logging & Automatic SQL Display](#π-query-logging--automatic-sql-display)
|
|
29
|
-
21. [Security Features](#π-security-features)
|
|
30
|
-
22. [Query Result Caching](#πΎ-query-result-caching)
|
|
31
|
-
23. [Query Optimization Hints](#π―-query-optimization-hints)
|
|
32
|
-
24. [Guided Query Builder/Fixer](#π€-guided-query-builderfixer)
|
|
33
|
-
25. [Bulk Operations](#π-bulk-operations)
|
|
34
|
-
26. [OpenAI Codex Integration](#π€-openai-codex-integration)
|
|
35
|
-
27. [Troubleshooting](#π οΈ-troubleshooting)
|
|
36
|
-
28. [License](#π-license)
|
|
37
|
-
29. [Roadmap](#πΊοΈ-roadmap)
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Dual-Layer Filtering System
|
|
42
29
|
|
|
43
30
|
Control which database operations are available to AI using a **dual-layer filtering system**:
|
|
44
31
|
|
|
@@ -754,6 +741,47 @@ Both tools support:
|
|
|
754
741
|
|
|
755
742
|
---
|
|
756
743
|
|
|
744
|
+
## π Workflow Macros
|
|
745
|
+
|
|
746
|
+
Workflow Macros are composite tools designed to execute complex, multi-step operations safely and efficiently. They encapsulate best practices and security policies (like data masking) into single, atomic tool calls.
|
|
747
|
+
|
|
748
|
+
### Available Macros
|
|
749
|
+
|
|
750
|
+
| Tool | Description |
|
|
751
|
+
|------|-------------|
|
|
752
|
+
| `safe_export_table` | Exports table data to CSV with mandated data masking (redaction/hashing) |
|
|
753
|
+
|
|
754
|
+
### safe_export_table
|
|
755
|
+
|
|
756
|
+
Exports table data to CSV format *with enforced data masking*. This is safer than standard export tools because it ensures sensitive data is masked before leaving the database layer, regardless of the global masking configuration.
|
|
757
|
+
|
|
758
|
+
**Parameters:**
|
|
759
|
+
- `table_name` (required): Name of the table to export.
|
|
760
|
+
- `masking_profile` (optional): "strict" (default), "partial", or "soft".
|
|
761
|
+
- `limit` (optional): Maximum rows to export (default 1000, max 10000).
|
|
762
|
+
- `include_headers` (optional): Whether to include CSV headers (default true).
|
|
763
|
+
|
|
764
|
+
**Example:**
|
|
765
|
+
*User prompt: "Safely export the users table to a CSV file"*
|
|
766
|
+
|
|
767
|
+
```json
|
|
768
|
+
{
|
|
769
|
+
"tool": "safe_export_table",
|
|
770
|
+
"arguments": {
|
|
771
|
+
"table_name": "users",
|
|
772
|
+
"masking_profile": "strict"
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
**Result:**
|
|
778
|
+
CSV content where:
|
|
779
|
+
- Emails are masked (e.g., `j***@domain.com`)
|
|
780
|
+
- Passwords/secrets are `[REDACTED]`
|
|
781
|
+
- Phone numbers are partially hidden
|
|
782
|
+
|
|
783
|
+
---
|
|
784
|
+
|
|
757
785
|
## π₯ Data Import Tools
|
|
758
786
|
|
|
759
787
|
The MySQL MCP Server provides tools to import data from various formats into your database tables.
|
|
@@ -3759,7 +3787,44 @@ Each bulk operation returns performance metrics:
|
|
|
3759
3787
|
|
|
3760
3788
|
---
|
|
3761
3789
|
|
|
3762
|
-
##
|
|
3790
|
+
## π Data Masking
|
|
3791
|
+
|
|
3792
|
+
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.
|
|
3793
|
+
|
|
3794
|
+
### Features
|
|
3795
|
+
|
|
3796
|
+
- **Profile-Based Masking**: Easy configuration profiles (`none`, `soft`, `partial`, `strict`)
|
|
3797
|
+
- **Automatic Detection**: Automatically identifies sensitive columns by name (e.g., `email`, `password`, `ssn`)
|
|
3798
|
+
- **Multiple Strategies**:
|
|
3799
|
+
- **REDACT**: Replaces value with `[REDACTED]`
|
|
3800
|
+
- **PARTIAL**: Partially masks email (`j***@d.com`) and phone/CC (`***1234`)
|
|
3801
|
+
- **HASH**: (Internal placeholder)
|
|
3802
|
+
|
|
3803
|
+
### Configuration
|
|
3804
|
+
|
|
3805
|
+
Configure the masking profile via the `MCP_MASKING_PROFILE` environment variable:
|
|
3806
|
+
|
|
3807
|
+
```bash
|
|
3808
|
+
MCP_MASKING_PROFILE=partial
|
|
3809
|
+
```
|
|
3810
|
+
|
|
3811
|
+
### Profiles Reference
|
|
3812
|
+
|
|
3813
|
+
| Profile | Description | Credentials (password, key) | PII (email, phone, ssn) |
|
|
3814
|
+
|---------|-------------|-----------------------------|-------------------------|
|
|
3815
|
+
| `none` | No masking (default) | Show | Show |
|
|
3816
|
+
| `soft` | Protect secrets only | **REDACT** | Show |
|
|
3817
|
+
| `partial` | Balanced security | **REDACT** | **PARTIAL** (j***@...) |
|
|
3818
|
+
| `strict` | Maximum security | **REDACT** | **REDACT** |
|
|
3819
|
+
|
|
3820
|
+
### Behavior
|
|
3821
|
+
|
|
3822
|
+
- Masking applies automatically to `run_query` and `read_records` results.
|
|
3823
|
+
- 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).
|
|
3824
|
+
|
|
3825
|
+
---
|
|
3826
|
+
|
|
3827
|
+
## π€ OpenAI Codex Integration
|
|
3763
3828
|
|
|
3764
3829
|
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
3830
|
|
|
@@ -4144,7 +4209,7 @@ MIT License - see [LICENSE](LICENSE) file for details.
|
|
|
4144
4209
|
| Drift & Migration Assistant (Schema diff + risk summary) | High | High | 4 | β
Completed |
|
|
4145
4210
|
| Safety Sandbox Mode (runQuery dry-run/EXPLAIN-only) | Medium | Low | 5 | β
Completed |
|
|
4146
4211
|
| Anomaly & Slow-Query Watcher | Medium | Medium | 6 | β
Completed |
|
|
4147
|
-
| Data Masking Profiles for Responses | Medium | Medium | 7 |
|
|
4212
|
+
| Data Masking Profiles for Responses | Medium | Medium | 7 | β
Completed |
|
|
4148
4213
|
| Workflow Macros (e.g., safe_export_table) | Medium | Low | 8 | Planned |
|
|
4149
4214
|
| Agent-Facing Changelog Feed | Medium | Low | 9 | Planned |
|
|
4150
4215
|
| Connection Profiles (dev/stage/prod with allow/deny) | High | Low | 10 | Planned |
|
package/README.md
CHANGED
|
@@ -50,26 +50,13 @@ 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)
|
|
57
57
|
|
|
58
58
|
---
|
|
59
|
-
|
|
60
|
-
## Features
|
|
61
|
-
|
|
62
|
-
| Category | Description |
|
|
63
|
-
|----------|-------------|
|
|
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
|
-
| **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 |
|
|
67
|
-
| **Adaptive Presets** | Built-in ReadOnly/Analyst/DBA Lite permission bundles with override merging |
|
|
68
|
-
| **Schema-Aware RAG Pack** | Compact schema snapshots (tables, PK/FK, row estimates) tailored for embeddings-friendly prompts |
|
|
69
|
-
| **Category Filtering** | 22 documentation categories for intuitive, fine-grained access control (backward compatible with 10 legacy categories) |
|
|
70
|
-
| **Transaction Support** | Full ACID transaction management (BEGIN, COMMIT, ROLLBACK) |
|
|
71
|
-
| **Schema Migrations** | Version control for database schema with up/down migrations |
|
|
72
|
-
| **Dual Mode** | Run as MCP server OR as REST API |
|
|
59
|
+
| **Data Masking** | Protect PII/Secrets in responses with configurable profiles (soft/partial/strict) |
|
|
73
60
|
| **TypeScript** | Fully typed with TypeScript definitions |
|
|
74
61
|
|
|
75
62
|
---
|
|
@@ -106,6 +93,7 @@ DB_USER=root
|
|
|
106
93
|
DB_PASSWORD=yourpassword
|
|
107
94
|
DB_NAME=yourdatabase
|
|
108
95
|
MCP_CONFIG=list,read,utility
|
|
96
|
+
MCP_MASKING_PROFILE=partial
|
|
109
97
|
```
|
|
110
98
|
|
|
111
99
|
### 2. Build Project (If Cloned Locally)
|
|
@@ -367,7 +355,8 @@ Alternative approach using environment variables instead of connection string:
|
|
|
367
355
|
"DB_PASSWORD": "your_password",
|
|
368
356
|
"DB_NAME": "your_database",
|
|
369
357
|
"MCP_PERMISSIONS": "list,read,utility",
|
|
370
|
-
"MCP_CATEGORIES": "database_discovery,performance_monitoring"
|
|
358
|
+
"MCP_CATEGORIES": "database_discovery,performance_monitoring",
|
|
359
|
+
"MCP_MASKING_PROFILE": "partial"
|
|
371
360
|
}
|
|
372
361
|
}
|
|
373
362
|
}
|
|
@@ -496,12 +485,12 @@ Use these categories for fine-grained control that matches the tool organization
|
|
|
496
485
|
| `server_management` | 9 | Process list, explain queries, server info |
|
|
497
486
|
| `performance_monitoring` | 10 | Metrics, slow queries, health checks |
|
|
498
487
|
| `cache_management` | 5 | Query cache stats and configuration |
|
|
499
|
-
| `query_optimization` |
|
|
488
|
+
| `query_optimization` | 3 | Analyze queries, get optimization hints |
|
|
500
489
|
| `backup_restore` | 5 | Backup/restore database and tables |
|
|
501
490
|
| `import_export` | 5 | Import/export JSON, CSV, SQL |
|
|
502
491
|
| `data_migration` | 5 | Copy, move, clone, sync table data |
|
|
503
492
|
| `schema_migrations` | 9 | Version control for database schema |
|
|
504
|
-
| `analysis` |
|
|
493
|
+
| `analysis` | 4 | AI context optimization and data analysis |
|
|
505
494
|
|
|
506
495
|
### Legacy Categories (Backward Compatible)
|
|
507
496
|
|
|
@@ -531,7 +520,7 @@ Use these categories for fine-grained control that matches the tool organization
|
|
|
531
520
|
| **Application Backend** | `database_discovery,crud_operations,bulk_operations,custom_queries,transaction_management` | Full app support |
|
|
532
521
|
| **Development & Testing** | `database_discovery,crud_operations,bulk_operations,custom_queries,schema_management,utilities,transaction_management` | Development access |
|
|
533
522
|
| **DBA & DevOps** | `database_discovery,schema_management,table_maintenance,backup_restore,schema_migrations,performance_monitoring` | Admin tasks |
|
|
534
|
-
| **Full Access** | *(leave empty)* | All
|
|
523
|
+
| **Full Access** | *(leave empty)* | All 124 tools enabled |
|
|
535
524
|
|
|
536
525
|
#### Using Legacy Categories (Backward Compatible)
|
|
537
526
|
|
|
@@ -652,11 +641,11 @@ Use both 2nd argument (permissions) and 3rd argument (categories):
|
|
|
652
641
|
|
|
653
642
|
## Available Tools
|
|
654
643
|
|
|
655
|
-
The MCP server provides **
|
|
644
|
+
The MCP server provides **124 powerful tools** organized into categories:
|
|
656
645
|
|
|
657
646
|
### Quick Reference
|
|
658
647
|
|
|
659
|
-
**
|
|
648
|
+
**124 Tools Available** - Organized into 22 categories
|
|
660
649
|
|
|
661
650
|
| Category | Count | Key Tools |
|
|
662
651
|
|----------|-------|-----------|
|
|
@@ -230,19 +230,6 @@ exports.toolCategoryMap = {
|
|
|
230
230
|
showReplicationStatus: ToolCategory.LIST,
|
|
231
231
|
// Backup and restore tools
|
|
232
232
|
backupTable: ToolCategory.UTILITY,
|
|
233
|
-
backupDatabase: ToolCategory.UTILITY,
|
|
234
|
-
restoreFromSql: ToolCategory.DDL,
|
|
235
|
-
getCreateTableStatement: ToolCategory.LIST,
|
|
236
|
-
getDatabaseSchema: ToolCategory.LIST,
|
|
237
|
-
// Extended data export/import tools
|
|
238
|
-
exportTableToJSON: ToolCategory.UTILITY,
|
|
239
|
-
exportQueryToJSON: ToolCategory.UTILITY,
|
|
240
|
-
exportTableToSql: ToolCategory.UTILITY,
|
|
241
|
-
importFromCSV: ToolCategory.CREATE,
|
|
242
|
-
importFromJSON: ToolCategory.CREATE,
|
|
243
|
-
// Data migration tools
|
|
244
|
-
copyTableData: ToolCategory.CREATE,
|
|
245
|
-
moveTableData: ToolCategory.DELETE,
|
|
246
233
|
cloneTable: ToolCategory.DDL,
|
|
247
234
|
compareTableStructure: ToolCategory.LIST,
|
|
248
235
|
syncTableData: ToolCategory.UPDATE,
|
|
@@ -397,6 +384,7 @@ exports.toolDocCategoryMap = {
|
|
|
397
384
|
exportTableToJSON: DocCategory.IMPORT_EXPORT,
|
|
398
385
|
exportQueryToJSON: DocCategory.IMPORT_EXPORT,
|
|
399
386
|
exportTableToSql: DocCategory.IMPORT_EXPORT,
|
|
387
|
+
safe_export_table: DocCategory.IMPORT_EXPORT,
|
|
400
388
|
importFromCSV: DocCategory.IMPORT_EXPORT,
|
|
401
389
|
importFromJSON: DocCategory.IMPORT_EXPORT,
|
|
402
390
|
// Data Migration
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export declare class MySQLMCP {
|
|
|
24
24
|
private performanceTools;
|
|
25
25
|
private analysisTools;
|
|
26
26
|
private aiTools;
|
|
27
|
+
private macroTools;
|
|
27
28
|
private security;
|
|
28
29
|
private featureConfig;
|
|
29
30
|
constructor(permissionsConfig?: string, categoriesConfig?: string, presetName?: string);
|
|
@@ -590,6 +591,16 @@ export declare class MySQLMCP {
|
|
|
590
591
|
suggestions?: string[];
|
|
591
592
|
error?: string;
|
|
592
593
|
}>;
|
|
594
|
+
safeExportTable(params: {
|
|
595
|
+
table_name: string;
|
|
596
|
+
masking_profile?: string;
|
|
597
|
+
limit?: number;
|
|
598
|
+
include_headers?: boolean;
|
|
599
|
+
}): Promise<{
|
|
600
|
+
status: string;
|
|
601
|
+
data?: any;
|
|
602
|
+
error?: string;
|
|
603
|
+
}>;
|
|
593
604
|
getFeatureStatus(): {
|
|
594
605
|
status: string;
|
|
595
606
|
data: {
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ const schemaVersioningTools_1 = require("./tools/schemaVersioningTools");
|
|
|
25
25
|
const performanceTools_1 = require("./tools/performanceTools");
|
|
26
26
|
const analysisTools_1 = require("./tools/analysisTools");
|
|
27
27
|
const aiTools_1 = require("./tools/aiTools");
|
|
28
|
+
const macroTools_1 = require("./tools/macroTools");
|
|
28
29
|
const securityLayer_1 = __importDefault(require("./security/securityLayer"));
|
|
29
30
|
const connection_1 = __importDefault(require("./db/connection"));
|
|
30
31
|
const featureConfig_1 = require("./config/featureConfig");
|
|
@@ -56,7 +57,9 @@ class MySQLMCP {
|
|
|
56
57
|
this.schemaVersioningTools = new schemaVersioningTools_1.SchemaVersioningTools(this.security);
|
|
57
58
|
this.performanceTools = new performanceTools_1.PerformanceTools(this.security);
|
|
58
59
|
this.analysisTools = new analysisTools_1.AnalysisTools(this.security);
|
|
60
|
+
this.analysisTools = new analysisTools_1.AnalysisTools(this.security);
|
|
59
61
|
this.aiTools = new aiTools_1.AiTools(this.security);
|
|
62
|
+
this.macroTools = new macroTools_1.MacroTools(this.security);
|
|
60
63
|
}
|
|
61
64
|
// Helper method to check if tool is enabled
|
|
62
65
|
checkToolEnabled(toolName) {
|
|
@@ -535,6 +538,14 @@ class MySQLMCP {
|
|
|
535
538
|
}
|
|
536
539
|
return await this.aiTools.repairQuery(params);
|
|
537
540
|
}
|
|
541
|
+
// Workflow Macros
|
|
542
|
+
async safeExportTable(params) {
|
|
543
|
+
const check = this.checkToolEnabled("safe_export_table");
|
|
544
|
+
if (!check.enabled) {
|
|
545
|
+
return { status: "error", error: check.error };
|
|
546
|
+
}
|
|
547
|
+
return await this.macroTools.safeExportTable(params);
|
|
548
|
+
}
|
|
538
549
|
// Get feature configuration status
|
|
539
550
|
getFeatureStatus() {
|
|
540
551
|
const snapshot = this.featureConfig.getConfigSnapshot();
|
package/dist/mcp-server.js
CHANGED
|
@@ -476,6 +476,33 @@ const TOOLS = [
|
|
|
476
476
|
required: ["query"],
|
|
477
477
|
},
|
|
478
478
|
},
|
|
479
|
+
{
|
|
480
|
+
name: "safe_export_table",
|
|
481
|
+
description: "Exports table data to CSV with enforced data masking rules to protect sensitive information.",
|
|
482
|
+
inputSchema: {
|
|
483
|
+
type: "object",
|
|
484
|
+
properties: {
|
|
485
|
+
table_name: {
|
|
486
|
+
type: "string",
|
|
487
|
+
description: "Name of the table to export",
|
|
488
|
+
},
|
|
489
|
+
masking_profile: {
|
|
490
|
+
type: "string",
|
|
491
|
+
enum: ["soft", "partial", "strict"],
|
|
492
|
+
description: "Masking profile to apply (default: strict). strict=redact all PII/secrets, partial=partial mask PII, soft=mask secrets only.",
|
|
493
|
+
},
|
|
494
|
+
limit: {
|
|
495
|
+
type: "number",
|
|
496
|
+
description: "Maximum number of rows to export (default: 1000, max: 10000)",
|
|
497
|
+
},
|
|
498
|
+
include_headers: {
|
|
499
|
+
type: "boolean",
|
|
500
|
+
description: "Whether to include CSV headers (default: true)",
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
required: ["table_name"],
|
|
504
|
+
},
|
|
505
|
+
},
|
|
479
506
|
{
|
|
480
507
|
name: "repair_query",
|
|
481
508
|
description: "Analyzes a SQL query (and optional error) to suggest repairs or optimizations using EXPLAIN and heuristics.",
|
|
@@ -3083,6 +3110,12 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
3083
3110
|
case "export_query_to_json":
|
|
3084
3111
|
result = await mysqlMCP.exportQueryToJSON((args || {}));
|
|
3085
3112
|
break;
|
|
3113
|
+
case "export_query_to_csv":
|
|
3114
|
+
result = await mysqlMCP.exportQueryToCSV((args || {}));
|
|
3115
|
+
break;
|
|
3116
|
+
case "safe_export_table":
|
|
3117
|
+
result = await mysqlMCP.safeExportTable((args || {}));
|
|
3118
|
+
break;
|
|
3086
3119
|
case "export_table_to_sql":
|
|
3087
3120
|
result = await mysqlMCP.exportTableToSql((args || {}));
|
|
3088
3121
|
break;
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import SecurityLayer from "../security/securityLayer";
|
|
2
|
+
export declare class MacroTools {
|
|
3
|
+
private db;
|
|
4
|
+
private security;
|
|
5
|
+
constructor(security: SecurityLayer);
|
|
6
|
+
/**
|
|
7
|
+
* Safe Export Table: Exports table data to CSV with enforced data masking
|
|
8
|
+
* This macro prioritizes data safety by applying masking rules before export.
|
|
9
|
+
*/
|
|
10
|
+
safeExportTable(params: {
|
|
11
|
+
table_name: string;
|
|
12
|
+
masking_profile?: string;
|
|
13
|
+
limit?: number;
|
|
14
|
+
include_headers?: boolean;
|
|
15
|
+
}): Promise<{
|
|
16
|
+
status: string;
|
|
17
|
+
data?: any;
|
|
18
|
+
error?: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MacroTools = void 0;
|
|
7
|
+
const connection_1 = __importDefault(require("../db/connection"));
|
|
8
|
+
const maskingLayer_1 = require("../security/maskingLayer");
|
|
9
|
+
class MacroTools {
|
|
10
|
+
constructor(security) {
|
|
11
|
+
this.db = connection_1.default.getInstance();
|
|
12
|
+
this.security = security;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Safe Export Table: Exports table data to CSV with enforced data masking
|
|
16
|
+
* This macro prioritizes data safety by applying masking rules before export.
|
|
17
|
+
*/
|
|
18
|
+
async safeExportTable(params) {
|
|
19
|
+
try {
|
|
20
|
+
const { table_name, masking_profile = "strict", limit = 1000, include_headers = true, } = params;
|
|
21
|
+
// 1. Validate table name
|
|
22
|
+
const tableValidation = this.security.validateIdentifier(table_name);
|
|
23
|
+
if (!tableValidation.valid) {
|
|
24
|
+
return {
|
|
25
|
+
status: "error",
|
|
26
|
+
error: tableValidation.error,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// 2. Fetch data (with hard limit to prevent OOM on large safe exports)
|
|
30
|
+
const maxLimit = 10000;
|
|
31
|
+
const actualLimit = Math.min(limit, maxLimit);
|
|
32
|
+
const escapedTableName = this.security.escapeIdentifier(table_name);
|
|
33
|
+
const query = `SELECT * FROM ${escapedTableName} LIMIT ?`;
|
|
34
|
+
const results = (await this.db.query(query, [actualLimit]));
|
|
35
|
+
if (results.length === 0) {
|
|
36
|
+
return {
|
|
37
|
+
status: "success",
|
|
38
|
+
data: {
|
|
39
|
+
csv: include_headers ? "" : "",
|
|
40
|
+
row_count: 0,
|
|
41
|
+
applied_profile: masking_profile,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// 3. Apply masking explicitly using a new temporary layer to ensure strictness
|
|
46
|
+
// We don't rely on the global masking profile here; we use the one requested (default strict)
|
|
47
|
+
const tempMaskingLayer = new maskingLayer_1.MaskingLayer(masking_profile);
|
|
48
|
+
const maskedResults = tempMaskingLayer.processResults(results);
|
|
49
|
+
// 4. Convert to CSV
|
|
50
|
+
let csv = "";
|
|
51
|
+
if (include_headers) {
|
|
52
|
+
const headers = Object.keys(maskedResults[0]).join(",");
|
|
53
|
+
csv += headers + "\n";
|
|
54
|
+
}
|
|
55
|
+
for (const row of maskedResults) {
|
|
56
|
+
const values = Object.values(row)
|
|
57
|
+
.map((value) => {
|
|
58
|
+
if (value === null)
|
|
59
|
+
return "";
|
|
60
|
+
if (value === undefined)
|
|
61
|
+
return "";
|
|
62
|
+
let str = String(value);
|
|
63
|
+
// Escape quotes and wrap in quotes if contains comma, newline or quotes
|
|
64
|
+
if (str.includes(",") ||
|
|
65
|
+
str.includes("\n") ||
|
|
66
|
+
str.includes('"')) {
|
|
67
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
68
|
+
}
|
|
69
|
+
return str;
|
|
70
|
+
})
|
|
71
|
+
.join(",");
|
|
72
|
+
csv += values + "\n";
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
status: "success",
|
|
76
|
+
data: {
|
|
77
|
+
csv: csv,
|
|
78
|
+
row_count: maskedResults.length,
|
|
79
|
+
applied_profile: tempMaskingLayer.getProfile(),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return {
|
|
85
|
+
status: "error",
|
|
86
|
+
error: error.message,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
exports.MacroTools = MacroTools;
|
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.1",
|
|
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",
|
|
@@ -86,4 +86,4 @@
|
|
|
86
86
|
"ts-node": "^10.9.1",
|
|
87
87
|
"typescript": "^5.2.2"
|
|
88
88
|
}
|
|
89
|
-
}
|
|
89
|
+
}
|