@berthojoris/mcp-mysql-server 1.40.6 → 1.40.7
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 +13 -0
- package/DOCUMENTATIONS.md +25 -14
- package/README.md +9 -1
- package/dist/config/featureConfig.js +3 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +11 -4
- package/dist/mcp-server.js +29 -3
- package/dist/security/securityLayer.d.ts +1 -1
- package/dist/security/securityLayer.js +2 -2
- package/dist/tools/dataExportTools.d.ts +11 -0
- package/dist/tools/dataExportTools.js +68 -27
- package/dist/tools/ddlTools.d.ts +2 -0
- package/dist/tools/ddlTools.js +125 -20
- package/dist/tools/toolArgumentValidation.js +5 -0
- package/dist/tools/utilityTools.d.ts +16 -1
- package/dist/tools/utilityTools.js +73 -18
- package/manifest.json +2539 -744
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ 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.7] - 2026-05-06
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Implemented `export_query_to_csv` end-to-end for SELECT query CSV exports.
|
|
12
|
+
- Enhanced `list_all_tools` with a live runtime tool catalog, enabled/disabled status, access profile, and AI-agent workflow guidance.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Fixed `export_query_to_csv` being advertised but failing with `Unknown tool`.
|
|
16
|
+
- Fixed `list_all_tools` returning stale manifest data instead of the active MCP tool catalog.
|
|
17
|
+
- Fixed `execute_in_transaction` permission checks to match its advertised transaction permission.
|
|
18
|
+
- Hardened DDL tools by validating raw DDL statements and checking structured table, column, and index inputs before building SQL.
|
|
19
|
+
- Added argument validation for Cursor bridge dispatches.
|
|
20
|
+
|
|
8
21
|
## [1.40.6] - 2026-05-04
|
|
9
22
|
|
|
10
23
|
### Added
|
package/DOCUMENTATIONS.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# MySQL MCP Server - Documentation
|
|
2
2
|
|
|
3
|
-
**Last Updated:** 2026-05-
|
|
4
|
-
**Version:** 1.40.
|
|
3
|
+
**Last Updated:** 2026-05-06 22:38:42
|
|
4
|
+
**Version:** 1.40.7
|
|
5
5
|
**Total Tools:** 79
|
|
6
6
|
|
|
7
7
|
Comprehensive documentation for the MySQL MCP Server. For quick start, see [README.md](README.md).
|
|
@@ -93,6 +93,14 @@ Supported `mode` values are `auto`, `select`, `write`, and `ddl`. Set `MYSQL_MCP
|
|
|
93
93
|
|
|
94
94
|
---
|
|
95
95
|
|
|
96
|
+
### AI Agent Tool Discovery
|
|
97
|
+
|
|
98
|
+
Use `list_all_tools` first when connecting from Codex, Claude Code CLI, Cursor, Droid CLI, or other MCP agents. It returns the live runtime catalog from the server, enabled/disabled status for each tool, the active permission/category profile, and recommended workflows for common agent tasks.
|
|
99
|
+
|
|
100
|
+
For CSV exports, use `export_table_to_csv` for table-based exports and `export_query_to_csv` for SELECT query exports.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
96
104
|
## Permission System
|
|
97
105
|
|
|
98
106
|
### Available Permissions
|
|
@@ -120,12 +128,11 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
120
128
|
|
|
121
129
|
## Tool Categories
|
|
122
130
|
|
|
123
|
-
### 1. Database Discovery (
|
|
131
|
+
### 1. Database Discovery (4 tools)
|
|
124
132
|
- `list_databases` - List all databases
|
|
125
133
|
- `list_tables` - List tables in database
|
|
126
134
|
- `read_table_schema` - Get table structure
|
|
127
135
|
- `get_all_tables_relationships` - Get all FK relationships
|
|
128
|
-
- `list_all_tools` - List available MCP tools
|
|
129
136
|
|
|
130
137
|
### 2. Analysis (4 tools)
|
|
131
138
|
- `get_database_summary` - Database overview with statistics
|
|
@@ -153,7 +160,11 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
153
160
|
- `drop_table` - Delete tables
|
|
154
161
|
- `execute_ddl` - Execute raw DDL
|
|
155
162
|
|
|
156
|
-
### 6.
|
|
163
|
+
### 6. Data Export (2 tools)
|
|
164
|
+
- `export_table_to_csv` - Export table data to CSV
|
|
165
|
+
- `export_query_to_csv` - Export SELECT query results to CSV
|
|
166
|
+
|
|
167
|
+
### 7. Index Management (10 tools)
|
|
157
168
|
- `list_indexes` - List table indexes
|
|
158
169
|
- `get_index_info` - Get index details
|
|
159
170
|
- `create_index` - Create indexes
|
|
@@ -165,7 +176,7 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
165
176
|
- `get_fulltext_stats` - Get FULLTEXT index statistics
|
|
166
177
|
- `optimize_fulltext` - Optimize FULLTEXT indexes
|
|
167
178
|
|
|
168
|
-
###
|
|
179
|
+
### 8. Constraint Management (7 tools)
|
|
169
180
|
- `list_foreign_keys` - List foreign keys
|
|
170
181
|
- `list_constraints` - List all constraints
|
|
171
182
|
- `add_foreign_key` - Add foreign key
|
|
@@ -174,7 +185,7 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
174
185
|
- `drop_constraint` - Remove constraint
|
|
175
186
|
- `add_check_constraint` - Add check constraint
|
|
176
187
|
|
|
177
|
-
###
|
|
188
|
+
### 9. Stored Procedures (6 tools)
|
|
178
189
|
- `list_stored_procedures` - List procedures
|
|
179
190
|
- `get_stored_procedure_info` - Get procedure details
|
|
180
191
|
- `execute_stored_procedure` - Execute procedures
|
|
@@ -182,7 +193,7 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
182
193
|
- `drop_stored_procedure` - Remove procedures
|
|
183
194
|
- `show_create_procedure` - Show CREATE statement
|
|
184
195
|
|
|
185
|
-
###
|
|
196
|
+
### 10. Views Management (6 tools)
|
|
186
197
|
- `list_views` - List views
|
|
187
198
|
- `get_view_info` - Get view details
|
|
188
199
|
- `create_view` - Create views
|
|
@@ -190,14 +201,14 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
190
201
|
- `drop_view` - Remove views
|
|
191
202
|
- `show_create_view` - Show CREATE statement
|
|
192
203
|
|
|
193
|
-
###
|
|
204
|
+
### 11. Triggers Management (5 tools)
|
|
194
205
|
- `list_triggers` - List triggers
|
|
195
206
|
- `get_trigger_info` - Get trigger details
|
|
196
207
|
- `create_trigger` - Create triggers
|
|
197
208
|
- `drop_trigger` - Remove triggers
|
|
198
209
|
- `show_create_trigger` - Show CREATE statement
|
|
199
210
|
|
|
200
|
-
###
|
|
211
|
+
### 12. Table Maintenance (8 tools)
|
|
201
212
|
- `analyze_table` - Update statistics
|
|
202
213
|
- `optimize_table` - Reclaim space
|
|
203
214
|
- `check_table` - Check for errors
|
|
@@ -207,24 +218,24 @@ Tool enabled = (Has Permission) AND (Has Category OR No categories specified)
|
|
|
207
218
|
- `flush_table` - Close/reopen table
|
|
208
219
|
- `get_table_size` - Get size information
|
|
209
220
|
|
|
210
|
-
###
|
|
221
|
+
### 13. Transaction Management (5 tools)
|
|
211
222
|
- `begin_transaction` - Start transaction
|
|
212
223
|
- `commit_transaction` - Commit transaction
|
|
213
224
|
- `rollback_transaction` - Rollback transaction
|
|
214
225
|
- `get_transaction_status` - Check transaction state
|
|
215
226
|
- `execute_in_transaction` - Execute within transaction
|
|
216
227
|
|
|
217
|
-
###
|
|
228
|
+
### 14. Query Optimization (3 tools)
|
|
218
229
|
- `analyze_query` - Analyze query performance
|
|
219
230
|
- `get_optimization_hints` - Get optimizer hints
|
|
220
231
|
- `repair_query` - Repair broken SQL queries
|
|
221
232
|
|
|
222
|
-
###
|
|
233
|
+
### 15. Utilities (5 tools)
|
|
223
234
|
- `test_connection` - Test connectivity
|
|
224
235
|
- `describe_connection` - Connection info
|
|
225
236
|
- `read_changelog` - Read changelog
|
|
226
|
-
- `invalidate_table_cache` - Clear table cache
|
|
227
237
|
- `cursor_execute_request` - Execute a file-backed request for clients that cannot send MCP arguments
|
|
238
|
+
- `list_all_tools` - Runtime tool catalog with agent guidance
|
|
228
239
|
|
|
229
240
|
---
|
|
230
241
|
|
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-05-
|
|
7
|
+
**Last Updated:** 2026-05-06 22:38:42
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/@berthojoris/mcp-mysql-server)
|
|
10
10
|
[](https://www.npmjs.com/package/@berthojoris/mcp-mysql-server)
|
|
@@ -237,6 +237,14 @@ Set `MYSQL_MCP_CURSOR_REQUEST_FILE` to override the request file path.
|
|
|
237
237
|
|
|
238
238
|
---
|
|
239
239
|
|
|
240
|
+
### AI Agent Tool Discovery
|
|
241
|
+
|
|
242
|
+
For Codex, Claude Code CLI, Cursor, Droid CLI, and other MCP agents, call `list_all_tools` first. It returns the live runtime catalog, enabled/disabled status, active permission/category profile, and recommended workflows for schema exploration, safe SELECT queries, CSV exports, transactions, and data changes.
|
|
243
|
+
|
|
244
|
+
Use `export_table_to_csv` for table-based exports and `export_query_to_csv` for SELECT query exports.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
240
248
|
## Permission System
|
|
241
249
|
|
|
242
250
|
Control database access with a **dual-layer filtering system** that provides both broad and fine-grained control:
|
|
@@ -53,7 +53,8 @@ exports.toolCategoryMap = {
|
|
|
53
53
|
listDatabases: ToolCategory.LIST,
|
|
54
54
|
listTables: ToolCategory.LIST,
|
|
55
55
|
readTableSchema: ToolCategory.LIST,
|
|
56
|
-
|
|
56
|
+
listAllTools: ToolCategory.UTILITY,
|
|
57
|
+
list_all_tools: ToolCategory.UTILITY,
|
|
57
58
|
// Analysis tools (added here to group with database tools)
|
|
58
59
|
getDatabaseSummary: ToolCategory.LIST,
|
|
59
60
|
getSchemaERD: ToolCategory.LIST,
|
|
@@ -194,6 +195,7 @@ exports.toolDocCategoryMap = {
|
|
|
194
195
|
exportQueryToCsv: DocCategory.UTILITIES,
|
|
195
196
|
export_query_to_csv: DocCategory.UTILITIES,
|
|
196
197
|
read_changelog: DocCategory.UTILITIES,
|
|
198
|
+
listAllTools: DocCategory.UTILITIES,
|
|
197
199
|
list_all_tools: DocCategory.UTILITIES,
|
|
198
200
|
// Transaction Management
|
|
199
201
|
beginTransaction: DocCategory.TRANSACTION_MANAGEMENT,
|
package/dist/index.d.ts
CHANGED
|
@@ -203,7 +203,19 @@ export declare class MySQLMCP {
|
|
|
203
203
|
data?: any;
|
|
204
204
|
error?: string;
|
|
205
205
|
}>;
|
|
206
|
-
listAllTools(
|
|
206
|
+
listAllTools(params?: {
|
|
207
|
+
tools?: Array<{
|
|
208
|
+
name: string;
|
|
209
|
+
description?: string;
|
|
210
|
+
inputSchema?: any;
|
|
211
|
+
input_schema?: any;
|
|
212
|
+
output_schema?: any;
|
|
213
|
+
}>;
|
|
214
|
+
enabledToolNames?: string[];
|
|
215
|
+
accessProfile?: any;
|
|
216
|
+
serverName?: string;
|
|
217
|
+
serverVersion?: string;
|
|
218
|
+
}): Promise<{
|
|
207
219
|
status: string;
|
|
208
220
|
data?: any;
|
|
209
221
|
error?: string;
|
|
@@ -315,6 +327,15 @@ export declare class MySQLMCP {
|
|
|
315
327
|
data?: any;
|
|
316
328
|
error?: string;
|
|
317
329
|
}>;
|
|
330
|
+
exportQueryToCSV(params: {
|
|
331
|
+
query: string;
|
|
332
|
+
params?: any[];
|
|
333
|
+
include_headers?: boolean;
|
|
334
|
+
}): Promise<{
|
|
335
|
+
status: string;
|
|
336
|
+
data?: any;
|
|
337
|
+
error?: string;
|
|
338
|
+
}>;
|
|
318
339
|
repairQuery(params: {
|
|
319
340
|
query: string;
|
|
320
341
|
error_message?: string;
|
package/dist/index.js
CHANGED
|
@@ -134,7 +134,7 @@ class MySQLMCP {
|
|
|
134
134
|
return { status: "error", error: check.error };
|
|
135
135
|
}
|
|
136
136
|
// Additional security check
|
|
137
|
-
if (!this.security.isReadOnlyQuery(params.query)) {
|
|
137
|
+
if (!this.security.isReadOnlyQuery(params.query, this.security.hasExecutePermission())) {
|
|
138
138
|
return {
|
|
139
139
|
status: "error",
|
|
140
140
|
error: "Only SELECT queries are allowed with run_select_query. Use execute_write_query for other operations.",
|
|
@@ -239,12 +239,12 @@ class MySQLMCP {
|
|
|
239
239
|
}
|
|
240
240
|
return await this.utilityTools.readChangelog(params);
|
|
241
241
|
}
|
|
242
|
-
async listAllTools() {
|
|
242
|
+
async listAllTools(params) {
|
|
243
243
|
const check = this.checkToolEnabled("list_all_tools");
|
|
244
244
|
if (!check.enabled) {
|
|
245
245
|
return { status: "error", error: check.error };
|
|
246
246
|
}
|
|
247
|
-
return await this.utilityTools.listAllTools();
|
|
247
|
+
return await this.utilityTools.listAllTools(params);
|
|
248
248
|
}
|
|
249
249
|
// Transaction Tools
|
|
250
250
|
async beginTransaction(params) {
|
|
@@ -276,7 +276,7 @@ class MySQLMCP {
|
|
|
276
276
|
return await this.transactionTools.getTransactionStatus();
|
|
277
277
|
}
|
|
278
278
|
async executeInTransaction(params) {
|
|
279
|
-
const check = this.checkToolEnabled("
|
|
279
|
+
const check = this.checkToolEnabled("executeInTransaction");
|
|
280
280
|
if (!check.enabled) {
|
|
281
281
|
return { status: "error", error: check.error };
|
|
282
282
|
}
|
|
@@ -333,6 +333,13 @@ class MySQLMCP {
|
|
|
333
333
|
}
|
|
334
334
|
return await this.dataExportTools.exportTableToCSV(params);
|
|
335
335
|
}
|
|
336
|
+
async exportQueryToCSV(params) {
|
|
337
|
+
const check = this.checkToolEnabled("exportQueryToCSV");
|
|
338
|
+
if (!check.enabled) {
|
|
339
|
+
return { status: "error", error: check.error };
|
|
340
|
+
}
|
|
341
|
+
return await this.dataExportTools.exportQueryToCSV(params);
|
|
342
|
+
}
|
|
336
343
|
// AI Productivity Tools
|
|
337
344
|
async repairQuery(params) {
|
|
338
345
|
const check = this.checkToolEnabled("repairQuery");
|
package/dist/mcp-server.js
CHANGED
|
@@ -17,6 +17,8 @@ const toolArgumentValidation_js_1 = require("./tools/toolArgumentValidation.js")
|
|
|
17
17
|
// Layer 2 (Categories): MCP_CATEGORIES (optional, for fine-grained control)
|
|
18
18
|
const permissions = process.env.MCP_PERMISSIONS || process.env.MCP_CONFIG || "";
|
|
19
19
|
const categories = process.env.MCP_CATEGORIES || "";
|
|
20
|
+
const SERVER_NAME = "mysql-mcp-server";
|
|
21
|
+
const SERVER_VERSION = "1.40.7";
|
|
20
22
|
// Declare the MySQL MCP instance (will be initialized in main())
|
|
21
23
|
let mysqlMCP;
|
|
22
24
|
// Define all available tools with their schemas
|
|
@@ -1862,8 +1864,8 @@ const TOOLS = [
|
|
|
1862
1864
|
];
|
|
1863
1865
|
// Create the MCP server
|
|
1864
1866
|
const server = new index_js_1.Server({
|
|
1865
|
-
name:
|
|
1866
|
-
version:
|
|
1867
|
+
name: SERVER_NAME,
|
|
1868
|
+
version: SERVER_VERSION,
|
|
1867
1869
|
}, {
|
|
1868
1870
|
capabilities: {
|
|
1869
1871
|
tools: {},
|
|
@@ -1884,6 +1886,20 @@ const TOOL_METHOD_OVERRIDES = {
|
|
|
1884
1886
|
export_query_to_csv: "exportQueryToCSV",
|
|
1885
1887
|
};
|
|
1886
1888
|
const toCamelCase = (value) => value.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1889
|
+
const getRuntimeToolCatalog = () => {
|
|
1890
|
+
const enabledTools = (0, toolRegistry_js_1.getEnabledTools)(mysqlMCP, TOOLS);
|
|
1891
|
+
return {
|
|
1892
|
+
tools: TOOLS.map((tool) => ({
|
|
1893
|
+
name: tool.name,
|
|
1894
|
+
description: tool.description,
|
|
1895
|
+
inputSchema: tool.inputSchema,
|
|
1896
|
+
})),
|
|
1897
|
+
enabledToolNames: enabledTools.map((tool) => tool.name),
|
|
1898
|
+
accessProfile: mysqlMCP.getAccessProfile(),
|
|
1899
|
+
serverName: SERVER_NAME,
|
|
1900
|
+
serverVersion: SERVER_VERSION,
|
|
1901
|
+
};
|
|
1902
|
+
};
|
|
1887
1903
|
const getCursorRequestFilePath = () => {
|
|
1888
1904
|
const configuredPath = process.env.MYSQL_MCP_CURSOR_REQUEST_FILE ||
|
|
1889
1905
|
process.env.MCP_MYSQL_REQUEST_FILE ||
|
|
@@ -1919,6 +1935,13 @@ const executeToolByName = async (toolName, args = {}) => {
|
|
|
1919
1935
|
if (toolName === "cursor_execute_request") {
|
|
1920
1936
|
throw new Error("cursor_execute_request cannot dispatch to itself");
|
|
1921
1937
|
}
|
|
1938
|
+
const validation = (0, toolArgumentValidation_js_1.validateToolArguments)(toolName, args);
|
|
1939
|
+
if (!validation.valid) {
|
|
1940
|
+
throw new Error(`Validation Error: ${validation.errors?.join(", ") || "Invalid arguments"}`);
|
|
1941
|
+
}
|
|
1942
|
+
if (toolName === "list_all_tools") {
|
|
1943
|
+
return await mysqlMCP.listAllTools(getRuntimeToolCatalog());
|
|
1944
|
+
}
|
|
1922
1945
|
const methodName = TOOL_METHOD_OVERRIDES[toolName] || toCamelCase(toolName);
|
|
1923
1946
|
const method = mysqlMCP[methodName];
|
|
1924
1947
|
if (typeof method !== "function") {
|
|
@@ -2055,7 +2078,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
2055
2078
|
result = await mysqlMCP.testConnection();
|
|
2056
2079
|
break;
|
|
2057
2080
|
case "list_all_tools":
|
|
2058
|
-
result = await mysqlMCP.listAllTools();
|
|
2081
|
+
result = await mysqlMCP.listAllTools(getRuntimeToolCatalog());
|
|
2059
2082
|
break;
|
|
2060
2083
|
case "read_changelog":
|
|
2061
2084
|
result = await mysqlMCP.readChangelog((args || {}));
|
|
@@ -2102,6 +2125,9 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
2102
2125
|
case "export_table_to_csv":
|
|
2103
2126
|
result = await mysqlMCP.exportTableToCSV((args || {}));
|
|
2104
2127
|
break;
|
|
2128
|
+
case "export_query_to_csv":
|
|
2129
|
+
result = await mysqlMCP.exportQueryToCSV((args || {}));
|
|
2130
|
+
break;
|
|
2105
2131
|
// Query Optimization Tools
|
|
2106
2132
|
case "analyze_query":
|
|
2107
2133
|
result = mysqlMCP.analyzeQuery((args || {}));
|
|
@@ -71,7 +71,7 @@ export declare class SecurityLayer {
|
|
|
71
71
|
/**
|
|
72
72
|
* Check if a query is a read-only SELECT query or information query (SHOW, DESCRIBE, etc.)
|
|
73
73
|
*/
|
|
74
|
-
isReadOnlyQuery(query: string): boolean;
|
|
74
|
+
isReadOnlyQuery(query: string, bypassDangerousCheck?: boolean): boolean;
|
|
75
75
|
/**
|
|
76
76
|
* Check if a query contains dangerous operations
|
|
77
77
|
*/
|
|
@@ -404,13 +404,13 @@ class SecurityLayer {
|
|
|
404
404
|
/**
|
|
405
405
|
* Check if a query is a read-only SELECT query or information query (SHOW, DESCRIBE, etc.)
|
|
406
406
|
*/
|
|
407
|
-
isReadOnlyQuery(query) {
|
|
407
|
+
isReadOnlyQuery(query, bypassDangerousCheck = false) {
|
|
408
408
|
// Check if it's an information query first (SHOW, DESCRIBE, EXPLAIN, etc.)
|
|
409
409
|
if (this.isInformationQuery(query)) {
|
|
410
410
|
return true;
|
|
411
411
|
}
|
|
412
412
|
// Check if it's a SELECT query
|
|
413
|
-
const validation = this.validateQuery(query);
|
|
413
|
+
const validation = this.validateQuery(query, bypassDangerousCheck);
|
|
414
414
|
return validation.valid && validation.queryType === "SELECT";
|
|
415
415
|
}
|
|
416
416
|
/**
|
|
@@ -12,6 +12,8 @@ export declare class DataExportTools {
|
|
|
12
12
|
* Escape string value for SQL INSERT statements
|
|
13
13
|
*/
|
|
14
14
|
private escapeValue;
|
|
15
|
+
private escapeCsvValue;
|
|
16
|
+
private rowsToCSV;
|
|
15
17
|
/**
|
|
16
18
|
* Export table data to CSV format
|
|
17
19
|
*/
|
|
@@ -26,4 +28,13 @@ export declare class DataExportTools {
|
|
|
26
28
|
data?: any;
|
|
27
29
|
error?: string;
|
|
28
30
|
}>;
|
|
31
|
+
exportQueryToCSV(queryParams: {
|
|
32
|
+
query: string;
|
|
33
|
+
params?: any[];
|
|
34
|
+
include_headers?: boolean;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
status: string;
|
|
37
|
+
data?: any;
|
|
38
|
+
error?: string;
|
|
39
|
+
}>;
|
|
29
40
|
}
|
|
@@ -62,6 +62,33 @@ class DataExportTools {
|
|
|
62
62
|
.replace(/\0/g, "\\0");
|
|
63
63
|
return `'${escaped}'`;
|
|
64
64
|
}
|
|
65
|
+
escapeCsvValue(value) {
|
|
66
|
+
if (value === null || value === undefined)
|
|
67
|
+
return "";
|
|
68
|
+
const normalizedValue = value instanceof Date
|
|
69
|
+
? value.toISOString()
|
|
70
|
+
: Buffer.isBuffer(value)
|
|
71
|
+
? value.toString("base64")
|
|
72
|
+
: String(value);
|
|
73
|
+
if (/[",\r\n]/.test(normalizedValue)) {
|
|
74
|
+
return `"${normalizedValue.replace(/"/g, '""')}"`;
|
|
75
|
+
}
|
|
76
|
+
return normalizedValue;
|
|
77
|
+
}
|
|
78
|
+
rowsToCSV(rows, includeHeaders) {
|
|
79
|
+
if (rows.length === 0) {
|
|
80
|
+
return "";
|
|
81
|
+
}
|
|
82
|
+
const columns = Object.keys(rows[0]);
|
|
83
|
+
const csvRows = [];
|
|
84
|
+
if (includeHeaders) {
|
|
85
|
+
csvRows.push(columns.map((column) => this.escapeCsvValue(column)).join(","));
|
|
86
|
+
}
|
|
87
|
+
for (const row of rows) {
|
|
88
|
+
csvRows.push(columns.map((column) => this.escapeCsvValue(row[column])).join(","));
|
|
89
|
+
}
|
|
90
|
+
return `${csvRows.join("\n")}\n`;
|
|
91
|
+
}
|
|
65
92
|
/**
|
|
66
93
|
* Export table data to CSV format
|
|
67
94
|
*/
|
|
@@ -179,33 +206,7 @@ class DataExportTools {
|
|
|
179
206
|
},
|
|
180
207
|
};
|
|
181
208
|
}
|
|
182
|
-
|
|
183
|
-
let csv = "";
|
|
184
|
-
// Add headers if requested
|
|
185
|
-
if (include_headers) {
|
|
186
|
-
const headers = Object.keys(results[0]).join(",");
|
|
187
|
-
csv += headers + "\n";
|
|
188
|
-
}
|
|
189
|
-
// Add data rows
|
|
190
|
-
for (const row of results) {
|
|
191
|
-
const values = Object.values(row)
|
|
192
|
-
.map((value) => {
|
|
193
|
-
if (value === null)
|
|
194
|
-
return "";
|
|
195
|
-
if (typeof value === "string") {
|
|
196
|
-
// Escape quotes and wrap in quotes if contains comma or newline
|
|
197
|
-
if (value.includes(",") ||
|
|
198
|
-
value.includes("\n") ||
|
|
199
|
-
value.includes('"')) {
|
|
200
|
-
return `"${value.replace(/"/g, '""')}"`;
|
|
201
|
-
}
|
|
202
|
-
return value;
|
|
203
|
-
}
|
|
204
|
-
return String(value);
|
|
205
|
-
})
|
|
206
|
-
.join(",");
|
|
207
|
-
csv += values + "\n";
|
|
208
|
-
}
|
|
209
|
+
const csv = this.rowsToCSV(results, include_headers);
|
|
209
210
|
return {
|
|
210
211
|
status: "success",
|
|
211
212
|
data: {
|
|
@@ -221,5 +222,45 @@ class DataExportTools {
|
|
|
221
222
|
};
|
|
222
223
|
}
|
|
223
224
|
}
|
|
225
|
+
async exportQueryToCSV(queryParams) {
|
|
226
|
+
try {
|
|
227
|
+
const { query, params = [], include_headers = true, } = queryParams;
|
|
228
|
+
const queryValidation = this.security.validateQuery(query, this.security.hasExecutePermission());
|
|
229
|
+
if (!queryValidation.valid) {
|
|
230
|
+
return {
|
|
231
|
+
status: "error",
|
|
232
|
+
error: `Query validation failed: ${queryValidation.error}`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (queryValidation.queryType !== "SELECT") {
|
|
236
|
+
return {
|
|
237
|
+
status: "error",
|
|
238
|
+
error: "export_query_to_csv only accepts SELECT queries.",
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const paramValidation = this.security.validateParameters(params);
|
|
242
|
+
if (!paramValidation.valid) {
|
|
243
|
+
return {
|
|
244
|
+
status: "error",
|
|
245
|
+
error: `Parameter validation failed: ${paramValidation.error}`,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const results = await this.db.query(query, paramValidation.sanitizedParams, false);
|
|
249
|
+
const maskedResults = this.security.masking.processResults(results);
|
|
250
|
+
return {
|
|
251
|
+
status: "success",
|
|
252
|
+
data: {
|
|
253
|
+
csv: this.rowsToCSV(maskedResults, include_headers),
|
|
254
|
+
row_count: maskedResults.length,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
return {
|
|
260
|
+
status: "error",
|
|
261
|
+
error: error.message,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
}
|
|
224
265
|
}
|
|
225
266
|
exports.DataExportTools = DataExportTools;
|