#
tokens: 7646/50000 5/5 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .env.example
├── .gitignore
├── .npmignore
├── Dockerfile
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── smithery.yaml
├── src
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | [![smithery badge](https://smithery.ai/badge/@sussa3007/mysql-mcp)](https://smithery.ai/server/@sussa3007/mysql-mcp)
  2 | 
  3 | 
  4 | <a href="https://glama.ai/mcp/servers/@sussa3007/mysql-mcp">
  5 |   <img width="380" height="200" src="https://glama.ai/mcp/servers/@sussa3007/mysql-mcp/badge" />
  6 | </a>
  7 | 
  8 | # MySQL MCP Server
  9 | 
 10 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
 11 | 
 12 | A Model Context Protocol (MCP) server for MySQL databases that enables AI models to interact with MySQL databases through a structured interface.
 13 | 
 14 | ## Overview
 15 | 
 16 | The MySQL MCP Server provides a bridge between AI models and MySQL databases, allowing AI agents to query and analyze MySQL data. This implementation follows the Model Context Protocol specification and offers both web server and CLI modes of operation.
 17 | 
 18 | ## Features
 19 | 
 20 | - MySQL database connection management
 21 | - SQL query execution
 22 | - Table listing and structure inspection
 23 | - Database listing and selection
 24 | - Real-time status monitoring via SSE (Server-Sent Events)
 25 | - Web interface for testing MCP tools
 26 | - Support for both stdio and SSE transport methods
 27 | - Docker deployment ready
 28 | 
 29 | ## Installation
 30 | 
 31 | ```bash
 32 | # Global installation
 33 | npm install -g mysql-mcp
 34 | 
 35 | # Local installation
 36 | npm install mysql-mcp
 37 | ```
 38 | 
 39 | ## Using with AI Assistants
 40 | 
 41 | ### Using the Published Server on Smithery.ai
 42 | 
 43 | The MySQL MCP Server is published on Smithery.ai and can be easily used with various AI assistants:
 44 | 
 45 | 1. **Access the server**: Visit [https://smithery.ai/server/@sussa3007/mysql-mcp](https://smithery.ai/server/@sussa3007/mysql-mcp)
 46 | 
 47 | 2. **Configure the server**:
 48 | 
 49 |    - Set your MySQL database connection details:
 50 |      - MYSQL_HOST
 51 |      - MYSQL_PORT
 52 |      - MYSQL_USER
 53 |      - MYSQL_PASSWORD
 54 |      - MYSQL_DATABASE
 55 |      - MYSQL_READONLY (optional, set to True for read-only access)
 56 | 
 57 | 3. **Connect with supported AI platforms**:
 58 | 
 59 |    - Anthropic Claude
 60 |    - Cursor AI
 61 |    - Windsurf
 62 |    - Cline
 63 |    - Witsy
 64 |    - Enconvo
 65 |    - Goose
 66 | 
 67 | 4. **Authentication**: Login to Smithery.ai to save your configuration and generate authentication tokens.
 68 | 
 69 | 5. **Use in AI prompts**: Once connected, you can utilize MySQL tools in your AI conversations by asking the assistant to perform database operations.
 70 | 
 71 | ### Using After Local Installation
 72 | 
 73 | To use a locally developed version:
 74 | 
 75 | 1. Run `npm link` in your project directory
 76 | 2. Configure your settings file as follows:
 77 | 
 78 | ```json
 79 | {
 80 |   "mcpServers": {
 81 |     "mysql": {
 82 |       "command": "node",
 83 |       "args": ["mysql-mcp"],
 84 |       "env": {
 85 |         "MYSQL_HOST": "localhost",
 86 |         "MYSQL_PORT": "3306",
 87 |         "MYSQL_USER": "root",
 88 |         "MYSQL_PASSWORD": "your_password",
 89 |         "MYSQL_DATABASE": "your_database",
 90 |         "MYSQL_READONLY": "true"
 91 |       }
 92 |     }
 93 |   }
 94 | }
 95 | ```
 96 | 
 97 | ## Tools
 98 | 
 99 | ### status
100 | 
101 | Check the current database connection status.
102 | 
103 | - **Inputs**: No parameters required
104 | - **Returns**: Connection status information, including host, port, database, and username if connected.
105 | 
106 | ### connect
107 | 
108 | Connect to a MySQL database.
109 | 
110 | - **Inputs**:
111 |   - host (optional string): Database server hostname or IP address
112 |   - port (optional string): Database server port
113 |   - user (optional string): Database username
114 |   - password (optional string): Database password
115 |   - database (optional string): Database name to connect to
116 | - **Returns**: Connection success message or error details.
117 | 
118 | ### disconnect
119 | 
120 | Close the current MySQL database connection.
121 | 
122 | - **Inputs**: No parameters required
123 | - **Returns**: Disconnection success message or error details.
124 | 
125 | ### query
126 | 
127 | Execute an SQL query on the connected database.
128 | 
129 | - **Inputs**:
130 |   - sql (string): SQL query to execute
131 |   - params (optional array): Parameters for prepared statements
132 | - **Returns**: Query results as JSON or error message.
133 | 
134 | ### list_tables
135 | 
136 | Get a list of tables in the current database.
137 | 
138 | - **Inputs**: No parameters required
139 | - **Returns**: List of table names in the current database.
140 | 
141 | ### describe_table
142 | 
143 | Get the structure of a specific table.
144 | 
145 | - **Inputs**:
146 |   - table (string): Name of the table to describe
147 | - **Returns**: Table structure details including columns, types, keys, and other attributes.
148 | 
149 | ### list_databases
150 | 
151 | Get a list of all accessible databases on the server.
152 | 
153 | - **Inputs**: No parameters required
154 | - **Returns**: List of database names available on the server.
155 | 
156 | ### use_database
157 | 
158 | Switch to a different database.
159 | 
160 | - **Inputs**:
161 |   - database (string): Name of the database to switch to
162 | - **Returns**: Confirmation message or error details.
163 | 
164 | ## Keywords
165 | 
166 | mysql, mcp, database, ai, model context protocol
167 | 
168 | ## License
169 | 
170 | MIT
171 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "esModuleInterop": true,
 7 |     "forceConsistentCasingInFileNames": true,
 8 |     "strict": true,
 9 |     "skipLibCheck": true,
10 |     "outDir": "dist",
11 |     "rootDir": "src",
12 |     "sourceMap": true,
13 |     "declaration": true
14 |   },
15 |   "include": ["src/**/*"],
16 |   "exclude": ["node_modules", "dist"]
17 | }
18 | 
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | # Smithery.ai configuration
 2 | startCommand:
 3 |   type: stdio
 4 |   configSchema:
 5 |     # JSON Schema defining the configuration options for the MCP.
 6 |     type: object
 7 |     properties:
 8 |       MYSQL_HOST:
 9 |         type: string
10 |         description: MySQL server address
11 |       MYSQL_PORT:
12 |         type: string
13 |         description: MySQL server port
14 |       MYSQL_USER:
15 |         type: string
16 |         description: MySQL username
17 |       MYSQL_PASSWORD:
18 |         type: string
19 |         description: MySQL password
20 |       MYSQL_DATABASE:
21 |         type: string
22 |         description: Initial database
23 |       MYSQL_READONLY:
24 |         type: boolean
25 |         description: Read-only mode, set true to enable
26 |   commandFunction:
27 |     # A function that produces the CLI command to start the MCP on stdio.
28 |     |-
29 |     (config) => ({
30 |       "command": "node",
31 |       "args": [
32 |         "dist/index.js"
33 |       ],
34 |       "env": {
35 |         MYSQL_HOST: config.MYSQL_HOST,
36 |         MYSQL_PORT: config.MYSQL_PORT,
37 |         MYSQL_USER: config.MYSQL_USER,
38 |         MYSQL_PASSWORD: config.MYSQL_PASSWORD,
39 |         MYSQL_DATABASE: config.MYSQL_DATABASE,
40 |         MYSQL_READONLY: config.MYSQL_READONLY.toString()
41 |       }
42 |     })
43 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mysql-mcp-connect",
 3 |   "version": "0.1.1",
 4 |   "description": "MySQL MCP 서버 - Model Context Protocol로 MySQL 데이터베이스에 접근하는 도구",
 5 |   "main": "dist/index.js",
 6 |   "types": "dist/index.d.ts",
 7 |   "bin": {
 8 |     "mysql-mcp": "./dist/index.js"
 9 |   },
10 |   "files": [
11 |     "dist",
12 |     "bin",
13 |     "public"
14 |   ],
15 |   "scripts": {
16 |     "start": "node dist/index.js",
17 |     "dev": "ts-node src/index.ts",
18 |     "build": "tsc",
19 |     "stdio": "node dist/index.js --stdio",
20 |     "stdio:dev": "ts-node src/index.ts --stdio",
21 |     "prepublishOnly": "npm run build"
22 |   },
23 |   "keywords": [
24 |     "mcp",
25 |     "mysql",
26 |     "cursor",
27 |     "model-context-protocol",
28 |     "database",
29 |     "ai",
30 |     "tool"
31 |   ],
32 |   "author": "sussa3007",
33 |   "license": "MIT",
34 |   "dependencies": {
35 |     "@modelcontextprotocol/sdk": "^1.7.0",
36 |     "cors": "^2.8.5",
37 |     "dotenv": "^16.4.7",
38 |     "express": "^4.18.2",
39 |     "express-sse": "^0.5.3",
40 |     "mysql2": "^3.14.0",
41 |     "zod": "^3.24.2"
42 |   },
43 |   "devDependencies": {
44 |     "@types/content-type": "^1.1.8",
45 |     "@types/cors": "^2.8.13",
46 |     "@types/express": "^4.17.17",
47 |     "@types/node": "^20.3.1",
48 |     "ts-node": "^10.9.1",
49 |     "typescript": "^5.1.3"
50 |   },
51 |   "engines": {
52 |     "node": ">=14.0.0"
53 |   }
54 | }
55 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
  2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  3 | import {
  4 |   CallToolRequestSchema,
  5 |   ListToolsRequestSchema
  6 | } from "@modelcontextprotocol/sdk/types.js";
  7 | import { createConnection, Connection, RowDataPacket } from "mysql2/promise";
  8 | 
  9 | // Set up logging
 10 | const logger = {
 11 |   info: (message: string) => console.log(`[INFO] ${message}`),
 12 |   error: (message: string) => console.error(`[ERROR] ${message}`),
 13 |   debug: (message: string) => console.debug(`[DEBUG] ${message}`)
 14 | };
 15 | 
 16 | // Query type enum for categorizing SQL operations
 17 | enum QueryType {
 18 |   SELECT = "SELECT",
 19 |   INSERT = "INSERT",
 20 |   UPDATE = "UPDATE",
 21 |   DELETE = "DELETE",
 22 |   CREATE = "CREATE",
 23 |   DROP = "DROP",
 24 |   ALTER = "ALTER",
 25 |   TRUNCATE = "TRUNCATE",
 26 |   USE = "USE",
 27 |   SHOW = "SHOW",
 28 |   DESCRIBE = "DESCRIBE",
 29 |   UNKNOWN = "UNKNOWN"
 30 | }
 31 | 
 32 | // Determine if query type is a write operation
 33 | function isWriteOperation(queryType: QueryType): boolean {
 34 |   const writeOperations = [
 35 |     QueryType.INSERT,
 36 |     QueryType.UPDATE,
 37 |     QueryType.DELETE,
 38 |     QueryType.CREATE,
 39 |     QueryType.DROP,
 40 |     QueryType.ALTER,
 41 |     QueryType.TRUNCATE
 42 |   ];
 43 |   return writeOperations.includes(queryType);
 44 | }
 45 | 
 46 | // Get query type from SQL query
 47 | function getQueryType(query: string): QueryType {
 48 |   const firstWord = query.trim().split(/\s+/)[0].toUpperCase();
 49 |   return Object.values(QueryType).includes(firstWord as QueryType)
 50 |     ? (firstWord as QueryType)
 51 |     : QueryType.UNKNOWN;
 52 | }
 53 | 
 54 | // Global connection state
 55 | let connection: Connection | null = null;
 56 | let connectionConfig = {
 57 |   host: process.env.MYSQL_HOST || "localhost",
 58 |   port: parseInt(process.env.MYSQL_PORT || "3306", 10),
 59 |   user: process.env.MYSQL_USER || "root",
 60 |   password: process.env.MYSQL_PASSWORD || "",
 61 |   database: process.env.MYSQL_DATABASE || "",
 62 |   readonly: process.env.MYSQL_READONLY === "true" || false
 63 | };
 64 | 
 65 | // Initialize MCP server
 66 | const server = new Server(
 67 |   {
 68 |     name: "mysql-mcp",
 69 |     version: "0.1.0"
 70 |   },
 71 |   {
 72 |     capabilities: {
 73 |       tools: {}
 74 |     }
 75 |   }
 76 | );
 77 | 
 78 | // Helper function to get current connection or throw error if not connected
 79 | async function getConnection(): Promise<Connection> {
 80 |   if (!connection) {
 81 |     throw new Error(
 82 |       "Database not connected. Please use the 'connect' tool first."
 83 |     );
 84 |   }
 85 |   return connection;
 86 | }
 87 | 
 88 | // Connect to MySQL with current config
 89 | async function connectToDatabase(
 90 |   config = connectionConfig
 91 | ): Promise<Connection> {
 92 |   try {
 93 |     if (connection) {
 94 |       logger.info("Closing existing connection before creating a new one");
 95 |       await connection.end();
 96 |     }
 97 | 
 98 |     connection = await createConnection({
 99 |       host: config.host,
100 |       port: config.port,
101 |       user: config.user,
102 |       password: config.password,
103 |       database: config.database,
104 |       multipleStatements: true // Allow multiple statements in one query
105 |     });
106 | 
107 |     connectionConfig = config;
108 |     logger.info(`Connected to MySQL database at ${config.host}:${config.port}`);
109 |     return connection;
110 |   } catch (error) {
111 |     logger.error(`Failed to connect to database: ${error}`);
112 |     throw error;
113 |   }
114 | }
115 | 
116 | // Validate query against readonly mode
117 | async function executeQuery(sql: string, params: any[] = []): Promise<any> {
118 |   const conn = await getConnection();
119 | 
120 |   // Check if in readonly mode and validate query type
121 |   if (connectionConfig.readonly) {
122 |     const queryType = getQueryType(sql);
123 |     if (isWriteOperation(queryType)) {
124 |       throw new Error(
125 |         "Server is in read-only mode. Write operations are not allowed."
126 |       );
127 |     }
128 |   }
129 | 
130 |   // Execute the query
131 |   const [rows] = await conn.query(sql, params);
132 |   return rows;
133 | }
134 | 
135 | // Define available tools
136 | server.setRequestHandler(ListToolsRequestSchema, async () => {
137 |   return {
138 |     tools: [
139 |       {
140 |         name: "status",
141 |         description: "Check the current database connection status.",
142 |         inputSchema: {
143 |           type: "object",
144 |           properties: {
145 |             random_string: {
146 |               type: "string",
147 |               description: "Dummy parameter for no-parameter tools"
148 |             }
149 |           },
150 |           required: ["random_string"]
151 |         }
152 |       },
153 |       {
154 |         name: "connect",
155 |         description: "Connect to a MySQL database.",
156 |         inputSchema: {
157 |           type: "object",
158 |           properties: {
159 |             host: {
160 |               type: "string",
161 |               description: "Database server hostname or IP address"
162 |             },
163 |             port: { type: "string", description: "Database server port" },
164 |             user: { type: "string", description: "Database username" },
165 |             password: { type: "string", description: "Database password" },
166 |             database: {
167 |               type: "string",
168 |               description: "Database name to connect to"
169 |             }
170 |           }
171 |         }
172 |       },
173 |       {
174 |         name: "disconnect",
175 |         description: "Close the current MySQL database connection.",
176 |         inputSchema: {
177 |           type: "object",
178 |           properties: {
179 |             random_string: {
180 |               type: "string",
181 |               description: "Dummy parameter for no-parameter tools"
182 |             }
183 |           },
184 |           required: ["random_string"]
185 |         }
186 |       },
187 |       {
188 |         name: "query",
189 |         description: "Execute an SQL query on the connected database.",
190 |         inputSchema: {
191 |           type: "object",
192 |           properties: {
193 |             sql: { type: "string", description: "SQL query to execute" },
194 |             params: {
195 |               type: "array",
196 |               description: "Parameters for prepared statements",
197 |               items: { type: "string" }
198 |             }
199 |           },
200 |           required: ["sql"]
201 |         }
202 |       },
203 |       {
204 |         name: "list_tables",
205 |         description: "Get a list of tables in the current database.",
206 |         inputSchema: {
207 |           type: "object",
208 |           properties: {
209 |             random_string: {
210 |               type: "string",
211 |               description: "Dummy parameter for no-parameter tools"
212 |             }
213 |           },
214 |           required: ["random_string"]
215 |         }
216 |       },
217 |       {
218 |         name: "describe_table",
219 |         description: "Get the structure of a specific table.",
220 |         inputSchema: {
221 |           type: "object",
222 |           properties: {
223 |             table: {
224 |               type: "string",
225 |               description: "Name of the table to describe"
226 |             }
227 |           },
228 |           required: ["table"]
229 |         }
230 |       },
231 |       {
232 |         name: "list_databases",
233 |         description: "Get a list of all accessible databases on the server.",
234 |         inputSchema: {
235 |           type: "object",
236 |           properties: {
237 |             random_string: {
238 |               type: "string",
239 |               description: "Dummy parameter for no-parameter tools"
240 |             }
241 |           },
242 |           required: ["random_string"]
243 |         }
244 |       },
245 |       {
246 |         name: "use_database",
247 |         description: "Switch to a different database.",
248 |         inputSchema: {
249 |           type: "object",
250 |           properties: {
251 |             database: {
252 |               type: "string",
253 |               description: "Name of the database to switch to"
254 |             }
255 |           },
256 |           required: ["database"]
257 |         }
258 |       },
259 |       {
260 |         name: "set_readonly",
261 |         description: "Enable or disable read-only mode",
262 |         inputSchema: {
263 |           type: "object",
264 |           properties: {
265 |             readonly: {
266 |               type: "boolean",
267 |               description:
268 |                 "Set to true to enable read-only mode, false to disable"
269 |             }
270 |           },
271 |           required: ["readonly"]
272 |         }
273 |       }
274 |     ]
275 |   };
276 | });
277 | 
278 | // Handle tool calls
279 | server.setRequestHandler(CallToolRequestSchema, async (request) => {
280 |   try {
281 |     switch (request.params.name) {
282 |       case "status": {
283 |         if (!connection) {
284 |           return {
285 |             content: [
286 |               {
287 |                 type: "text",
288 |                 text: JSON.stringify(
289 |                   {
290 |                     connected: false,
291 |                     message:
292 |                       "Database not connected. Need to use 'connect' tool after checking current environment variable information. The password is sensitive information and is stored in an environment variable, so it is not required in the request parameters.",
293 |                     host: connectionConfig.host,
294 |                     port: connectionConfig.port,
295 |                     user: connectionConfig.user,
296 |                     database: connectionConfig.database,
297 |                     readonly: connectionConfig.readonly
298 |                   },
299 |                   null,
300 |                   2
301 |                 )
302 |               }
303 |             ],
304 |             isError: false
305 |           };
306 |         }
307 | 
308 |         return {
309 |           content: [
310 |             {
311 |               type: "text",
312 |               text: JSON.stringify(
313 |                 {
314 |                   connected: true,
315 |                   host: connectionConfig.host,
316 |                   port: connectionConfig.port,
317 |                   user: connectionConfig.user,
318 |                   database: connectionConfig.database,
319 |                   threadId: connection.threadId,
320 |                   readonly: connectionConfig.readonly
321 |                 },
322 |                 null,
323 |                 2
324 |               )
325 |             }
326 |           ],
327 |           isError: false
328 |         };
329 |       }
330 | 
331 |       case "connect": {
332 |         const args = request.params.arguments || {};
333 |         const newConfig = {
334 |           host: (args.host as string) || connectionConfig.host,
335 |           port: parseInt(
336 |             (args.port as string) || connectionConfig.port.toString()
337 |           ),
338 |           user: (args.user as string) || connectionConfig.user,
339 |           password: (args.password as string) || connectionConfig.password,
340 |           database: (args.database as string) || connectionConfig.database,
341 |           readonly: connectionConfig.readonly // Maintain existing readonly setting
342 |         };
343 | 
344 |         try {
345 |           await connectToDatabase(newConfig);
346 |         } catch (error) {
347 |           return {
348 |             content: [
349 |               {
350 |                 type: "text",
351 |                 text: JSON.stringify(
352 |                   {
353 |                     connected: false,
354 |                     message:
355 |                       "Database connection failed, please request with new information or check the environment variable information."
356 |                   },
357 |                   null,
358 |                   2
359 |                 )
360 |               }
361 |             ],
362 |             isError: true
363 |           };
364 |         }
365 | 
366 |         return {
367 |           content: [
368 |             {
369 |               type: "text",
370 |               text: `Successfully connected to MySQL at ${newConfig.host}:${
371 |                 newConfig.port
372 |               }, database: ${newConfig.database}, read-only mode: ${
373 |                 newConfig.readonly ? "enabled" : "disabled"
374 |               }`
375 |             }
376 |           ],
377 |           isError: false
378 |         };
379 |       }
380 | 
381 |       case "disconnect": {
382 |         if (!connection) {
383 |           return {
384 |             content: [{ type: "text", text: "Not connected to any database" }],
385 |             isError: false
386 |           };
387 |         }
388 | 
389 |         await connection.end();
390 |         connection = null;
391 | 
392 |         return {
393 |           content: [
394 |             {
395 |               type: "text",
396 |               text: "Successfully disconnected from database"
397 |             }
398 |           ],
399 |           isError: false
400 |         };
401 |       }
402 | 
403 |       case "query": {
404 |         try {
405 |           const sql = request.params.arguments?.sql as string;
406 |           const params = (request.params.arguments?.params as any[]) || [];
407 | 
408 |           // Execute query with validation
409 |           const rows = await executeQuery(sql, params);
410 | 
411 |           return {
412 |             content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
413 |             isError: false
414 |           };
415 |         } catch (error) {
416 |           return {
417 |             content: [
418 |               {
419 |                 type: "text",
420 |                 text:
421 |                   error instanceof Error
422 |                     ? error.message
423 |                     : "Unknown error occurred"
424 |               }
425 |             ],
426 |             isError: true
427 |           };
428 |         }
429 |       }
430 | 
431 |       case "list_tables": {
432 |         try {
433 |           const rows = await executeQuery("SHOW TABLES");
434 |           return {
435 |             content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
436 |             isError: false
437 |           };
438 |         } catch (error) {
439 |           return {
440 |             content: [
441 |               {
442 |                 type: "text",
443 |                 text:
444 |                   error instanceof Error
445 |                     ? error.message
446 |                     : "Unknown error occurred"
447 |               }
448 |             ],
449 |             isError: true
450 |           };
451 |         }
452 |       }
453 | 
454 |       case "describe_table": {
455 |         try {
456 |           const tableName = request.params.arguments?.table as string;
457 | 
458 |           if (!tableName) {
459 |             throw new Error("Table name is required");
460 |           }
461 | 
462 |           const rows = await executeQuery("DESCRIBE ??", [tableName]);
463 | 
464 |           return {
465 |             content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
466 |             isError: false
467 |           };
468 |         } catch (error) {
469 |           return {
470 |             content: [
471 |               {
472 |                 type: "text",
473 |                 text:
474 |                   error instanceof Error
475 |                     ? error.message
476 |                     : "Unknown error occurred"
477 |               }
478 |             ],
479 |             isError: true
480 |           };
481 |         }
482 |       }
483 | 
484 |       case "list_databases": {
485 |         try {
486 |           const rows = await executeQuery("SHOW DATABASES");
487 |           return {
488 |             content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
489 |             isError: false
490 |           };
491 |         } catch (error) {
492 |           return {
493 |             content: [
494 |               {
495 |                 type: "text",
496 |                 text:
497 |                   error instanceof Error
498 |                     ? error.message
499 |                     : "Unknown error occurred"
500 |               }
501 |             ],
502 |             isError: true
503 |           };
504 |         }
505 |       }
506 | 
507 |       case "use_database": {
508 |         try {
509 |           const dbName = request.params.arguments?.database as string;
510 | 
511 |           if (!dbName) {
512 |             throw new Error("Database name is required");
513 |           }
514 | 
515 |           await executeQuery("USE ??", [dbName]);
516 |           connectionConfig.database = dbName;
517 | 
518 |           return {
519 |             content: [
520 |               {
521 |                 type: "text",
522 |                 text: `Successfully switched to database: ${dbName}`
523 |               }
524 |             ],
525 |             isError: false
526 |           };
527 |         } catch (error) {
528 |           return {
529 |             content: [
530 |               {
531 |                 type: "text",
532 |                 text:
533 |                   error instanceof Error
534 |                     ? error.message
535 |                     : "Unknown error occurred"
536 |               }
537 |             ],
538 |             isError: true
539 |           };
540 |         }
541 |       }
542 | 
543 |       case "set_readonly": {
544 |         try {
545 |           const readonly = request.params.arguments?.readonly as boolean;
546 | 
547 |           if (readonly === undefined) {
548 |             throw new Error("readonly parameter is required");
549 |           }
550 | 
551 |           connectionConfig.readonly = readonly;
552 | 
553 |           return {
554 |             content: [
555 |               {
556 |                 type: "text",
557 |                 text: `Read-only mode ${readonly ? "enabled" : "disabled"}`
558 |               }
559 |             ],
560 |             isError: false
561 |           };
562 |         } catch (error) {
563 |           return {
564 |             content: [
565 |               {
566 |                 type: "text",
567 |                 text:
568 |                   error instanceof Error
569 |                     ? error.message
570 |                     : "Unknown error occurred"
571 |               }
572 |             ],
573 |             isError: true
574 |           };
575 |         }
576 |       }
577 | 
578 |       default:
579 |         throw new Error(`Unknown tool: ${request.params.name}`);
580 |     }
581 |   } catch (error) {
582 |     logger.error(`Error handling request: ${error}`);
583 |     return {
584 |       content: [
585 |         {
586 |           type: "text",
587 |           text:
588 |             error instanceof Error ? error.message : "Unknown error occurred"
589 |         }
590 |       ],
591 |       isError: true
592 |     };
593 |   }
594 | });
595 | 
596 | async function main() {
597 |   logger.info("Starting MySQL MCP server...");
598 |   const transport = new StdioServerTransport();
599 |   await server.connect(transport);
600 |   logger.info("Server connected to transport");
601 | }
602 | 
603 | // Handle termination signals
604 | process.once("SIGTERM", () => {
605 |   logger.info("SIGTERM received, closing server");
606 | 
607 |   // Close database connection if open
608 |   if (connection) {
609 |     connection.end().catch((err) => {
610 |       logger.error(`Error closing database connection: ${err}`);
611 |     });
612 |   }
613 | 
614 |   server.close().then(() => {
615 |     logger.info("Server closed, exiting");
616 |     process.exit(0);
617 |   });
618 | });
619 | 
620 | process.once("SIGINT", () => {
621 |   logger.info("SIGINT received, closing server");
622 | 
623 |   // Close database connection if open
624 |   if (connection) {
625 |     connection.end().catch((err) => {
626 |       logger.error(`Error closing database connection: ${err}`);
627 |     });
628 |   }
629 | 
630 |   server.close().then(() => {
631 |     logger.info("Server closed, exiting");
632 |     process.exit(0);
633 |   });
634 | });
635 | 
636 | main().catch((error) => {
637 |   logger.error(
638 |     error instanceof Error ? error.message : "Unknown error occurred"
639 |   );
640 |   process.exit(1);
641 | });
642 | 
```