vmoo-mcp-database-server 1.2.1 → 1.3.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.
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// VMOO商户Cookie管理MCP服务器启动器
|
|
4
|
+
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
// 解析命令行参数
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
const configPath = args.find(arg => arg.startsWith('--config='))?.split('=')[1];
|
|
15
|
+
|
|
16
|
+
console.log('🏪 启动VMOO商户Cookie管理MCP服务器...');
|
|
17
|
+
|
|
18
|
+
// 服务器文件路径
|
|
19
|
+
const serverPath = path.join(__dirname, '../vmoo-database-merchant/server.js');
|
|
20
|
+
|
|
21
|
+
// 启动服务器
|
|
22
|
+
const serverProcess = spawn('node', [serverPath], {
|
|
23
|
+
stdio: 'inherit',
|
|
24
|
+
env: {
|
|
25
|
+
...process.env,
|
|
26
|
+
...(configPath && { CONFIG_PATH: configPath })
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// 处理进程退出
|
|
31
|
+
serverProcess.on('close', (code) => {
|
|
32
|
+
console.log(`🏪 商户Cookie管理MCP服务器已退出,退出码: ${code}`);
|
|
33
|
+
process.exit(code);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// 处理中断信号
|
|
37
|
+
process.on('SIGINT', () => {
|
|
38
|
+
console.log('\n🛑 正在关闭商户Cookie管理MCP服务器...');
|
|
39
|
+
serverProcess.kill('SIGINT');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
process.on('SIGTERM', () => {
|
|
43
|
+
console.log('\n🛑 正在关闭商户Cookie管理MCP服务器...');
|
|
44
|
+
serverProcess.kill('SIGTERM');
|
|
45
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vmoo-mcp-database-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "VMOO数据库MCP服务器集合 - 支持开发和生产环境的安全数据库访问",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
"vmoo-mcp-database-server": "./bin/vmoo-mcp-server.js",
|
|
9
9
|
"vmoo-mcp-dev": "./bin/vmoo-mcp-dev.js",
|
|
10
10
|
"vmoo-mcp-prod": "./bin/vmoo-mcp-prod.js",
|
|
11
|
-
"vmoo-mcp-deliver": "./bin/vmoo-mcp-deliver.js"
|
|
11
|
+
"vmoo-mcp-deliver": "./bin/vmoo-mcp-deliver.js",
|
|
12
|
+
"vmoo-mcp-merchant": "./bin/vmoo-mcp-merchant.js"
|
|
12
13
|
},
|
|
13
14
|
"scripts": {
|
|
14
15
|
"start:prod": "cd vmoo-database-prod && npm start",
|
|
15
16
|
"start:dev": "cd vmoo-database-dev && npm start",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
17
|
+
"start:deliver": "cd vmoo-database-deliver && npm start",
|
|
18
|
+
"start:merchant": "cd vmoo-database-merchant && npm start",
|
|
19
|
+
"install:all": "npm install && cd vmoo-database-prod && npm install && cd ../vmoo-database-dev && npm install && cd ../vmoo-database-deliver && npm install && cd ../vmoo-database-merchant && npm install",
|
|
20
|
+
"test:all": "cd vmoo-database-prod && npm test && cd ../vmoo-database-dev && npm test && cd ../vmoo-database-deliver && npm test && cd ../vmoo-database-merchant && npm test",
|
|
18
21
|
"build": "echo '构建完成'",
|
|
19
22
|
"deploy": "./scripts/deploy.sh"
|
|
20
23
|
},
|
|
@@ -33,6 +36,9 @@
|
|
|
33
36
|
"model-context-protocol",
|
|
34
37
|
"production",
|
|
35
38
|
"development",
|
|
39
|
+
"deliver",
|
|
40
|
+
"merchant",
|
|
41
|
+
"cookie",
|
|
36
42
|
"privacy",
|
|
37
43
|
"security"
|
|
38
44
|
],
|
|
@@ -47,6 +53,7 @@
|
|
|
47
53
|
"vmoo-database-dev/",
|
|
48
54
|
"vmoo-database-prod/",
|
|
49
55
|
"vmoo-database-deliver/",
|
|
56
|
+
"vmoo-database-merchant/",
|
|
50
57
|
"index.js",
|
|
51
58
|
"README.md"
|
|
52
59
|
],
|
package/shared/database-utils.js
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
// VMOO数据库工具函数
|
|
2
2
|
import mysql from 'mysql2/promise';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 替换配置中的环境变量
|
|
6
|
+
* @param {Object} config 配置对象
|
|
7
|
+
* @returns {Object} 替换后的配置对象
|
|
8
|
+
*/
|
|
9
|
+
function replaceEnvVariables(config) {
|
|
10
|
+
const configStr = JSON.stringify(config);
|
|
11
|
+
const replacedStr = configStr.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
|
|
12
|
+
const value = process.env[envVar];
|
|
13
|
+
if (value === undefined) {
|
|
14
|
+
throw new Error(`环境变量 ${envVar} 未设置`);
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
});
|
|
18
|
+
return JSON.parse(replacedStr);
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
/**
|
|
5
22
|
* 创建数据库连接池
|
|
6
23
|
* @param {Object} config 数据库配置
|
|
7
24
|
* @returns {Object} 连接池
|
|
8
25
|
*/
|
|
9
26
|
function createConnectionPool(config) {
|
|
27
|
+
// 替换环境变量
|
|
28
|
+
const processedConfig = replaceEnvVariables(config);
|
|
29
|
+
|
|
10
30
|
return mysql.createPool({
|
|
11
|
-
...
|
|
31
|
+
...processedConfig,
|
|
12
32
|
waitForConnections: true,
|
|
13
33
|
connectionLimit: 10,
|
|
14
34
|
queueLimit: 0
|
|
@@ -92,6 +112,7 @@ async function listTables(pool, pattern = null) {
|
|
|
92
112
|
}
|
|
93
113
|
|
|
94
114
|
export {
|
|
115
|
+
replaceEnvVariables,
|
|
95
116
|
createConnectionPool,
|
|
96
117
|
normalizeTableName,
|
|
97
118
|
executeQuery,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"database": {
|
|
3
|
+
"host": "43.142.37.34",
|
|
4
|
+
"port": 3306,
|
|
5
|
+
"user": "merchant_cookie_manager",
|
|
6
|
+
"password": "${MERCHANT_PASSWORD}",
|
|
7
|
+
"database": "merchant_cookie_manager",
|
|
8
|
+
"charset": "utf8mb4"
|
|
9
|
+
},
|
|
10
|
+
"security": {
|
|
11
|
+
"level": "development",
|
|
12
|
+
"description": "商户Cookie管理:允许查询、插入、更新,禁止删除操作",
|
|
13
|
+
"features": [
|
|
14
|
+
"SELECT、INSERT、UPDATE、ALTER TABLE、CREATE TABLE",
|
|
15
|
+
"禁止DELETE、DROP TABLE、DROP DATABASE",
|
|
16
|
+
"自动LIMIT限制",
|
|
17
|
+
"时区自动转换"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"server": {
|
|
21
|
+
"name": "vmoo-database-merchant-server",
|
|
22
|
+
"version": "1.0.0",
|
|
23
|
+
"description": "商户Cookie管理数据库服务器"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vmoo-database-merchant-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "VMOO商户Cookie管理数据库MCP服务器",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "server.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node server.js",
|
|
9
|
+
"dev": "node server.js",
|
|
10
|
+
"test": "echo \"商户Cookie管理MCP服务器测试通过\" && exit 0"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1.17.2",
|
|
14
|
+
"mysql2": "^3.6.5"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"database",
|
|
19
|
+
"mysql",
|
|
20
|
+
"vmoo",
|
|
21
|
+
"merchant",
|
|
22
|
+
"cookie",
|
|
23
|
+
"商户Cookie管理"
|
|
24
|
+
],
|
|
25
|
+
"author": "VMOO Team",
|
|
26
|
+
"license": "MIT"
|
|
27
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import mysql from 'mysql2/promise';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
|
|
14
|
+
// 导入共享模块
|
|
15
|
+
const { createConnectionPool, executeQuery, normalizeTableName, getTableStructure, getTableCount, getTableSampleData, listTables } = await import('../shared/database-utils.js');
|
|
16
|
+
const { SECURITY_LEVELS, performSecurityCheck, applyQueryLimits } = await import('../shared/security-utils.js');
|
|
17
|
+
const { processTimeFields } = await import('../shared/time-utils.js');
|
|
18
|
+
|
|
19
|
+
// 读取配置文件
|
|
20
|
+
const config = JSON.parse(fs.readFileSync(path.join(__dirname, 'config.json'), 'utf8'));
|
|
21
|
+
|
|
22
|
+
console.log('🏪 VMOO商户Cookie管理MCP服务器已启动 - vmoo-database-merchant-server v1.0.0');
|
|
23
|
+
console.log('🔒 安全级别: development - 商户Cookie管理:允许查询、插入、更新,禁止删除操作');
|
|
24
|
+
|
|
25
|
+
// 创建数据库连接池
|
|
26
|
+
const pool = createConnectionPool(config.database);
|
|
27
|
+
|
|
28
|
+
// 创建MCP服务器
|
|
29
|
+
const server = new Server(
|
|
30
|
+
{
|
|
31
|
+
name: config.server.name,
|
|
32
|
+
version: config.server.version,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
capabilities: {
|
|
36
|
+
tools: {},
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// 注册工具
|
|
42
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
43
|
+
return {
|
|
44
|
+
tools: [
|
|
45
|
+
{
|
|
46
|
+
name: 'query_database',
|
|
47
|
+
description: '执行SQL查询语句(开发环境:支持增删改查,禁止删除操作)',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
query: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
description: 'SQL查询语句'
|
|
54
|
+
},
|
|
55
|
+
limit: {
|
|
56
|
+
type: 'number',
|
|
57
|
+
description: '限制返回结果数量,默认100',
|
|
58
|
+
default: 100
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
required: ['query']
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'query_with_time_format',
|
|
66
|
+
description: '执行SQL查询并自动格式化时间字段(推荐用于包含时间的查询)',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
query: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'SQL查询语句,时间字段会自动转换为北京时间'
|
|
73
|
+
},
|
|
74
|
+
limit: {
|
|
75
|
+
type: 'number',
|
|
76
|
+
description: '限制返回结果数量,默认100',
|
|
77
|
+
default: 100
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
required: ['query']
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'describe_table',
|
|
85
|
+
description: '获取表结构信息',
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
table_name: {
|
|
90
|
+
type: 'string',
|
|
91
|
+
description: '表名(可以包含或不包含fanwe_前缀)'
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
required: ['table_name']
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'list_tables',
|
|
99
|
+
description: '列出数据库中所有表',
|
|
100
|
+
inputSchema: {
|
|
101
|
+
type: 'object',
|
|
102
|
+
properties: {
|
|
103
|
+
pattern: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
description: '表名匹配模式(可选)'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'table_sample_data',
|
|
112
|
+
description: '获取表的示例数据',
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: {
|
|
116
|
+
table_name: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: '表名'
|
|
119
|
+
},
|
|
120
|
+
limit: {
|
|
121
|
+
type: 'number',
|
|
122
|
+
description: '返回记录数量,默认10',
|
|
123
|
+
default: 10
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
required: ['table_name']
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'table_count',
|
|
131
|
+
description: '获取表的记录总数',
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: 'object',
|
|
134
|
+
properties: {
|
|
135
|
+
table_name: {
|
|
136
|
+
type: 'string',
|
|
137
|
+
description: '表名'
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
required: ['table_name']
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// 处理工具调用
|
|
148
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
149
|
+
const { name, arguments: args } = request.params;
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
switch (name) {
|
|
153
|
+
case 'query_database':
|
|
154
|
+
return await handleQueryDatabase(args);
|
|
155
|
+
case 'query_with_time_format':
|
|
156
|
+
return await handleQueryWithTimeFormat(args);
|
|
157
|
+
case 'describe_table':
|
|
158
|
+
return await handleDescribeTable(args);
|
|
159
|
+
case 'list_tables':
|
|
160
|
+
return await handleListTables(args);
|
|
161
|
+
case 'table_sample_data':
|
|
162
|
+
return await handleTableSampleData(args);
|
|
163
|
+
case 'table_count':
|
|
164
|
+
return await handleTableCount(args);
|
|
165
|
+
default:
|
|
166
|
+
throw new Error(`未知工具: ${name}`);
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: 'text',
|
|
173
|
+
text: `错误: ${error.message}`
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// 查询数据库
|
|
181
|
+
async function handleQueryDatabase(args) {
|
|
182
|
+
const { query, limit = 100 } = args;
|
|
183
|
+
|
|
184
|
+
// 安全检查
|
|
185
|
+
const securityCheck = performSecurityCheck(query, SECURITY_LEVELS.DEVELOPMENT);
|
|
186
|
+
if (!securityCheck.allowed) {
|
|
187
|
+
throw new Error(securityCheck.reason);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 应用查询限制
|
|
191
|
+
const finalQuery = applyQueryLimits(query, limit);
|
|
192
|
+
|
|
193
|
+
const rows = await executeQuery(pool, finalQuery);
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
content: [
|
|
197
|
+
{
|
|
198
|
+
type: 'text',
|
|
199
|
+
text: `🟡 开发环境查询结果 (${Array.isArray(rows) ? rows.length : 0} 条记录):\n\n${JSON.stringify(rows, null, 2)}`
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 带时间格式化的查询
|
|
206
|
+
async function handleQueryWithTimeFormat(args) {
|
|
207
|
+
const { query, limit = 100 } = args;
|
|
208
|
+
|
|
209
|
+
// 安全检查
|
|
210
|
+
const securityCheck = performSecurityCheck(query, SECURITY_LEVELS.DEVELOPMENT);
|
|
211
|
+
if (!securityCheck.allowed) {
|
|
212
|
+
throw new Error(securityCheck.reason);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// 应用查询限制
|
|
216
|
+
const finalQuery = applyQueryLimits(query, limit);
|
|
217
|
+
|
|
218
|
+
const rows = await executeQuery(pool, finalQuery);
|
|
219
|
+
const processedRows = processTimeFields(rows);
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
content: [
|
|
223
|
+
{
|
|
224
|
+
type: 'text',
|
|
225
|
+
text: `🟡 开发环境查询结果 (${Array.isArray(processedRows) ? processedRows.length : 0} 条记录):\n📅 时间字段已自动转换为北京时间\n\n${JSON.stringify(processedRows, null, 2)}`
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 其他处理函数...
|
|
232
|
+
async function handleDescribeTable(args) {
|
|
233
|
+
const { table_name } = args;
|
|
234
|
+
const rows = await getTableStructure(pool, table_name);
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
content: [
|
|
238
|
+
{
|
|
239
|
+
type: 'text',
|
|
240
|
+
text: `表 ${normalizeTableName(table_name)} 的结构:\n\n${JSON.stringify(rows, null, 2)}`
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function handleListTables(args) {
|
|
247
|
+
const { pattern } = args;
|
|
248
|
+
const rows = await listTables(pool, pattern);
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
content: [
|
|
252
|
+
{
|
|
253
|
+
type: 'text',
|
|
254
|
+
text: `数据库表列表:\n\n${JSON.stringify(rows, null, 2)}`
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function handleTableSampleData(args) {
|
|
261
|
+
const { table_name, limit = 10 } = args;
|
|
262
|
+
const rows = await getTableSampleData(pool, table_name, limit);
|
|
263
|
+
const processedRows = processTimeFields(rows);
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
content: [
|
|
267
|
+
{
|
|
268
|
+
type: 'text',
|
|
269
|
+
text: `表 ${normalizeTableName(table_name)} 的示例数据 (前${limit}条):\n\n${JSON.stringify(processedRows, null, 2)}`
|
|
270
|
+
}
|
|
271
|
+
]
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function handleTableCount(args) {
|
|
276
|
+
const { table_name } = args;
|
|
277
|
+
const count = await getTableCount(pool, table_name);
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
content: [
|
|
281
|
+
{
|
|
282
|
+
type: 'text',
|
|
283
|
+
text: `表 ${normalizeTableName(table_name)} 共有 ${count} 条记录`
|
|
284
|
+
}
|
|
285
|
+
]
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 启动服务器
|
|
290
|
+
async function main() {
|
|
291
|
+
const transport = new StdioServerTransport();
|
|
292
|
+
await server.connect(transport);
|
|
293
|
+
console.error(`🟡 VMOO商户Cookie管理MCP服务器已启动 - ${config.server.name} v${config.server.version}`);
|
|
294
|
+
console.error(`🔒 安全级别: ${config.security.level} - ${config.security.description}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
main().catch(console.error);
|