@berthojoris/mcp-mysql-server 1.14.1 → 1.15.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 CHANGED
@@ -5,6 +5,17 @@ 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.15.0] - 2025-12-08
9
+
10
+ ### Added
11
+ - **Connection Profiles** - New `dev`, `stage`, and `prod` presets.
12
+ - `dev`: Full access to all tools.
13
+ - `stage`: Allows CRUD but blocks destructive DDL (drop, truncate).
14
+ - `prod`: Strict read-only with explicit denials for modification tools.
15
+ - Implements allowed/denied tools logic for robust security enforcement.
16
+ - **Agent-Facing Changelog Feed** - New `read_changelog` tool.
17
+ - Allows AI agents to read the CHANGELOG.md directly to understand new features and changes.
18
+
8
19
  ## [1.14.1] - 2025-12-08
9
20
 
10
21
  ### Added
package/DOCUMENTATIONS.md CHANGED
@@ -192,6 +192,16 @@ Preset bundles provide safe starting points and **merge** with any explicit perm
192
192
  | `readonly` | `list,read,utility` | `database_discovery,crud_operations,custom_queries,utilities,import_export,performance_monitoring,analysis` | Safe read-only access, exports, and diagnostics |
193
193
  | `analyst` | `list,read,utility` | `database_discovery,crud_operations,custom_queries,utilities,import_export,performance_monitoring,analysis,query_optimization,cache_management,server_management` | Exploration with EXPLAIN, cache, and performance visibility |
194
194
  | `dba-lite` | `list,read,utility,ddl,transaction,procedure` | `database_discovery,custom_queries,utilities,server_management,schema_management,table_maintenance,index_management,constraint_management,backup_restore,schema_migrations,performance_monitoring,views_management,triggers_management,functions_management,stored_procedures` | Admin-lite schema care, maintenance, and migrations |
195
+ | `dev` | ALL | ALL | Full access to all tools (Development environment) |
196
+ | `stage` | `list,read,create,update,delete,utility,transaction` | Most categories (except schema_management) | Data modification allowed, but destructive DDL (drop_table, truncate_table) is **explicitly denied** |
197
+ | `prod` | `list,read,utility` | `database_discovery,crud_operations,custom_queries,utilities,performance_monitoring,analysis` | Strict read-only. Data modification and DDL are **strictly denied** (even if permissions suggest otherwise) |
198
+
199
+ ### Connection Profiles (Allow/Deny Lists)
200
+
201
+ The new mechanism introduces "Connection Profiles" which can enforce strict `allow` and `deny` lists for tools, providing security beyond standard permissions.
202
+
203
+ - **Explicit Deny**: Tools in the `deniedTools` list are blocked *regardless* of their permissions. E.g., `prod` profile denies `create_record` even if `create` permission is somehow granted.
204
+ - **Explicit Allow**: Tools in the `allowedTools` list are enabled even if their category is not listed (unless denied).
195
205
 
196
206
  **Usage**
197
207
 
@@ -324,6 +334,7 @@ This section provides a comprehensive reference of all 120 available tools organ
324
334
  |------|-------------|
325
335
  | `test_connection` | Test database connectivity and measure latency |
326
336
  | `describe_connection` | Get current connection information |
337
+ | `read_changelog` | Read the changelog to see new features/changes |
327
338
  | `export_table_to_csv` | Export table data to CSV format |
328
339
  | `export_query_to_csv` | Export query results to CSV format |
329
340
 
@@ -520,6 +531,11 @@ This section provides a comprehensive reference of all 120 available tools organ
520
531
  - Safety: respects the connected database only—cannot introspect other schemas—and notes when tables/columns are truncated.
521
532
  - Output includes per-table PKs, FK targets, nullable flags, and approximate row counts from `INFORMATION_SCHEMA.TABLES` (InnoDB estimates).
522
533
 
534
+ #### Agent-Facing Changelog Feed
535
+ - **`read_changelog`**: Allows AI agents to read the project's CHANGELOG.md directly.
536
+ - **Purpose**: Enables the agent to understand new features, changes, and deprecations in the version it is running.
537
+ - **Usage**: Call `read_changelog()` to get the latest changes, or `read_changelog(version='1.15.0')` for specific version details.
538
+
523
539
  ---
524
540
 
525
541
  ## 🏗️ DDL Operations
@@ -3824,6 +3840,61 @@ Each bulk operation returns performance metrics:
3824
3840
 
3825
3841
  ---
3826
3842
 
3843
+
3844
+ ---
3845
+
3846
+ ## ⚡ Workflow Macros
3847
+
3848
+ Workflow macros are high-level tools that combine multiple operations into a single, safe, and efficient workflow. They are designed to simplify complex tasks and ensure best practices (like data masking) are automatically applied.
3849
+
3850
+ ### Safe Export Table
3851
+
3852
+ The `safe_export_table` tool allows you to export table data to CSV while strictly enforcing data masking rules. This ensures that sensitive information (PII) is never leaked during exports, even if the agent forgets to apply masking manually.
3853
+
3854
+ #### Features
3855
+
3856
+ - **Forced Masking**: Applies a masking profile (default: `strict`) to all exported data.
3857
+ - **Hard Limit**: Enforces a maximum row limit (10,000) to prevent Out-Of-Memory errors during large exports.
3858
+ - **CSV Formatting**: Automatically handles special characters, quotes, and newlines.
3859
+ - **Header Control**: Option to include or exclude CSV headers.
3860
+
3861
+ #### Usage
3862
+
3863
+ ```json
3864
+ {
3865
+ "tool": "safe_export_table",
3866
+ "arguments": {
3867
+ "table_name": "users",
3868
+ "masking_profile": "partial",
3869
+ "limit": 1000
3870
+ }
3871
+ }
3872
+ ```
3873
+
3874
+ #### Parameters
3875
+
3876
+ | Parameter | Type | Required | Description | Default |
3877
+ |-----------|------|----------|-------------|---------|
3878
+ | `table_name` | string | Yes | Name of the table to export | - |
3879
+ | `masking_profile` | string | No | Masking profile to apply (`strict`, `partial`, `soft`) | `strict` |
3880
+ | `limit` | number | No | Number of rows to export (max 10,000) | 1000 |
3881
+ | `include_headers` | boolean | No | Whether to include CSV headers | `true` |
3882
+
3883
+ #### Response
3884
+
3885
+ ```json
3886
+ {
3887
+ "status": "success",
3888
+ "data": {
3889
+ "csv": "id,name,email\n1,John Doe,j***@example.com...",
3890
+ "row_count": 50,
3891
+ "applied_profile": "partial"
3892
+ }
3893
+ }
3894
+ ```
3895
+
3896
+ ---
3897
+
3827
3898
  ## 🤖 OpenAI Codex Integration
3828
3899
 
3829
3900
  OpenAI Codex CLI and VS Code Extension support MCP servers through a shared TOML configuration file. This section provides detailed setup instructions for integrating the MySQL MCP Server with Codex.
@@ -4210,9 +4281,9 @@ MIT License - see [LICENSE](LICENSE) file for details.
4210
4281
  | Safety Sandbox Mode (runQuery dry-run/EXPLAIN-only) | Medium | Low | 5 | ✅ Completed |
4211
4282
  | Anomaly & Slow-Query Watcher | Medium | Medium | 6 | ✅ Completed |
4212
4283
  | Data Masking Profiles for Responses | Medium | Medium | 7 | ✅ Completed |
4213
- | Workflow Macros (e.g., safe_export_table) | Medium | Low | 8 | Planned |
4214
- | Agent-Facing Changelog Feed | Medium | Low | 9 | Planned |
4215
- | Connection Profiles (dev/stage/prod with allow/deny) | High | Low | 10 | Planned |
4284
+ | Workflow Macros (e.g., safe_export_table) | Medium | Low | 8 | Completed |
4285
+ | Agent-Facing Changelog Feed | Medium | Low | 9 | Completed |
4286
+ | Connection Profiles (dev/stage/prod with allow/deny) | High | Low | 10 | Completed |
4216
4287
 
4217
4288
  ---
4218
4289
 
package/README.md CHANGED
@@ -50,7 +50,7 @@ Add to your AI agent config (`.mcp.json`, `.cursor/mcp.json`, etc.):
50
50
  - [Environment Variables](#environment-variables-configuration)
51
51
  - [Local Development](#local-path-configuration)
52
52
  - [Permission System](#-permission-system)
53
- - [Available Tools (124 total)](#-available-tools)
53
+ - [Available Tools (126 total)](#-available-tools)
54
54
  - [Documentation](#-detailed-documentation)
55
55
  - [Comparison: MCP vs Manual Access](#-mysql-mcp-vs-manual-database-access)
56
56
  - [License](#-license)
@@ -388,6 +388,12 @@ Alternative approach using environment variables instead of connection string:
388
388
 
389
389
  Add `MCP_PRESET` for the base bundle and optionally layer on `MCP_PERMISSIONS` / `MCP_CATEGORIES` for project-specific overrides.
390
390
 
391
+ #### Connection Profiles (dev/stage/prod)
392
+ New presets are available for environment-specific control:
393
+ - `dev`: Full access to all tools (explicitly allows everything).
394
+ - `stage`: Allows data modification but blocks destructive DDL (drop/truncate).
395
+ - `prod`: Strict read-only mode, explicitly denying keys modification keys.
396
+
391
397
  ---
392
398
 
393
399
  ### Local Path Configuration
@@ -641,11 +647,11 @@ Use both 2nd argument (permissions) and 3rd argument (categories):
641
647
 
642
648
  ## Available Tools
643
649
 
644
- The MCP server provides **124 powerful tools** organized into categories:
650
+ The MCP server provides **126 powerful tools** organized into 22 categories:
645
651
 
646
652
  ### Quick Reference
647
653
 
648
- **124 Tools Available** - Organized into 22 categories
654
+ **126 Tools Available** - Organized into 22 categories
649
655
 
650
656
  | Category | Count | Key Tools |
651
657
  |----------|-------|-----------|
@@ -667,7 +673,7 @@ The MCP server provides **124 powerful tools** organized into categories:
667
673
  | Cache | 5 | `get_cache_stats`, `clear_cache` |
668
674
  | Query Optimization | 3 | `analyze_query`, `get_optimization_hints`, `repair_query` |
669
675
  | Backup & Restore | 5 | `backup_database`, `restore_from_sql` |
670
- | Import/Export | 5 | `export_table_to_json`, `import_from_csv` |
676
+ | Import/Export | 6 | `safe_export_table`, `export_table_to_json`, `import_from_csv` |
671
677
  | Data Migration | 5 | `copy_table_data`, `sync_table_data` |
672
678
  | Schema Migrations | 9 | `create_migration`, `apply_migrations` |
673
679
  | Utilities | 4 | `test_connection`, `export_table_to_csv` |
@@ -50,6 +50,8 @@ export interface PermissionPreset {
50
50
  description: string;
51
51
  permissions: ToolCategory[];
52
52
  categories: DocCategory[];
53
+ allowedTools?: string[];
54
+ deniedTools?: string[];
53
55
  }
54
56
  /**
55
57
  * Map of tool names to their legacy categories
@@ -68,6 +70,8 @@ export declare const toolDocCategoryMap: Record<string, DocCategory>;
68
70
  export declare class FeatureConfig {
69
71
  private enabledLegacyCategories;
70
72
  private enabledDocCategories;
73
+ private allowedTools;
74
+ private deniedTools;
71
75
  private originalPermissionsString;
72
76
  private originalCategoriesString;
73
77
  private useDualLayer;
@@ -115,6 +115,58 @@ const permissionPresets = {
115
115
  DocCategory.STORED_PROCEDURES,
116
116
  ],
117
117
  },
118
+ dev: {
119
+ name: "dev",
120
+ description: "Development profile with full access to all tools",
121
+ permissions: Object.values(ToolCategory),
122
+ categories: Object.values(DocCategory),
123
+ deniedTools: [], // Explicitly allow everything
124
+ },
125
+ stage: {
126
+ name: "stage",
127
+ description: "Staging profile with data modification but no destructive DDL",
128
+ permissions: [
129
+ ToolCategory.LIST,
130
+ ToolCategory.READ,
131
+ ToolCategory.CREATE,
132
+ ToolCategory.UPDATE,
133
+ ToolCategory.DELETE,
134
+ ToolCategory.UTILITY,
135
+ ToolCategory.TRANSACTION,
136
+ ],
137
+ categories: [
138
+ DocCategory.DATABASE_DISCOVERY,
139
+ DocCategory.CRUD_OPERATIONS,
140
+ DocCategory.BULK_OPERATIONS,
141
+ DocCategory.CUSTOM_QUERIES,
142
+ DocCategory.UTILITIES,
143
+ DocCategory.TRANSACTION_MANAGEMENT,
144
+ DocCategory.IMPORT_EXPORT,
145
+ DocCategory.DATA_MIGRATION,
146
+ DocCategory.PERFORMANCE_MONITORING,
147
+ DocCategory.ANALYSIS,
148
+ ],
149
+ deniedTools: ["drop_table", "truncate_table", "drop_database"],
150
+ },
151
+ prod: {
152
+ name: "prod",
153
+ description: "Production profile with strict read-only access and safety checks",
154
+ permissions: [ToolCategory.LIST, ToolCategory.READ, ToolCategory.UTILITY],
155
+ categories: [
156
+ DocCategory.DATABASE_DISCOVERY,
157
+ DocCategory.CRUD_OPERATIONS, // Read only via permissions
158
+ DocCategory.CUSTOM_QUERIES,
159
+ DocCategory.UTILITIES,
160
+ DocCategory.PERFORMANCE_MONITORING,
161
+ DocCategory.ANALYSIS,
162
+ ],
163
+ deniedTools: [
164
+ "create_table", "alter_table", "drop_table", "truncate_table",
165
+ "create_record", "update_record", "delete_record",
166
+ "bulk_insert", "bulk_update", "bulk_delete",
167
+ "execute_sql", "execute_ddl"
168
+ ],
169
+ },
118
170
  };
119
171
  /**
120
172
  * Map of tool names to their legacy categories
@@ -153,6 +205,8 @@ exports.toolCategoryMap = {
153
205
  getTableRelationships: ToolCategory.UTILITY,
154
206
  exportTableToCSV: ToolCategory.UTILITY,
155
207
  exportQueryToCSV: ToolCategory.UTILITY,
208
+ safe_export_table: ToolCategory.UTILITY,
209
+ read_changelog: ToolCategory.UTILITY,
156
210
  // Transaction tools
157
211
  beginTransaction: ToolCategory.TRANSACTION,
158
212
  commitTransaction: ToolCategory.TRANSACTION,
@@ -288,6 +342,7 @@ exports.toolDocCategoryMap = {
288
342
  describeConnection: DocCategory.UTILITIES,
289
343
  exportTableToCSV: DocCategory.UTILITIES,
290
344
  exportQueryToCSV: DocCategory.UTILITIES,
345
+ read_changelog: DocCategory.UTILITIES,
291
346
  // Transaction Management
292
347
  beginTransaction: DocCategory.TRANSACTION_MANAGEMENT,
293
348
  commitTransaction: DocCategory.TRANSACTION_MANAGEMENT,
@@ -518,6 +573,9 @@ class FeatureConfig {
518
573
  const parsed = this.parseConfig(mergedPermissions, mergedCategories);
519
574
  this.enabledLegacyCategories = parsed.legacy;
520
575
  this.enabledDocCategories = parsed.doc;
576
+ // Initialize Allow/Deny Lists
577
+ this.allowedTools = new Set(this.activePreset?.allowedTools || []);
578
+ this.deniedTools = new Set(this.activePreset?.deniedTools || []);
521
579
  }
522
580
  /**
523
581
  * Normalize and merge preset + user-supplied configuration lists
@@ -576,6 +634,9 @@ class FeatureConfig {
576
634
  docCats.forEach((dc) => docSet.add(dc));
577
635
  });
578
636
  }
637
+ // Re-initialize Allow/Deny Lists if preset changed
638
+ this.allowedTools = new Set(this.activePreset?.allowedTools || []);
639
+ this.deniedTools = new Set(this.activePreset?.deniedTools || []);
579
640
  return {
580
641
  legacy: legacySet,
581
642
  doc: docSet,
@@ -632,6 +693,15 @@ class FeatureConfig {
632
693
  console.warn(`Unknown tool: ${toolName}`);
633
694
  return false;
634
695
  }
696
+ // Layer 0: Check explicit Deny/Allow lists
697
+ // Deny takes precedence
698
+ if (this.deniedTools.has(toolName)) {
699
+ return false;
700
+ }
701
+ // Allow overrides other checks
702
+ if (this.allowedTools.has(toolName)) {
703
+ return true;
704
+ }
635
705
  // Layer 1: Check permission (legacy category)
636
706
  const hasPermission = legacyCategory
637
707
  ? this.enabledLegacyCategories.has(legacyCategory)
@@ -658,6 +728,9 @@ class FeatureConfig {
658
728
  if (!docCategory && !legacyCategory) {
659
729
  return `Unknown tool '${toolName}'. This tool is not recognized by the MCP server.`;
660
730
  }
731
+ if (this.deniedTools.has(toolName)) {
732
+ return `Permission denied: Tool '${toolName}' is explicitly denied by the current profile ('${this.presetName}').`;
733
+ }
661
734
  const isAllEnabled = !this.originalPermissionsString.trim() &&
662
735
  !this.originalCategoriesString.trim();
663
736
  if (isAllEnabled) {
package/dist/index.d.ts CHANGED
@@ -190,6 +190,14 @@ export declare class MySQLMCP {
190
190
  data?: any;
191
191
  error?: string;
192
192
  }>;
193
+ readChangelog(params?: {
194
+ version?: string;
195
+ limit?: number;
196
+ }): Promise<{
197
+ status: string;
198
+ data?: any;
199
+ error?: string;
200
+ }>;
193
201
  beginTransaction(params?: {
194
202
  transactionId?: string;
195
203
  }): Promise<import("./tools/transactionTools").TransactionResult | {
package/dist/index.js CHANGED
@@ -234,6 +234,13 @@ class MySQLMCP {
234
234
  }
235
235
  return await this.utilityTools.getTableRelationships(params);
236
236
  }
237
+ async readChangelog(params) {
238
+ const check = this.checkToolEnabled("read_changelog");
239
+ if (!check.enabled) {
240
+ return { status: "error", error: check.error };
241
+ }
242
+ return await this.utilityTools.readChangelog(params);
243
+ }
237
244
  // Transaction Tools
238
245
  async beginTransaction(params) {
239
246
  const check = this.checkToolEnabled("beginTransaction");
@@ -659,6 +659,23 @@ const TOOLS = [
659
659
  properties: {},
660
660
  },
661
661
  },
662
+ {
663
+ name: "read_changelog",
664
+ description: "Reads the changelog to see what features are new or changed.",
665
+ inputSchema: {
666
+ type: "object",
667
+ properties: {
668
+ version: {
669
+ type: "string",
670
+ description: "Optional: specific version to read (e.g., '1.0.0')",
671
+ },
672
+ limit: {
673
+ type: "number",
674
+ description: "Optional: limit character count (default: 5000)",
675
+ },
676
+ },
677
+ },
678
+ },
662
679
  {
663
680
  name: "test_connection",
664
681
  description: "Tests the database connection and returns latency information.",
@@ -2874,6 +2891,9 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
2874
2891
  case "test_connection":
2875
2892
  result = await mysqlMCP.testConnection();
2876
2893
  break;
2894
+ case "read_changelog":
2895
+ result = await mysqlMCP.readChangelog((args || {}));
2896
+ break;
2877
2897
  case "get_table_relationships":
2878
2898
  result = await mysqlMCP.getTableRelationships((args || {}));
2879
2899
  break;
@@ -27,4 +27,15 @@ export declare class UtilityTools {
27
27
  data?: any;
28
28
  error?: string;
29
29
  }>;
30
+ /**
31
+ * Reads the CHANGELOG.md file from the project root
32
+ */
33
+ readChangelog(params?: {
34
+ version?: string;
35
+ limit?: number;
36
+ }): Promise<{
37
+ status: string;
38
+ data?: any;
39
+ error?: string;
40
+ }>;
30
41
  }
@@ -7,6 +7,8 @@ exports.UtilityTools = void 0;
7
7
  const connection_1 = __importDefault(require("../db/connection"));
8
8
  const config_1 = require("../config/config");
9
9
  const schemas_1 = require("../validation/schemas");
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
10
12
  class UtilityTools {
11
13
  constructor() {
12
14
  this.db = connection_1.default.getInstance();
@@ -208,5 +210,74 @@ class UtilityTools {
208
210
  };
209
211
  }
210
212
  }
213
+ /**
214
+ * Reads the CHANGELOG.md file from the project root
215
+ */
216
+ async readChangelog(params) {
217
+ try {
218
+ // Resolve path relative to the built file (dist/tools/utilityTools.js -> ../../CHANGELOG.md)
219
+ // or source file (src/tools/utilityTools.ts -> ../../CHANGELOG.md)
220
+ const changelogPath = path_1.default.resolve(__dirname, "..", "..", "CHANGELOG.md");
221
+ if (!fs_1.default.existsSync(changelogPath)) {
222
+ return {
223
+ status: "error",
224
+ error: "CHANGELOG.md not found in the project root.",
225
+ };
226
+ }
227
+ const content = fs_1.default.readFileSync(changelogPath, "utf-8");
228
+ // If version specified, try to parse and find it
229
+ if (params?.version) {
230
+ // Simple parsing - look for headers like "## [1.2.3]"
231
+ const versionHeader = `## [${params.version}]`;
232
+ const lines = content.split("\n");
233
+ let found = false;
234
+ let versionContent = "";
235
+ for (const line of lines) {
236
+ if (line.startsWith(versionHeader)) {
237
+ found = true;
238
+ versionContent += line + "\n";
239
+ continue;
240
+ }
241
+ if (found) {
242
+ if (line.startsWith("## ["))
243
+ break; // Next version starts
244
+ versionContent += line + "\n";
245
+ }
246
+ }
247
+ if (!found) {
248
+ return {
249
+ status: "error",
250
+ error: `Version ${params.version} not found in CHANGELOG.md`,
251
+ };
252
+ }
253
+ return {
254
+ status: "success",
255
+ data: {
256
+ version: params.version,
257
+ content: versionContent.trim(),
258
+ },
259
+ };
260
+ }
261
+ // If no version, return the whole file or top N characters/lines?
262
+ // For now, let's return the most recent versions.
263
+ // Limit default to 3000 chars to avoid overflowing context
264
+ const maxLength = params?.limit || 5000;
265
+ const truncated = content.length > maxLength
266
+ ? content.substring(0, maxLength) + "\n... (truncated)"
267
+ : content;
268
+ return {
269
+ status: "success",
270
+ data: {
271
+ content: truncated,
272
+ },
273
+ };
274
+ }
275
+ catch (error) {
276
+ return {
277
+ status: "error",
278
+ error: `Failed to read changelog: ${error.message}`,
279
+ };
280
+ }
281
+ }
211
282
  }
212
283
  exports.UtilityTools = UtilityTools;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@berthojoris/mcp-mysql-server",
3
- "version": "1.14.1",
3
+ "version": "1.15.0",
4
4
  "description": "Model Context Protocol server for MySQL database integration with dynamic per-project permissions, backup/restore, data import/export, and data migration capabilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -86,4 +86,4 @@
86
86
  "ts-node": "^10.9.1",
87
87
  "typescript": "^5.2.2"
88
88
  }
89
- }
89
+ }