@berthojoris/mcp-mysql-server 1.10.5 → 1.12.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 +47 -18
- package/DOCUMENTATIONS.md +55 -27
- package/README.md +79 -8
- package/bin/mcp-mysql.js +107 -39
- package/dist/config/featureConfig.d.ts +41 -3
- package/dist/config/featureConfig.js +188 -13
- package/dist/index.d.ts +70 -1
- package/dist/index.js +44 -2
- package/dist/mcp-server.js +101 -11
- package/dist/tools/analysisTools.d.ts +35 -0
- package/dist/tools/analysisTools.js +327 -0
- package/dist/tools/databaseTools.d.ts +21 -0
- package/dist/tools/databaseTools.js +138 -0
- package/dist/tools/queryTools.d.ts +5 -0
- package/dist/tools/queryTools.js +27 -0
- package/manifest.json +22 -1
- package/package.json +89 -89
|
@@ -51,7 +51,71 @@ var DocCategory;
|
|
|
51
51
|
DocCategory["IMPORT_EXPORT"] = "import_export";
|
|
52
52
|
DocCategory["DATA_MIGRATION"] = "data_migration";
|
|
53
53
|
DocCategory["SCHEMA_MIGRATIONS"] = "schema_migrations";
|
|
54
|
+
DocCategory["ANALYSIS"] = "analysis";
|
|
54
55
|
})(DocCategory || (exports.DocCategory = DocCategory = {}));
|
|
56
|
+
const normalizePresetName = (value) => (value || "").toLowerCase().replace(/[\s_-]/g, "");
|
|
57
|
+
const permissionPresets = {
|
|
58
|
+
readonly: {
|
|
59
|
+
name: "readonly",
|
|
60
|
+
description: "Safe read-only profile with discovery, querying, exports, and diagnostics",
|
|
61
|
+
permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
|
|
62
|
+
categories: [
|
|
63
|
+
DocCategory.DATABASE_DISCOVERY,
|
|
64
|
+
DocCategory.CRUD_OPERATIONS,
|
|
65
|
+
DocCategory.CUSTOM_QUERIES,
|
|
66
|
+
DocCategory.UTILITIES,
|
|
67
|
+
DocCategory.IMPORT_EXPORT,
|
|
68
|
+
DocCategory.PERFORMANCE_MONITORING,
|
|
69
|
+
DocCategory.ANALYSIS,
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
analyst: {
|
|
73
|
+
name: "analyst",
|
|
74
|
+
description: "Exploratory analytics profile with query insights and safe exports",
|
|
75
|
+
permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
|
|
76
|
+
categories: [
|
|
77
|
+
DocCategory.DATABASE_DISCOVERY,
|
|
78
|
+
DocCategory.CRUD_OPERATIONS,
|
|
79
|
+
DocCategory.CUSTOM_QUERIES,
|
|
80
|
+
DocCategory.UTILITIES,
|
|
81
|
+
DocCategory.IMPORT_EXPORT,
|
|
82
|
+
DocCategory.PERFORMANCE_MONITORING,
|
|
83
|
+
DocCategory.ANALYSIS,
|
|
84
|
+
DocCategory.QUERY_OPTIMIZATION,
|
|
85
|
+
DocCategory.CACHE_MANAGEMENT,
|
|
86
|
+
DocCategory.SERVER_MANAGEMENT,
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
dbalite: {
|
|
90
|
+
name: "dba-lite",
|
|
91
|
+
description: "Admin-lite profile for schema care, migrations, and maintenance",
|
|
92
|
+
permissions: [
|
|
93
|
+
ToolCategory.LIST,
|
|
94
|
+
ToolCategory.READ,
|
|
95
|
+
ToolCategory.UTILITY,
|
|
96
|
+
ToolCategory.DDL,
|
|
97
|
+
ToolCategory.TRANSACTION,
|
|
98
|
+
ToolCategory.PROCEDURE,
|
|
99
|
+
],
|
|
100
|
+
categories: [
|
|
101
|
+
DocCategory.DATABASE_DISCOVERY,
|
|
102
|
+
DocCategory.CUSTOM_QUERIES,
|
|
103
|
+
DocCategory.UTILITIES,
|
|
104
|
+
DocCategory.SERVER_MANAGEMENT,
|
|
105
|
+
DocCategory.SCHEMA_MANAGEMENT,
|
|
106
|
+
DocCategory.TABLE_MAINTENANCE,
|
|
107
|
+
DocCategory.INDEX_MANAGEMENT,
|
|
108
|
+
DocCategory.CONSTRAINT_MANAGEMENT,
|
|
109
|
+
DocCategory.BACKUP_RESTORE,
|
|
110
|
+
DocCategory.SCHEMA_MIGRATIONS,
|
|
111
|
+
DocCategory.PERFORMANCE_MONITORING,
|
|
112
|
+
DocCategory.VIEWS_MANAGEMENT,
|
|
113
|
+
DocCategory.TRIGGERS_MANAGEMENT,
|
|
114
|
+
DocCategory.FUNCTIONS_MANAGEMENT,
|
|
115
|
+
DocCategory.STORED_PROCEDURES,
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
};
|
|
55
119
|
/**
|
|
56
120
|
* Map of tool names to their legacy categories
|
|
57
121
|
*/
|
|
@@ -60,11 +124,17 @@ exports.toolCategoryMap = {
|
|
|
60
124
|
listDatabases: ToolCategory.LIST,
|
|
61
125
|
listTables: ToolCategory.LIST,
|
|
62
126
|
readTableSchema: ToolCategory.LIST,
|
|
127
|
+
// Analysis tools (added here to group with database tools)
|
|
128
|
+
getDatabaseSummary: ToolCategory.LIST,
|
|
129
|
+
getSchemaERD: ToolCategory.LIST,
|
|
130
|
+
getSchemaRagContext: ToolCategory.LIST,
|
|
63
131
|
// CRUD tools
|
|
64
132
|
createRecord: ToolCategory.CREATE,
|
|
65
133
|
readRecords: ToolCategory.READ,
|
|
66
134
|
updateRecord: ToolCategory.UPDATE,
|
|
67
135
|
deleteRecord: ToolCategory.DELETE,
|
|
136
|
+
// Analysis tools (added here to group with read tools)
|
|
137
|
+
getColumnStatistics: ToolCategory.READ,
|
|
68
138
|
// Bulk operations
|
|
69
139
|
bulkInsert: ToolCategory.CREATE,
|
|
70
140
|
bulkUpdate: ToolCategory.UPDATE,
|
|
@@ -186,6 +256,8 @@ exports.toolCategoryMap = {
|
|
|
186
256
|
validateMigrations: ToolCategory.LIST,
|
|
187
257
|
resetFailedMigration: ToolCategory.DDL,
|
|
188
258
|
generateMigrationFromDiff: ToolCategory.DDL,
|
|
259
|
+
// Analysis tools - MOVED here to avoid duplication
|
|
260
|
+
// Note: keys must be unique in the object literal
|
|
189
261
|
// Performance monitoring tools
|
|
190
262
|
getPerformanceMetrics: ToolCategory.UTILITY,
|
|
191
263
|
getTopQueriesByTime: ToolCategory.UTILITY,
|
|
@@ -343,6 +415,11 @@ exports.toolDocCategoryMap = {
|
|
|
343
415
|
validateMigrations: DocCategory.SCHEMA_MIGRATIONS,
|
|
344
416
|
resetFailedMigration: DocCategory.SCHEMA_MIGRATIONS,
|
|
345
417
|
generateMigrationFromDiff: DocCategory.SCHEMA_MIGRATIONS,
|
|
418
|
+
// Analysis
|
|
419
|
+
getDatabaseSummary: DocCategory.ANALYSIS,
|
|
420
|
+
getSchemaERD: DocCategory.ANALYSIS,
|
|
421
|
+
getColumnStatistics: DocCategory.ANALYSIS,
|
|
422
|
+
getSchemaRagContext: DocCategory.ANALYSIS,
|
|
346
423
|
};
|
|
347
424
|
/**
|
|
348
425
|
* Mapping between legacy categories and documentation categories
|
|
@@ -360,8 +437,9 @@ const legacyToDocCategoryMap = {
|
|
|
360
437
|
DocCategory.TABLE_MAINTENANCE,
|
|
361
438
|
DocCategory.SERVER_MANAGEMENT,
|
|
362
439
|
DocCategory.SCHEMA_MIGRATIONS,
|
|
440
|
+
DocCategory.ANALYSIS,
|
|
363
441
|
],
|
|
364
|
-
read: [DocCategory.CRUD_OPERATIONS, DocCategory.CUSTOM_QUERIES],
|
|
442
|
+
read: [DocCategory.CRUD_OPERATIONS, DocCategory.CUSTOM_QUERIES, DocCategory.ANALYSIS],
|
|
365
443
|
create: [
|
|
366
444
|
DocCategory.CRUD_OPERATIONS,
|
|
367
445
|
DocCategory.BULK_OPERATIONS,
|
|
@@ -409,20 +487,66 @@ const legacyToDocCategoryMap = {
|
|
|
409
487
|
* - Layer 2 (Categories): Documentation categories (fine-grained control, optional)
|
|
410
488
|
*/
|
|
411
489
|
class FeatureConfig {
|
|
412
|
-
constructor(permissionsStr, categoriesStr) {
|
|
490
|
+
constructor(permissionsStr, categoriesStr, presetName) {
|
|
491
|
+
const presetInput = presetName ||
|
|
492
|
+
process.env.MCP_PERMISSION_PRESET ||
|
|
493
|
+
process.env.MCP_PRESET ||
|
|
494
|
+
"";
|
|
495
|
+
this.activePreset = this.resolvePreset(presetInput);
|
|
496
|
+
this.presetName = this.activePreset?.name;
|
|
497
|
+
const presetRequested = !!presetInput.trim();
|
|
413
498
|
// Support both old single-parameter and new dual-parameter signatures
|
|
414
|
-
const
|
|
499
|
+
const permissionsInput = permissionsStr ||
|
|
415
500
|
process.env.MCP_PERMISSIONS ||
|
|
416
501
|
process.env.MCP_CONFIG ||
|
|
417
502
|
"";
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
const
|
|
503
|
+
const categoriesInput = categoriesStr || process.env.MCP_CATEGORIES || "";
|
|
504
|
+
// Use preset values when available, otherwise fall back to user input
|
|
505
|
+
// If an unknown preset is requested without explicit permissions/categories,
|
|
506
|
+
// default to a safe read-only baseline rather than enabling everything.
|
|
507
|
+
const basePermissions = this.activePreset
|
|
508
|
+
? this.activePreset.permissions.join(",")
|
|
509
|
+
: presetRequested && !permissionsInput
|
|
510
|
+
? [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY].join(",")
|
|
511
|
+
: "";
|
|
512
|
+
const baseCategories = this.activePreset
|
|
513
|
+
? this.activePreset.categories.join(",")
|
|
514
|
+
: presetRequested && !categoriesInput
|
|
515
|
+
? [
|
|
516
|
+
DocCategory.DATABASE_DISCOVERY,
|
|
517
|
+
DocCategory.CRUD_OPERATIONS,
|
|
518
|
+
DocCategory.CUSTOM_QUERIES,
|
|
519
|
+
DocCategory.UTILITIES,
|
|
520
|
+
].join(",")
|
|
521
|
+
: "";
|
|
522
|
+
if (presetRequested && !this.activePreset) {
|
|
523
|
+
console.warn(`Preset '${presetInput}' not recognized. Falling back to safe read-only defaults.`);
|
|
524
|
+
}
|
|
525
|
+
const mergedPermissions = this.mergeConfigStrings(basePermissions, permissionsInput);
|
|
526
|
+
const mergedCategories = this.mergeConfigStrings(baseCategories, categoriesInput);
|
|
527
|
+
this.originalPermissionsString = mergedPermissions;
|
|
528
|
+
this.originalCategoriesString = mergedCategories;
|
|
529
|
+
this.useDualLayer = !!mergedCategories.trim();
|
|
530
|
+
const parsed = this.parseConfig(mergedPermissions, mergedCategories);
|
|
423
531
|
this.enabledLegacyCategories = parsed.legacy;
|
|
424
532
|
this.enabledDocCategories = parsed.doc;
|
|
425
533
|
}
|
|
534
|
+
/**
|
|
535
|
+
* Normalize and merge preset + user-supplied configuration lists
|
|
536
|
+
*/
|
|
537
|
+
mergeConfigStrings(base, override) {
|
|
538
|
+
const items = [...(base || "").split(","), ...(override || "").split(",")]
|
|
539
|
+
.map((c) => c.trim().toLowerCase())
|
|
540
|
+
.filter(Boolean);
|
|
541
|
+
return Array.from(new Set(items)).join(",");
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Resolve a preset name to its configuration
|
|
545
|
+
*/
|
|
546
|
+
resolvePreset(name) {
|
|
547
|
+
const normalized = normalizePresetName(name);
|
|
548
|
+
return normalized ? permissionPresets[normalized] : undefined;
|
|
549
|
+
}
|
|
426
550
|
/**
|
|
427
551
|
* Parse permissions and categories for dual-layer filtering
|
|
428
552
|
* Layer 1 (permissions): Broad control using legacy categories
|
|
@@ -472,11 +596,37 @@ class FeatureConfig {
|
|
|
472
596
|
/**
|
|
473
597
|
* Update configuration at runtime
|
|
474
598
|
*/
|
|
475
|
-
setConfig(permissionsStr, categoriesStr) {
|
|
476
|
-
|
|
477
|
-
this.
|
|
478
|
-
|
|
479
|
-
|
|
599
|
+
setConfig(permissionsStr, categoriesStr, presetName) {
|
|
600
|
+
const presetRequested = presetName !== undefined ? !!presetName.trim() : !!this.presetName;
|
|
601
|
+
this.activePreset =
|
|
602
|
+
presetName === ""
|
|
603
|
+
? undefined
|
|
604
|
+
: this.resolvePreset(presetName || this.presetName);
|
|
605
|
+
this.presetName = this.activePreset?.name;
|
|
606
|
+
const basePermissions = this.activePreset
|
|
607
|
+
? this.activePreset.permissions.join(",")
|
|
608
|
+
: presetRequested && !permissionsStr
|
|
609
|
+
? [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY].join(",")
|
|
610
|
+
: "";
|
|
611
|
+
const baseCategories = this.activePreset
|
|
612
|
+
? this.activePreset.categories.join(",")
|
|
613
|
+
: presetRequested && !categoriesStr
|
|
614
|
+
? [
|
|
615
|
+
DocCategory.DATABASE_DISCOVERY,
|
|
616
|
+
DocCategory.CRUD_OPERATIONS,
|
|
617
|
+
DocCategory.CUSTOM_QUERIES,
|
|
618
|
+
DocCategory.UTILITIES,
|
|
619
|
+
].join(",")
|
|
620
|
+
: "";
|
|
621
|
+
if (presetRequested && !this.activePreset) {
|
|
622
|
+
console.warn(`Preset '${presetName}' not recognized. Falling back to safe read-only defaults.`);
|
|
623
|
+
}
|
|
624
|
+
const mergedPermissions = this.mergeConfigStrings(basePermissions, permissionsStr);
|
|
625
|
+
const mergedCategories = this.mergeConfigStrings(baseCategories, categoriesStr || "");
|
|
626
|
+
this.originalPermissionsString = mergedPermissions;
|
|
627
|
+
this.originalCategoriesString = mergedCategories;
|
|
628
|
+
this.useDualLayer = !!(mergedCategories && mergedCategories.trim());
|
|
629
|
+
const parsed = this.parseConfig(mergedPermissions, mergedCategories || "");
|
|
480
630
|
this.enabledLegacyCategories = parsed.legacy;
|
|
481
631
|
this.enabledDocCategories = parsed.doc;
|
|
482
632
|
}
|
|
@@ -602,6 +752,31 @@ class FeatureConfig {
|
|
|
602
752
|
isUsingDualLayer() {
|
|
603
753
|
return this.useDualLayer;
|
|
604
754
|
}
|
|
755
|
+
/**
|
|
756
|
+
* Get the active preset (if any)
|
|
757
|
+
*/
|
|
758
|
+
getActivePreset() {
|
|
759
|
+
return this.activePreset;
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Snapshot of the resolved configuration for logging/telemetry
|
|
763
|
+
*/
|
|
764
|
+
getConfigSnapshot() {
|
|
765
|
+
return {
|
|
766
|
+
preset: this.activePreset
|
|
767
|
+
? {
|
|
768
|
+
name: this.activePreset.name,
|
|
769
|
+
description: this.activePreset.description,
|
|
770
|
+
}
|
|
771
|
+
: undefined,
|
|
772
|
+
permissions: this.originalPermissionsString || "all",
|
|
773
|
+
categories: this.originalCategoriesString ||
|
|
774
|
+
(this.useDualLayer ? "" : "derived from permissions"),
|
|
775
|
+
filteringMode: this.getFilteringMode(),
|
|
776
|
+
enabledLegacy: this.getEnabledCategories(),
|
|
777
|
+
enabledDoc: this.getEnabledDocCategories(),
|
|
778
|
+
};
|
|
779
|
+
}
|
|
605
780
|
/**
|
|
606
781
|
* Get filtering mode description
|
|
607
782
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -22,9 +22,10 @@ export declare class MySQLMCP {
|
|
|
22
22
|
private migrationTools;
|
|
23
23
|
private schemaVersioningTools;
|
|
24
24
|
private performanceTools;
|
|
25
|
+
private analysisTools;
|
|
25
26
|
private security;
|
|
26
27
|
private featureConfig;
|
|
27
|
-
constructor(permissionsConfig?: string, categoriesConfig?: string);
|
|
28
|
+
constructor(permissionsConfig?: string, categoriesConfig?: string, presetName?: string);
|
|
28
29
|
private checkToolEnabled;
|
|
29
30
|
listDatabases(): Promise<{
|
|
30
31
|
status: string;
|
|
@@ -45,6 +46,20 @@ export declare class MySQLMCP {
|
|
|
45
46
|
data?: import("./validation/schemas").ColumnInfo[];
|
|
46
47
|
error?: string;
|
|
47
48
|
}>;
|
|
49
|
+
getDatabaseSummary(params: {
|
|
50
|
+
database?: string;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
status: string;
|
|
53
|
+
data?: string;
|
|
54
|
+
error?: string;
|
|
55
|
+
}>;
|
|
56
|
+
getSchemaERD(params: {
|
|
57
|
+
database?: string;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
status: string;
|
|
60
|
+
data?: string;
|
|
61
|
+
error?: string;
|
|
62
|
+
}>;
|
|
48
63
|
createRecord(params: {
|
|
49
64
|
table_name: string;
|
|
50
65
|
data: Record<string, any>;
|
|
@@ -94,11 +109,18 @@ export declare class MySQLMCP {
|
|
|
94
109
|
runQuery(params: {
|
|
95
110
|
query: string;
|
|
96
111
|
params?: any[];
|
|
112
|
+
hints?: any;
|
|
113
|
+
useCache?: boolean;
|
|
114
|
+
dry_run?: boolean;
|
|
97
115
|
}): Promise<{
|
|
98
116
|
status: string;
|
|
99
117
|
data?: any[];
|
|
100
118
|
error?: string;
|
|
101
119
|
optimizedQuery?: string;
|
|
120
|
+
dry_run?: boolean;
|
|
121
|
+
execution_plan?: any;
|
|
122
|
+
estimated_cost?: string;
|
|
123
|
+
message?: string;
|
|
102
124
|
}>;
|
|
103
125
|
executeSql(params: {
|
|
104
126
|
query: string;
|
|
@@ -108,6 +130,25 @@ export declare class MySQLMCP {
|
|
|
108
130
|
data?: any;
|
|
109
131
|
error?: string;
|
|
110
132
|
}>;
|
|
133
|
+
getColumnStatistics(params: {
|
|
134
|
+
table_name: string;
|
|
135
|
+
column_name: string;
|
|
136
|
+
database?: string;
|
|
137
|
+
}): Promise<{
|
|
138
|
+
status: string;
|
|
139
|
+
data?: any;
|
|
140
|
+
error?: string;
|
|
141
|
+
}>;
|
|
142
|
+
getSchemaRagContext(params: {
|
|
143
|
+
database?: string;
|
|
144
|
+
max_tables?: number;
|
|
145
|
+
max_columns?: number;
|
|
146
|
+
include_relationships?: boolean;
|
|
147
|
+
}): Promise<{
|
|
148
|
+
status: string;
|
|
149
|
+
data?: any;
|
|
150
|
+
error?: string;
|
|
151
|
+
}>;
|
|
111
152
|
createTable(params: any): Promise<{
|
|
112
153
|
status: string;
|
|
113
154
|
data?: any;
|
|
@@ -541,8 +582,22 @@ export declare class MySQLMCP {
|
|
|
541
582
|
getFeatureStatus(): {
|
|
542
583
|
status: string;
|
|
543
584
|
data: {
|
|
585
|
+
config: {
|
|
586
|
+
preset?: {
|
|
587
|
+
name: string;
|
|
588
|
+
description: string;
|
|
589
|
+
};
|
|
590
|
+
permissions: string;
|
|
591
|
+
categories: string;
|
|
592
|
+
filteringMode: string;
|
|
593
|
+
enabledLegacy: import("./config/featureConfig").ToolCategory[];
|
|
594
|
+
enabledDoc: import("./config/featureConfig").DocCategory[];
|
|
595
|
+
};
|
|
596
|
+
preset: import("./config/featureConfig").PermissionPreset | undefined;
|
|
597
|
+
filteringMode: string;
|
|
544
598
|
enabledCategories: import("./config/featureConfig").ToolCategory[];
|
|
545
599
|
categoryStatus: Record<import("./config/featureConfig").ToolCategory, boolean>;
|
|
600
|
+
docCategoryStatus: Record<import("./config/featureConfig").DocCategory, boolean>;
|
|
546
601
|
};
|
|
547
602
|
};
|
|
548
603
|
/**
|
|
@@ -551,6 +606,20 @@ export declare class MySQLMCP {
|
|
|
551
606
|
* @returns boolean indicating if the tool is enabled
|
|
552
607
|
*/
|
|
553
608
|
isToolEnabled(toolName: string): boolean;
|
|
609
|
+
/**
|
|
610
|
+
* Expose resolved access profile (preset + merged permissions/categories)
|
|
611
|
+
*/
|
|
612
|
+
getAccessProfile(): {
|
|
613
|
+
preset?: {
|
|
614
|
+
name: string;
|
|
615
|
+
description: string;
|
|
616
|
+
};
|
|
617
|
+
permissions: string;
|
|
618
|
+
categories: string;
|
|
619
|
+
filteringMode: string;
|
|
620
|
+
enabledLegacy: import("./config/featureConfig").ToolCategory[];
|
|
621
|
+
enabledDoc: import("./config/featureConfig").DocCategory[];
|
|
622
|
+
};
|
|
554
623
|
/**
|
|
555
624
|
* Bulk insert multiple records into the specified table
|
|
556
625
|
*/
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ const backupRestoreTools_1 = require("./tools/backupRestoreTools");
|
|
|
23
23
|
const migrationTools_1 = require("./tools/migrationTools");
|
|
24
24
|
const schemaVersioningTools_1 = require("./tools/schemaVersioningTools");
|
|
25
25
|
const performanceTools_1 = require("./tools/performanceTools");
|
|
26
|
+
const analysisTools_1 = require("./tools/analysisTools");
|
|
26
27
|
const securityLayer_1 = __importDefault(require("./security/securityLayer"));
|
|
27
28
|
const connection_1 = __importDefault(require("./db/connection"));
|
|
28
29
|
const featureConfig_1 = require("./config/featureConfig");
|
|
@@ -31,8 +32,8 @@ const featureConfig_1 = require("./config/featureConfig");
|
|
|
31
32
|
* A secure interface for AI models to interact with MySQL databases
|
|
32
33
|
*/
|
|
33
34
|
class MySQLMCP {
|
|
34
|
-
constructor(permissionsConfig, categoriesConfig) {
|
|
35
|
-
this.featureConfig = new featureConfig_1.FeatureConfig(permissionsConfig, categoriesConfig);
|
|
35
|
+
constructor(permissionsConfig, categoriesConfig, presetName) {
|
|
36
|
+
this.featureConfig = new featureConfig_1.FeatureConfig(permissionsConfig, categoriesConfig, presetName);
|
|
36
37
|
this.security = new securityLayer_1.default(this.featureConfig);
|
|
37
38
|
this.dbTools = new databaseTools_1.DatabaseTools();
|
|
38
39
|
this.crudTools = new crudTools_1.CrudTools(this.security);
|
|
@@ -53,6 +54,7 @@ class MySQLMCP {
|
|
|
53
54
|
this.migrationTools = new migrationTools_1.MigrationTools(this.security);
|
|
54
55
|
this.schemaVersioningTools = new schemaVersioningTools_1.SchemaVersioningTools(this.security);
|
|
55
56
|
this.performanceTools = new performanceTools_1.PerformanceTools(this.security);
|
|
57
|
+
this.analysisTools = new analysisTools_1.AnalysisTools(this.security);
|
|
56
58
|
}
|
|
57
59
|
// Helper method to check if tool is enabled
|
|
58
60
|
checkToolEnabled(toolName) {
|
|
@@ -86,6 +88,20 @@ class MySQLMCP {
|
|
|
86
88
|
}
|
|
87
89
|
return await this.dbTools.readTableSchema(params);
|
|
88
90
|
}
|
|
91
|
+
async getDatabaseSummary(params) {
|
|
92
|
+
const check = this.checkToolEnabled("getDatabaseSummary");
|
|
93
|
+
if (!check.enabled) {
|
|
94
|
+
return { status: "error", error: check.error };
|
|
95
|
+
}
|
|
96
|
+
return await this.dbTools.getDatabaseSummary(params);
|
|
97
|
+
}
|
|
98
|
+
async getSchemaERD(params) {
|
|
99
|
+
const check = this.checkToolEnabled("getSchemaERD");
|
|
100
|
+
if (!check.enabled) {
|
|
101
|
+
return { status: "error", error: check.error };
|
|
102
|
+
}
|
|
103
|
+
return await this.dbTools.getSchemaERD(params);
|
|
104
|
+
}
|
|
89
105
|
// CRUD Tools
|
|
90
106
|
async createRecord(params) {
|
|
91
107
|
const check = this.checkToolEnabled("createRecord");
|
|
@@ -147,6 +163,21 @@ class MySQLMCP {
|
|
|
147
163
|
}
|
|
148
164
|
return await this.queryTools.executeSql(params);
|
|
149
165
|
}
|
|
166
|
+
// Analysis Tools
|
|
167
|
+
async getColumnStatistics(params) {
|
|
168
|
+
const check = this.checkToolEnabled("getColumnStatistics");
|
|
169
|
+
if (!check.enabled) {
|
|
170
|
+
return { status: "error", error: check.error };
|
|
171
|
+
}
|
|
172
|
+
return await this.analysisTools.getColumnStatistics(params);
|
|
173
|
+
}
|
|
174
|
+
async getSchemaRagContext(params) {
|
|
175
|
+
const check = this.checkToolEnabled("getSchemaRagContext");
|
|
176
|
+
if (!check.enabled) {
|
|
177
|
+
return { status: "error", error: check.error };
|
|
178
|
+
}
|
|
179
|
+
return await this.analysisTools.getSchemaRagContext(params);
|
|
180
|
+
}
|
|
150
181
|
// DDL Tools
|
|
151
182
|
async createTable(params) {
|
|
152
183
|
const check = this.checkToolEnabled("createTable");
|
|
@@ -496,11 +527,16 @@ class MySQLMCP {
|
|
|
496
527
|
}
|
|
497
528
|
// Get feature configuration status
|
|
498
529
|
getFeatureStatus() {
|
|
530
|
+
const snapshot = this.featureConfig.getConfigSnapshot();
|
|
499
531
|
return {
|
|
500
532
|
status: "success",
|
|
501
533
|
data: {
|
|
534
|
+
config: snapshot,
|
|
535
|
+
preset: this.featureConfig.getActivePreset(),
|
|
536
|
+
filteringMode: this.featureConfig.getFilteringMode(),
|
|
502
537
|
enabledCategories: this.featureConfig.getEnabledCategories(),
|
|
503
538
|
categoryStatus: this.featureConfig.getCategoryStatus(),
|
|
539
|
+
docCategoryStatus: this.featureConfig.getDocCategoryStatus(),
|
|
504
540
|
},
|
|
505
541
|
};
|
|
506
542
|
}
|
|
@@ -512,6 +548,12 @@ class MySQLMCP {
|
|
|
512
548
|
isToolEnabled(toolName) {
|
|
513
549
|
return this.featureConfig.isToolEnabled(toolName);
|
|
514
550
|
}
|
|
551
|
+
/**
|
|
552
|
+
* Expose resolved access profile (preset + merged permissions/categories)
|
|
553
|
+
*/
|
|
554
|
+
getAccessProfile() {
|
|
555
|
+
return this.featureConfig.getConfigSnapshot();
|
|
556
|
+
}
|
|
515
557
|
/**
|
|
516
558
|
* Bulk insert multiple records into the specified table
|
|
517
559
|
*/
|
package/dist/mcp-server.js
CHANGED
|
@@ -10,6 +10,7 @@ const index_js_2 = require("./index.js");
|
|
|
10
10
|
// Layer 2 (Categories): MCP_CATEGORIES (optional, for fine-grained control)
|
|
11
11
|
const permissions = process.env.MCP_PERMISSIONS || process.env.MCP_CONFIG || "";
|
|
12
12
|
const categories = process.env.MCP_CATEGORIES || "";
|
|
13
|
+
const preset = process.env.MCP_PRESET || process.env.MCP_PERMISSION_PRESET || "";
|
|
13
14
|
// Declare the MySQL MCP instance (will be initialized in main())
|
|
14
15
|
let mysqlMCP;
|
|
15
16
|
// Define all available tools with their schemas
|
|
@@ -35,6 +36,79 @@ const TOOLS = [
|
|
|
35
36
|
},
|
|
36
37
|
},
|
|
37
38
|
},
|
|
39
|
+
{
|
|
40
|
+
name: "get_database_summary",
|
|
41
|
+
description: "Get a high-level summary of the database (tables, columns, row counts) optimized for AI context.",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
database: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Optional: specific database name",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "get_schema_erd",
|
|
54
|
+
description: "Get a Mermaid.js ER diagram string representing the database schema and relationships.",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
properties: {
|
|
58
|
+
database: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "Optional: specific database name",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "get_schema_rag_context",
|
|
67
|
+
description: "Return a compact schema-aware context pack (tables, PK/FK, columns, row estimates) optimized for RAG prompts.",
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: "object",
|
|
70
|
+
properties: {
|
|
71
|
+
database: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Optional: specific database name",
|
|
74
|
+
},
|
|
75
|
+
max_tables: {
|
|
76
|
+
type: "number",
|
|
77
|
+
description: "Optional: maximum number of tables to include (default 50, max 200)",
|
|
78
|
+
},
|
|
79
|
+
max_columns: {
|
|
80
|
+
type: "number",
|
|
81
|
+
description: "Optional: maximum number of columns per table (default 12, max 200)",
|
|
82
|
+
},
|
|
83
|
+
include_relationships: {
|
|
84
|
+
type: "boolean",
|
|
85
|
+
description: "Whether to include FK relationships section (default: true)",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "get_column_statistics",
|
|
92
|
+
description: "Get detailed statistics for a specific column (min, max, avg, distinct counts, nulls).",
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: "object",
|
|
95
|
+
properties: {
|
|
96
|
+
table_name: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Name of the table",
|
|
99
|
+
},
|
|
100
|
+
column_name: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "Name of the column",
|
|
103
|
+
},
|
|
104
|
+
database: {
|
|
105
|
+
type: "string",
|
|
106
|
+
description: "Optional: specific database name",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ["table_name", "column_name"],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
38
112
|
{
|
|
39
113
|
name: "read_table_schema",
|
|
40
114
|
description: "Reads the schema of a specified table, including columns, types, keys, and indexes.",
|
|
@@ -375,6 +449,10 @@ const TOOLS = [
|
|
|
375
449
|
type: "boolean",
|
|
376
450
|
description: "Whether to use query result caching (default: true)",
|
|
377
451
|
},
|
|
452
|
+
dry_run: {
|
|
453
|
+
type: "boolean",
|
|
454
|
+
description: "If true, returns query plan and estimated cost without executing (Safe Mode)",
|
|
455
|
+
},
|
|
378
456
|
},
|
|
379
457
|
required: ["query"],
|
|
380
458
|
},
|
|
@@ -2687,7 +2765,7 @@ const TOOLS = [
|
|
|
2687
2765
|
// Create the MCP server
|
|
2688
2766
|
const server = new index_js_1.Server({
|
|
2689
2767
|
name: "mysql-mcp-server",
|
|
2690
|
-
version: "1.
|
|
2768
|
+
version: "1.12.0",
|
|
2691
2769
|
}, {
|
|
2692
2770
|
capabilities: {
|
|
2693
2771
|
tools: {},
|
|
@@ -2721,6 +2799,18 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
2721
2799
|
case "list_tables":
|
|
2722
2800
|
result = await mysqlMCP.listTables((args || {}));
|
|
2723
2801
|
break;
|
|
2802
|
+
case "get_database_summary":
|
|
2803
|
+
result = await mysqlMCP.getDatabaseSummary((args || {}));
|
|
2804
|
+
break;
|
|
2805
|
+
case "get_schema_erd":
|
|
2806
|
+
result = await mysqlMCP.getSchemaERD((args || {}));
|
|
2807
|
+
break;
|
|
2808
|
+
case "get_schema_rag_context":
|
|
2809
|
+
result = await mysqlMCP.getSchemaRagContext((args || {}));
|
|
2810
|
+
break;
|
|
2811
|
+
case "get_column_statistics":
|
|
2812
|
+
result = await mysqlMCP.getColumnStatistics((args || {}));
|
|
2813
|
+
break;
|
|
2724
2814
|
case "read_table_schema":
|
|
2725
2815
|
result = await mysqlMCP.readTableSchema((args || {}));
|
|
2726
2816
|
break;
|
|
@@ -3173,20 +3263,20 @@ async function main() {
|
|
|
3173
3263
|
await server.connect(transport);
|
|
3174
3264
|
// Initialize the MySQL MCP instance AFTER transport is connected
|
|
3175
3265
|
// This ensures the database connection pool is created when the server is ready
|
|
3176
|
-
mysqlMCP = new index_js_2.MySQLMCP(permissions, categories);
|
|
3266
|
+
mysqlMCP = new index_js_2.MySQLMCP(permissions, categories, preset);
|
|
3177
3267
|
// Log the effective filtering configuration to stderr
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
console.error(`
|
|
3181
|
-
console.error("Filtering mode: Dual-layer");
|
|
3268
|
+
const accessProfile = mysqlMCP.getAccessProfile();
|
|
3269
|
+
if (accessProfile.preset) {
|
|
3270
|
+
console.error(`Preset: ${accessProfile.preset.name} (${accessProfile.preset.description})`);
|
|
3182
3271
|
}
|
|
3183
|
-
else if (
|
|
3184
|
-
console.error(`
|
|
3185
|
-
console.error("Filtering mode: Single-layer");
|
|
3272
|
+
else if (preset) {
|
|
3273
|
+
console.error(`Preset requested but not recognized: ${preset}`);
|
|
3186
3274
|
}
|
|
3187
|
-
|
|
3188
|
-
|
|
3275
|
+
console.error(`Permissions (resolved): ${accessProfile.permissions}`);
|
|
3276
|
+
if (accessProfile.categories) {
|
|
3277
|
+
console.error(`Categories (resolved): ${accessProfile.categories}`);
|
|
3189
3278
|
}
|
|
3279
|
+
console.error(`Filtering mode: ${accessProfile.filteringMode}`);
|
|
3190
3280
|
// Log to stderr (not stdout, which is used for MCP protocol)
|
|
3191
3281
|
console.error("MySQL MCP Server running on stdio");
|
|
3192
3282
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { SecurityLayer } from "../security/securityLayer";
|
|
2
|
+
export declare class AnalysisTools {
|
|
3
|
+
private db;
|
|
4
|
+
private security;
|
|
5
|
+
constructor(security: SecurityLayer);
|
|
6
|
+
/**
|
|
7
|
+
* Validate database access - ensures only the connected database can be accessed
|
|
8
|
+
*/
|
|
9
|
+
private validateDatabaseAccess;
|
|
10
|
+
/**
|
|
11
|
+
* Get statistics for a specific column
|
|
12
|
+
*/
|
|
13
|
+
getColumnStatistics(params: {
|
|
14
|
+
table_name: string;
|
|
15
|
+
column_name: string;
|
|
16
|
+
database?: string;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
status: string;
|
|
19
|
+
data?: any;
|
|
20
|
+
error?: string;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Build a compact, schema-aware context pack for RAG (tables, PK/FK, columns, row estimates)
|
|
24
|
+
*/
|
|
25
|
+
getSchemaRagContext(params?: {
|
|
26
|
+
database?: string;
|
|
27
|
+
max_tables?: number;
|
|
28
|
+
max_columns?: number;
|
|
29
|
+
include_relationships?: boolean;
|
|
30
|
+
}): Promise<{
|
|
31
|
+
status: string;
|
|
32
|
+
data?: any;
|
|
33
|
+
error?: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|