@cainli/mcp-server-mysql 2.0.8 → 2.0.10

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/README.md CHANGED
@@ -49,6 +49,27 @@ A Model Context Protocol server that provides access to MySQL databases through
49
49
  - MySQL user with appropriate permissions for the operations you need
50
50
  - For write operations: MySQL user with INSERT, UPDATE, and/or DELETE privileges
51
51
 
52
+ ## Platform Support
53
+
54
+ This MCP server is fully compatible with:
55
+
56
+ - **macOS Big Sur (11.x) and later** - Full support including Unix Socket connections
57
+ - **Windows 10 and later** - Full support via TCP/IP connections
58
+ - **Linux** - Full support including Unix Socket connections
59
+
60
+ ### Platform-Specific Notes
61
+
62
+ #### macOS Big Sur
63
+ - Can use either TCP/IP or Unix Socket connections
64
+ - Unix Socket path example: `/tmp/mysql.sock` or `/var/run/mysql.sock`
65
+ - No additional configuration required
66
+
67
+ #### Windows 10
68
+ - Must use TCP/IP connections (Unix Sockets are not supported)
69
+ - Ensure MySQL is configured to accept TCP/IP connections
70
+ - Default connection: `MYSQL_HOST=127.0.0.1` and `MYSQL_PORT=3306`
71
+ - If using WSL2, you can connect to MySQL via Windows host IP (e.g., `MYSQL_HOST=172.x.x.x`)
72
+
52
73
  ## Installation
53
74
 
54
75
  ### Using Smithery
@@ -514,13 +535,54 @@ For more control over the MCP server's behavior, you can use these advanced conf
514
535
 
515
536
  ### Basic Connection
516
537
 
517
- - `MYSQL_SOCKET_PATH`: Unix socket path for local connections (e.g., "/tmp/mysql.sock")
518
- - `MYSQL_HOST`: MySQL server host (default: "127.0.0.1") - ignored if MYSQL_SOCKET_PATH is set
519
- - `MYSQL_PORT`: MySQL server port (default: "3306") - ignored if MYSQL_SOCKET_PATH is set
538
+ - `MYSQL_SOCKET_PATH`: Unix socket path for local connections (e.g., "/tmp/mysql.sock") - **macOS/Linux only**
539
+ - `MYSQL_HOST`: MySQL server host (default: "127.0.0.1") - **required for Windows**
540
+ - `MYSQL_PORT`: MySQL server port (default: "3306") - **required for Windows**
520
541
  - `MYSQL_USER`: MySQL username (default: "root")
521
542
  - `MYSQL_PASS`: MySQL password
522
543
  - `MYSQL_DB`: Target database name (leave empty for multi-DB mode)
523
544
 
545
+ ### Platform-Specific Configuration
546
+
547
+ #### macOS Big Sur
548
+
549
+ On macOS, you can use either Unix sockets or TCP/IP:
550
+
551
+ **Option 1: Unix Socket (Recommended for local MySQL)**
552
+ ```bash
553
+ MYSQL_SOCKET_PATH="/tmp/mysql.sock"
554
+ MYSQL_USER="root"
555
+ MYSQL_PASS="your_password"
556
+ MYSQL_DB="your_database"
557
+ ```
558
+
559
+ **Option 2: TCP/IP**
560
+ ```bash
561
+ MYSQL_HOST="127.0.0.1"
562
+ MYSQL_PORT="3306"
563
+ MYSQL_USER="root"
564
+ MYSQL_PASS="your_password"
565
+ MYSQL_DB="your_database"
566
+ ```
567
+
568
+ #### Windows 10
569
+
570
+ On Windows, you must use TCP/IP connections:
571
+
572
+ ```bash
573
+ MYSQL_HOST="127.0.0.1"
574
+ MYSQL_PORT="3306"
575
+ MYSQL_USER="root"
576
+ MYSQL_PASS="your_password"
577
+ MYSQL_DB="your_database"
578
+ ```
579
+
580
+ **Windows-Specific Notes:**
581
+ - If MySQL is running in WSL2, you may need to use the Windows host IP instead of `127.0.0.1`
582
+ - To find WSL2 IP: Run `wsl hostname -I` in Windows Command Prompt or PowerShell
583
+ - Ensure MySQL is configured to accept TCP/IP connections (check `bind-address` in `my.cnf` or `my.ini`)
584
+ - Windows Firewall may block connections - ensure port 3306 is allowed
585
+
524
586
  #### Alternative: Connection String
525
587
 
526
588
  For scenarios requiring frequent credential rotation or temporary connections, you can use a MySQL connection string instead of individual environment variables:
package/dist/evals.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -72,6 +72,10 @@ export const configSchema = z.object({
72
72
  debug: z.boolean().default(false).describe("Enable debug logging"),
73
73
  });
74
74
  export default function createMcpServer({ sessionId, config, }) {
75
+ const isDebugEnabled = config?.debug ?? false;
76
+ if (isDebugEnabled) {
77
+ log("info", "Debug mode enabled");
78
+ }
75
79
  const server = new Server({
76
80
  name: "MySQL MCP Server",
77
81
  version: process.env.npm_package_version || "1.0.0",
@@ -123,12 +127,17 @@ export default function createMcpServer({ sessionId, config, }) {
123
127
  information_schema.tables
124
128
  WHERE
125
129
  table_schema NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys')
126
- ${!isMultiDbMode && mcpConfig.mysql.database ? `AND table_schema = '${mcpConfig.mysql.database}'` : ''}
127
130
  ORDER BY
128
131
  table_schema, table_name
129
132
  `;
130
- const queryResult = (await executeReadOnlyQuery(tablesQuery));
131
- const tables = JSON.parse(queryResult.content[0].text);
133
+ let queryParams = [];
134
+ let finalQuery = tablesQuery;
135
+ if (!isMultiDbMode && mcpConfig.mysql.database) {
136
+ finalQuery = tablesQuery.replace('ORDER BY', `AND table_schema = ?\n ORDER BY`);
137
+ queryParams.push(mcpConfig.mysql.database);
138
+ }
139
+ const rawResult = await executeQuery(finalQuery, queryParams);
140
+ const tables = rawResult[0];
132
141
  log("info", `Found ${tables.length} tables`);
133
142
  const resources = tables.map((table) => ({
134
143
  uri: `mysql://tables/${table.name}`,
@@ -164,11 +173,18 @@ export default function createMcpServer({ sessionId, config, }) {
164
173
  let columnsQuery = "SELECT column_name, data_type FROM information_schema.columns WHERE table_name = ?";
165
174
  let queryParams = [tableName];
166
175
  const schemaName = dbName || (!isMultiDbMode ? mcpConfig.mysql.database : null);
176
+ if (!/^[a-zA-Z0-9_]+$/.test(tableName)) {
177
+ throw new Error(`Invalid table name: ${tableName}`);
178
+ }
179
+ if (schemaName && !/^[a-zA-Z0-9_]+$/.test(schemaName)) {
180
+ throw new Error(`Invalid schema name: ${schemaName}`);
181
+ }
167
182
  if (schemaName) {
168
183
  columnsQuery += " AND table_schema = ?";
169
184
  queryParams.push(schemaName);
170
185
  }
171
- const results = (await executeQuery(columnsQuery, queryParams));
186
+ const rawResult = await executeQuery(columnsQuery, queryParams);
187
+ const results = rawResult[0];
172
188
  return {
173
189
  contents: [
174
190
  {
@@ -306,7 +322,7 @@ if (isMainModule()) {
306
322
  log("info", "Running in standalone mode");
307
323
  (async () => {
308
324
  try {
309
- const mcpServer = createMcpServer({ config: { debug: false } });
325
+ const mcpServer = createMcpServer({});
310
326
  if (IS_REMOTE_MCP && REMOTE_SECRET_KEY?.length) {
311
327
  const app = express();
312
328
  app.use(express.json());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cainli/mcp-server-mysql",
3
- "version": "2.0.8",
3
+ "version": "2.0.10",
4
4
  "description": "MCP server for interacting with MySQL databases with write operations support",
5
5
  "license": "MIT",
6
6
  "author": "cainli (https://github.com/cainli)",
@@ -16,30 +16,6 @@
16
16
  "README.md",
17
17
  "assets"
18
18
  ],
19
- "scripts": {
20
- "start": "node dist/index.js",
21
- "dev": "ts-node index.ts",
22
- "build": "tsc && shx chmod +x dist/*.js",
23
- "prepare": "npm run build",
24
- "watch": "tsc --watch",
25
- "setup:test:db": "tsx scripts/setup-test-db.ts",
26
- "pretest": "pnpm run setup:test:db",
27
- "test": "pnpm run setup:test:db && vitest run",
28
- "test:socket": "pnpm run setup:test:db && vitest run tests/integration/socket-connection.test.ts",
29
- "test:watch": "pnpm run setup:test:db && vitest",
30
- "test:coverage": "vitest run --coverage",
31
- "test:unit": "vitest run --config vitest.unit.config.ts",
32
- "test:integration": "vitest run --config vitest.integration.config.ts",
33
- "test:e2e": "vitest run --config vitest.e2e.config.ts",
34
- "stdio": "node dist/index.js --stdio",
35
- "exec": " pnpm build && npx node --env-file=.env dist/index.js",
36
- "lint": "npm run lint:eslint && npm run lint:markdown",
37
- "lint:eslint": "eslint .",
38
- "lint:markdown": "markdownlint .",
39
- "lint:fix": "npm run lint:eslint:fix && npm run lint:markdown:fix",
40
- "lint:eslint:fix": "eslint . --fix",
41
- "lint:markdown:fix": "markdownlint . --fix"
42
- },
43
19
  "dependencies": {
44
20
  "@ai-sdk/openai": "^1.3.22",
45
21
  "@modelcontextprotocol/sdk": "1.15.1",
@@ -83,5 +59,29 @@
83
59
  "mcp-patch",
84
60
  "mcp-options",
85
61
  "mcp-head"
86
- ]
87
- }
62
+ ],
63
+ "scripts": {
64
+ "start": "node dist/index.js",
65
+ "dev": "ts-node index.ts",
66
+ "build": "tsc && npm run chmod:win",
67
+ "chmod:win": "node -e \"const os=require('os');if(os.platform()!=='win32'){const fs=require('fs');const dist='dist';const files=fs.readdirSync(dist).filter(f=>f.endsWith('.js'));files.forEach(f=>fs.chmodSync(dist+'/'+f,0o755));console.log('Made executable:',files.join(', '))}\"",
68
+ "watch": "tsc --watch",
69
+ "setup:test:db": "tsx scripts/setup-test-db.ts",
70
+ "pretest": "pnpm run setup:test:db",
71
+ "test": "pnpm run setup:test:db && vitest run",
72
+ "test:socket": "pnpm run setup:test:db && vitest run tests/integration/socket-connection.test.ts",
73
+ "test:watch": "pnpm run setup:test:db && vitest",
74
+ "test:coverage": "vitest run --coverage",
75
+ "test:unit": "vitest run --config vitest.unit.config.ts",
76
+ "test:integration": "vitest run --config vitest.integration.config.ts",
77
+ "test:e2e": "vitest run --config vitest.e2e.config.ts",
78
+ "stdio": "node dist/index.js --stdio",
79
+ "exec": " pnpm build && npx node --env-file=.env dist/index.js",
80
+ "lint": "npm run lint:eslint && npm run lint:markdown",
81
+ "lint:eslint": "eslint .",
82
+ "lint:markdown": "markdownlint .",
83
+ "lint:fix": "npm run lint:eslint:fix && npm run lint:markdown:fix",
84
+ "lint:eslint:fix": "eslint . --fix",
85
+ "lint:markdown:fix": "markdownlint . --fix"
86
+ }
87
+ }