@berthojoris/mcp-mysql-server 1.40.3 → 1.40.5
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 +78 -51
- package/DOCUMENTATIONS.md +11 -11
- package/README.md +5 -5
- package/dist/mcp-server.js +1 -1
- package/dist/tools/databaseTools.js +2 -0
- package/manifest.json +1 -1
- package/package.json +4 -6
- package/dist/server.d.ts +0 -2
- package/dist/server.js +0 -345
package/CHANGELOG.md
CHANGED
|
@@ -5,57 +5,84 @@ All notable changes to the MySQL MCP Server will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [1.40.
|
|
9
|
-
|
|
10
|
-
### Fixed
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
## [1.40.
|
|
20
|
-
|
|
21
|
-
### Removed
|
|
22
|
-
- **
|
|
23
|
-
- `
|
|
24
|
-
|
|
25
|
-
- `
|
|
26
|
-
- `
|
|
27
|
-
- `
|
|
28
|
-
- `
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
###
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
- Updated `README.md` and `DOCUMENTATIONS.md`
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
|
|
8
|
+
## [1.40.5] - 2026-04-08
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Critical Bug in `read_table_schema`**: Fixed query that returned mixed data from multiple schemas
|
|
12
|
+
- Query was missing `TABLE_SCHEMA` filter, causing columns from tables with same name in different databases to be returned
|
|
13
|
+
- Added `TABLE_SCHEMA = ?` filter to ensure only columns from the connected database are returned
|
|
14
|
+
- This fixes the issue where AI agents received incorrect schema information when multiple databases had tables with identical names
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Synchronized version to `1.40.5` in `package.json`, `src/mcp-server.ts`
|
|
18
|
+
|
|
19
|
+
## [1.40.4] - 2026-03-07
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
- **Unused Dependencies**: Removed `winston` and `@types/winston` packages (26 packages total)
|
|
23
|
+
- `winston` logging library was not used - project uses custom in-memory `QueryLogger` instead
|
|
24
|
+
- **Dead Code**: Removed unused tool modules left from previous refactoring
|
|
25
|
+
- `src/tools/processTools.ts` - Remnant from removed `server_management` category
|
|
26
|
+
- `src/tools/performanceTools.ts` - Remnant from removed `performance_monitoring` category
|
|
27
|
+
- `src/auth/` folder - Empty unused directory
|
|
28
|
+
- `dist/server.js` and `dist/server.d.ts` - Obsolete build artifacts
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- Updated dependencies: 6 → 5 production dependencies (removed winston)
|
|
32
|
+
- Updated devDependencies: 10 → 9 (removed @types/winston)
|
|
33
|
+
- Synchronized version to `1.40.4` in `package.json`
|
|
34
|
+
|
|
35
|
+
## [1.40.3] - 2026-03-07
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- Added missing tool-category mappings for snake_case and camelCase variants in `src/config/featureConfig.ts`
|
|
39
|
+
- Resolved unmapped tools: `get_schema_erd`, `repair_query`, `export_table_to_csv`, `export_query_to_csv`, `analyze_query`, `get_optimization_hints`
|
|
40
|
+
- Ensured dual-layer filtering recognizes all registered MCP tools from `src/mcp-server.ts`
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- Synchronized versions to `1.40.3` in `package.json`, `src/mcp-server.ts`, and `manifest.json`
|
|
44
|
+
- Updated `README.md` and `DOCUMENTATIONS.md` Last Updated metadata
|
|
45
|
+
|
|
46
|
+
## [1.40.2] - 2026-03-07
|
|
47
|
+
|
|
48
|
+
### Removed
|
|
49
|
+
- **Functions Management Category**: Removed category `functions_management` and all 6 tools
|
|
50
|
+
- `list_functions` - List user-defined functions
|
|
51
|
+
- `get_function_info` - Get function details
|
|
52
|
+
- `create_function` - Create function
|
|
53
|
+
- `drop_function` - Drop function
|
|
54
|
+
- `show_create_function` - Show CREATE FUNCTION statement
|
|
55
|
+
- `execute_function` - Execute function
|
|
56
|
+
- **Backup & Restore Category**: Removed category `backup_restore` and all 5 tools
|
|
57
|
+
- `backup_table` - Backup a single table
|
|
58
|
+
- `backup_database` - Backup entire database
|
|
59
|
+
- `restore_from_sql` - Restore from SQL dump
|
|
60
|
+
- `get_create_table_statement` - Get CREATE TABLE statement
|
|
61
|
+
- `get_database_schema` - Get complete schema snapshot
|
|
62
|
+
- Removed tool modules `src/tools/functionTools.ts` and `src/tools/backupRestoreTools.ts`
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
- Updated tool count: 73 → 62 tools (-11 tools)
|
|
66
|
+
- Updated category count: 15 → 13 categories (removed `functions_management`, `backup_restore`)
|
|
67
|
+
- Updated `src/mcp-server.ts` to remove tool schemas and call routing for all 11 tools
|
|
68
|
+
- Updated `src/index.ts` to remove MySQLMCP imports, fields, initialization, and methods for removed tool sets
|
|
69
|
+
- Updated `src/config/featureConfig.ts` to remove enum values and category mappings for both categories
|
|
70
|
+
- Updated CLI category help in `bin/mcp-mysql.js`
|
|
71
|
+
- Updated `README.md` and `DOCUMENTATIONS.md` to reflect new category list and tool totals
|
|
72
|
+
- Synchronized versions to `1.40.2` in `package.json`, `src/mcp-server.ts`, and `manifest.json`
|
|
73
|
+
|
|
74
|
+
### Fixed
|
|
75
|
+
- Updated procedure permission example in docs to reference `execute_stored_procedure` instead of removed function execution tool
|
|
76
|
+
|
|
77
|
+
## [1.40.1] - 2026-03-06
|
|
78
|
+
|
|
79
|
+
### Changed
|
|
80
|
+
- Synchronized version metadata across `package.json`, `src/mcp-server.ts`, and `manifest.json` to `1.40.1`
|
|
81
|
+
- Updated CLI category examples in `bin/mcp-mysql.js` to match active category set
|
|
82
|
+
- Updated category examples in `README.md` and `DOCUMENTATIONS.md` to remove removed categories
|
|
83
|
+
- Refactored tool registry filtering into `src/tools/toolRegistry.ts`
|
|
84
|
+
- Refactored tool argument validation registry into `src/tools/toolArgumentValidation.ts`
|
|
85
|
+
|
|
59
86
|
## [1.40.0] - 2026-03-06
|
|
60
87
|
|
|
61
88
|
### Removed
|
package/DOCUMENTATIONS.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# MySQL MCP Server - Documentation
|
|
2
2
|
|
|
3
|
-
**Last Updated:** 2026-
|
|
4
|
-
**Version:** 1.40.
|
|
5
|
-
**Total Tools:** 62
|
|
3
|
+
**Last Updated:** 2026-04-08 14:30:00
|
|
4
|
+
**Version:** 1.40.5
|
|
5
|
+
**Total Tools:** 62
|
|
6
6
|
|
|
7
7
|
Comprehensive documentation for the MySQL MCP Server. For quick start, see [README.md](README.md).
|
|
8
8
|
|
|
@@ -37,7 +37,7 @@ Configure MySQL MCP with two access-control layers:
|
|
|
37
37
|
"@berthojoris/mcp-mysql-server",
|
|
38
38
|
"mysql://user:password@localhost:3306/database",
|
|
39
39
|
"list,read,utility",
|
|
40
|
-
"database_discovery,custom_queries,analysis"
|
|
40
|
+
"database_discovery,custom_queries,analysis"
|
|
41
41
|
]
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -58,7 +58,7 @@ Configure MySQL MCP with two access-control layers:
|
|
|
58
58
|
"DB_PASSWORD": "your_password",
|
|
59
59
|
"DB_NAME": "your_database",
|
|
60
60
|
"MCP_PERMISSIONS": "list,read,utility",
|
|
61
|
-
"MCP_CATEGORIES": "database_discovery,custom_queries,analysis"
|
|
61
|
+
"MCP_CATEGORIES": "database_discovery,custom_queries,analysis"
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
```
|
|
@@ -80,7 +80,7 @@ Configure MySQL MCP with two access-control layers:
|
|
|
80
80
|
| `ddl` | Schema changes | `create_table`, `alter_table` |
|
|
81
81
|
| `utility` | Utility operations | `test_connection`, `analyze_table` |
|
|
82
82
|
| `transaction` | Transaction management | `begin_transaction`, `commit_transaction` |
|
|
83
|
-
| `procedure` | Stored procedures | `create_stored_procedure`, `execute_stored_procedure` |
|
|
83
|
+
| `procedure` | Stored procedures | `create_stored_procedure`, `execute_stored_procedure` |
|
|
84
84
|
|
|
85
85
|
### Filtering Logic
|
|
86
86
|
|
|
@@ -169,7 +169,7 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
169
169
|
- `drop_trigger` - Remove triggers
|
|
170
170
|
- `show_create_trigger` - Show CREATE statement
|
|
171
171
|
|
|
172
|
-
### 11. Table Maintenance (8 tools)
|
|
172
|
+
### 11. Table Maintenance (8 tools)
|
|
173
173
|
- `analyze_table` - Update statistics
|
|
174
174
|
- `optimize_table` - Reclaim space
|
|
175
175
|
- `check_table` - Check for errors
|
|
@@ -179,19 +179,19 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
179
179
|
- `flush_table` - Close/reopen table
|
|
180
180
|
- `get_table_size` - Get size information
|
|
181
181
|
|
|
182
|
-
### 12. Transaction Management (5 tools)
|
|
182
|
+
### 12. Transaction Management (5 tools)
|
|
183
183
|
- `begin_transaction` - Start transaction
|
|
184
184
|
- `commit_transaction` - Commit transaction
|
|
185
185
|
- `rollback_transaction` - Rollback transaction
|
|
186
186
|
- `get_transaction_status` - Check transaction state
|
|
187
187
|
- `execute_in_transaction` - Execute within transaction
|
|
188
188
|
|
|
189
|
-
### 13. Query Optimization (3 tools)
|
|
189
|
+
### 13. Query Optimization (3 tools)
|
|
190
190
|
- `analyze_query` - Analyze query performance
|
|
191
191
|
- `get_optimization_hints` - Get optimizer hints
|
|
192
192
|
- `repair_query` - Repair broken SQL queries
|
|
193
193
|
|
|
194
|
-
### 14. Utilities (4 tools)
|
|
194
|
+
### 14. Utilities (4 tools)
|
|
195
195
|
- `test_connection` - Test connectivity
|
|
196
196
|
- `describe_connection` - Connection info
|
|
197
197
|
- `read_changelog` - Read changelog
|
|
@@ -383,4 +383,4 @@ The system provides detailed error messages indicating:
|
|
|
383
383
|
|
|
384
384
|
---
|
|
385
385
|
|
|
386
|
-
*For detailed examples and advanced usage patterns, see the project README.md.*
|
|
386
|
+
*For detailed examples and advanced usage patterns, see the project README.md.*
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**A production-ready Model Context Protocol (MCP) server for MySQL database integration with AI agents**
|
|
6
6
|
|
|
7
|
-
**Last Updated:** 2026-
|
|
7
|
+
**Last Updated:** 2026-04-08 14:30:00
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/@berthojoris/mcp-mysql-server)
|
|
10
10
|
[](https://www.npmjs.com/package/@berthojoris/mcp-mysql-server)
|
|
@@ -200,7 +200,7 @@ Alternative approach using environment variables instead of connection string:
|
|
|
200
200
|
"DB_PASSWORD": "your_password",
|
|
201
201
|
"DB_NAME": "your_database",
|
|
202
202
|
"MCP_PERMISSIONS": "list,read,utility,create,update,delete",
|
|
203
|
-
"MCP_CATEGORIES": "database_discovery,custom_queries,analysis"
|
|
203
|
+
"MCP_CATEGORIES": "database_discovery,custom_queries,analysis"
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
}
|
|
@@ -245,7 +245,7 @@ Use documentation categories to fine-tune which tools are exposed (Layer 2):
|
|
|
245
245
|
|
|
246
246
|
| Category List | Use Case | List Tools |
|
|
247
247
|
|---------------|----------|-----------|
|
|
248
|
-
| `database_discovery` | Explore databases, tables, and schema structure | `get_all_tables_relationships, list_databases, list_tables, read_table_schema` |
|
|
248
|
+
| `database_discovery` | Explore databases, tables, and schema structure | `get_all_tables_relationships, list_databases, list_tables, read_table_schema` |
|
|
249
249
|
| `crud_operations` | Create, read, update, delete operations on data | `create_record, delete_record, read_records, update_record` |
|
|
250
250
|
| `bulk_operations` | High-performance batch processing operations | `bulk_delete, bulk_insert, bulk_update` |
|
|
251
251
|
| `custom_queries` | Execute custom SQL queries and advanced operations | `execute_write_query, run_select_query` |
|
|
@@ -265,7 +265,7 @@ Use documentation categories to fine-tune which tools are exposed (Layer 2):
|
|
|
265
265
|
<summary>Copy/paste list (comma-separated, no spaces)</summary>
|
|
266
266
|
|
|
267
267
|
```text
|
|
268
|
-
database_discovery,crud_operations,bulk_operations,custom_queries,schema_management,utilities,transaction_management,stored_procedures,views_management,triggers_management,index_management,constraint_management,table_maintenance,query_optimization,analysis
|
|
268
|
+
database_discovery,crud_operations,bulk_operations,custom_queries,schema_management,utilities,transaction_management,stored_procedures,views_management,triggers_management,index_management,constraint_management,table_maintenance,query_optimization,analysis
|
|
269
269
|
```
|
|
270
270
|
|
|
271
271
|
</details>
|
|
@@ -276,7 +276,7 @@ Full category → tool mapping (and examples) lives in **[DOCUMENTATIONS.md →
|
|
|
276
276
|
|
|
277
277
|
## Available Tools
|
|
278
278
|
|
|
279
|
-
The server exposes **62 tools** organized into categories (CRUD, schema, and utilities).
|
|
279
|
+
The server exposes **62 tools** organized into categories (CRUD, schema, and utilities).
|
|
280
280
|
|
|
281
281
|
- Complete list of tools: **[DOCUMENTATIONS.md → Complete Tools Reference](DOCUMENTATIONS.md#🔧-complete-tools-reference)**
|
|
282
282
|
|
package/dist/mcp-server.js
CHANGED
|
@@ -120,11 +120,13 @@ class DatabaseTools {
|
|
|
120
120
|
FROM
|
|
121
121
|
INFORMATION_SCHEMA.COLUMNS
|
|
122
122
|
WHERE
|
|
123
|
+
TABLE_SCHEMA = ? AND
|
|
123
124
|
TABLE_NAME = ?
|
|
124
125
|
ORDER BY
|
|
125
126
|
ORDINAL_POSITION
|
|
126
127
|
`;
|
|
127
128
|
const results = await this.db.query(query, [
|
|
129
|
+
config_1.dbConfig.database,
|
|
128
130
|
params.table_name,
|
|
129
131
|
]);
|
|
130
132
|
return {
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@berthojoris/mcp-mysql-server",
|
|
3
|
-
"version": "1.40.
|
|
4
|
-
"description": "Model Context Protocol server for MySQL database integration with dynamic per-project permissions",
|
|
3
|
+
"version": "1.40.5",
|
|
4
|
+
"description": "Model Context Protocol server for MySQL database integration with dynamic per-project permissions",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "commonjs",
|
|
@@ -62,13 +62,11 @@
|
|
|
62
62
|
"ajv": "^8.12.0",
|
|
63
63
|
"ajv-formats": "^3.0.1",
|
|
64
64
|
"dotenv": "^16.3.1",
|
|
65
|
-
"mysql2": "^3.6.1"
|
|
66
|
-
"winston": "^3.11.0"
|
|
65
|
+
"mysql2": "^3.6.1"
|
|
67
66
|
},
|
|
68
67
|
"devDependencies": {
|
|
69
68
|
"@types/jest": "^29.5.4",
|
|
70
69
|
"@types/node": "^20.6.0",
|
|
71
|
-
"@types/winston": "^2.4.4",
|
|
72
70
|
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
|
73
71
|
"@typescript-eslint/parser": "^8.50.1",
|
|
74
72
|
"eslint": "^9.39.2",
|
|
@@ -77,4 +75,4 @@
|
|
|
77
75
|
"ts-node": "^10.9.1",
|
|
78
76
|
"typescript": "^5.2.2"
|
|
79
77
|
}
|
|
80
|
-
}
|
|
78
|
+
}
|
package/dist/server.d.ts
DELETED
package/dist/server.js
DELETED
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const express_1 = __importDefault(require("express"));
|
|
7
|
-
const cors_1 = __importDefault(require("cors"));
|
|
8
|
-
const helmet_1 = __importDefault(require("helmet"));
|
|
9
|
-
const morgan_1 = __importDefault(require("morgan"));
|
|
10
|
-
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
|
11
|
-
const index_1 = require("./index");
|
|
12
|
-
const winston_1 = require("winston");
|
|
13
|
-
const inputValidation_1 = require("./validation/inputValidation");
|
|
14
|
-
// Initialize the MCP instance
|
|
15
|
-
const mcp = new index_1.MySQLMCP();
|
|
16
|
-
// Create Winston logger
|
|
17
|
-
const logger = (0, winston_1.createLogger)({
|
|
18
|
-
level: 'info',
|
|
19
|
-
format: winston_1.format.combine(winston_1.format.timestamp(), winston_1.format.json()),
|
|
20
|
-
transports: [
|
|
21
|
-
new winston_1.transports.Console(),
|
|
22
|
-
new winston_1.transports.File({ filename: 'logs/error.log', level: 'error' }),
|
|
23
|
-
new winston_1.transports.File({ filename: 'logs/combined.log' })
|
|
24
|
-
]
|
|
25
|
-
});
|
|
26
|
-
// Initialize Express app
|
|
27
|
-
const app = (0, express_1.default)();
|
|
28
|
-
const PORT = process.env.PORT || 3000;
|
|
29
|
-
// Middleware
|
|
30
|
-
app.use((0, helmet_1.default)()); // Security headers
|
|
31
|
-
app.use((0, cors_1.default)()); // Enable CORS
|
|
32
|
-
app.use(express_1.default.json()); // Parse JSON bodies
|
|
33
|
-
app.use((0, morgan_1.default)('combined')); // HTTP request logging
|
|
34
|
-
// Rate limiting
|
|
35
|
-
const apiLimiter = (0, express_rate_limit_1.default)({
|
|
36
|
-
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
37
|
-
max: 100, // Limit each IP to 100 requests per windowMs
|
|
38
|
-
standardHeaders: true,
|
|
39
|
-
legacyHeaders: false,
|
|
40
|
-
});
|
|
41
|
-
app.use(apiLimiter);
|
|
42
|
-
// No authentication middleware needed for MCP server
|
|
43
|
-
// Input validation middleware
|
|
44
|
-
const validateInput = (validator) => {
|
|
45
|
-
return (req, res, next) => {
|
|
46
|
-
const validation = validator(req.body);
|
|
47
|
-
if (!validation.valid) {
|
|
48
|
-
return res.status(400).json({
|
|
49
|
-
error: {
|
|
50
|
-
code: 'VALIDATION_ERROR',
|
|
51
|
-
message: 'Input validation failed',
|
|
52
|
-
details: validation.errors
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
next();
|
|
57
|
-
};
|
|
58
|
-
};
|
|
59
|
-
// Parameter sanitization middleware
|
|
60
|
-
const sanitizeParams = (req, res, next) => {
|
|
61
|
-
// Sanitize route parameters
|
|
62
|
-
if (req.params.tableName) {
|
|
63
|
-
req.params.tableName = (0, inputValidation_1.sanitizeTableName)(req.params.tableName);
|
|
64
|
-
}
|
|
65
|
-
if (req.params.id) {
|
|
66
|
-
req.params.id = (0, inputValidation_1.sanitizeFieldName)(req.params.id);
|
|
67
|
-
}
|
|
68
|
-
// Sanitize query parameters
|
|
69
|
-
if (req.query.id_field) {
|
|
70
|
-
req.query.id_field = (0, inputValidation_1.sanitizeFieldName)(req.query.id_field);
|
|
71
|
-
}
|
|
72
|
-
if (req.query.sort_by) {
|
|
73
|
-
req.query.sort_by = (0, inputValidation_1.sanitizeFieldName)(req.query.sort_by);
|
|
74
|
-
}
|
|
75
|
-
// Sanitize request body
|
|
76
|
-
if (req.body.query) {
|
|
77
|
-
req.body.query = (0, inputValidation_1.sanitizeQuery)(req.body.query);
|
|
78
|
-
}
|
|
79
|
-
next();
|
|
80
|
-
};
|
|
81
|
-
// Request size limiting middleware
|
|
82
|
-
const requestSizeLimit = (maxSize) => {
|
|
83
|
-
return (req, res, next) => {
|
|
84
|
-
const contentLength = parseInt(req.headers['content-length'] || '0');
|
|
85
|
-
if (contentLength > maxSize) {
|
|
86
|
-
return res.status(413).json({
|
|
87
|
-
error: {
|
|
88
|
-
code: 'REQUEST_TOO_LARGE',
|
|
89
|
-
message: `Request body too large. Maximum size is ${maxSize} bytes`
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
next();
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
// Error handling middleware
|
|
97
|
-
const errorHandler = (err, req, res, next) => {
|
|
98
|
-
logger.error(`${err.name}: ${err.message}`, {
|
|
99
|
-
path: req.path,
|
|
100
|
-
method: req.method,
|
|
101
|
-
body: req.body,
|
|
102
|
-
stack: err.stack
|
|
103
|
-
});
|
|
104
|
-
res.status(500).json({
|
|
105
|
-
error: {
|
|
106
|
-
code: 'SERVER_ERROR',
|
|
107
|
-
message: 'An unexpected error occurred',
|
|
108
|
-
details: process.env.NODE_ENV === 'production' ? 'See server logs for details' : err.message
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
// Health check endpoint (no auth required)
|
|
113
|
-
app.get('/health', (req, res) => {
|
|
114
|
-
res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
115
|
-
});
|
|
116
|
-
// Feature configuration status endpoint
|
|
117
|
-
app.get('/features', (req, res) => {
|
|
118
|
-
try {
|
|
119
|
-
const featureStatus = mcp.getFeatureStatus();
|
|
120
|
-
res.status(200).json(featureStatus);
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
logger.error('Error getting feature status', { error });
|
|
124
|
-
res.status(500).json({
|
|
125
|
-
status: 'error',
|
|
126
|
-
error: 'Failed to retrieve feature configuration status'
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
// API routes - no authentication required for MCP server
|
|
131
|
-
const apiRouter = express_1.default.Router();
|
|
132
|
-
app.use('/api', sanitizeParams, apiRouter);
|
|
133
|
-
// Database Tools Routes
|
|
134
|
-
apiRouter.get('/databases', async (req, res, next) => {
|
|
135
|
-
try {
|
|
136
|
-
const result = await mcp.listDatabases();
|
|
137
|
-
res.json(result);
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
next(error);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
apiRouter.get('/tables', async (req, res, next) => {
|
|
144
|
-
try {
|
|
145
|
-
const result = await mcp.listTables({ database: undefined });
|
|
146
|
-
res.json(result);
|
|
147
|
-
}
|
|
148
|
-
catch (error) {
|
|
149
|
-
next(error);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
apiRouter.get('/tables/:tableName/schema', async (req, res, next) => {
|
|
153
|
-
try {
|
|
154
|
-
const { tableName } = req.params;
|
|
155
|
-
const result = await mcp.readTableSchema({ table_name: tableName });
|
|
156
|
-
res.json(result);
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
next(error);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
// CRUD Operations Routes
|
|
163
|
-
apiRouter.post('/tables/:tableName/records', requestSizeLimit(inputValidation_1.INPUT_LIMITS.MAX_QUERY_LENGTH), validateInput((req) => (0, inputValidation_1.validateCreateRecord)({ table_name: req.params.tableName, data: req.body.data })), async (req, res, next) => {
|
|
164
|
-
try {
|
|
165
|
-
const { tableName } = req.params;
|
|
166
|
-
const { data } = req.body;
|
|
167
|
-
const result = await mcp.createRecord({
|
|
168
|
-
table_name: tableName,
|
|
169
|
-
data
|
|
170
|
-
});
|
|
171
|
-
res.status(201).json(result);
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
next(error);
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
apiRouter.get('/tables/:tableName/records', async (req, res, next) => {
|
|
178
|
-
try {
|
|
179
|
-
const { tableName } = req.params;
|
|
180
|
-
const { filters, limit, offset, sort_by, sort_direction } = req.query;
|
|
181
|
-
// Validate and parse filters
|
|
182
|
-
let parsedFilters;
|
|
183
|
-
if (filters) {
|
|
184
|
-
try {
|
|
185
|
-
parsedFilters = JSON.parse(filters);
|
|
186
|
-
}
|
|
187
|
-
catch (e) {
|
|
188
|
-
return res.status(400).json({
|
|
189
|
-
error: {
|
|
190
|
-
code: 'INVALID_FILTERS',
|
|
191
|
-
message: 'Invalid JSON in filters parameter'
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// Validate the complete request
|
|
197
|
-
const validation = (0, inputValidation_1.validateReadRecords)({
|
|
198
|
-
table_name: tableName,
|
|
199
|
-
filters: parsedFilters,
|
|
200
|
-
pagination: {
|
|
201
|
-
page: offset ? Math.floor(parseInt(offset) / (limit ? parseInt(limit) : 10)) + 1 : 1,
|
|
202
|
-
limit: limit ? parseInt(limit) : 10
|
|
203
|
-
},
|
|
204
|
-
sorting: sort_by ? {
|
|
205
|
-
field: sort_by,
|
|
206
|
-
direction: sort_direction?.toLowerCase() === 'desc' ? 'desc' : 'asc'
|
|
207
|
-
} : undefined
|
|
208
|
-
});
|
|
209
|
-
if (!validation.valid) {
|
|
210
|
-
return res.status(400).json({
|
|
211
|
-
error: {
|
|
212
|
-
code: 'VALIDATION_ERROR',
|
|
213
|
-
message: 'Input validation failed',
|
|
214
|
-
details: validation.errors
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
const result = await mcp.readRecords({
|
|
219
|
-
table_name: tableName,
|
|
220
|
-
filters: parsedFilters,
|
|
221
|
-
pagination: {
|
|
222
|
-
page: offset ? Math.floor(parseInt(offset) / (limit ? parseInt(limit) : 10)) + 1 : 1,
|
|
223
|
-
limit: limit ? parseInt(limit) : 10
|
|
224
|
-
},
|
|
225
|
-
sorting: sort_by ? {
|
|
226
|
-
field: sort_by,
|
|
227
|
-
direction: sort_direction?.toLowerCase() === 'desc' ? 'desc' : 'asc'
|
|
228
|
-
} : undefined
|
|
229
|
-
});
|
|
230
|
-
res.json(result);
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
next(error);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
apiRouter.put('/tables/:tableName/records/:id', requestSizeLimit(inputValidation_1.INPUT_LIMITS.MAX_QUERY_LENGTH), validateInput((req) => (0, inputValidation_1.validateUpdateRecord)({
|
|
237
|
-
table_name: req.params.tableName,
|
|
238
|
-
data: req.body.data,
|
|
239
|
-
conditions: [{ field: req.body.id_field || 'id', operator: '=', value: req.params.id }]
|
|
240
|
-
})), async (req, res, next) => {
|
|
241
|
-
try {
|
|
242
|
-
const { tableName, id } = req.params;
|
|
243
|
-
const { data, id_field } = req.body;
|
|
244
|
-
const result = await mcp.updateRecord({
|
|
245
|
-
table_name: tableName,
|
|
246
|
-
data,
|
|
247
|
-
conditions: [{ field: id_field || 'id', operator: '=', value: id }]
|
|
248
|
-
});
|
|
249
|
-
res.json(result);
|
|
250
|
-
}
|
|
251
|
-
catch (error) {
|
|
252
|
-
next(error);
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
apiRouter.delete('/tables/:tableName/records/:id', validateInput((req) => (0, inputValidation_1.validateDeleteRecord)({
|
|
256
|
-
table_name: req.params.tableName,
|
|
257
|
-
conditions: [{ field: req.query.id_field || 'id', operator: '=', value: req.params.id }]
|
|
258
|
-
})), async (req, res, next) => {
|
|
259
|
-
try {
|
|
260
|
-
const { tableName, id } = req.params;
|
|
261
|
-
const { id_field } = req.query;
|
|
262
|
-
const result = await mcp.deleteRecord({
|
|
263
|
-
table_name: tableName,
|
|
264
|
-
conditions: [{ field: id_field || 'id', operator: '=', value: id }]
|
|
265
|
-
});
|
|
266
|
-
res.json(result);
|
|
267
|
-
}
|
|
268
|
-
catch (error) {
|
|
269
|
-
next(error);
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
// Query Tools Routes
|
|
273
|
-
apiRouter.post('/query', requestSizeLimit(inputValidation_1.INPUT_LIMITS.MAX_QUERY_LENGTH), validateInput(inputValidation_1.validateQuery), async (req, res, next) => {
|
|
274
|
-
try {
|
|
275
|
-
const { query, params } = req.body;
|
|
276
|
-
const result = await mcp.runSelectQuery({
|
|
277
|
-
query,
|
|
278
|
-
params: params || []
|
|
279
|
-
});
|
|
280
|
-
res.json(result);
|
|
281
|
-
}
|
|
282
|
-
catch (error) {
|
|
283
|
-
next(error);
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
apiRouter.post('/execute', requestSizeLimit(inputValidation_1.INPUT_LIMITS.MAX_QUERY_LENGTH), validateInput(inputValidation_1.validateQuery), async (req, res, next) => {
|
|
287
|
-
try {
|
|
288
|
-
const { query, params } = req.body;
|
|
289
|
-
const result = await mcp.executeWriteQuery({
|
|
290
|
-
query,
|
|
291
|
-
params: params || []
|
|
292
|
-
});
|
|
293
|
-
res.json(result);
|
|
294
|
-
}
|
|
295
|
-
catch (error) {
|
|
296
|
-
next(error);
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
// Utility Tools Routes
|
|
300
|
-
apiRouter.get('/connection', async (req, res, next) => {
|
|
301
|
-
try {
|
|
302
|
-
const result = await mcp.describeConnection();
|
|
303
|
-
res.json(result);
|
|
304
|
-
}
|
|
305
|
-
catch (error) {
|
|
306
|
-
next(error);
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
apiRouter.get('/connection/test', async (req, res, next) => {
|
|
310
|
-
try {
|
|
311
|
-
const result = await mcp.testConnection();
|
|
312
|
-
res.json(result);
|
|
313
|
-
}
|
|
314
|
-
catch (error) {
|
|
315
|
-
next(error);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
apiRouter.get('/tables/:tableName/relationships', async (req, res, next) => {
|
|
319
|
-
try {
|
|
320
|
-
const { tableName } = req.params;
|
|
321
|
-
const result = await mcp.getTableRelationships({ table_name: tableName });
|
|
322
|
-
res.json(result);
|
|
323
|
-
}
|
|
324
|
-
catch (error) {
|
|
325
|
-
next(error);
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
// Apply error handler
|
|
329
|
-
app.use(errorHandler);
|
|
330
|
-
// Start the server
|
|
331
|
-
const server = app.listen(PORT, () => {
|
|
332
|
-
logger.info(`MCP MySQL Server running on port ${PORT}`);
|
|
333
|
-
console.log(`MCP MySQL Server running on port ${PORT}`);
|
|
334
|
-
});
|
|
335
|
-
// Graceful shutdown
|
|
336
|
-
process.on('SIGTERM', () => {
|
|
337
|
-
logger.info('SIGTERM signal received: closing HTTP server');
|
|
338
|
-
server.close(async () => {
|
|
339
|
-
logger.info('HTTP server closed');
|
|
340
|
-
await mcp.close();
|
|
341
|
-
logger.info('Database connections closed');
|
|
342
|
-
process.exit(0);
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
exports.default = app;
|