@praise25/mcp-server-mysql 1.0.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.
Binary file
package/dist/evals.js ADDED
@@ -0,0 +1,32 @@
1
+ import { openai } from "@ai-sdk/openai";
2
+ import { grade } from "mcp-evals";
3
+ const mysqlQueryToolEval = {
4
+ name: 'mysql_query Tool Evaluation',
5
+ description: 'Evaluates the MySQL query execution functionality',
6
+ run: async () => {
7
+ const result = await grade(openai("gpt-4"), "Please execute the following SQL query and return the results: SELECT * FROM employees WHERE status='ACTIVE';");
8
+ return JSON.parse(result);
9
+ }
10
+ };
11
+ const mysqlQueryGenerationEval = {
12
+ name: 'mysql_query Tool Generation Evaluation',
13
+ description: 'Evaluates the MySQL query tool for correct SQL generation and execution',
14
+ run: async () => {
15
+ const result = await grade(openai("gpt-4"), "Use the mysql_query tool to select all rows from the 'users' table where isActive = 1. Provide the SQL query in the correct format.");
16
+ return JSON.parse(result);
17
+ }
18
+ };
19
+ const mysqlQueryColumnsEval = {
20
+ name: 'mysql_query Columns Evaluation',
21
+ description: 'Evaluates the mysql_query tool for column selection',
22
+ run: async () => {
23
+ const result = await grade(openai("gpt-4"), "Please provide a SQL query to retrieve the id, name, and email columns for all records in the users table.");
24
+ return JSON.parse(result);
25
+ }
26
+ };
27
+ const config = {
28
+ model: openai("gpt-4"),
29
+ evals: [mysqlQueryToolEval, mysqlQueryGenerationEval, mysqlQueryColumnsEval]
30
+ };
31
+ export default config;
32
+ export const evals = [mysqlQueryToolEval, mysqlQueryGenerationEval, mysqlQueryColumnsEval];
package/dist/index.js ADDED
@@ -0,0 +1,405 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
+ import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
6
+ import { z } from "zod";
7
+ import { log } from "./src/utils/index.js";
8
+ import { ALLOW_DELETE_OPERATION, ALLOW_DDL_OPERATION, ALLOW_INSERT_OPERATION, ALLOW_UPDATE_OPERATION, SCHEMA_DELETE_PERMISSIONS, SCHEMA_DDL_PERMISSIONS, SCHEMA_INSERT_PERMISSIONS, SCHEMA_UPDATE_PERMISSIONS, isMultiDbMode, mcpConfig as config, MCP_VERSION as version, IS_REMOTE_MCP, REMOTE_SECRET_KEY, PORT, } from "./src/config/index.js";
9
+ import { safeExit, getPool, executeQuery, executeVerifiedQuery, poolPromise, } from "./src/db/index.js";
10
+ import path from 'path';
11
+ import express from "express";
12
+ import { fileURLToPath } from 'url';
13
+ import crypto from 'crypto';
14
+ import helmet from 'helmet';
15
+ log("info", `Starting MySQL MCP server v${version}...`);
16
+ const toolVersion = `MySQL MCP Server [v${process.env.npm_package_version}]`;
17
+ let toolDescription = `[${toolVersion}] Run SQL queries against MySQL database`;
18
+ if (isMultiDbMode) {
19
+ toolDescription += " (Multi-DB mode enabled)";
20
+ }
21
+ if (ALLOW_INSERT_OPERATION ||
22
+ ALLOW_UPDATE_OPERATION ||
23
+ ALLOW_DELETE_OPERATION ||
24
+ ALLOW_DDL_OPERATION) {
25
+ toolDescription += " with support for:";
26
+ if (ALLOW_INSERT_OPERATION) {
27
+ toolDescription += " INSERT,";
28
+ }
29
+ if (ALLOW_UPDATE_OPERATION) {
30
+ toolDescription += " UPDATE,";
31
+ }
32
+ if (ALLOW_DELETE_OPERATION) {
33
+ toolDescription += " DELETE,";
34
+ }
35
+ if (ALLOW_DDL_OPERATION) {
36
+ toolDescription += " DDL,";
37
+ }
38
+ toolDescription = toolDescription.replace(/,$/, "") + " and READ operations";
39
+ if (Object.keys(SCHEMA_INSERT_PERMISSIONS).length > 0 ||
40
+ Object.keys(SCHEMA_UPDATE_PERMISSIONS).length > 0 ||
41
+ Object.keys(SCHEMA_DELETE_PERMISSIONS).length > 0 ||
42
+ Object.keys(SCHEMA_DDL_PERMISSIONS).length > 0) {
43
+ toolDescription += " (Schema-specific permissions enabled)";
44
+ }
45
+ }
46
+ else {
47
+ toolDescription += " (READ-ONLY)";
48
+ }
49
+ log("info", "MySQL Configuration:", JSON.stringify({
50
+ ...(process.env.MYSQL_SOCKET_PATH
51
+ ? {
52
+ socketPath: process.env.MYSQL_SOCKET_PATH,
53
+ connectionType: "Unix Socket",
54
+ }
55
+ : {
56
+ host: process.env.MYSQL_HOST || "127.0.0.1",
57
+ port: process.env.MYSQL_PORT || "3306",
58
+ connectionType: "TCP/IP",
59
+ }),
60
+ user: config.mysql.user,
61
+ password: config.mysql.password ? "******" : "not set",
62
+ database: config.mysql.database || "MULTI_DB_MODE",
63
+ ssl: process.env.MYSQL_SSL === "true" ? "enabled" : "disabled",
64
+ multiDbMode: isMultiDbMode ? "enabled" : "disabled",
65
+ }, null, 2));
66
+ export const configSchema = z.object({
67
+ debug: z.boolean().default(false).describe("Enable debug logging"),
68
+ });
69
+ export default function createMcpServer({ sessionId, config, }) {
70
+ const server = new Server({
71
+ name: "MySQL MCP Server",
72
+ version: process.env.npm_package_version || "1.0.0",
73
+ }, {
74
+ capabilities: {
75
+ resources: {},
76
+ tools: {
77
+ mysql_query: {
78
+ description: toolDescription,
79
+ inputSchema: {
80
+ type: "object",
81
+ properties: {
82
+ sql: {
83
+ type: "string",
84
+ description: "The SQL query to execute, with '?' as placeholders for parameters.",
85
+ },
86
+ params: {
87
+ type: "array",
88
+ description: "An array of parameters to bind to the placeholders in the SQL query.",
89
+ items: {
90
+ type: "string",
91
+ }
92
+ }
93
+ },
94
+ required: ["sql", "params"],
95
+ },
96
+ },
97
+ },
98
+ },
99
+ });
100
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
101
+ try {
102
+ log("info", "Handling ListResourcesRequest");
103
+ const connectionInfo = process.env.MYSQL_SOCKET_PATH
104
+ ? `socket: ${process.env.MYSQL_SOCKET_PATH}`
105
+ : `host: ${process.env.MYSQL_HOST || "localhost"}, port: ${process.env.MYSQL_PORT || 3306}`;
106
+ log("info", `Connection info: ${connectionInfo}`);
107
+ const tablesQuery = `
108
+ SELECT
109
+ table_name as name,
110
+ table_schema as \`database\`,
111
+ table_comment as description,
112
+ table_rows as rowCount,
113
+ data_length as dataSize,
114
+ index_length as indexSize,
115
+ create_time as createTime,
116
+ update_time as updateTime
117
+ FROM
118
+ information_schema.tables
119
+ WHERE
120
+ table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
121
+ ORDER BY
122
+ table_schema, table_name
123
+ `;
124
+ const queryResult = (await executeVerifiedQuery(tablesQuery, []));
125
+ const tables = JSON.parse(queryResult.content[0].text);
126
+ log("info", `Found ${tables.length} tables`);
127
+ const resources = tables.map((table) => ({
128
+ uri: `mysql://tables/${table.name}`,
129
+ name: table.name,
130
+ title: `${table.database}.${table.name}`,
131
+ description: table.description ||
132
+ `Table ${table.name} in database ${table.database}`,
133
+ mimeType: "application/json",
134
+ }));
135
+ resources.push({
136
+ uri: "mysql://tables",
137
+ name: "Tables",
138
+ title: "MySQL Tables",
139
+ description: "List of all MySQL tables",
140
+ mimeType: "application/json",
141
+ });
142
+ return { resources };
143
+ }
144
+ catch (error) {
145
+ log("error", "Error in ListResourcesRequest handler:", error);
146
+ throw error;
147
+ }
148
+ });
149
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
150
+ try {
151
+ log("info", "Handling ReadResourceRequest:", request.params.uri);
152
+ const uriParts = request.params.uri.split("/");
153
+ const tableName = uriParts.pop();
154
+ const dbName = uriParts.length > 0 ? uriParts.pop() : null;
155
+ if (!tableName) {
156
+ throw new Error(`Invalid resource URI: ${request.params.uri}`);
157
+ }
158
+ let columnsQuery = "SELECT column_name, data_type FROM information_schema.columns WHERE table_name = ?";
159
+ let queryParams = [tableName];
160
+ if (dbName) {
161
+ columnsQuery += " AND table_schema = ?";
162
+ queryParams.push(dbName);
163
+ }
164
+ const results = (await executeQuery(columnsQuery, queryParams));
165
+ return {
166
+ contents: [
167
+ {
168
+ uri: request.params.uri,
169
+ mimeType: "application/json",
170
+ text: JSON.stringify(results, null, 2),
171
+ },
172
+ ],
173
+ };
174
+ }
175
+ catch (error) {
176
+ log("error", "Error in ReadResourceRequest handler:", error);
177
+ throw error;
178
+ }
179
+ });
180
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
181
+ try {
182
+ log("info", "Handling CallToolRequest:", request.params.name);
183
+ if (request.params.name !== "mysql_query") {
184
+ throw new Error(`Unknown tool: ${request.params.name}`);
185
+ }
186
+ const { sql, params } = request.params.arguments;
187
+ return await executeVerifiedQuery(sql, params);
188
+ }
189
+ catch (err) {
190
+ const error = err;
191
+ log("error", "Error in CallToolRequest handler:", error);
192
+ const message = process.env.DEBUG === 'true' ? error.message : "An internal error occurred while processing the tool call.";
193
+ return {
194
+ content: [{
195
+ type: "text",
196
+ text: `Error: ${message}`
197
+ }],
198
+ isError: true
199
+ };
200
+ }
201
+ });
202
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
203
+ log("info", "Handling ListToolsRequest");
204
+ const toolsResponse = {
205
+ tools: [
206
+ {
207
+ name: "mysql_query",
208
+ description: toolDescription,
209
+ inputSchema: {
210
+ type: "object",
211
+ properties: {
212
+ sql: {
213
+ type: "string",
214
+ description: "The SQL query to execute, with '?' as placeholders for parameters.",
215
+ },
216
+ params: {
217
+ type: "array",
218
+ description: "An array of parameters to bind to the placeholders in the SQL query.",
219
+ items: {
220
+ type: "string",
221
+ }
222
+ }
223
+ },
224
+ required: ["sql", "params"],
225
+ },
226
+ },
227
+ ],
228
+ };
229
+ log("info", "ListToolsRequest response:", JSON.stringify(toolsResponse, null, 2));
230
+ return toolsResponse;
231
+ });
232
+ (async () => {
233
+ try {
234
+ log("info", "Attempting to test database connection...");
235
+ const pool = await getPool();
236
+ const connection = await pool.getConnection();
237
+ log("info", "Database connection test successful");
238
+ connection.release();
239
+ }
240
+ catch (error) {
241
+ log("error", "Fatal error during server startup:", error);
242
+ safeExit(1);
243
+ }
244
+ })();
245
+ const shutdown = async (signal) => {
246
+ log("error", `Received ${signal}. Shutting down...`);
247
+ try {
248
+ if (poolPromise) {
249
+ const pool = await poolPromise;
250
+ await pool.end();
251
+ }
252
+ }
253
+ catch (err) {
254
+ log("error", "Error closing pool:", err);
255
+ throw err;
256
+ }
257
+ };
258
+ process.on("SIGINT", async () => {
259
+ try {
260
+ await shutdown("SIGINT");
261
+ process.exit(0);
262
+ }
263
+ catch (err) {
264
+ log("error", "Error during SIGINT shutdown:", err);
265
+ safeExit(1);
266
+ }
267
+ });
268
+ process.on("SIGTERM", async () => {
269
+ try {
270
+ await shutdown("SIGTERM");
271
+ process.exit(0);
272
+ }
273
+ catch (err) {
274
+ log("error", "Error during SIGTERM shutdown:", err);
275
+ safeExit(1);
276
+ }
277
+ });
278
+ process.on("uncaughtException", (error) => {
279
+ log("error", "Uncaught exception:", error);
280
+ safeExit(1);
281
+ });
282
+ process.on("unhandledRejection", (reason, promise) => {
283
+ log("error", "Unhandled rejection at:", promise, "reason:", reason);
284
+ safeExit(1);
285
+ });
286
+ return server;
287
+ }
288
+ const isMainModule = () => {
289
+ if (typeof require !== 'undefined' && require.main === module) {
290
+ return true;
291
+ }
292
+ if (typeof import.meta !== 'undefined' && import.meta.url && process.argv[1]) {
293
+ const currentModulePath = fileURLToPath(import.meta.url);
294
+ const mainScriptPath = path.resolve(process.argv[1]);
295
+ return currentModulePath === mainScriptPath;
296
+ }
297
+ return false;
298
+ };
299
+ if (isMainModule()) {
300
+ log("info", "Running in standalone mode");
301
+ if (IS_REMOTE_MCP && !REMOTE_SECRET_KEY) {
302
+ log("error", "FATAL: Server is configured for remote mode (IS_REMOTE_MCP=true) but no REMOTE_SECRET_KEY is provided.");
303
+ safeExit(1);
304
+ }
305
+ (async () => {
306
+ try {
307
+ const mcpServer = createMcpServer({ config: { debug: false } });
308
+ if (IS_REMOTE_MCP && REMOTE_SECRET_KEY?.length) {
309
+ const app = express();
310
+ app.use(helmet());
311
+ app.use(express.json());
312
+ app.post("/mcp", async (req, res) => {
313
+ const authHeader = req.get("Authorization");
314
+ let providedToken = "";
315
+ if (authHeader && authHeader.startsWith("Bearer ")) {
316
+ providedToken = authHeader.substring(7);
317
+ }
318
+ try {
319
+ const secretBuffer = Buffer.from(REMOTE_SECRET_KEY, 'utf8');
320
+ const providedTokenBuffer = Buffer.from(providedToken, 'utf8');
321
+ if (secretBuffer.length !== providedTokenBuffer.length || !crypto.timingSafeEqual(secretBuffer, providedTokenBuffer)) {
322
+ throw new Error("Invalid token");
323
+ }
324
+ }
325
+ catch {
326
+ console.error("Missing or invalid Authorization header");
327
+ res.status(401).json({
328
+ jsonrpc: "2.0",
329
+ error: {
330
+ code: -32603,
331
+ message: "Missing or invalid Authorization header",
332
+ },
333
+ id: null,
334
+ });
335
+ return;
336
+ }
337
+ try {
338
+ const server = mcpServer;
339
+ const transport = new StreamableHTTPServerTransport({
340
+ sessionIdGenerator: undefined,
341
+ });
342
+ res.on("close", () => {
343
+ log("info", "Request closed");
344
+ transport.close();
345
+ server.close();
346
+ });
347
+ await server.connect(transport);
348
+ await transport.handleRequest(req, res, req.body);
349
+ }
350
+ catch (error) {
351
+ log("error", "Error handling MCP request:", error);
352
+ if (!res.headersSent) {
353
+ res.status(500).json({
354
+ jsonrpc: "2.0",
355
+ error: {
356
+ code: -32603,
357
+ message: error.message,
358
+ },
359
+ id: null,
360
+ });
361
+ }
362
+ }
363
+ });
364
+ app.get("/mcp", async (req, res) => {
365
+ console.log("Received GET MCP request");
366
+ res.writeHead(405).end(JSON.stringify({
367
+ jsonrpc: "2.0",
368
+ error: {
369
+ code: -32000,
370
+ message: "Method not allowed.",
371
+ },
372
+ id: null,
373
+ }));
374
+ });
375
+ app.delete("/mcp", async (req, res) => {
376
+ console.log("Received DELETE MCP request");
377
+ res.writeHead(405).end(JSON.stringify({
378
+ jsonrpc: "2.0",
379
+ error: {
380
+ code: -32000,
381
+ message: "Method not allowed.",
382
+ },
383
+ id: null,
384
+ }));
385
+ });
386
+ app.listen(PORT, (error) => {
387
+ if (error) {
388
+ console.error("Failed to start server:", error);
389
+ process.exit(1);
390
+ }
391
+ console.log(`MCP Stateless Streamable HTTP Server listening on port ${PORT}`);
392
+ });
393
+ }
394
+ else {
395
+ const transport = new StdioServerTransport();
396
+ await mcpServer.connect(transport);
397
+ log("info", "Server started and listening on stdio");
398
+ }
399
+ }
400
+ catch (error) {
401
+ log("error", "Server error:", error);
402
+ safeExit(1);
403
+ }
404
+ })();
405
+ }
@@ -0,0 +1,51 @@
1
+ import * as dotenv from "dotenv";
2
+ import { parseSchemaPermissions } from "../utils/index.js";
3
+ export const MCP_VERSION = "2.0.2";
4
+ dotenv.config();
5
+ if (process.env.NODE_ENV === "test" && !process.env.MYSQL_DB) {
6
+ process.env.MYSQL_DB = "mcp_test_db";
7
+ }
8
+ export const ALLOW_INSERT_OPERATION = process.env.ALLOW_INSERT_OPERATION === "true";
9
+ export const ALLOW_UPDATE_OPERATION = process.env.ALLOW_UPDATE_OPERATION === "true";
10
+ export const ALLOW_DELETE_OPERATION = process.env.ALLOW_DELETE_OPERATION === "true";
11
+ export const ALLOW_DDL_OPERATION = process.env.ALLOW_DDL_OPERATION === "true";
12
+ export const SCHEMA_INSERT_PERMISSIONS = parseSchemaPermissions(process.env.SCHEMA_INSERT_PERMISSIONS);
13
+ export const SCHEMA_UPDATE_PERMISSIONS = parseSchemaPermissions(process.env.SCHEMA_UPDATE_PERMISSIONS);
14
+ export const SCHEMA_DELETE_PERMISSIONS = parseSchemaPermissions(process.env.SCHEMA_DELETE_PERMISSIONS);
15
+ export const SCHEMA_DDL_PERMISSIONS = parseSchemaPermissions(process.env.SCHEMA_DDL_PERMISSIONS);
16
+ export const IS_REMOTE_MCP = process.env.IS_REMOTE_MCP === "true";
17
+ export const REMOTE_SECRET_KEY = process.env.REMOTE_SECRET_KEY || "";
18
+ export const PORT = process.env.PORT || 3000;
19
+ export const MYSQL_MAX_QUERY_COMPLEXITY = Number(process.env.MYSQL_MAX_QUERY_COMPLEXITY) || 1000;
20
+ export const isMultiDbMode = !process.env.MYSQL_DB || process.env.MYSQL_DB.trim() === "";
21
+ export const mcpConfig = {
22
+ server: {
23
+ name: "@benborla29/mcp-server-mysql",
24
+ version: MCP_VERSION,
25
+ connectionTypes: ["stdio", "streamableHttp"],
26
+ },
27
+ mysql: {
28
+ ...(process.env.MYSQL_SOCKET_PATH
29
+ ? {
30
+ socketPath: process.env.MYSQL_SOCKET_PATH,
31
+ }
32
+ : {
33
+ host: process.env.MYSQL_HOST || "127.0.0.1",
34
+ port: Number(process.env.MYSQL_PORT || "3306"),
35
+ }),
36
+ user: process.env.MYSQL_USER || "root",
37
+ password: process.env.MYSQL_PASS,
38
+ database: process.env.MYSQL_DB || undefined,
39
+ connectionLimit: 10,
40
+ ...(process.env.MYSQL_SSL === "true"
41
+ ? {
42
+ ssl: {
43
+ rejectUnauthorized: process.env.MYSQL_SSL_REJECT_UNAUTHORIZED === "true",
44
+ },
45
+ }
46
+ : {}),
47
+ },
48
+ paths: {
49
+ schema: "schema",
50
+ },
51
+ };
@@ -0,0 +1,142 @@
1
+ import { performance } from "perf_hooks";
2
+ import { isMultiDbMode, MYSQL_MAX_QUERY_COMPLEXITY } from "./../config/index.js";
3
+ import { isDDLAllowedForSchema, isInsertAllowedForSchema, isUpdateAllowedForSchema, isDeleteAllowedForSchema, } from "./permissions.js";
4
+ import { getAllSchemasFromQuery, getQueryTypes, getAST, calculateComplexity } from "./utils.js";
5
+ import * as mysql2 from "mysql2/promise";
6
+ import { log } from "./../utils/index.js";
7
+ import { mcpConfig as config } from "./../config/index.js";
8
+ if (isMultiDbMode && process.env.MULTI_DB_WRITE_MODE !== "true") {
9
+ log("error", "Multi-DB mode detected - enabling read-only mode for safety");
10
+ }
11
+ const isTestEnvironment = process.env.NODE_ENV === "test" || process.env.VITEST;
12
+ function safeExit(code) {
13
+ if (!isTestEnvironment) {
14
+ process.exit(code);
15
+ }
16
+ else {
17
+ log("error", `[Test mode] Would have called process.exit(${code})`);
18
+ }
19
+ }
20
+ let poolPromise;
21
+ const getPool = () => {
22
+ if (!poolPromise) {
23
+ poolPromise = new Promise((resolve, reject) => {
24
+ try {
25
+ const pool = mysql2.createPool(config.mysql);
26
+ log("info", "MySQL pool created successfully");
27
+ resolve(pool);
28
+ }
29
+ catch (error) {
30
+ log("error", "Error creating MySQL pool:", error);
31
+ reject(error);
32
+ }
33
+ });
34
+ }
35
+ return poolPromise;
36
+ };
37
+ async function executeQuery(sql, params = []) {
38
+ let connection;
39
+ try {
40
+ const pool = await getPool();
41
+ connection = await pool.getConnection();
42
+ const result = await connection.query(sql, params);
43
+ return (Array.isArray(result) ? result[0] : result);
44
+ }
45
+ catch (error) {
46
+ log("error", "Error executing query:", error);
47
+ throw error;
48
+ }
49
+ finally {
50
+ if (connection) {
51
+ connection.release();
52
+ log("error", "Connection released");
53
+ }
54
+ }
55
+ }
56
+ async function executeVerifiedQuery(sql, params) {
57
+ let connection;
58
+ try {
59
+ const pool = await getPool();
60
+ connection = await pool.getConnection();
61
+ log("info", "Connection acquired for verified query");
62
+ const ast = getAST(sql);
63
+ const complexity = calculateComplexity(ast);
64
+ if (complexity > MYSQL_MAX_QUERY_COMPLEXITY) {
65
+ throw new Error(`Query is too complex to execute (complexity: ${complexity}, max: ${MYSQL_MAX_QUERY_COMPLEXITY}). Please simplify the query.`);
66
+ }
67
+ const queryTypes = await getQueryTypes(ast);
68
+ const schemas = getAllSchemasFromQuery(sql, ast);
69
+ const isUpdateOperation = queryTypes.some((type) => ["update"].includes(type));
70
+ const isInsertOperation = queryTypes.some((type) => ["insert"].includes(type));
71
+ const isDeleteOperation = queryTypes.some((type) => ["delete"].includes(type));
72
+ const isDDLOperation = queryTypes.some((type) => ["create", "alter", "drop", "truncate"].includes(type));
73
+ const isWriteOperation = isUpdateOperation || isInsertOperation || isDeleteOperation || isDDLOperation;
74
+ for (const schema of schemas) {
75
+ if (isInsertOperation && !isInsertAllowedForSchema(schema)) {
76
+ throw new Error(`INSERT operations are not allowed for schema '${schema || "default"}'.`);
77
+ }
78
+ if (isUpdateOperation && !isUpdateAllowedForSchema(schema)) {
79
+ throw new Error(`UPDATE operations are not allowed for schema '${schema || "default"}'.`);
80
+ }
81
+ if (isDeleteOperation && !isDeleteAllowedForSchema(schema)) {
82
+ throw new Error(`DELETE operations are not allowed for schema '${schema || "default"}'.`);
83
+ }
84
+ if (isDDLOperation && !isDDLAllowedForSchema(schema)) {
85
+ throw new Error(`DDL operations are not allowed for schema '${schema || "default"}'.`);
86
+ }
87
+ }
88
+ await connection.beginTransaction();
89
+ try {
90
+ const startTime = performance.now();
91
+ const [result] = await connection.query(sql, params);
92
+ const endTime = performance.now();
93
+ const duration = endTime - startTime;
94
+ await connection.commit();
95
+ let responseText = JSON.stringify(result, null, 2);
96
+ if (isWriteOperation) {
97
+ const resultHeader = result;
98
+ if (isInsertOperation) {
99
+ responseText = `Insert successful on the relevant schema(s). Affected rows: ${resultHeader.affectedRows}, Last insert ID: ${resultHeader.insertId}`;
100
+ }
101
+ else if (isUpdateOperation) {
102
+ responseText = `Update successful on the relevant schema(s). Affected rows: ${resultHeader.affectedRows}, Changed rows: ${resultHeader.changedRows || 0}`;
103
+ }
104
+ else if (isDeleteOperation) {
105
+ responseText = `Delete successful on the relevant schema(s). Affected rows: ${resultHeader.affectedRows}`;
106
+ }
107
+ else if (isDDLOperation) {
108
+ responseText = `DDL operation successful on the relevant schema(s).`;
109
+ }
110
+ }
111
+ return {
112
+ content: [
113
+ { type: "text", text: responseText },
114
+ { type: "text", text: `Query execution time: ${duration.toFixed(2)} ms` },
115
+ ],
116
+ isError: false,
117
+ };
118
+ }
119
+ catch (error) {
120
+ await connection.rollback();
121
+ throw error;
122
+ }
123
+ }
124
+ catch (error) {
125
+ const isSafeError = error instanceof Error && (error.message.includes("not allowed for schema") ||
126
+ error.message.includes("Query is too complex"));
127
+ const message = error instanceof Error ? error.message : String(error);
128
+ log("error", "Error in executeVerifiedQuery:", message);
129
+ const returnedMessage = process.env.DEBUG === 'true' || isSafeError ? message : "An internal server error occurred.";
130
+ return {
131
+ content: [{ type: "text", text: `Error: ${returnedMessage}` }],
132
+ isError: true,
133
+ };
134
+ }
135
+ finally {
136
+ if (connection) {
137
+ connection.release();
138
+ log("info", "Connection released");
139
+ }
140
+ }
141
+ }
142
+ export { isTestEnvironment, safeExit, executeQuery, getPool, executeVerifiedQuery, poolPromise, };
@@ -0,0 +1,34 @@
1
+ import { ALLOW_DELETE_OPERATION, ALLOW_DDL_OPERATION, ALLOW_INSERT_OPERATION, ALLOW_UPDATE_OPERATION, SCHEMA_DELETE_PERMISSIONS, SCHEMA_DDL_PERMISSIONS, SCHEMA_INSERT_PERMISSIONS, SCHEMA_UPDATE_PERMISSIONS, } from "../config/index.js";
2
+ function isInsertAllowedForSchema(schema) {
3
+ if (!schema) {
4
+ return ALLOW_INSERT_OPERATION;
5
+ }
6
+ return schema in SCHEMA_INSERT_PERMISSIONS
7
+ ? SCHEMA_INSERT_PERMISSIONS[schema]
8
+ : ALLOW_INSERT_OPERATION;
9
+ }
10
+ function isUpdateAllowedForSchema(schema) {
11
+ if (!schema) {
12
+ return ALLOW_UPDATE_OPERATION;
13
+ }
14
+ return schema in SCHEMA_UPDATE_PERMISSIONS
15
+ ? SCHEMA_UPDATE_PERMISSIONS[schema]
16
+ : ALLOW_UPDATE_OPERATION;
17
+ }
18
+ function isDeleteAllowedForSchema(schema) {
19
+ if (!schema) {
20
+ return ALLOW_DELETE_OPERATION;
21
+ }
22
+ return schema in SCHEMA_DELETE_PERMISSIONS
23
+ ? SCHEMA_DELETE_PERMISSIONS[schema]
24
+ : ALLOW_DELETE_OPERATION;
25
+ }
26
+ function isDDLAllowedForSchema(schema) {
27
+ if (!schema) {
28
+ return ALLOW_DDL_OPERATION;
29
+ }
30
+ return schema in SCHEMA_DDL_PERMISSIONS
31
+ ? SCHEMA_DDL_PERMISSIONS[schema]
32
+ : ALLOW_DDL_OPERATION;
33
+ }
34
+ export { isInsertAllowedForSchema, isUpdateAllowedForSchema, isDeleteAllowedForSchema, isDDLAllowedForSchema, };