@berthojoris/mcp-mysql-server 1.28.0 → 1.30.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 +24 -0
- package/DOCUMENTATIONS.md +10 -2
- package/README.md +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/mcp-server.js +9 -1
- package/dist/security/securityLayer.js +3 -3
- package/dist/tools/databaseTools.d.ts +3 -1
- package/dist/tools/databaseTools.js +94 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ 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.30.0] - 2025-12-22
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Security Layer Comment Detection** - Fixed overly aggressive regex patterns that incorrectly flagged legitimate SQL syntax:
|
|
12
|
+
- Fixed `/\/*/` pattern that incorrectly matched forward slashes in function calls like `YEAR(start_date)`
|
|
13
|
+
- Improved `--` pattern to `--\s` to avoid false positives with date formats
|
|
14
|
+
- Now properly detects actual comment patterns while allowing legitimate SQL syntax
|
|
15
|
+
- Resolves issue where valid SELECT queries with date functions were being rejected
|
|
16
|
+
|
|
17
|
+
## [1.29.0] - 2025-12-21
|
|
18
|
+
|
|
19
|
+
### Enhanced
|
|
20
|
+
- **`get_database_summary` tool** - Significantly improved output format and functionality:
|
|
21
|
+
- Added database **Overview section** displaying total tables, tables shown, and total estimated rows with thousands separators
|
|
22
|
+
- Enhanced **column display**: now one per line with data type, nullable status, and key indicators (PK, UNI, FK with target table)
|
|
23
|
+
- Added **Primary Key section** per table showing all PK columns
|
|
24
|
+
- Included **Foreign Key relationships** section showing all FK connections across tables (can be disabled with `include_relationships: false`)
|
|
25
|
+
- Added `max_tables` parameter (max 500) to limit output for large databases
|
|
26
|
+
- Added `include_relationships` parameter (default: true) to control FK relationships display
|
|
27
|
+
- Improved **markdown formatting** with clear hierarchy using H1, H2, H3 headings
|
|
28
|
+
- Enhanced **foreign key tracking**: shows FK targets inline per column and in dedicated relationships summary
|
|
29
|
+
- Added footer note when tables are truncated due to `max_tables` limit
|
|
30
|
+
- Better **readability** for AI context consumption and human review
|
|
31
|
+
|
|
8
32
|
## [1.28.0] - 2025-12-21
|
|
9
33
|
|
|
10
34
|
### Changed
|
package/DOCUMENTATIONS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MySQL MCP Server - Detailed Documentation
|
|
2
2
|
|
|
3
|
-
**Last Updated:** 2025-12-
|
|
3
|
+
**Last Updated:** 2025-12-22 15:30:00
|
|
4
4
|
|
|
5
5
|
This file contains detailed documentation for all features of the MySQL MCP Server. For quick start and basic information, see [README.md](README.md).
|
|
6
6
|
|
|
@@ -439,11 +439,19 @@ This section provides a comprehensive reference of all 145 available tools organ
|
|
|
439
439
|
|
|
440
440
|
| Tool | Description | Requires |
|
|
441
441
|
|------|-------------|----------|
|
|
442
|
-
| `get_database_summary` |
|
|
442
|
+
| `get_database_summary` | Enhanced database overview with overview section, per-table breakdown (PKs, columns with nullable info, FK references), optional relationships summary, and configurable table limits | `list` |
|
|
443
443
|
| `get_schema_erd` | Generate Mermaid.js ER diagram for visualization | `list` |
|
|
444
444
|
| `get_schema_rag_context` | Condensed schema snapshot (tables, PK/FK, row estimates) for RAG prompts | `list` |
|
|
445
445
|
| `get_column_statistics` | Profile data (min, max, nulls, distinct) for analysis | `read` |
|
|
446
446
|
|
|
447
|
+
#### Database Summary Enhancement
|
|
448
|
+
- **Better readability**: Columns displayed one per line with type, nullable status, and key indicators (PK, UNI, FK with target)
|
|
449
|
+
- **Overview section**: Shows total tables, tables displayed, and total estimated rows
|
|
450
|
+
- **Configurable limits**: Use `max_tables` parameter (max 500) to limit output for large databases
|
|
451
|
+
- **Relationship tracking**: Automatically includes foreign key relationships (set `include_relationships: false` to disable)
|
|
452
|
+
- **Formatted output**: Uses markdown headings for clear hierarchy, number formatting with thousands separators
|
|
453
|
+
- **Foreign key details**: Shows FK targets inline per column and in dedicated relationships section
|
|
454
|
+
|
|
447
455
|
#### Schema-Aware RAG Context Pack
|
|
448
456
|
- Purpose-built for embeddings: returns a `context_text` block plus structured `tables` and `relationships` so agents can self-orient without pulling a full ERD.
|
|
449
457
|
- Tunable size: `max_tables` (default 50, max 200) and `max_columns` (default 12) to control output length; set `include_relationships` to `false` to omit FK lines.
|
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:** 2025-12-
|
|
7
|
+
**Last Updated:** 2025-12-22 15:30:00
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/@berthojoris/mysql-mcp)
|
|
10
10
|
[](https://www.npmjs.com/package/@berthojoris/mysql-mcp)
|
package/dist/index.d.ts
CHANGED
package/dist/mcp-server.js
CHANGED
|
@@ -37,7 +37,7 @@ const TOOLS = [
|
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
name: "get_database_summary",
|
|
40
|
-
description: "📊 Returns a high-level overview of the database including all tables, their columns, data types,
|
|
40
|
+
description: "📊 Returns a high-level overview of the database including all tables, their columns, data types, row counts, and relationships. RECOMMENDED: Use this first when exploring a new database to understand its structure quickly. Features: database overview, per-table breakdown with primary keys, columns with nullable info and foreign key references, optional relationship summary, and configurable table limits.",
|
|
41
41
|
inputSchema: {
|
|
42
42
|
type: "object",
|
|
43
43
|
properties: {
|
|
@@ -45,6 +45,14 @@ const TOOLS = [
|
|
|
45
45
|
type: "string",
|
|
46
46
|
description: "Optional: specific database name",
|
|
47
47
|
},
|
|
48
|
+
max_tables: {
|
|
49
|
+
type: "number",
|
|
50
|
+
description: "Optional: maximum number of tables to include (default: all tables, max 500)",
|
|
51
|
+
},
|
|
52
|
+
include_relationships: {
|
|
53
|
+
type: "boolean",
|
|
54
|
+
description: "Optional: include foreign key relationships section (default: true)",
|
|
55
|
+
},
|
|
48
56
|
},
|
|
49
57
|
},
|
|
50
58
|
},
|
|
@@ -201,9 +201,9 @@ class SecurityLayer {
|
|
|
201
201
|
}
|
|
202
202
|
// Check for comment-like sequences that could be used for injection
|
|
203
203
|
const suspiciousPatterns = [
|
|
204
|
-
|
|
205
|
-
/\*\//, // Potential comment end
|
|
206
|
-
|
|
204
|
+
/\/\*/, // Potential comment start (literal /*)
|
|
205
|
+
/\*\//, // Potential comment end (literal */)
|
|
206
|
+
/--\s/, // Potential comment (-- followed by space, not -- in dates)
|
|
207
207
|
/#/, // Potential comment
|
|
208
208
|
];
|
|
209
209
|
for (const pattern of suspiciousPatterns) {
|
|
@@ -33,10 +33,12 @@ export declare class DatabaseTools {
|
|
|
33
33
|
}>;
|
|
34
34
|
/**
|
|
35
35
|
* Get a high-level summary of the database (tables, columns, row counts)
|
|
36
|
-
* Optimized for AI context window
|
|
36
|
+
* Optimized for AI context window with better formatting and optional limits
|
|
37
37
|
*/
|
|
38
38
|
getDatabaseSummary(params: {
|
|
39
39
|
database?: string;
|
|
40
|
+
max_tables?: number;
|
|
41
|
+
include_relationships?: boolean;
|
|
40
42
|
}): Promise<{
|
|
41
43
|
status: string;
|
|
42
44
|
data?: string;
|
|
@@ -141,7 +141,7 @@ class DatabaseTools {
|
|
|
141
141
|
}
|
|
142
142
|
/**
|
|
143
143
|
* Get a high-level summary of the database (tables, columns, row counts)
|
|
144
|
-
* Optimized for AI context window
|
|
144
|
+
* Optimized for AI context window with better formatting and optional limits
|
|
145
145
|
*/
|
|
146
146
|
async getDatabaseSummary(params) {
|
|
147
147
|
try {
|
|
@@ -159,37 +159,112 @@ class DatabaseTools {
|
|
|
159
159
|
error: "No database specified and none connected.",
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
|
-
|
|
162
|
+
const maxTables = params.max_tables ? Math.min(Math.max(params.max_tables, 1), 500) : undefined;
|
|
163
|
+
const includeRelationships = params.include_relationships ?? true;
|
|
164
|
+
// Get total table count first
|
|
165
|
+
const totalCountQuery = `
|
|
166
|
+
SELECT COUNT(*) as total
|
|
167
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
168
|
+
WHERE TABLE_SCHEMA = ?
|
|
169
|
+
`;
|
|
170
|
+
const totalCountResult = await this.db.query(totalCountQuery, [database]);
|
|
171
|
+
const totalTables = totalCountResult[0]?.total || 0;
|
|
172
|
+
// Get tables and row counts with optional limit
|
|
163
173
|
const tablesQuery = `
|
|
164
174
|
SELECT TABLE_NAME, TABLE_ROWS
|
|
165
175
|
FROM INFORMATION_SCHEMA.TABLES
|
|
166
176
|
WHERE TABLE_SCHEMA = ?
|
|
177
|
+
ORDER BY TABLE_NAME
|
|
178
|
+
${maxTables ? `LIMIT ${maxTables}` : ''}
|
|
167
179
|
`;
|
|
168
180
|
const tables = await this.db.query(tablesQuery, [database]);
|
|
169
|
-
|
|
181
|
+
if (tables.length === 0) {
|
|
182
|
+
return {
|
|
183
|
+
status: "success",
|
|
184
|
+
data: `# Database Summary: ${database}\n\nNo tables found in this database.`
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// Get columns for displayed tables
|
|
188
|
+
const tableNames = tables.map(t => t.TABLE_NAME);
|
|
189
|
+
const placeholders = tableNames.map(() => '?').join(',');
|
|
170
190
|
const columnsQuery = `
|
|
171
|
-
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, COLUMN_KEY
|
|
191
|
+
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE, COLUMN_KEY, IS_NULLABLE
|
|
172
192
|
FROM INFORMATION_SCHEMA.COLUMNS
|
|
173
|
-
WHERE TABLE_SCHEMA = ?
|
|
193
|
+
WHERE TABLE_SCHEMA = ? AND TABLE_NAME IN (${placeholders})
|
|
174
194
|
ORDER BY TABLE_NAME, ORDINAL_POSITION
|
|
175
195
|
`;
|
|
176
|
-
const columns = await this.db.query(columnsQuery, [database]);
|
|
177
|
-
//
|
|
196
|
+
const columns = await this.db.query(columnsQuery, [database, ...tableNames]);
|
|
197
|
+
// Get foreign key relationships if requested
|
|
198
|
+
let foreignKeys = [];
|
|
199
|
+
if (includeRelationships) {
|
|
200
|
+
const fkQuery = `
|
|
201
|
+
SELECT
|
|
202
|
+
TABLE_NAME,
|
|
203
|
+
COLUMN_NAME,
|
|
204
|
+
REFERENCED_TABLE_NAME,
|
|
205
|
+
REFERENCED_COLUMN_NAME
|
|
206
|
+
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
|
207
|
+
WHERE TABLE_SCHEMA = ?
|
|
208
|
+
AND TABLE_NAME IN (${placeholders})
|
|
209
|
+
AND REFERENCED_TABLE_NAME IS NOT NULL
|
|
210
|
+
`;
|
|
211
|
+
foreignKeys = await this.db.query(fkQuery, [database, ...tableNames]);
|
|
212
|
+
}
|
|
213
|
+
// Build enhanced summary
|
|
178
214
|
let summary = `# Database Summary: ${database}\n\n`;
|
|
215
|
+
// Overview section
|
|
216
|
+
summary += `## Overview\n`;
|
|
217
|
+
summary += `- **Total Tables**: ${totalTables}\n`;
|
|
218
|
+
summary += `- **Tables Shown**: ${tables.length}${maxTables && totalTables > maxTables ? ` (limited to ${maxTables})` : ''}\n`;
|
|
219
|
+
const totalRows = tables.reduce((sum, t) => sum + (parseInt(t.TABLE_ROWS) || 0), 0);
|
|
220
|
+
summary += `- **Total Estimated Rows**: ~${totalRows.toLocaleString()}\n\n`;
|
|
221
|
+
// Tables section
|
|
222
|
+
summary += `## Tables\n\n`;
|
|
179
223
|
for (const table of tables) {
|
|
180
|
-
|
|
224
|
+
const rowCount = parseInt(table.TABLE_ROWS) || 0;
|
|
225
|
+
summary += `### ${table.TABLE_NAME}\n`;
|
|
226
|
+
summary += `**Rows**: ~${rowCount.toLocaleString()}\n\n`;
|
|
181
227
|
const tableColumns = columns.filter(c => c.TABLE_NAME === table.TABLE_NAME);
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
228
|
+
const tableFKs = foreignKeys.filter(fk => fk.TABLE_NAME === table.TABLE_NAME);
|
|
229
|
+
// Primary keys
|
|
230
|
+
const primaryKeys = tableColumns.filter(c => c.COLUMN_KEY === 'PRI');
|
|
231
|
+
if (primaryKeys.length > 0) {
|
|
232
|
+
summary += `**Primary Key(s)**: ${primaryKeys.map(c => c.COLUMN_NAME).join(', ')}\n\n`;
|
|
233
|
+
}
|
|
234
|
+
// Columns
|
|
235
|
+
summary += `**Columns** (${tableColumns.length}):\n`;
|
|
236
|
+
for (const col of tableColumns) {
|
|
237
|
+
const nullable = col.IS_NULLABLE === 'YES' ? 'nullable' : 'not null';
|
|
238
|
+
let keys = [];
|
|
239
|
+
if (col.COLUMN_KEY === 'PRI')
|
|
240
|
+
keys.push('PK');
|
|
241
|
+
else if (col.COLUMN_KEY === 'UNI')
|
|
242
|
+
keys.push('UNI');
|
|
243
|
+
// Check if this column is a foreign key
|
|
244
|
+
const fk = tableFKs.find(f => f.COLUMN_NAME === col.COLUMN_NAME);
|
|
245
|
+
if (fk) {
|
|
246
|
+
keys.push(`FK → ${fk.REFERENCED_TABLE_NAME}.${fk.REFERENCED_COLUMN_NAME}`);
|
|
247
|
+
}
|
|
248
|
+
else if (col.COLUMN_KEY === 'MUL') {
|
|
249
|
+
keys.push('Index');
|
|
250
|
+
}
|
|
251
|
+
const keyInfo = keys.length > 0 ? ` [${keys.join(', ')}]` : '';
|
|
252
|
+
summary += ` - ${col.COLUMN_NAME}: ${col.DATA_TYPE} (${nullable})${keyInfo}\n`;
|
|
253
|
+
}
|
|
254
|
+
summary += `\n`;
|
|
255
|
+
}
|
|
256
|
+
// Relationships summary if included and exists
|
|
257
|
+
if (includeRelationships && foreignKeys.length > 0) {
|
|
258
|
+
summary += `## Foreign Key Relationships (${foreignKeys.length})\n\n`;
|
|
259
|
+
for (const fk of foreignKeys) {
|
|
260
|
+
summary += `- ${fk.TABLE_NAME}.${fk.COLUMN_NAME} → ${fk.REFERENCED_TABLE_NAME}.${fk.REFERENCED_COLUMN_NAME}\n`;
|
|
261
|
+
}
|
|
262
|
+
summary += `\n`;
|
|
263
|
+
}
|
|
264
|
+
// Footer note if tables were limited
|
|
265
|
+
if (maxTables && totalTables > maxTables) {
|
|
266
|
+
summary += `\n---\n`;
|
|
267
|
+
summary += `*Note: ${totalTables - maxTables} table(s) not shown. Increase \`max_tables\` parameter to see more.*\n`;
|
|
193
268
|
}
|
|
194
269
|
return {
|
|
195
270
|
status: "success",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@berthojoris/mcp-mysql-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.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",
|