@berthojoris/mcp-mysql-server 1.16.4 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/DOCUMENTATIONS.md +109 -548
- package/README.md +85 -486
- package/bin/mcp-mysql.js +57 -107
- package/dist/config/featureConfig.d.ts +2 -29
- package/dist/config/featureConfig.js +18 -211
- package/dist/index.d.ts +59 -12
- package/dist/index.js +44 -4
- package/dist/mcp-server.js +155 -9
- package/dist/security/securityLayer.js +2 -3
- package/dist/tools/forecastingTools.d.ts +36 -0
- package/dist/tools/forecastingTools.js +256 -0
- package/dist/tools/queryVisualizationTools.d.ts +22 -0
- package/dist/tools/queryVisualizationTools.js +155 -0
- package/dist/tools/schemaPatternTools.d.ts +19 -0
- package/dist/tools/schemaPatternTools.js +253 -0
- package/dist/tools/testDataTools.d.ts +26 -0
- package/dist/tools/testDataTools.js +325 -0
- package/manifest.json +109 -1
- package/package.json +1 -1
package/bin/mcp-mysql.js
CHANGED
|
@@ -10,55 +10,32 @@ const path = require("path");
|
|
|
10
10
|
const { spawn } = require("child_process");
|
|
11
11
|
require("dotenv").config();
|
|
12
12
|
|
|
13
|
-
// Get MySQL connection string, permissions,
|
|
14
|
-
const args = process.argv.slice(2);
|
|
15
|
-
const mysqlUrl = args.shift();
|
|
16
|
-
|
|
17
|
-
let permissions;
|
|
18
|
-
let categories;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (normalized.startsWith("preset:")) {
|
|
37
|
-
preset = arg.split(":")[1];
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (["readonly", "analyst", "dba-lite", "dba_lite", "dba lite"].includes(normalized)) {
|
|
42
|
-
preset = arg;
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (permissions === undefined) {
|
|
47
|
-
permissions = arg;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (categories === undefined) {
|
|
52
|
-
categories = arg;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
13
|
+
// Get MySQL connection string, permissions, and optional categories
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const mysqlUrl = args.shift();
|
|
16
|
+
|
|
17
|
+
let permissions;
|
|
18
|
+
let categories;
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
const arg = args[i];
|
|
22
|
+
|
|
23
|
+
if (permissions === undefined) {
|
|
24
|
+
permissions = arg;
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (categories === undefined) {
|
|
29
|
+
categories = arg;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
56
33
|
|
|
57
34
|
if (!mysqlUrl) {
|
|
58
35
|
console.error("Error: MySQL connection URL is required");
|
|
59
|
-
console.error(
|
|
60
|
-
"Usage: mcp-mysql mysql://user:password@host:port/dbname [permissions] [categories]
|
|
61
|
-
);
|
|
36
|
+
console.error(
|
|
37
|
+
"Usage: mcp-mysql mysql://user:password@host:port/dbname [permissions] [categories]",
|
|
38
|
+
);
|
|
62
39
|
console.error("");
|
|
63
40
|
console.error("Examples:");
|
|
64
41
|
console.error(" # All tools enabled (no filtering)");
|
|
@@ -72,17 +49,9 @@ if (!mysqlUrl) {
|
|
|
72
49
|
console.error(
|
|
73
50
|
" # Dual-layer: Permissions + Categories (fine-grained control)",
|
|
74
51
|
);
|
|
75
|
-
console.error(
|
|
76
|
-
' mcp-mysql mysql://root:pass@localhost:3306/mydb "list,read,utility" "database_discovery,performance_monitoring"',
|
|
77
|
-
);
|
|
78
|
-
console.error("");
|
|
79
|
-
console.error(" # Adaptive presets (auto-merge with overrides)");
|
|
80
|
-
console.error(
|
|
81
|
-
' mcp-mysql mysql://root:pass@localhost:3306/mydb --preset readonly',
|
|
82
|
-
);
|
|
83
|
-
console.error(
|
|
84
|
-
' mcp-mysql mysql://root:pass@localhost:3306/mydb --preset analyst "performance_monitoring"',
|
|
85
|
-
);
|
|
52
|
+
console.error(
|
|
53
|
+
' mcp-mysql mysql://root:pass@localhost:3306/mydb "list,read,utility" "database_discovery,performance_monitoring"',
|
|
54
|
+
);
|
|
86
55
|
console.error("");
|
|
87
56
|
console.error("Permissions (Layer 1 - Broad Control):");
|
|
88
57
|
console.error(
|
|
@@ -105,26 +74,20 @@ if (!mysqlUrl) {
|
|
|
105
74
|
console.error(
|
|
106
75
|
" performance_monitoring, cache_management, query_optimization,",
|
|
107
76
|
);
|
|
108
|
-
console.error(
|
|
109
|
-
" backup_restore, import_export, data_migration, schema_migrations",
|
|
110
|
-
);
|
|
111
|
-
console.error("");
|
|
112
|
-
console.error("Filtering Logic:");
|
|
77
|
+
console.error(
|
|
78
|
+
" backup_restore, import_export, data_migration, schema_migrations",
|
|
79
|
+
);
|
|
80
|
+
console.error("");
|
|
81
|
+
console.error("Filtering Logic:");
|
|
113
82
|
console.error(
|
|
114
83
|
" - If only permissions: All tools within those permissions enabled",
|
|
115
84
|
);
|
|
116
|
-
console.error(
|
|
117
|
-
" - If permissions + categories: Only tools matching BOTH layers enabled",
|
|
118
|
-
);
|
|
119
|
-
console.error(" - If nothing specified: All
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
console.error(" readonly, analyst, dba-lite");
|
|
123
|
-
console.error(
|
|
124
|
-
" Presets merge with provided permissions/categories so you can add project-specific overrides.",
|
|
125
|
-
);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
85
|
+
console.error(
|
|
86
|
+
" - If permissions + categories: Only tools matching BOTH layers enabled",
|
|
87
|
+
);
|
|
88
|
+
console.error(" - If nothing specified: All tools enabled");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
128
91
|
|
|
129
92
|
// Parse the MySQL URL to extract connection details
|
|
130
93
|
let connectionConfig;
|
|
@@ -168,39 +131,26 @@ const dbMessage = database
|
|
|
168
131
|
? `${connectionConfig.host}:${connectionConfig.port}/${database}`
|
|
169
132
|
: `${connectionConfig.host}:${connectionConfig.port} (no specific database selected)`;
|
|
170
133
|
|
|
171
|
-
// Set permissions/categories
|
|
172
|
-
if (permissions) {
|
|
173
|
-
process.env.MCP_PERMISSIONS = permissions;
|
|
174
|
-
console.error(`Permissions (Layer 1): ${permissions}`);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (categories) {
|
|
178
|
-
process.env.MCP_CATEGORIES = categories;
|
|
179
|
-
console.error(`Categories (Layer 2): ${categories}`);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
} else if (permissions && categories) {
|
|
192
|
-
console.error(
|
|
193
|
-
`Filtering Mode: Dual-layer (Permissions + Categories${preset ? ", preset merged" : ""})`,
|
|
194
|
-
);
|
|
195
|
-
} else if (permissions && !categories) {
|
|
196
|
-
console.error(
|
|
197
|
-
`Filtering Mode: Permission-based only${preset ? " (preset merged)" : ""}`,
|
|
198
|
-
);
|
|
199
|
-
} else if (!permissions && categories) {
|
|
200
|
-
console.error(
|
|
201
|
-
`Filtering Mode: Category-only${preset ? " (preset merged)" : ""}`,
|
|
202
|
-
);
|
|
203
|
-
}
|
|
134
|
+
// Set permissions/categories as environment variables if provided
|
|
135
|
+
if (permissions) {
|
|
136
|
+
process.env.MCP_PERMISSIONS = permissions;
|
|
137
|
+
console.error(`Permissions (Layer 1): ${permissions}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (categories) {
|
|
141
|
+
process.env.MCP_CATEGORIES = categories;
|
|
142
|
+
console.error(`Categories (Layer 2): ${categories}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!permissions && !categories) {
|
|
146
|
+
console.error("Access Control: All tools enabled (no filtering)");
|
|
147
|
+
} else if (permissions && categories) {
|
|
148
|
+
console.error("Filtering Mode: Dual-layer (Permissions + Categories)");
|
|
149
|
+
} else if (permissions && !categories) {
|
|
150
|
+
console.error("Filtering Mode: Permission-based only");
|
|
151
|
+
} else if (!permissions && categories) {
|
|
152
|
+
console.error("Filtering Mode: Category-only");
|
|
153
|
+
}
|
|
204
154
|
|
|
205
155
|
// Log to stderr (not stdout, which is used for MCP protocol)
|
|
206
156
|
console.error(`Starting MySQL MCP server with connection to ${dbMessage}`);
|
|
@@ -43,17 +43,6 @@ export declare enum DocCategory {
|
|
|
43
43
|
ANALYSIS = "analysis",
|
|
44
44
|
AI_ENHANCEMENT = "ai_enhancement"
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Permission preset bundles for faster, safer configuration
|
|
48
|
-
*/
|
|
49
|
-
export interface PermissionPreset {
|
|
50
|
-
name: string;
|
|
51
|
-
description: string;
|
|
52
|
-
permissions: ToolCategory[];
|
|
53
|
-
categories: DocCategory[];
|
|
54
|
-
allowedTools?: string[];
|
|
55
|
-
deniedTools?: string[];
|
|
56
|
-
}
|
|
57
46
|
/**
|
|
58
47
|
* Map of tool names to their legacy categories
|
|
59
48
|
*/
|
|
@@ -71,22 +60,14 @@ export declare const toolDocCategoryMap: Record<string, DocCategory>;
|
|
|
71
60
|
export declare class FeatureConfig {
|
|
72
61
|
private enabledLegacyCategories;
|
|
73
62
|
private enabledDocCategories;
|
|
74
|
-
private allowedTools;
|
|
75
|
-
private deniedTools;
|
|
76
63
|
private originalPermissionsString;
|
|
77
64
|
private originalCategoriesString;
|
|
78
65
|
private useDualLayer;
|
|
79
|
-
|
|
80
|
-
private presetName?;
|
|
81
|
-
constructor(permissionsStr?: string, categoriesStr?: string, presetName?: string);
|
|
66
|
+
constructor(permissionsStr?: string, categoriesStr?: string);
|
|
82
67
|
/**
|
|
83
68
|
* Normalize and merge preset + user-supplied configuration lists
|
|
84
69
|
*/
|
|
85
70
|
private mergeConfigStrings;
|
|
86
|
-
/**
|
|
87
|
-
* Resolve a preset name to its configuration
|
|
88
|
-
*/
|
|
89
|
-
private resolvePreset;
|
|
90
71
|
/**
|
|
91
72
|
* Parse permissions and categories for dual-layer filtering
|
|
92
73
|
* Layer 1 (permissions): Broad control using legacy categories
|
|
@@ -96,7 +77,7 @@ export declare class FeatureConfig {
|
|
|
96
77
|
/**
|
|
97
78
|
* Update configuration at runtime
|
|
98
79
|
*/
|
|
99
|
-
setConfig(permissionsStr: string, categoriesStr?: string
|
|
80
|
+
setConfig(permissionsStr: string, categoriesStr?: string): void;
|
|
100
81
|
/**
|
|
101
82
|
* Check if a specific tool is enabled
|
|
102
83
|
* Dual-layer logic:
|
|
@@ -136,18 +117,10 @@ export declare class FeatureConfig {
|
|
|
136
117
|
* Check if using dual-layer filtering mode
|
|
137
118
|
*/
|
|
138
119
|
isUsingDualLayer(): boolean;
|
|
139
|
-
/**
|
|
140
|
-
* Get the active preset (if any)
|
|
141
|
-
*/
|
|
142
|
-
getActivePreset(): PermissionPreset | undefined;
|
|
143
120
|
/**
|
|
144
121
|
* Snapshot of the resolved configuration for logging/telemetry
|
|
145
122
|
*/
|
|
146
123
|
getConfigSnapshot(): {
|
|
147
|
-
preset?: {
|
|
148
|
-
name: string;
|
|
149
|
-
description: string;
|
|
150
|
-
};
|
|
151
124
|
permissions: string;
|
|
152
125
|
categories: string;
|
|
153
126
|
filteringMode: string;
|
|
@@ -54,122 +54,6 @@ var DocCategory;
|
|
|
54
54
|
DocCategory["ANALYSIS"] = "analysis";
|
|
55
55
|
DocCategory["AI_ENHANCEMENT"] = "ai_enhancement";
|
|
56
56
|
})(DocCategory || (exports.DocCategory = DocCategory = {}));
|
|
57
|
-
const normalizePresetName = (value) => (value || "").toLowerCase().replace(/[\s_-]/g, "");
|
|
58
|
-
const permissionPresets = {
|
|
59
|
-
readonly: {
|
|
60
|
-
name: "readonly",
|
|
61
|
-
description: "Safe read-only profile with discovery, querying, exports, and diagnostics",
|
|
62
|
-
permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
|
|
63
|
-
categories: [
|
|
64
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
65
|
-
DocCategory.CRUD_OPERATIONS,
|
|
66
|
-
DocCategory.CUSTOM_QUERIES,
|
|
67
|
-
DocCategory.UTILITIES,
|
|
68
|
-
DocCategory.IMPORT_EXPORT,
|
|
69
|
-
DocCategory.PERFORMANCE_MONITORING,
|
|
70
|
-
DocCategory.ANALYSIS,
|
|
71
|
-
],
|
|
72
|
-
},
|
|
73
|
-
analyst: {
|
|
74
|
-
name: "analyst",
|
|
75
|
-
description: "Exploratory analytics profile with query insights and safe exports",
|
|
76
|
-
permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
|
|
77
|
-
categories: [
|
|
78
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
79
|
-
DocCategory.CRUD_OPERATIONS,
|
|
80
|
-
DocCategory.CUSTOM_QUERIES,
|
|
81
|
-
DocCategory.UTILITIES,
|
|
82
|
-
DocCategory.IMPORT_EXPORT,
|
|
83
|
-
DocCategory.PERFORMANCE_MONITORING,
|
|
84
|
-
DocCategory.ANALYSIS,
|
|
85
|
-
DocCategory.QUERY_OPTIMIZATION,
|
|
86
|
-
DocCategory.CACHE_MANAGEMENT,
|
|
87
|
-
DocCategory.SERVER_MANAGEMENT,
|
|
88
|
-
DocCategory.AI_ENHANCEMENT,
|
|
89
|
-
],
|
|
90
|
-
},
|
|
91
|
-
dbalite: {
|
|
92
|
-
name: "dba-lite",
|
|
93
|
-
description: "Admin-lite profile for schema care, migrations, and maintenance",
|
|
94
|
-
permissions: [
|
|
95
|
-
ToolCategory.LIST,
|
|
96
|
-
ToolCategory.READ,
|
|
97
|
-
ToolCategory.UTILITY,
|
|
98
|
-
ToolCategory.DDL,
|
|
99
|
-
ToolCategory.TRANSACTION,
|
|
100
|
-
ToolCategory.PROCEDURE,
|
|
101
|
-
],
|
|
102
|
-
categories: [
|
|
103
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
104
|
-
DocCategory.CUSTOM_QUERIES,
|
|
105
|
-
DocCategory.UTILITIES,
|
|
106
|
-
DocCategory.SERVER_MANAGEMENT,
|
|
107
|
-
DocCategory.SCHEMA_MANAGEMENT,
|
|
108
|
-
DocCategory.TABLE_MAINTENANCE,
|
|
109
|
-
DocCategory.INDEX_MANAGEMENT,
|
|
110
|
-
DocCategory.CONSTRAINT_MANAGEMENT,
|
|
111
|
-
DocCategory.BACKUP_RESTORE,
|
|
112
|
-
DocCategory.SCHEMA_MIGRATIONS,
|
|
113
|
-
DocCategory.PERFORMANCE_MONITORING,
|
|
114
|
-
DocCategory.VIEWS_MANAGEMENT,
|
|
115
|
-
DocCategory.TRIGGERS_MANAGEMENT,
|
|
116
|
-
DocCategory.FUNCTIONS_MANAGEMENT,
|
|
117
|
-
DocCategory.STORED_PROCEDURES,
|
|
118
|
-
],
|
|
119
|
-
},
|
|
120
|
-
dev: {
|
|
121
|
-
name: "dev",
|
|
122
|
-
description: "Development profile with full access to all tools",
|
|
123
|
-
permissions: Object.values(ToolCategory),
|
|
124
|
-
categories: Object.values(DocCategory),
|
|
125
|
-
deniedTools: [], // Explicitly allow everything
|
|
126
|
-
},
|
|
127
|
-
stage: {
|
|
128
|
-
name: "stage",
|
|
129
|
-
description: "Staging profile with data modification but no destructive DDL",
|
|
130
|
-
permissions: [
|
|
131
|
-
ToolCategory.LIST,
|
|
132
|
-
ToolCategory.READ,
|
|
133
|
-
ToolCategory.CREATE,
|
|
134
|
-
ToolCategory.UPDATE,
|
|
135
|
-
ToolCategory.DELETE,
|
|
136
|
-
ToolCategory.UTILITY,
|
|
137
|
-
ToolCategory.TRANSACTION,
|
|
138
|
-
],
|
|
139
|
-
categories: [
|
|
140
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
141
|
-
DocCategory.CRUD_OPERATIONS,
|
|
142
|
-
DocCategory.BULK_OPERATIONS,
|
|
143
|
-
DocCategory.CUSTOM_QUERIES,
|
|
144
|
-
DocCategory.UTILITIES,
|
|
145
|
-
DocCategory.TRANSACTION_MANAGEMENT,
|
|
146
|
-
DocCategory.IMPORT_EXPORT,
|
|
147
|
-
DocCategory.DATA_MIGRATION,
|
|
148
|
-
DocCategory.PERFORMANCE_MONITORING,
|
|
149
|
-
DocCategory.ANALYSIS,
|
|
150
|
-
],
|
|
151
|
-
deniedTools: ["drop_table", "truncate_table", "drop_database"],
|
|
152
|
-
},
|
|
153
|
-
prod: {
|
|
154
|
-
name: "prod",
|
|
155
|
-
description: "Production profile with strict read-only access and safety checks",
|
|
156
|
-
permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
|
|
157
|
-
categories: [
|
|
158
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
159
|
-
DocCategory.CRUD_OPERATIONS, // Read only via permissions
|
|
160
|
-
DocCategory.CUSTOM_QUERIES,
|
|
161
|
-
DocCategory.UTILITIES,
|
|
162
|
-
DocCategory.PERFORMANCE_MONITORING,
|
|
163
|
-
DocCategory.ANALYSIS,
|
|
164
|
-
],
|
|
165
|
-
deniedTools: [
|
|
166
|
-
"create_table", "alter_table", "drop_table", "truncate_table",
|
|
167
|
-
"create_record", "update_record", "delete_record",
|
|
168
|
-
"bulk_insert", "bulk_update", "bulk_delete",
|
|
169
|
-
"execute_sql", "execute_ddl"
|
|
170
|
-
],
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
57
|
/**
|
|
174
58
|
* Map of tool names to their legacy categories
|
|
175
59
|
*/
|
|
@@ -325,6 +209,12 @@ exports.toolCategoryMap = {
|
|
|
325
209
|
designSchemaFromRequirements: ToolCategory.UTILITY,
|
|
326
210
|
auditDatabaseSecurity: ToolCategory.UTILITY,
|
|
327
211
|
recommendIndexes: ToolCategory.UTILITY,
|
|
212
|
+
// Phase 3: AI Enhancement Tools
|
|
213
|
+
generateTestData: ToolCategory.UTILITY,
|
|
214
|
+
analyzeSchemaPatterns: ToolCategory.UTILITY,
|
|
215
|
+
visualizeQuery: ToolCategory.UTILITY,
|
|
216
|
+
predictQueryPerformance: ToolCategory.UTILITY,
|
|
217
|
+
forecastDatabaseGrowth: ToolCategory.UTILITY,
|
|
328
218
|
};
|
|
329
219
|
/**
|
|
330
220
|
* Map of tool names to their documentation categories (New Enhanced System)
|
|
@@ -491,6 +381,12 @@ exports.toolDocCategoryMap = {
|
|
|
491
381
|
designSchemaFromRequirements: DocCategory.AI_ENHANCEMENT,
|
|
492
382
|
auditDatabaseSecurity: DocCategory.AI_ENHANCEMENT,
|
|
493
383
|
recommendIndexes: DocCategory.AI_ENHANCEMENT,
|
|
384
|
+
// Phase 3: AI Enhancement
|
|
385
|
+
generateTestData: DocCategory.AI_ENHANCEMENT,
|
|
386
|
+
analyzeSchemaPatterns: DocCategory.AI_ENHANCEMENT,
|
|
387
|
+
visualizeQuery: DocCategory.AI_ENHANCEMENT,
|
|
388
|
+
predictQueryPerformance: DocCategory.AI_ENHANCEMENT,
|
|
389
|
+
forecastDatabaseGrowth: DocCategory.AI_ENHANCEMENT,
|
|
494
390
|
};
|
|
495
391
|
/**
|
|
496
392
|
* Mapping between legacy categories and documentation categories
|
|
@@ -558,52 +454,21 @@ const legacyToDocCategoryMap = {
|
|
|
558
454
|
* - Layer 2 (Categories): Documentation categories (fine-grained control, optional)
|
|
559
455
|
*/
|
|
560
456
|
class FeatureConfig {
|
|
561
|
-
constructor(permissionsStr, categoriesStr
|
|
562
|
-
const presetInput = presetName ||
|
|
563
|
-
process.env.MCP_PERMISSION_PRESET ||
|
|
564
|
-
process.env.MCP_PRESET ||
|
|
565
|
-
"";
|
|
566
|
-
this.activePreset = this.resolvePreset(presetInput);
|
|
567
|
-
this.presetName = this.activePreset?.name;
|
|
568
|
-
const presetRequested = !!presetInput.trim();
|
|
457
|
+
constructor(permissionsStr, categoriesStr) {
|
|
569
458
|
// Support both old single-parameter and new dual-parameter signatures
|
|
570
459
|
const permissionsInput = permissionsStr ||
|
|
571
460
|
process.env.MCP_PERMISSIONS ||
|
|
572
461
|
process.env.MCP_CONFIG ||
|
|
573
462
|
"";
|
|
574
463
|
const categoriesInput = categoriesStr || process.env.MCP_CATEGORIES || "";
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
// default to a safe read-only baseline rather than enabling everything.
|
|
578
|
-
const basePermissions = this.activePreset
|
|
579
|
-
? this.activePreset.permissions.join(",")
|
|
580
|
-
: presetRequested && !permissionsInput
|
|
581
|
-
? [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY].join(",")
|
|
582
|
-
: "";
|
|
583
|
-
const baseCategories = this.activePreset
|
|
584
|
-
? this.activePreset.categories.join(",")
|
|
585
|
-
: presetRequested && !categoriesInput
|
|
586
|
-
? [
|
|
587
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
588
|
-
DocCategory.CRUD_OPERATIONS,
|
|
589
|
-
DocCategory.CUSTOM_QUERIES,
|
|
590
|
-
DocCategory.UTILITIES,
|
|
591
|
-
].join(",")
|
|
592
|
-
: "";
|
|
593
|
-
if (presetRequested && !this.activePreset) {
|
|
594
|
-
console.warn(`Preset '${presetInput}' not recognized. Falling back to safe read-only defaults.`);
|
|
595
|
-
}
|
|
596
|
-
const mergedPermissions = this.mergeConfigStrings(basePermissions, permissionsInput);
|
|
597
|
-
const mergedCategories = this.mergeConfigStrings(baseCategories, categoriesInput);
|
|
464
|
+
const mergedPermissions = this.mergeConfigStrings("", permissionsInput);
|
|
465
|
+
const mergedCategories = this.mergeConfigStrings("", categoriesInput);
|
|
598
466
|
this.originalPermissionsString = mergedPermissions;
|
|
599
467
|
this.originalCategoriesString = mergedCategories;
|
|
600
468
|
this.useDualLayer = !!mergedCategories.trim();
|
|
601
469
|
const parsed = this.parseConfig(mergedPermissions, mergedCategories);
|
|
602
470
|
this.enabledLegacyCategories = parsed.legacy;
|
|
603
471
|
this.enabledDocCategories = parsed.doc;
|
|
604
|
-
// Initialize Allow/Deny Lists
|
|
605
|
-
this.allowedTools = new Set(this.activePreset?.allowedTools || []);
|
|
606
|
-
this.deniedTools = new Set(this.activePreset?.deniedTools || []);
|
|
607
472
|
}
|
|
608
473
|
/**
|
|
609
474
|
* Normalize and merge preset + user-supplied configuration lists
|
|
@@ -614,13 +479,6 @@ class FeatureConfig {
|
|
|
614
479
|
.filter(Boolean);
|
|
615
480
|
return Array.from(new Set(items)).join(",");
|
|
616
481
|
}
|
|
617
|
-
/**
|
|
618
|
-
* Resolve a preset name to its configuration
|
|
619
|
-
*/
|
|
620
|
-
resolvePreset(name) {
|
|
621
|
-
const normalized = normalizePresetName(name);
|
|
622
|
-
return normalized ? permissionPresets[normalized] : undefined;
|
|
623
|
-
}
|
|
624
482
|
/**
|
|
625
483
|
* Parse permissions and categories for dual-layer filtering
|
|
626
484
|
* Layer 1 (permissions): Broad control using legacy categories
|
|
@@ -662,9 +520,6 @@ class FeatureConfig {
|
|
|
662
520
|
docCats.forEach((dc) => docSet.add(dc));
|
|
663
521
|
});
|
|
664
522
|
}
|
|
665
|
-
// Re-initialize Allow/Deny Lists if preset changed
|
|
666
|
-
this.allowedTools = new Set(this.activePreset?.allowedTools || []);
|
|
667
|
-
this.deniedTools = new Set(this.activePreset?.deniedTools || []);
|
|
668
523
|
return {
|
|
669
524
|
legacy: legacySet,
|
|
670
525
|
doc: docSet,
|
|
@@ -673,33 +528,9 @@ class FeatureConfig {
|
|
|
673
528
|
/**
|
|
674
529
|
* Update configuration at runtime
|
|
675
530
|
*/
|
|
676
|
-
setConfig(permissionsStr, categoriesStr
|
|
677
|
-
const
|
|
678
|
-
this.
|
|
679
|
-
presetName === ""
|
|
680
|
-
? undefined
|
|
681
|
-
: this.resolvePreset(presetName || this.presetName);
|
|
682
|
-
this.presetName = this.activePreset?.name;
|
|
683
|
-
const basePermissions = this.activePreset
|
|
684
|
-
? this.activePreset.permissions.join(",")
|
|
685
|
-
: presetRequested && !permissionsStr
|
|
686
|
-
? [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY].join(",")
|
|
687
|
-
: "";
|
|
688
|
-
const baseCategories = this.activePreset
|
|
689
|
-
? this.activePreset.categories.join(",")
|
|
690
|
-
: presetRequested && !categoriesStr
|
|
691
|
-
? [
|
|
692
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
693
|
-
DocCategory.CRUD_OPERATIONS,
|
|
694
|
-
DocCategory.CUSTOM_QUERIES,
|
|
695
|
-
DocCategory.UTILITIES,
|
|
696
|
-
].join(",")
|
|
697
|
-
: "";
|
|
698
|
-
if (presetRequested && !this.activePreset) {
|
|
699
|
-
console.warn(`Preset '${presetName}' not recognized. Falling back to safe read-only defaults.`);
|
|
700
|
-
}
|
|
701
|
-
const mergedPermissions = this.mergeConfigStrings(basePermissions, permissionsStr);
|
|
702
|
-
const mergedCategories = this.mergeConfigStrings(baseCategories, categoriesStr || "");
|
|
531
|
+
setConfig(permissionsStr, categoriesStr) {
|
|
532
|
+
const mergedPermissions = this.mergeConfigStrings("", permissionsStr);
|
|
533
|
+
const mergedCategories = this.mergeConfigStrings("", categoriesStr || "");
|
|
703
534
|
this.originalPermissionsString = mergedPermissions;
|
|
704
535
|
this.originalCategoriesString = mergedCategories;
|
|
705
536
|
this.useDualLayer = !!(mergedCategories && mergedCategories.trim());
|
|
@@ -721,15 +552,6 @@ class FeatureConfig {
|
|
|
721
552
|
console.warn(`Unknown tool: ${toolName}`);
|
|
722
553
|
return false;
|
|
723
554
|
}
|
|
724
|
-
// Layer 0: Check explicit Deny/Allow lists
|
|
725
|
-
// Deny takes precedence
|
|
726
|
-
if (this.deniedTools.has(toolName)) {
|
|
727
|
-
return false;
|
|
728
|
-
}
|
|
729
|
-
// Allow overrides other checks
|
|
730
|
-
if (this.allowedTools.has(toolName)) {
|
|
731
|
-
return true;
|
|
732
|
-
}
|
|
733
555
|
// Layer 1: Check permission (legacy category)
|
|
734
556
|
const hasPermission = legacyCategory
|
|
735
557
|
? this.enabledLegacyCategories.has(legacyCategory)
|
|
@@ -756,9 +578,6 @@ class FeatureConfig {
|
|
|
756
578
|
if (!docCategory && !legacyCategory) {
|
|
757
579
|
return `Unknown tool '${toolName}'. This tool is not recognized by the MCP server.`;
|
|
758
580
|
}
|
|
759
|
-
if (this.deniedTools.has(toolName)) {
|
|
760
|
-
return `Permission denied: Tool '${toolName}' is explicitly denied by the current profile ('${this.presetName}').`;
|
|
761
|
-
}
|
|
762
581
|
const isAllEnabled = !this.originalPermissionsString.trim() &&
|
|
763
582
|
!this.originalCategoriesString.trim();
|
|
764
583
|
if (isAllEnabled) {
|
|
@@ -841,23 +660,11 @@ class FeatureConfig {
|
|
|
841
660
|
isUsingDualLayer() {
|
|
842
661
|
return this.useDualLayer;
|
|
843
662
|
}
|
|
844
|
-
/**
|
|
845
|
-
* Get the active preset (if any)
|
|
846
|
-
*/
|
|
847
|
-
getActivePreset() {
|
|
848
|
-
return this.activePreset;
|
|
849
|
-
}
|
|
850
663
|
/**
|
|
851
664
|
* Snapshot of the resolved configuration for logging/telemetry
|
|
852
665
|
*/
|
|
853
666
|
getConfigSnapshot() {
|
|
854
667
|
return {
|
|
855
|
-
preset: this.activePreset
|
|
856
|
-
? {
|
|
857
|
-
name: this.activePreset.name,
|
|
858
|
-
description: this.activePreset.description,
|
|
859
|
-
}
|
|
860
|
-
: undefined,
|
|
861
668
|
permissions: this.originalPermissionsString || "all",
|
|
862
669
|
categories: this.originalCategoriesString ||
|
|
863
670
|
(this.useDualLayer ? "" : "derived from permissions"),
|