@berthojoris/mcp-mysql-server 1.0.2 → 1.1.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/README.md
CHANGED
|
@@ -925,48 +925,7 @@ curl http://localhost:3000/health
|
|
|
925
925
|
|
|
926
926
|
---
|
|
927
927
|
|
|
928
|
-
##
|
|
929
|
-
|
|
930
|
-
To make your MCP server available to the world:
|
|
931
|
-
|
|
932
|
-
### 1. Update package.json
|
|
933
|
-
|
|
934
|
-
```json
|
|
935
|
-
{
|
|
936
|
-
"name": "@your-username/mcp-mysql",
|
|
937
|
-
"author": "Your Name <your.email@example.com>",
|
|
938
|
-
"repository": {
|
|
939
|
-
"type": "git",
|
|
940
|
-
"url": "https://github.com/berthojoris/mysql-mcp.git"
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
```
|
|
944
|
-
|
|
945
|
-
### 2. Build
|
|
946
|
-
|
|
947
|
-
```bash
|
|
948
|
-
npm run build
|
|
949
|
-
```
|
|
950
|
-
|
|
951
|
-
### 3. Publish
|
|
952
|
-
|
|
953
|
-
```bash
|
|
954
|
-
# Login to npm
|
|
955
|
-
npm login
|
|
956
|
-
|
|
957
|
-
# Publish (for scoped packages)
|
|
958
|
-
npm publish --access public
|
|
959
|
-
```
|
|
960
|
-
|
|
961
|
-
### 4. Users Can Install
|
|
962
|
-
|
|
963
|
-
```bash
|
|
964
|
-
npx @your-username/mcp-mysql mysql://user:pass@localhost:3306/db "list,read,utility"
|
|
965
|
-
```
|
|
966
|
-
|
|
967
|
-
---
|
|
968
|
-
|
|
969
|
-
## 🐛 Troubleshooting
|
|
928
|
+
## Troubleshooting
|
|
970
929
|
|
|
971
930
|
### MCP Server Not Connecting
|
|
972
931
|
|
|
@@ -3,7 +3,8 @@ export declare class DatabaseTools {
|
|
|
3
3
|
private db;
|
|
4
4
|
constructor();
|
|
5
5
|
/**
|
|
6
|
-
* List
|
|
6
|
+
* List only the connected database (security restriction)
|
|
7
|
+
* This prevents access to other databases on the MySQL server
|
|
7
8
|
*/
|
|
8
9
|
listDatabases(): Promise<{
|
|
9
10
|
status: string;
|
|
@@ -6,20 +6,37 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.DatabaseTools = void 0;
|
|
7
7
|
const connection_1 = __importDefault(require("../db/connection"));
|
|
8
8
|
const schemas_1 = require("../validation/schemas");
|
|
9
|
+
const config_1 = require("../config/config");
|
|
9
10
|
class DatabaseTools {
|
|
10
11
|
constructor() {
|
|
11
12
|
this.db = connection_1.default.getInstance();
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
14
|
-
* List
|
|
15
|
+
* List only the connected database (security restriction)
|
|
16
|
+
* This prevents access to other databases on the MySQL server
|
|
15
17
|
*/
|
|
16
18
|
async listDatabases() {
|
|
17
19
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
// Only return the database specified in the connection string
|
|
21
|
+
// This is a security measure to prevent access to other databases
|
|
22
|
+
if (!config_1.dbConfig.database) {
|
|
23
|
+
return {
|
|
24
|
+
status: 'error',
|
|
25
|
+
error: 'No database specified in connection string. Please specify a database name in your MySQL connection URL.'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
// Verify the database exists and is accessible
|
|
29
|
+
const results = await this.db.query('SELECT DATABASE() as current_database');
|
|
30
|
+
const currentDatabase = results[0]?.current_database;
|
|
31
|
+
if (!currentDatabase) {
|
|
32
|
+
return {
|
|
33
|
+
status: 'error',
|
|
34
|
+
error: 'No database selected. Please ensure your connection string includes a valid database name.'
|
|
35
|
+
};
|
|
36
|
+
}
|
|
20
37
|
return {
|
|
21
38
|
status: 'success',
|
|
22
|
-
data:
|
|
39
|
+
data: [currentDatabase]
|
|
23
40
|
};
|
|
24
41
|
}
|
|
25
42
|
catch (error) {
|
|
@@ -41,8 +58,23 @@ class DatabaseTools {
|
|
|
41
58
|
};
|
|
42
59
|
}
|
|
43
60
|
try {
|
|
61
|
+
// Security validation: if database is specified, ensure it matches the connected database
|
|
62
|
+
if (params.database) {
|
|
63
|
+
if (!config_1.dbConfig.database) {
|
|
64
|
+
return {
|
|
65
|
+
status: 'error',
|
|
66
|
+
error: 'No database specified in connection string. Cannot access other databases.'
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (params.database !== config_1.dbConfig.database) {
|
|
70
|
+
return {
|
|
71
|
+
status: 'error',
|
|
72
|
+
error: `Access denied. You can only access the connected database '${config_1.dbConfig.database}'. Requested database '${params.database}' is not allowed.`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
44
76
|
let query = 'SHOW TABLES';
|
|
45
|
-
// If database is specified, use it
|
|
77
|
+
// If database is specified and validated, use it
|
|
46
78
|
if (params.database) {
|
|
47
79
|
query = `SHOW TABLES FROM \`${params.database}\``;
|
|
48
80
|
}
|
|
@@ -3,6 +3,10 @@ export declare class StoredProcedureTools {
|
|
|
3
3
|
private db;
|
|
4
4
|
private security;
|
|
5
5
|
constructor(security: SecurityLayer);
|
|
6
|
+
/**
|
|
7
|
+
* Validate database access - ensures only the connected database can be accessed
|
|
8
|
+
*/
|
|
9
|
+
private validateDatabaseAccess;
|
|
6
10
|
/**
|
|
7
11
|
* List all stored procedures in the current database
|
|
8
12
|
*/
|
|
@@ -12,6 +12,38 @@ class StoredProcedureTools {
|
|
|
12
12
|
this.db = connection_1.default.getInstance();
|
|
13
13
|
this.security = security;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Validate database access - ensures only the connected database can be accessed
|
|
17
|
+
*/
|
|
18
|
+
validateDatabaseAccess(requestedDatabase) {
|
|
19
|
+
const connectedDatabase = config_1.dbConfig.database;
|
|
20
|
+
if (!connectedDatabase) {
|
|
21
|
+
return {
|
|
22
|
+
valid: false,
|
|
23
|
+
database: '',
|
|
24
|
+
error: 'No database specified in connection string. Cannot access any database.'
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// If no database is requested, use the connected database
|
|
28
|
+
if (!requestedDatabase) {
|
|
29
|
+
return {
|
|
30
|
+
valid: true,
|
|
31
|
+
database: connectedDatabase
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// If a specific database is requested, ensure it matches the connected database
|
|
35
|
+
if (requestedDatabase !== connectedDatabase) {
|
|
36
|
+
return {
|
|
37
|
+
valid: false,
|
|
38
|
+
database: '',
|
|
39
|
+
error: `Access denied. You can only access the connected database '${connectedDatabase}'. Requested database '${requestedDatabase}' is not allowed.`
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
valid: true,
|
|
44
|
+
database: connectedDatabase
|
|
45
|
+
};
|
|
46
|
+
}
|
|
15
47
|
/**
|
|
16
48
|
* List all stored procedures in the current database
|
|
17
49
|
*/
|
|
@@ -24,13 +56,15 @@ class StoredProcedureTools {
|
|
|
24
56
|
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateListStoredProcedures.errors)
|
|
25
57
|
};
|
|
26
58
|
}
|
|
27
|
-
|
|
28
|
-
|
|
59
|
+
// Validate database access
|
|
60
|
+
const dbValidation = this.validateDatabaseAccess(params.database);
|
|
61
|
+
if (!dbValidation.valid) {
|
|
29
62
|
return {
|
|
30
63
|
status: 'error',
|
|
31
|
-
error:
|
|
64
|
+
error: dbValidation.error
|
|
32
65
|
};
|
|
33
66
|
}
|
|
67
|
+
const database = dbValidation.database;
|
|
34
68
|
const query = `
|
|
35
69
|
SELECT
|
|
36
70
|
ROUTINE_NAME as name,
|
|
@@ -72,14 +106,16 @@ class StoredProcedureTools {
|
|
|
72
106
|
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateGetStoredProcedureInfo.errors)
|
|
73
107
|
};
|
|
74
108
|
}
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
if (!
|
|
109
|
+
// Validate database access
|
|
110
|
+
const dbValidation = this.validateDatabaseAccess(params.database);
|
|
111
|
+
if (!dbValidation.valid) {
|
|
78
112
|
return {
|
|
79
113
|
status: 'error',
|
|
80
|
-
error:
|
|
114
|
+
error: dbValidation.error
|
|
81
115
|
};
|
|
82
116
|
}
|
|
117
|
+
const { procedure_name } = params;
|
|
118
|
+
const database = dbValidation.database;
|
|
83
119
|
// Get procedure information
|
|
84
120
|
const procedureQuery = `
|
|
85
121
|
SELECT
|
|
@@ -148,14 +184,16 @@ class StoredProcedureTools {
|
|
|
148
184
|
};
|
|
149
185
|
}
|
|
150
186
|
try {
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
if (!
|
|
187
|
+
// Validate database access
|
|
188
|
+
const dbValidation = this.validateDatabaseAccess(params.database);
|
|
189
|
+
if (!dbValidation.valid) {
|
|
154
190
|
return {
|
|
155
191
|
status: 'error',
|
|
156
|
-
error:
|
|
192
|
+
error: dbValidation.error
|
|
157
193
|
};
|
|
158
194
|
}
|
|
195
|
+
const { procedure_name, parameters = [] } = params;
|
|
196
|
+
const database = dbValidation.database;
|
|
159
197
|
// Validate procedure name
|
|
160
198
|
const identifierValidation = this.security.validateIdentifier(procedure_name);
|
|
161
199
|
if (!identifierValidation.valid) {
|
|
@@ -260,14 +298,16 @@ class StoredProcedureTools {
|
|
|
260
298
|
};
|
|
261
299
|
}
|
|
262
300
|
try {
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
if (!
|
|
301
|
+
// Validate database access
|
|
302
|
+
const dbValidation = this.validateDatabaseAccess(params.database);
|
|
303
|
+
if (!dbValidation.valid) {
|
|
266
304
|
return {
|
|
267
305
|
status: 'error',
|
|
268
|
-
error:
|
|
306
|
+
error: dbValidation.error
|
|
269
307
|
};
|
|
270
308
|
}
|
|
309
|
+
const { procedure_name, parameters = [], body, comment } = params;
|
|
310
|
+
const database = dbValidation.database;
|
|
271
311
|
// Validate procedure name
|
|
272
312
|
const identifierValidation = this.security.validateIdentifier(procedure_name);
|
|
273
313
|
if (!identifierValidation.valid) {
|
|
@@ -327,14 +367,16 @@ class StoredProcedureTools {
|
|
|
327
367
|
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateDropStoredProcedure.errors)
|
|
328
368
|
};
|
|
329
369
|
}
|
|
330
|
-
|
|
331
|
-
const
|
|
332
|
-
if (!
|
|
370
|
+
// Validate database access
|
|
371
|
+
const dbValidation = this.validateDatabaseAccess(params.database);
|
|
372
|
+
if (!dbValidation.valid) {
|
|
333
373
|
return {
|
|
334
374
|
status: 'error',
|
|
335
|
-
error:
|
|
375
|
+
error: dbValidation.error
|
|
336
376
|
};
|
|
337
377
|
}
|
|
378
|
+
const { procedure_name, if_exists = false } = params;
|
|
379
|
+
const database = dbValidation.database;
|
|
338
380
|
// Validate procedure name
|
|
339
381
|
const identifierValidation = this.security.validateIdentifier(procedure_name);
|
|
340
382
|
if (!identifierValidation.valid) {
|
|
@@ -371,14 +413,16 @@ class StoredProcedureTools {
|
|
|
371
413
|
error: 'Invalid parameters: ' + JSON.stringify(schemas_1.validateShowCreateProcedure.errors)
|
|
372
414
|
};
|
|
373
415
|
}
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
if (!
|
|
416
|
+
// Validate database access
|
|
417
|
+
const dbValidation = this.validateDatabaseAccess(params.database);
|
|
418
|
+
if (!dbValidation.valid) {
|
|
377
419
|
return {
|
|
378
420
|
status: 'error',
|
|
379
|
-
error:
|
|
421
|
+
error: dbValidation.error
|
|
380
422
|
};
|
|
381
423
|
}
|
|
424
|
+
const { procedure_name } = params;
|
|
425
|
+
const database = dbValidation.database;
|
|
382
426
|
// Validate procedure name
|
|
383
427
|
const identifierValidation = this.security.validateIdentifier(procedure_name);
|
|
384
428
|
if (!identifierValidation.valid) {
|
package/package.json
CHANGED