easy-mysql-mcp 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.
- package/README.md +157 -0
- package/build/db.js +30 -0
- package/build/index.js +129 -0
- package/build/proxy.js +25 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# easy-mysql-mcp
|
|
2
|
+
|
|
3
|
+
A lightweight [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that lets AI assistants inspect and query a MySQL database through a safe, structured tool interface.
|
|
4
|
+
|
|
5
|
+
This project uses Node.js, TypeScript, the official MCP SDK, and `mysql2/promise`. It runs over stdio, so it can be used directly by MCP clients such as Claude Desktop.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- MySQL connection pooling powered by `mysql2/promise`
|
|
10
|
+
- Read-only query tool for data retrieval
|
|
11
|
+
- Execute tool for data modification statements
|
|
12
|
+
- Schema discovery tools for tables, views, indexes, and triggers
|
|
13
|
+
- Query plan inspection with `EXPLAIN`
|
|
14
|
+
- Current user and privilege inspection
|
|
15
|
+
- stdout protection to prevent non-MCP logs from polluting the stdio protocol
|
|
16
|
+
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js 18 or newer
|
|
20
|
+
- npm
|
|
21
|
+
- A reachable MySQL-compatible database
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
Run the server directly with `npx`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx -y easy-mysql-mcp
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For local development after cloning the repository:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cd easy-mysql-mcp
|
|
35
|
+
npm install
|
|
36
|
+
npm run build
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Configuration
|
|
40
|
+
|
|
41
|
+
Configure the server with environment variables. You can provide them through your MCP client configuration or by creating a local `.env` file.
|
|
42
|
+
|
|
43
|
+
| Variable | Required | Default | Description |
|
|
44
|
+
| --- | --- | --- | --- |
|
|
45
|
+
| `MYSQL_HOST` | Yes | - | MySQL host name or IP address |
|
|
46
|
+
| `MYSQL_PORT` | No | `3306` | MySQL port |
|
|
47
|
+
| `MYSQL_USER` | Yes | - | MySQL user name |
|
|
48
|
+
| `MYSQL_PASSWORD` | Yes | - | MySQL password |
|
|
49
|
+
| `MYSQL_DATABASE` | Yes | - | Default database/schema |
|
|
50
|
+
| `MYSQL_CONNECTION_LIMIT` | No | `10` | Maximum number of active pool connections |
|
|
51
|
+
| `MYSQL_MAX_IDLE` | No | `10` | Maximum number of idle pool connections |
|
|
52
|
+
| `MYSQL_IDLE_TIMEOUT` | No | `60000` | Idle connection timeout in milliseconds |
|
|
53
|
+
| `MYSQL_QUEUE_LIMIT` | No | `0` | Maximum queued connection requests, where `0` means unlimited |
|
|
54
|
+
| `MYSQL_WAIT_FOR_CONNECTIONS` | No | `true` | Whether the pool waits when all connections are busy |
|
|
55
|
+
| `MYSQL_ENABLE_KEEP_ALIVE` | No | `true` | Whether TCP keep-alive is enabled |
|
|
56
|
+
| `MYSQL_KEEP_ALIVE_INITIAL_DELAY` | No | `0` | Initial TCP keep-alive delay in milliseconds |
|
|
57
|
+
|
|
58
|
+
Example `.env`:
|
|
59
|
+
|
|
60
|
+
```env
|
|
61
|
+
MYSQL_HOST=localhost
|
|
62
|
+
MYSQL_PORT=3306
|
|
63
|
+
MYSQL_USER=root
|
|
64
|
+
MYSQL_PASSWORD=your_password
|
|
65
|
+
MYSQL_DATABASE=your_database
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
Configure your MCP client to launch the package through `npx`.
|
|
71
|
+
|
|
72
|
+
For local development, build the TypeScript source first:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm run build
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Start the MCP server:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm start
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The server communicates over stdio and is normally launched by an MCP client rather than run manually.
|
|
85
|
+
|
|
86
|
+
## Claude Desktop Example
|
|
87
|
+
|
|
88
|
+
Add the server to your `claude_desktop_config.json`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"mcpServers": {
|
|
93
|
+
"easy-mysql-mcp": {
|
|
94
|
+
"command": "npx",
|
|
95
|
+
"args": ["-y", "easy-mysql-mcp"],
|
|
96
|
+
"env": {
|
|
97
|
+
"MYSQL_HOST": "localhost",
|
|
98
|
+
"MYSQL_PORT": "3306",
|
|
99
|
+
"MYSQL_USER": "root",
|
|
100
|
+
"MYSQL_PASSWORD": "your_password",
|
|
101
|
+
"MYSQL_DATABASE": "your_database"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Restart Claude Desktop after updating the configuration.
|
|
109
|
+
|
|
110
|
+
## Available Tools
|
|
111
|
+
|
|
112
|
+
| Tool | Description |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `mysql_query` | Execute a SQL query intended for data retrieval, such as `SELECT` |
|
|
115
|
+
| `mysql_execute` | Execute a data modification statement, such as `INSERT`, `UPDATE`, or `DELETE` |
|
|
116
|
+
| `explain_query` | Run `EXPLAIN` for a SQL query and return the execution plan |
|
|
117
|
+
| `list_tables` | List base tables in the current database, including approximate row counts and comments |
|
|
118
|
+
| `list_views` | List views in the current database |
|
|
119
|
+
| `describe_table` | Show column information for one or more tables |
|
|
120
|
+
| `describe_index` | Show indexes for a table |
|
|
121
|
+
| `list_triggers` | List triggers in the current database |
|
|
122
|
+
| `get_current_privileges` | Show the current MySQL user and grants |
|
|
123
|
+
|
|
124
|
+
## Security Notes
|
|
125
|
+
|
|
126
|
+
- Use a dedicated MySQL user with the minimum permissions your assistant needs.
|
|
127
|
+
- Prefer read-only database credentials if you only need inspection and reporting.
|
|
128
|
+
- Be careful with `mysql_execute`, because it can modify data.
|
|
129
|
+
- Do not commit `.env` files or real database credentials to GitHub.
|
|
130
|
+
- Review generated SQL before running it against production data.
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm run dev
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
This runs TypeScript in watch mode.
|
|
139
|
+
|
|
140
|
+
To create a production build:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
npm run build
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Project Structure
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
src/
|
|
150
|
+
db.ts MySQL pool and query helpers
|
|
151
|
+
index.ts MCP server and tool registration
|
|
152
|
+
proxy.ts stdout protection for stdio-based MCP transport
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
ISC
|
package/build/db.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import mysql from 'mysql2/promise';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
const { MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, MYSQL_CONNECTION_LIMIT, MYSQL_MAX_IDLE, MYSQL_IDLE_TIMEOUT, MYSQL_QUEUE_LIMIT, MYSQL_WAIT_FOR_CONNECTIONS, MYSQL_ENABLE_KEEP_ALIVE, MYSQL_KEEP_ALIVE_INITIAL_DELAY, } = process.env;
|
|
5
|
+
if (!MYSQL_HOST || !MYSQL_USER || !MYSQL_PASSWORD || !MYSQL_DATABASE) {
|
|
6
|
+
console.error('Missing required environment variables for MySQL connection.');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
export const pool = mysql.createPool({
|
|
10
|
+
host: MYSQL_HOST,
|
|
11
|
+
port: MYSQL_PORT ? parseInt(MYSQL_PORT) : 3306,
|
|
12
|
+
user: MYSQL_USER,
|
|
13
|
+
password: MYSQL_PASSWORD,
|
|
14
|
+
database: MYSQL_DATABASE,
|
|
15
|
+
waitForConnections: MYSQL_WAIT_FOR_CONNECTIONS !== 'false',
|
|
16
|
+
connectionLimit: MYSQL_CONNECTION_LIMIT ? parseInt(MYSQL_CONNECTION_LIMIT) : 10,
|
|
17
|
+
maxIdle: MYSQL_MAX_IDLE ? parseInt(MYSQL_MAX_IDLE) : 10,
|
|
18
|
+
idleTimeout: MYSQL_IDLE_TIMEOUT ? parseInt(MYSQL_IDLE_TIMEOUT) : 60000,
|
|
19
|
+
queueLimit: MYSQL_QUEUE_LIMIT ? parseInt(MYSQL_QUEUE_LIMIT) : 0,
|
|
20
|
+
enableKeepAlive: MYSQL_ENABLE_KEEP_ALIVE !== 'false',
|
|
21
|
+
keepAliveInitialDelay: MYSQL_KEEP_ALIVE_INITIAL_DELAY ? parseInt(MYSQL_KEEP_ALIVE_INITIAL_DELAY) : 0,
|
|
22
|
+
});
|
|
23
|
+
export async function query(sql, params) {
|
|
24
|
+
const [rows] = await pool.query(sql, params);
|
|
25
|
+
return rows;
|
|
26
|
+
}
|
|
27
|
+
export async function execute(sql, params) {
|
|
28
|
+
const [result] = await pool.execute(sql, params);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import './proxy.js';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import * as db from './db.js';
|
|
7
|
+
// Initialize MCP Server
|
|
8
|
+
const server = new McpServer({
|
|
9
|
+
name: 'easy-mysql-mcp',
|
|
10
|
+
version: '1.0.0',
|
|
11
|
+
});
|
|
12
|
+
// --- Register Tools ---
|
|
13
|
+
server.registerTool('mysql_query', {
|
|
14
|
+
description: 'Execute a read-only SQL query (e.g., SELECT). Use this for data retrieval.',
|
|
15
|
+
inputSchema: z.object({
|
|
16
|
+
sql: z.string().describe('The SQL query to execute.'),
|
|
17
|
+
}),
|
|
18
|
+
}, async ({ sql }) => {
|
|
19
|
+
const results = await db.query(sql);
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
server.registerTool('mysql_execute', {
|
|
25
|
+
description: 'Execute a data modification SQL statement (e.g., INSERT, UPDATE, DELETE).',
|
|
26
|
+
inputSchema: z.object({
|
|
27
|
+
sql: z.string().describe('The SQL statement to execute.'),
|
|
28
|
+
params: z.array(z.any()).optional().describe('Optional parameters for the statement.'),
|
|
29
|
+
}),
|
|
30
|
+
}, async ({ sql, params }) => {
|
|
31
|
+
const result = await db.execute(sql, params);
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
server.registerTool('explain_query', {
|
|
37
|
+
description: 'Run EXPLAIN on a SQL query to analyze its execution plan and performance.',
|
|
38
|
+
inputSchema: z.object({
|
|
39
|
+
sql: z.string().describe('The SQL query to explain.'),
|
|
40
|
+
}),
|
|
41
|
+
}, async ({ sql }) => {
|
|
42
|
+
const results = await db.query(`EXPLAIN ${sql}`);
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
server.registerTool('list_tables', {
|
|
48
|
+
description: 'List all base tables in the current database with row counts and comments.',
|
|
49
|
+
inputSchema: z.object({}),
|
|
50
|
+
}, async () => {
|
|
51
|
+
const results = await db.query(`
|
|
52
|
+
SELECT TABLE_NAME, TABLE_ROWS, TABLE_COMMENT
|
|
53
|
+
FROM information_schema.TABLES
|
|
54
|
+
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE'
|
|
55
|
+
`);
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
server.registerTool('list_views', {
|
|
61
|
+
description: 'List all views in the current database.',
|
|
62
|
+
inputSchema: z.object({}),
|
|
63
|
+
}, async () => {
|
|
64
|
+
const results = await db.query(`
|
|
65
|
+
SELECT TABLE_NAME, VIEW_DEFINITION
|
|
66
|
+
FROM information_schema.VIEWS
|
|
67
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
68
|
+
`);
|
|
69
|
+
return {
|
|
70
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
server.registerTool('describe_table', {
|
|
74
|
+
description: 'Show the schema/structure of one or more specific tables.',
|
|
75
|
+
inputSchema: z.object({
|
|
76
|
+
tables: z.array(z.string()).describe('The names of the tables to describe.'),
|
|
77
|
+
}),
|
|
78
|
+
}, async ({ tables }) => {
|
|
79
|
+
const results = {};
|
|
80
|
+
for (const table of tables) {
|
|
81
|
+
results[table] = await db.query(`DESCRIBE ${table}`);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
server.registerTool('describe_index', {
|
|
88
|
+
description: 'Show indexes for a specific table.',
|
|
89
|
+
inputSchema: z.object({
|
|
90
|
+
table: z.string().describe('The name of the table to show indexes for.'),
|
|
91
|
+
}),
|
|
92
|
+
}, async ({ table }) => {
|
|
93
|
+
const results = await db.query(`SHOW INDEX FROM ${table}`);
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
server.registerTool('list_triggers', {
|
|
99
|
+
description: 'List all triggers in the current database.',
|
|
100
|
+
inputSchema: z.object({}),
|
|
101
|
+
}, async () => {
|
|
102
|
+
const results = await db.query('SHOW TRIGGERS');
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: 'text', text: JSON.stringify(results, null, 2) }],
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
server.registerTool('get_current_privileges', {
|
|
108
|
+
description: 'Check the permissions and grants of the current database user. Useful for debugging access issues.',
|
|
109
|
+
inputSchema: z.object({}),
|
|
110
|
+
}, async () => {
|
|
111
|
+
const user = await db.query('SELECT CURRENT_USER() as user');
|
|
112
|
+
const grants = await db.query('SHOW GRANTS');
|
|
113
|
+
return {
|
|
114
|
+
content: [{
|
|
115
|
+
type: 'text',
|
|
116
|
+
text: JSON.stringify({ currentUser: user, grants }, null, 2)
|
|
117
|
+
}],
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
// Start server
|
|
121
|
+
async function main() {
|
|
122
|
+
const transport = new StdioServerTransport();
|
|
123
|
+
await server.connect(transport);
|
|
124
|
+
console.error('MySQL MCP Server running on stdio');
|
|
125
|
+
}
|
|
126
|
+
main().catch((error) => {
|
|
127
|
+
console.error('Fatal error in main():', error);
|
|
128
|
+
process.exit(1);
|
|
129
|
+
});
|
package/build/proxy.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stdout Protection Proxy
|
|
3
|
+
* This module MUST be imported as the very first import in the application.
|
|
4
|
+
* It intercepts all writes to stdout and redirects non-MCP JSON output to stderr.
|
|
5
|
+
* This prevents protocol pollution from libraries that log to stdout (like dotenvx).
|
|
6
|
+
*/
|
|
7
|
+
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
process.stdout.write = (chunk, encoding, callback) => {
|
|
10
|
+
if (typeof chunk === 'string' || Buffer.isBuffer(chunk)) {
|
|
11
|
+
const str = chunk.toString();
|
|
12
|
+
// Allow MCP JSON-RPC messages and Content-Length headers (used in some transports)
|
|
13
|
+
if (str.includes('"jsonrpc":"2.0"') || str.includes('Content-Length:')) {
|
|
14
|
+
return originalStdoutWrite(chunk, encoding, callback);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Redirect everything else to stderr
|
|
18
|
+
return process.stderr.write(chunk, encoding, callback);
|
|
19
|
+
};
|
|
20
|
+
// Also patch console.log to use stderr, just in case
|
|
21
|
+
const originalConsoleLog = console.log;
|
|
22
|
+
console.log = (...args) => {
|
|
23
|
+
console.error(...args);
|
|
24
|
+
};
|
|
25
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "easy-mysql-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "High performance MySQL MCP Server using mysql2",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"easy-mysql-mcp": "build/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"build",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"model-context-protocol",
|
|
20
|
+
"mysql",
|
|
21
|
+
"mysql2",
|
|
22
|
+
"claude"
|
|
23
|
+
],
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"start": "node build/index.js",
|
|
31
|
+
"dev": "tsc --watch",
|
|
32
|
+
"prepack": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
36
|
+
"dotenv": "^17.4.2",
|
|
37
|
+
"mysql2": "^3.22.2",
|
|
38
|
+
"zod": "^4.3.6"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/node": "^25.6.0",
|
|
42
|
+
"typescript": "^6.0.3"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|