@berthojoris/mcp-mysql-server 1.40.3 → 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,22 @@ 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
+
8
24
  ## [1.40.3] - 2026-03-07
9
25
 
10
26
  ### Fixed
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 11:05: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)
@@ -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.3",
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.3",
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.3",
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;