@berthojoris/mcp-mysql-server 1.40.2 → 1.40.4

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 CHANGED
@@ -5,6 +5,33 @@ 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.4] - 2026-03-07
9
+
10
+ ### Removed
11
+ - **Unused Dependencies**: Removed `winston` and `@types/winston` packages (26 packages total)
12
+ - `winston` logging library was not used - project uses custom in-memory `QueryLogger` instead
13
+ - **Dead Code**: Removed unused tool modules left from previous refactoring
14
+ - `src/tools/processTools.ts` - Remnant from removed `server_management` category
15
+ - `src/tools/performanceTools.ts` - Remnant from removed `performance_monitoring` category
16
+ - `src/auth/` folder - Empty unused directory
17
+ - `dist/server.js` and `dist/server.d.ts` - Obsolete build artifacts
18
+
19
+ ### Changed
20
+ - Updated dependencies: 6 → 5 production dependencies (removed winston)
21
+ - Updated devDependencies: 10 → 9 (removed @types/winston)
22
+ - Synchronized version to `1.40.4` in `package.json`
23
+
24
+ ## [1.40.3] - 2026-03-07
25
+
26
+ ### Fixed
27
+ - Added missing tool-category mappings for snake_case and camelCase variants in `src/config/featureConfig.ts`
28
+ - Resolved unmapped tools: `get_schema_erd`, `repair_query`, `export_table_to_csv`, `export_query_to_csv`, `analyze_query`, `get_optimization_hints`
29
+ - Ensured dual-layer filtering recognizes all registered MCP tools from `src/mcp-server.ts`
30
+
31
+ ### Changed
32
+ - Synchronized versions to `1.40.3` in `package.json`, `src/mcp-server.ts`, and `manifest.json`
33
+ - Updated `README.md` and `DOCUMENTATIONS.md` Last Updated metadata
34
+
8
35
  ## [1.40.2] - 2026-03-07
9
36
 
10
37
  ### Removed
package/DOCUMENTATIONS.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # MySQL MCP Server - Documentation
2
2
 
3
- **Last Updated:** 2026-03-07 10:30:00
4
- **Version:** 1.40.2
3
+ **Last Updated:** 2026-03-07 11:05:00
4
+ **Version:** 1.40.3
5
5
  **Total Tools:** 62
6
6
 
7
7
  Comprehensive documentation for the MySQL MCP Server. For quick start, see [README.md](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-03-07 10:30:00
7
+ **Last Updated:** 2026-03-07 12:45:00
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/@berthojoris/mcp-mysql-server)](https://www.npmjs.com/package/@berthojoris/mcp-mysql-server)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/@berthojoris/mcp-mysql-server)](https://www.npmjs.com/package/@berthojoris/mcp-mysql-server)
@@ -57,6 +57,8 @@ exports.toolCategoryMap = {
57
57
  // Analysis tools (added here to group with database tools)
58
58
  getDatabaseSummary: ToolCategory.LIST,
59
59
  getSchemaERD: ToolCategory.LIST,
60
+ getSchemaErd: ToolCategory.LIST,
61
+ get_schema_erd: ToolCategory.LIST,
60
62
  getSchemaRagContext: ToolCategory.LIST,
61
63
  // CRUD tools
62
64
  createRecord: ToolCategory.CREATE,
@@ -82,7 +84,11 @@ exports.toolCategoryMap = {
82
84
  testConnection: ToolCategory.UTILITY,
83
85
  getAllTablesRelationships: ToolCategory.UTILITY,
84
86
  exportTableToCSV: ToolCategory.UTILITY,
87
+ exportTableToCsv: ToolCategory.UTILITY,
88
+ export_table_to_csv: ToolCategory.UTILITY,
85
89
  exportQueryToCSV: ToolCategory.UTILITY,
90
+ exportQueryToCsv: ToolCategory.UTILITY,
91
+ export_query_to_csv: ToolCategory.UTILITY,
86
92
  read_changelog: ToolCategory.UTILITY,
87
93
  // Transaction tools
88
94
  beginTransaction: ToolCategory.TRANSACTION,
@@ -133,6 +139,13 @@ exports.toolCategoryMap = {
133
139
  getTableStatus: ToolCategory.LIST,
134
140
  flushTable: ToolCategory.UTILITY,
135
141
  getTableSize: ToolCategory.LIST,
142
+ // Query optimization tools
143
+ analyzeQuery: ToolCategory.UTILITY,
144
+ analyze_query: ToolCategory.UTILITY,
145
+ getOptimizationHints: ToolCategory.UTILITY,
146
+ get_optimization_hints: ToolCategory.UTILITY,
147
+ repairQuery: ToolCategory.UTILITY,
148
+ repair_query: ToolCategory.UTILITY,
136
149
  // Full-Text Search Tools
137
150
  createFulltextIndex: ToolCategory.DDL,
138
151
  fulltextSearch: ToolCategory.READ,
@@ -171,7 +184,11 @@ exports.toolDocCategoryMap = {
171
184
  testConnection: DocCategory.UTILITIES,
172
185
  describeConnection: DocCategory.UTILITIES,
173
186
  exportTableToCSV: DocCategory.UTILITIES,
187
+ exportTableToCsv: DocCategory.UTILITIES,
188
+ export_table_to_csv: DocCategory.UTILITIES,
174
189
  exportQueryToCSV: DocCategory.UTILITIES,
190
+ exportQueryToCsv: DocCategory.UTILITIES,
191
+ export_query_to_csv: DocCategory.UTILITIES,
175
192
  read_changelog: DocCategory.UTILITIES,
176
193
  list_all_tools: DocCategory.UTILITIES,
177
194
  // Transaction Management
@@ -225,11 +242,16 @@ exports.toolDocCategoryMap = {
225
242
  getTableSize: DocCategory.TABLE_MAINTENANCE,
226
243
  // Query Optimization
227
244
  analyzeQuery: DocCategory.QUERY_OPTIMIZATION,
245
+ analyze_query: DocCategory.QUERY_OPTIMIZATION,
228
246
  getOptimizationHints: DocCategory.QUERY_OPTIMIZATION,
247
+ get_optimization_hints: DocCategory.QUERY_OPTIMIZATION,
229
248
  repairQuery: DocCategory.QUERY_OPTIMIZATION,
249
+ repair_query: DocCategory.QUERY_OPTIMIZATION,
230
250
  // Analysis
231
251
  getDatabaseSummary: DocCategory.ANALYSIS,
232
252
  getSchemaERD: DocCategory.ANALYSIS,
253
+ getSchemaErd: DocCategory.ANALYSIS,
254
+ get_schema_erd: DocCategory.ANALYSIS,
233
255
  getColumnStatistics: DocCategory.ANALYSIS,
234
256
  getSchemaRagContext: DocCategory.ANALYSIS,
235
257
  // Full-Text Search
@@ -1850,7 +1850,7 @@ const TOOLS = [
1850
1850
  // Create the MCP server
1851
1851
  const server = new index_js_1.Server({
1852
1852
  name: "mysql-mcp-server",
1853
- version: "1.40.2",
1853
+ version: "1.40.4",
1854
1854
  }, {
1855
1855
  capabilities: {
1856
1856
  tools: {},
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mysql-mcp",
3
3
  "description": "A Model Context Protocol for MySQL database interaction",
4
- "version": "1.40.2",
4
+ "version": "1.40.4",
5
5
  "tools": [
6
6
  {
7
7
  "name": "list_databases",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@berthojoris/mcp-mysql-server",
3
- "version": "1.40.2",
4
- "description": "Model Context Protocol server for MySQL database integration with dynamic per-project permissions",
3
+ "version": "1.40.4",
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
@@ -1,2 +0,0 @@
1
- declare const app: import("express-serve-static-core").Express;
2
- export default app;
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;