@berthojoris/mcp-mysql-server 1.17.0 → 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 +6 -0
- package/DOCUMENTATIONS.md +45 -756
- package/README.md +58 -37
- package/bin/mcp-mysql.js +57 -107
- package/dist/config/featureConfig.d.ts +2 -29
- package/dist/config/featureConfig.js +6 -211
- package/dist/index.d.ts +5 -12
- package/dist/index.js +3 -4
- package/dist/mcp-server.js +1 -8
- package/dist/security/securityLayer.js +2 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,7 +78,6 @@ DB_USER=root
|
|
|
78
78
|
DB_PASSWORD=yourpassword
|
|
79
79
|
DB_NAME=yourdatabase
|
|
80
80
|
MCP_CONFIG=list,read,utility
|
|
81
|
-
MCP_MASKING_PROFILE=partial
|
|
82
81
|
```
|
|
83
82
|
|
|
84
83
|
### 2. Build Project (If Cloned Locally)
|
|
@@ -194,40 +193,14 @@ Alternative approach using environment variables instead of connection string:
|
|
|
194
193
|
"DB_PASSWORD": "your_password",
|
|
195
194
|
"DB_NAME": "your_database",
|
|
196
195
|
"MCP_PERMISSIONS": "list,read,utility",
|
|
197
|
-
"MCP_CATEGORIES": "database_discovery,performance_monitoring"
|
|
198
|
-
"MCP_MASKING_PROFILE": "partial"
|
|
196
|
+
"MCP_CATEGORIES": "database_discovery,performance_monitoring"
|
|
199
197
|
}
|
|
200
198
|
}
|
|
201
199
|
}
|
|
202
200
|
}
|
|
203
201
|
```
|
|
204
202
|
|
|
205
|
-
**
|
|
206
|
-
|
|
207
|
-
```json
|
|
208
|
-
{
|
|
209
|
-
"mcpServers": {
|
|
210
|
-
"mysql": {
|
|
211
|
-
"command": "npx",
|
|
212
|
-
"args": ["-y", "@berthojoris/mysql-mcp"],
|
|
213
|
-
"env": {
|
|
214
|
-
"DB_HOST": "localhost",
|
|
215
|
-
"DB_PORT": "3306",
|
|
216
|
-
"DB_USER": "root",
|
|
217
|
-
"DB_PASSWORD": "your_password",
|
|
218
|
-
"DB_NAME": "your_database",
|
|
219
|
-
"MCP_PRESET": "readonly",
|
|
220
|
-
"MCP_PERMISSIONS": "list,read,utility",
|
|
221
|
-
"MCP_CATEGORIES": "performance_monitoring"
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Add `MCP_PRESET` for the base bundle and optionally layer on `MCP_PERMISSIONS` / `MCP_CATEGORIES` for project-specific overrides.
|
|
229
|
-
|
|
230
|
-
For more environment-variable patterns (presets/profiles, client-specific env blocks), see **[DOCUMENTATIONS.md → Setup & Configuration](DOCUMENTATIONS.md#setup--configuration-extended)**.
|
|
203
|
+
For more client-specific config snippets, see **[DOCUMENTATIONS.md → Setup & Configuration](DOCUMENTATIONS.md#setup--configuration-extended)**.
|
|
231
204
|
|
|
232
205
|
---
|
|
233
206
|
|
|
@@ -244,16 +217,64 @@ Control database access with a **dual-layer filtering system** that provides bot
|
|
|
244
217
|
|
|
245
218
|
Use documentation categories to fine-tune which tools are exposed (Layer 2):
|
|
246
219
|
|
|
220
|
+
<table>
|
|
221
|
+
<thead>
|
|
222
|
+
<tr>
|
|
223
|
+
<th align="left">Category A</th>
|
|
224
|
+
<th align="left">Category B</th>
|
|
225
|
+
<th align="left">Category C</th>
|
|
226
|
+
<th align="left">Category D</th>
|
|
227
|
+
</tr>
|
|
228
|
+
</thead>
|
|
229
|
+
<tbody>
|
|
230
|
+
<tr>
|
|
231
|
+
<td><code>database_discovery</code></td>
|
|
232
|
+
<td><code>crud_operations</code></td>
|
|
233
|
+
<td><code>bulk_operations</code></td>
|
|
234
|
+
<td><code>custom_queries</code></td>
|
|
235
|
+
</tr>
|
|
236
|
+
<tr>
|
|
237
|
+
<td><code>schema_management</code></td>
|
|
238
|
+
<td><code>utilities</code></td>
|
|
239
|
+
<td><code>transaction_management</code></td>
|
|
240
|
+
<td><code>stored_procedures</code></td>
|
|
241
|
+
</tr>
|
|
242
|
+
<tr>
|
|
243
|
+
<td><code>views_management</code></td>
|
|
244
|
+
<td><code>triggers_management</code></td>
|
|
245
|
+
<td><code>functions_management</code></td>
|
|
246
|
+
<td><code>index_management</code></td>
|
|
247
|
+
</tr>
|
|
248
|
+
<tr>
|
|
249
|
+
<td><code>constraint_management</code></td>
|
|
250
|
+
<td><code>table_maintenance</code></td>
|
|
251
|
+
<td><code>server_management</code></td>
|
|
252
|
+
<td><code>performance_monitoring</code></td>
|
|
253
|
+
</tr>
|
|
254
|
+
<tr>
|
|
255
|
+
<td><code>cache_management</code></td>
|
|
256
|
+
<td><code>query_optimization</code></td>
|
|
257
|
+
<td><code>backup_restore</code></td>
|
|
258
|
+
<td><code>import_export</code></td>
|
|
259
|
+
</tr>
|
|
260
|
+
<tr>
|
|
261
|
+
<td><code>data_migration</code></td>
|
|
262
|
+
<td><code>schema_migrations</code></td>
|
|
263
|
+
<td><code>analysis</code></td>
|
|
264
|
+
<td><code>ai_enhancement</code></td>
|
|
265
|
+
</tr>
|
|
266
|
+
</tbody>
|
|
267
|
+
</table>
|
|
268
|
+
|
|
269
|
+
<details>
|
|
270
|
+
<summary>Copy/paste list (comma-separated, no spaces)</summary>
|
|
271
|
+
|
|
247
272
|
```text
|
|
248
|
-
database_discovery,crud_operations,bulk_operations,custom_queries,
|
|
249
|
-
schema_management,utilities,transaction_management,stored_procedures,
|
|
250
|
-
views_management,triggers_management,functions_management,index_management,
|
|
251
|
-
constraint_management,table_maintenance,server_management,
|
|
252
|
-
performance_monitoring,cache_management,query_optimization,
|
|
253
|
-
backup_restore,import_export,data_migration,schema_migrations,
|
|
254
|
-
analysis,ai_enhancement
|
|
273
|
+
database_discovery,crud_operations,bulk_operations,custom_queries,schema_management,utilities,transaction_management,stored_procedures,views_management,triggers_management,functions_management,index_management,constraint_management,table_maintenance,server_management,performance_monitoring,cache_management,query_optimization,backup_restore,import_export,data_migration,schema_migrations,analysis,ai_enhancement
|
|
255
274
|
```
|
|
256
275
|
|
|
276
|
+
</details>
|
|
277
|
+
|
|
257
278
|
Full category → tool mapping (and examples) lives in **[DOCUMENTATIONS.md → Category Filtering System](DOCUMENTATIONS.md#🆕-category-filtering-system)**.
|
|
258
279
|
|
|
259
280
|
### Legacy Categories (Backward Compatible)
|
|
@@ -271,7 +292,7 @@ Full category → tool mapping (and examples) lives in **[DOCUMENTATIONS.md →
|
|
|
271
292
|
| `transaction` | BEGIN, COMMIT, ROLLBACK | ACID operations |
|
|
272
293
|
| `utility` | Connection testing, diagnostics | Troubleshooting |
|
|
273
294
|
|
|
274
|
-
Common
|
|
295
|
+
Common configuration examples are documented in **[DOCUMENTATIONS.md → Category Filtering System](DOCUMENTATIONS.md#🆕-category-filtering-system)**.
|
|
275
296
|
|
|
276
297
|
---
|
|
277
298
|
|
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
|
*/
|
|
@@ -570,52 +454,21 @@ const legacyToDocCategoryMap = {
|
|
|
570
454
|
* - Layer 2 (Categories): Documentation categories (fine-grained control, optional)
|
|
571
455
|
*/
|
|
572
456
|
class FeatureConfig {
|
|
573
|
-
constructor(permissionsStr, categoriesStr
|
|
574
|
-
const presetInput = presetName ||
|
|
575
|
-
process.env.MCP_PERMISSION_PRESET ||
|
|
576
|
-
process.env.MCP_PRESET ||
|
|
577
|
-
"";
|
|
578
|
-
this.activePreset = this.resolvePreset(presetInput);
|
|
579
|
-
this.presetName = this.activePreset?.name;
|
|
580
|
-
const presetRequested = !!presetInput.trim();
|
|
457
|
+
constructor(permissionsStr, categoriesStr) {
|
|
581
458
|
// Support both old single-parameter and new dual-parameter signatures
|
|
582
459
|
const permissionsInput = permissionsStr ||
|
|
583
460
|
process.env.MCP_PERMISSIONS ||
|
|
584
461
|
process.env.MCP_CONFIG ||
|
|
585
462
|
"";
|
|
586
463
|
const categoriesInput = categoriesStr || process.env.MCP_CATEGORIES || "";
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
// default to a safe read-only baseline rather than enabling everything.
|
|
590
|
-
const basePermissions = this.activePreset
|
|
591
|
-
? this.activePreset.permissions.join(",")
|
|
592
|
-
: presetRequested && !permissionsInput
|
|
593
|
-
? [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY].join(",")
|
|
594
|
-
: "";
|
|
595
|
-
const baseCategories = this.activePreset
|
|
596
|
-
? this.activePreset.categories.join(",")
|
|
597
|
-
: presetRequested && !categoriesInput
|
|
598
|
-
? [
|
|
599
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
600
|
-
DocCategory.CRUD_OPERATIONS,
|
|
601
|
-
DocCategory.CUSTOM_QUERIES,
|
|
602
|
-
DocCategory.UTILITIES,
|
|
603
|
-
].join(",")
|
|
604
|
-
: "";
|
|
605
|
-
if (presetRequested && !this.activePreset) {
|
|
606
|
-
console.warn(`Preset '${presetInput}' not recognized. Falling back to safe read-only defaults.`);
|
|
607
|
-
}
|
|
608
|
-
const mergedPermissions = this.mergeConfigStrings(basePermissions, permissionsInput);
|
|
609
|
-
const mergedCategories = this.mergeConfigStrings(baseCategories, categoriesInput);
|
|
464
|
+
const mergedPermissions = this.mergeConfigStrings("", permissionsInput);
|
|
465
|
+
const mergedCategories = this.mergeConfigStrings("", categoriesInput);
|
|
610
466
|
this.originalPermissionsString = mergedPermissions;
|
|
611
467
|
this.originalCategoriesString = mergedCategories;
|
|
612
468
|
this.useDualLayer = !!mergedCategories.trim();
|
|
613
469
|
const parsed = this.parseConfig(mergedPermissions, mergedCategories);
|
|
614
470
|
this.enabledLegacyCategories = parsed.legacy;
|
|
615
471
|
this.enabledDocCategories = parsed.doc;
|
|
616
|
-
// Initialize Allow/Deny Lists
|
|
617
|
-
this.allowedTools = new Set(this.activePreset?.allowedTools || []);
|
|
618
|
-
this.deniedTools = new Set(this.activePreset?.deniedTools || []);
|
|
619
472
|
}
|
|
620
473
|
/**
|
|
621
474
|
* Normalize and merge preset + user-supplied configuration lists
|
|
@@ -626,13 +479,6 @@ class FeatureConfig {
|
|
|
626
479
|
.filter(Boolean);
|
|
627
480
|
return Array.from(new Set(items)).join(",");
|
|
628
481
|
}
|
|
629
|
-
/**
|
|
630
|
-
* Resolve a preset name to its configuration
|
|
631
|
-
*/
|
|
632
|
-
resolvePreset(name) {
|
|
633
|
-
const normalized = normalizePresetName(name);
|
|
634
|
-
return normalized ? permissionPresets[normalized] : undefined;
|
|
635
|
-
}
|
|
636
482
|
/**
|
|
637
483
|
* Parse permissions and categories for dual-layer filtering
|
|
638
484
|
* Layer 1 (permissions): Broad control using legacy categories
|
|
@@ -674,9 +520,6 @@ class FeatureConfig {
|
|
|
674
520
|
docCats.forEach((dc) => docSet.add(dc));
|
|
675
521
|
});
|
|
676
522
|
}
|
|
677
|
-
// Re-initialize Allow/Deny Lists if preset changed
|
|
678
|
-
this.allowedTools = new Set(this.activePreset?.allowedTools || []);
|
|
679
|
-
this.deniedTools = new Set(this.activePreset?.deniedTools || []);
|
|
680
523
|
return {
|
|
681
524
|
legacy: legacySet,
|
|
682
525
|
doc: docSet,
|
|
@@ -685,33 +528,9 @@ class FeatureConfig {
|
|
|
685
528
|
/**
|
|
686
529
|
* Update configuration at runtime
|
|
687
530
|
*/
|
|
688
|
-
setConfig(permissionsStr, categoriesStr
|
|
689
|
-
const
|
|
690
|
-
this.
|
|
691
|
-
presetName === ""
|
|
692
|
-
? undefined
|
|
693
|
-
: this.resolvePreset(presetName || this.presetName);
|
|
694
|
-
this.presetName = this.activePreset?.name;
|
|
695
|
-
const basePermissions = this.activePreset
|
|
696
|
-
? this.activePreset.permissions.join(",")
|
|
697
|
-
: presetRequested && !permissionsStr
|
|
698
|
-
? [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY].join(",")
|
|
699
|
-
: "";
|
|
700
|
-
const baseCategories = this.activePreset
|
|
701
|
-
? this.activePreset.categories.join(",")
|
|
702
|
-
: presetRequested && !categoriesStr
|
|
703
|
-
? [
|
|
704
|
-
DocCategory.DATABASE_DISCOVERY,
|
|
705
|
-
DocCategory.CRUD_OPERATIONS,
|
|
706
|
-
DocCategory.CUSTOM_QUERIES,
|
|
707
|
-
DocCategory.UTILITIES,
|
|
708
|
-
].join(",")
|
|
709
|
-
: "";
|
|
710
|
-
if (presetRequested && !this.activePreset) {
|
|
711
|
-
console.warn(`Preset '${presetName}' not recognized. Falling back to safe read-only defaults.`);
|
|
712
|
-
}
|
|
713
|
-
const mergedPermissions = this.mergeConfigStrings(basePermissions, permissionsStr);
|
|
714
|
-
const mergedCategories = this.mergeConfigStrings(baseCategories, categoriesStr || "");
|
|
531
|
+
setConfig(permissionsStr, categoriesStr) {
|
|
532
|
+
const mergedPermissions = this.mergeConfigStrings("", permissionsStr);
|
|
533
|
+
const mergedCategories = this.mergeConfigStrings("", categoriesStr || "");
|
|
715
534
|
this.originalPermissionsString = mergedPermissions;
|
|
716
535
|
this.originalCategoriesString = mergedCategories;
|
|
717
536
|
this.useDualLayer = !!(mergedCategories && mergedCategories.trim());
|
|
@@ -733,15 +552,6 @@ class FeatureConfig {
|
|
|
733
552
|
console.warn(`Unknown tool: ${toolName}`);
|
|
734
553
|
return false;
|
|
735
554
|
}
|
|
736
|
-
// Layer 0: Check explicit Deny/Allow lists
|
|
737
|
-
// Deny takes precedence
|
|
738
|
-
if (this.deniedTools.has(toolName)) {
|
|
739
|
-
return false;
|
|
740
|
-
}
|
|
741
|
-
// Allow overrides other checks
|
|
742
|
-
if (this.allowedTools.has(toolName)) {
|
|
743
|
-
return true;
|
|
744
|
-
}
|
|
745
555
|
// Layer 1: Check permission (legacy category)
|
|
746
556
|
const hasPermission = legacyCategory
|
|
747
557
|
? this.enabledLegacyCategories.has(legacyCategory)
|
|
@@ -768,9 +578,6 @@ class FeatureConfig {
|
|
|
768
578
|
if (!docCategory && !legacyCategory) {
|
|
769
579
|
return `Unknown tool '${toolName}'. This tool is not recognized by the MCP server.`;
|
|
770
580
|
}
|
|
771
|
-
if (this.deniedTools.has(toolName)) {
|
|
772
|
-
return `Permission denied: Tool '${toolName}' is explicitly denied by the current profile ('${this.presetName}').`;
|
|
773
|
-
}
|
|
774
581
|
const isAllEnabled = !this.originalPermissionsString.trim() &&
|
|
775
582
|
!this.originalCategoriesString.trim();
|
|
776
583
|
if (isAllEnabled) {
|
|
@@ -853,23 +660,11 @@ class FeatureConfig {
|
|
|
853
660
|
isUsingDualLayer() {
|
|
854
661
|
return this.useDualLayer;
|
|
855
662
|
}
|
|
856
|
-
/**
|
|
857
|
-
* Get the active preset (if any)
|
|
858
|
-
*/
|
|
859
|
-
getActivePreset() {
|
|
860
|
-
return this.activePreset;
|
|
861
|
-
}
|
|
862
663
|
/**
|
|
863
664
|
* Snapshot of the resolved configuration for logging/telemetry
|
|
864
665
|
*/
|
|
865
666
|
getConfigSnapshot() {
|
|
866
667
|
return {
|
|
867
|
-
preset: this.activePreset
|
|
868
|
-
? {
|
|
869
|
-
name: this.activePreset.name,
|
|
870
|
-
description: this.activePreset.description,
|
|
871
|
-
}
|
|
872
|
-
: undefined,
|
|
873
668
|
permissions: this.originalPermissionsString || "all",
|
|
874
669
|
categories: this.originalCategoriesString ||
|
|
875
670
|
(this.useDualLayer ? "" : "derived from permissions"),
|