@dockeryu/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 +86 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +186 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @dockeryu/mysql-mcp
|
|
2
|
+
|
|
3
|
+
基于 [Model Context Protocol](https://modelcontextprotocol.io) 的 MySQL 数据库服务,让 AI 助手可以直接操作 MySQL。
|
|
4
|
+
|
|
5
|
+
## 安装与运行
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx -y @dockeryu/mysql-mcp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
作用域包公开发布后可用;若尚未发布到 npm,请用本地 `npm run build` 后通过 `node dist/index.js` 或 Cursor 里配置 `command` 为 `node`、`args` 指向本地 `dist/index.js`。
|
|
12
|
+
|
|
13
|
+
## 配置
|
|
14
|
+
|
|
15
|
+
通过环境变量配置数据库连接和操作权限。
|
|
16
|
+
|
|
17
|
+
### 连接配置
|
|
18
|
+
|
|
19
|
+
| 环境变量 | 默认值 | 说明 |
|
|
20
|
+
|----------|--------|------|
|
|
21
|
+
| `MYSQL_HOST` | `127.0.0.1` | 数据库地址 |
|
|
22
|
+
| `MYSQL_PORT` | `3306` | 端口 |
|
|
23
|
+
| `MYSQL_USER` | `root` | 用户名 |
|
|
24
|
+
| `MYSQL_PASSWORD` | _(空)_ | 密码 |
|
|
25
|
+
| `MYSQL_DATABASE` | _(无)_ | 默认数据库(可选)|
|
|
26
|
+
|
|
27
|
+
### 操作权限
|
|
28
|
+
|
|
29
|
+
危险操作默认禁止,需显式开启。
|
|
30
|
+
|
|
31
|
+
| 环境变量 | 默认 | 说明 |
|
|
32
|
+
|----------|------|------|
|
|
33
|
+
| `MYSQL_ALLOW_INSERT` | `true` | 允许 INSERT |
|
|
34
|
+
| `MYSQL_ALLOW_CREATE` | `true` | 允许 CREATE |
|
|
35
|
+
| `MYSQL_ALLOW_UPDATE` | `false` | 允许 UPDATE |
|
|
36
|
+
| `MYSQL_ALLOW_DELETE` | `false` | 允许 DELETE |
|
|
37
|
+
| `MYSQL_ALLOW_DROP` | `false` | 允许 DROP |
|
|
38
|
+
| `MYSQL_ALLOW_TRUNCATE` | `false` | 允许 TRUNCATE |
|
|
39
|
+
|
|
40
|
+
## 在 Cursor / Claude Desktop 中使用
|
|
41
|
+
|
|
42
|
+
编辑 MCP 配置文件(`mcp.json`):
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"mcpServers": {
|
|
47
|
+
"mysql": {
|
|
48
|
+
"command": "npx",
|
|
49
|
+
"args": ["-y", "@dockeryu/mysql-mcp"],
|
|
50
|
+
"env": {
|
|
51
|
+
"MYSQL_HOST": "127.0.0.1",
|
|
52
|
+
"MYSQL_PORT": "3306",
|
|
53
|
+
"MYSQL_USER": "root",
|
|
54
|
+
"MYSQL_PASSWORD": "your_password",
|
|
55
|
+
"MYSQL_DATABASE": "your_db",
|
|
56
|
+
"MYSQL_ALLOW_UPDATE": "true",
|
|
57
|
+
"MYSQL_ALLOW_DELETE": "true"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 工具列表
|
|
65
|
+
|
|
66
|
+
| 工具 | 说明 |
|
|
67
|
+
|------|------|
|
|
68
|
+
| `query` | 执行 SELECT 查询 |
|
|
69
|
+
| `execute` | 执行写操作(受权限控制) |
|
|
70
|
+
| `list_databases` | 列出所有数据库 |
|
|
71
|
+
| `list_tables` | 列出指定库的所有表 |
|
|
72
|
+
| `describe_table` | 查看表结构 |
|
|
73
|
+
|
|
74
|
+
## 本地开发
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm install
|
|
78
|
+
npm run build
|
|
79
|
+
npm start
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
发布公开作用域包:`npm publish --access public`
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
8
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
10
|
+
const promise_1 = __importDefault(require("mysql2/promise"));
|
|
11
|
+
const zod_1 = require("zod");
|
|
12
|
+
const ConfigSchema = zod_1.z.object({
|
|
13
|
+
host: zod_1.z.string().default("127.0.0.1"),
|
|
14
|
+
port: zod_1.z.number().default(3306),
|
|
15
|
+
user: zod_1.z.string().default("root"),
|
|
16
|
+
password: zod_1.z.string().default(""),
|
|
17
|
+
database: zod_1.z.string().optional(),
|
|
18
|
+
allowUpdate: zod_1.z.boolean().default(false),
|
|
19
|
+
allowDelete: zod_1.z.boolean().default(false),
|
|
20
|
+
allowDrop: zod_1.z.boolean().default(false),
|
|
21
|
+
allowTruncate: zod_1.z.boolean().default(false),
|
|
22
|
+
allowCreate: zod_1.z.boolean().default(true),
|
|
23
|
+
allowInsert: zod_1.z.boolean().default(true),
|
|
24
|
+
});
|
|
25
|
+
let _config = null;
|
|
26
|
+
function getConfig() {
|
|
27
|
+
if (!_config) {
|
|
28
|
+
_config = ConfigSchema.parse({
|
|
29
|
+
host: process.env.MYSQL_HOST,
|
|
30
|
+
port: process.env.MYSQL_PORT ? parseInt(process.env.MYSQL_PORT) : undefined,
|
|
31
|
+
user: process.env.MYSQL_USER,
|
|
32
|
+
password: process.env.MYSQL_PASSWORD,
|
|
33
|
+
database: process.env.MYSQL_DATABASE,
|
|
34
|
+
allowUpdate: process.env.MYSQL_ALLOW_UPDATE === "true",
|
|
35
|
+
allowDelete: process.env.MYSQL_ALLOW_DELETE === "true",
|
|
36
|
+
allowDrop: process.env.MYSQL_ALLOW_DROP === "true",
|
|
37
|
+
allowTruncate: process.env.MYSQL_ALLOW_TRUNCATE === "true",
|
|
38
|
+
allowCreate: process.env.MYSQL_ALLOW_CREATE !== "false",
|
|
39
|
+
allowInsert: process.env.MYSQL_ALLOW_INSERT !== "false",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return _config;
|
|
43
|
+
}
|
|
44
|
+
const DANGEROUS_PATTERNS = [
|
|
45
|
+
{ regex: /^\s*UPDATE\s/i, key: "allowUpdate", label: "UPDATE" },
|
|
46
|
+
{ regex: /^\s*DELETE\s/i, key: "allowDelete", label: "DELETE" },
|
|
47
|
+
{ regex: /^\s*DROP\s/i, key: "allowDrop", label: "DROP" },
|
|
48
|
+
{ regex: /^\s*TRUNCATE\s/i, key: "allowTruncate", label: "TRUNCATE" },
|
|
49
|
+
{ regex: /^\s*INSERT\s/i, key: "allowInsert", label: "INSERT" },
|
|
50
|
+
{ regex: /^\s*CREATE\s/i, key: "allowCreate", label: "CREATE" },
|
|
51
|
+
];
|
|
52
|
+
function checkPermission(sql) {
|
|
53
|
+
const cfg = getConfig();
|
|
54
|
+
for (const { regex, key, label } of DANGEROUS_PATTERNS) {
|
|
55
|
+
if (regex.test(sql) && !cfg[key]) {
|
|
56
|
+
throw new Error(`${label} 操作未被允许,请设置环境变量 MYSQL_ALLOW_${label}=true`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
let pool = null;
|
|
61
|
+
function getPool() {
|
|
62
|
+
if (!pool) {
|
|
63
|
+
const { host, port, user, password, database } = getConfig();
|
|
64
|
+
pool = promise_1.default.createPool({
|
|
65
|
+
host, port, user, password, database,
|
|
66
|
+
waitForConnections: true,
|
|
67
|
+
connectionLimit: 10,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return pool;
|
|
71
|
+
}
|
|
72
|
+
async function executeQuery(sql, params = []) {
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
74
|
+
const [rows] = await getPool().execute(sql, params);
|
|
75
|
+
return rows;
|
|
76
|
+
}
|
|
77
|
+
const server = new index_js_1.Server({ name: "mysql-mcp", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
78
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
79
|
+
tools: [
|
|
80
|
+
{
|
|
81
|
+
name: "query",
|
|
82
|
+
description: "执行 SQL 查询(SELECT)",
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
sql: { type: "string", description: "SQL 查询语句" },
|
|
87
|
+
params: { type: "array", description: "查询参数", items: {} },
|
|
88
|
+
},
|
|
89
|
+
required: ["sql"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "execute",
|
|
94
|
+
description: "执行 SQL 写操作(INSERT/UPDATE/DELETE/CREATE 等)",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
type: "object",
|
|
97
|
+
properties: {
|
|
98
|
+
sql: { type: "string", description: "SQL 语句" },
|
|
99
|
+
params: { type: "array", description: "参数列表", items: {} },
|
|
100
|
+
},
|
|
101
|
+
required: ["sql"],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "list_databases",
|
|
106
|
+
description: "列出所有数据库",
|
|
107
|
+
inputSchema: { type: "object", properties: {} },
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "list_tables",
|
|
111
|
+
description: "列出指定数据库的所有表",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
database: { type: "string", description: "数据库名(不填则用连接默认库)" },
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "describe_table",
|
|
121
|
+
description: "查看表结构",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
table: { type: "string", description: "表名" },
|
|
126
|
+
database: { type: "string", description: "数据库名(可选)" },
|
|
127
|
+
},
|
|
128
|
+
required: ["table"],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
}));
|
|
133
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
134
|
+
const { name, arguments: args } = request.params;
|
|
135
|
+
try {
|
|
136
|
+
let result;
|
|
137
|
+
if (name === "query") {
|
|
138
|
+
const { sql, params = [] } = args;
|
|
139
|
+
result = await executeQuery(sql, params);
|
|
140
|
+
}
|
|
141
|
+
else if (name === "execute") {
|
|
142
|
+
const { sql, params = [] } = args;
|
|
143
|
+
checkPermission(sql);
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
145
|
+
const [res] = await getPool().execute(sql, params);
|
|
146
|
+
result = res;
|
|
147
|
+
}
|
|
148
|
+
else if (name === "list_databases") {
|
|
149
|
+
result = await executeQuery("SHOW DATABASES");
|
|
150
|
+
}
|
|
151
|
+
else if (name === "list_tables") {
|
|
152
|
+
const { database } = (args ?? {});
|
|
153
|
+
const sql = database
|
|
154
|
+
? `SHOW TABLES FROM \`${database}\``
|
|
155
|
+
: "SHOW TABLES";
|
|
156
|
+
result = await executeQuery(sql);
|
|
157
|
+
}
|
|
158
|
+
else if (name === "describe_table") {
|
|
159
|
+
const { table, database } = args;
|
|
160
|
+
const fullTable = database ? `\`${database}\`.\`${table}\`` : `\`${table}\``;
|
|
161
|
+
result = await executeQuery(`DESCRIBE ${fullTable}`);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
throw new Error(`未知工具: ${name}`);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
172
|
+
return {
|
|
173
|
+
content: [{ type: "text", text: `错误: ${message}` }],
|
|
174
|
+
isError: true,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
async function main() {
|
|
179
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
180
|
+
await server.connect(transport);
|
|
181
|
+
process.stderr.write("MySQL MCP Server 已启动\n");
|
|
182
|
+
}
|
|
183
|
+
main().catch((err) => {
|
|
184
|
+
process.stderr.write(`启动失败: ${err}\n`);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dockeryu/mysql-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MySQL MCP Server",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mysql-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"start": "node dist/index.js",
|
|
12
|
+
"dev": "tsc --watch"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"keywords": ["mcp", "mysql", "modelcontextprotocol"],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.8.0",
|
|
24
|
+
"mysql2": "^3.14.0",
|
|
25
|
+
"zod": "^3.24.2"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^22.13.14",
|
|
29
|
+
"typescript": "^5.8.2"
|
|
30
|
+
}
|
|
31
|
+
}
|