vmoo-mcp-database-server 1.3.8 → 1.3.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
@@ -188,6 +188,26 @@ node mcp-vmoo-database-server.js 2> debug.log
188
188
  - 验证Augment配置文件路径正确
189
189
  - 确保Node.js版本 >= 18.0.0
190
190
 
191
+ ## 🚀 自动发布
192
+
193
+ 本项目已配置 GitHub Actions CI/CD,推送到 `main` 分支时会自动发布新版本到 npm。
194
+
195
+ ### 发版流程
196
+
197
+ ```bash
198
+ # 1. 修改 package.json 中的 version
199
+ # 2. 提交并推送
200
+ git add .
201
+ git commit -m "bump: 版本 x.x.x - 变更说明"
202
+ git push origin main
203
+ ```
204
+
205
+ - ✅ 自动检测版本号变化,仅在版本号更新时触发发布
206
+ - ✅ 仅核心文件变更时触发(`shared/`、`vmoo-database-*/`、`bin/`、`index.js`、`package.json`)
207
+ - ⏭️ 版本号未变化时自动跳过
208
+
209
+ > 首次配置请参考 [NPM 自动发布配置指南](docs/npm-publish-setup.md)
210
+
191
211
  ## 许可证
192
212
 
193
213
  MIT License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vmoo-mcp-database-server",
3
- "version": "1.3.8",
3
+ "version": "1.3.10",
4
4
  "description": "VMOO数据库MCP服务器集合 - 支持开发和生产环境的安全数据库访问",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -30,7 +30,7 @@ function createConnectionPool(config) {
30
30
  return mysql.createPool({
31
31
  ...processedConfig,
32
32
  waitForConnections: true,
33
- connectionLimit: 10,
33
+ connectionLimit: 3,
34
34
  queueLimit: 0
35
35
  });
36
36
  }
@@ -139,42 +139,42 @@ function anonymizeBankCard(bankCard) {
139
139
 
140
140
  /**
141
141
  * 对查询结果进行数据匿名化处理
142
+ * 使用精确字段名匹配,避免误伤业务数据(如 location_name, goods_name 等)
142
143
  * @param {Array} rows 查询结果
143
144
  * @returns {Array} 匿名化后的结果
144
145
  */
145
146
  function anonymizeQueryResults(rows) {
146
147
  if (!Array.isArray(rows)) return rows;
147
148
 
148
- // 需要匿名化的字段映射
149
- const fieldMappings = {
150
- // 手机号字段
151
- mobile: anonymizePhone,
152
- phone: anonymizePhone,
153
- tel: anonymizePhone,
154
- telephone: anonymizePhone,
149
+ // 精确匹配的字段名 -> 匿名化函数映射
150
+ // 只列举数据库中真正包含用户隐私的字段名
151
+ const exactFieldMappings = {
152
+ // === 手机号字段 ===
153
+ 'mobile': anonymizePhone, // fanwe_user.mobile, fanwe_dc_consignee.mobile, fanwe_dc_order.mobile, ps_user.mobile
154
+ 'phone': anonymizePhone, // ps_sender.phone, ps_receiver.phone
155
+ 'pre_mobile': anonymizePhone, // fanwe_dc_order.pre_mobile
156
+ 'lottery_mobile': anonymizePhone, // fanwe_user.lottery_mobile
155
157
 
156
- // 身份证字段
157
- id_card: anonymizeIdCard,
158
- identity_card: anonymizeIdCard,
159
- id_number: anonymizeIdCard,
158
+ // === 身份证字段 ===
159
+ 'id_card': anonymizeIdCard, // ps_user.id_card
160
160
 
161
- // 邮箱字段
162
- email: anonymizeEmail,
163
- mail: anonymizeEmail,
161
+ // === 邮箱字段 ===
162
+ 'email': anonymizeEmail, // fanwe_user.email
164
163
 
165
- // 姓名字段
166
- name: anonymizeName,
167
- real_name: anonymizeName,
168
- user_name: anonymizeName,
169
- consignee: anonymizeName,
164
+ // === 真实姓名字段(仅限真正的人名,不包括业务名称) ===
165
+ 'true_name': anonymizeName, // ps_user.true_name, ps_order.true_name
166
+ 'trun_old_true_name': anonymizeName,// ps_order.trun_old_true_name (转单前骑手名)
167
+ 'consignee': anonymizeName, // fanwe_dc_consignee.consignee, fanwe_dc_order.consignee (收货人姓名)
170
168
 
171
- // 地址字段
172
- address: anonymizeAddress,
173
- delivery_address: anonymizeAddress,
169
+ // === 用户地址字段(仅限收货/配送地址,不包括店铺地址等业务地址) ===
170
+ 'address': anonymizeAddress, // fanwe_dc_consignee.address, ps_sender.address, ps_receiver.address
171
+ 'api_address': anonymizeAddress, // fanwe_dc_consignee.api_address, fanwe_dc_order.api_address
172
+ 'address_detail': anonymizeAddress, // ps_sender.address_detail, ps_receiver.address_detail
173
+ 'geo_address': anonymizeAddress, // fanwe_user.geo_address
174
174
 
175
- // 银行卡字段
176
- bank_card: anonymizeBankCard,
177
- card_number: anonymizeBankCard
175
+ // === 银行卡字段 ===
176
+ 'bank_card': anonymizeBankCard,
177
+ 'card_number': anonymizeBankCard
178
178
  };
179
179
 
180
180
  return rows.map(row => {
@@ -183,13 +183,11 @@ function anonymizeQueryResults(rows) {
183
183
  Object.keys(anonymizedRow).forEach(key => {
184
184
  const lowerKey = key.toLowerCase();
185
185
 
186
- // 检查是否需要匿名化
187
- Object.keys(fieldMappings).forEach(fieldPattern => {
188
- if (lowerKey.includes(fieldPattern)) {
189
- const anonymizeFunc = fieldMappings[fieldPattern];
190
- anonymizedRow[key] = anonymizeFunc(anonymizedRow[key]);
191
- }
192
- });
186
+ // 使用精确匹配,而非 includes 模糊匹配
187
+ if (exactFieldMappings[lowerKey]) {
188
+ const anonymizeFunc = exactFieldMappings[lowerKey];
189
+ anonymizedRow[key] = anonymizeFunc(anonymizedRow[key]);
190
+ }
193
191
  });
194
192
 
195
193
  return anonymizedRow;
@@ -17,8 +17,9 @@ class QueryRateLimiter {
17
17
  this.maxQueriesPerMinute = 10;
18
18
  this.cleanupInterval = 60000; // 1分钟清理一次
19
19
 
20
- // 定期清理过期记录
21
- setInterval(() => this.cleanup(), this.cleanupInterval);
20
+ // 定期清理过期记录(使用 unref 防止 timer 阻止进程退出)
21
+ const cleanupTimer = setInterval(() => this.cleanup(), this.cleanupInterval);
22
+ cleanupTimer.unref();
22
23
  }
23
24
 
24
25
  /**
@@ -283,6 +283,17 @@ async function main() {
283
283
  console.error(`VMOO配送测试站MCP服务器已启动 - ${config.server.name} v${config.server.version}`);
284
284
  console.error(`安全级别: ${config.security.level} - ${config.security.description}`);
285
285
  console.error(`️ 表前缀: ${prefixConfig ? (prefixConfig.enabled ? prefixConfig.prefix : '无前缀') : 'fanwe_(默认)'}`);
286
+
287
+ // Graceful shutdown:当 MCP 通信管道关闭时,主动退出进程
288
+ const gracefulShutdown = async () => {
289
+ console.error('MCP服务器正在关闭...');
290
+ try { await pool.end(); } catch (e) { /* ignore */ }
291
+ process.exit(0);
292
+ };
293
+
294
+ process.stdin.on('end', gracefulShutdown);
295
+ process.on('SIGTERM', gracefulShutdown);
296
+ process.on('SIGINT', gracefulShutdown);
286
297
  }
287
298
 
288
299
  main().catch(console.error);
@@ -299,6 +299,17 @@ async function main() {
299
299
  console.error(`安全级别: ${config.security.level} - ${config.security.description}`);
300
300
  console.error(`️ 防护功能: 频率限制防爬虫数据匿名化`);
301
301
  console.error(`️ 表前缀: ${prefixConfig ? (prefixConfig.enabled ? prefixConfig.prefix : '无前缀') : 'fanwe_(默认)'}`);
302
+
303
+ // Graceful shutdown:当 MCP 通信管道关闭时,主动退出进程
304
+ const gracefulShutdown = async () => {
305
+ console.error('MCP服务器正在关闭...');
306
+ try { await pool.end(); } catch (e) { /* ignore */ }
307
+ process.exit(0);
308
+ };
309
+
310
+ process.stdin.on('end', gracefulShutdown);
311
+ process.on('SIGTERM', gracefulShutdown);
312
+ process.on('SIGINT', gracefulShutdown);
302
313
  }
303
314
 
304
315
  main().catch(console.error);
@@ -276,6 +276,17 @@ async function main() {
276
276
  await server.connect(transport);
277
277
  console.error(`VMOO开发环境MCP服务器已启动 - ${config.server.name} v${config.server.version}`);
278
278
  console.error(`安全级别: ${config.security.level} - ${config.security.description}`);
279
+
280
+ // Graceful shutdown:当 MCP 通信管道关闭时,主动退出进程
281
+ const gracefulShutdown = async () => {
282
+ console.error('MCP服务器正在关闭...');
283
+ try { await pool.end(); } catch (e) { /* ignore */ }
284
+ process.exit(0);
285
+ };
286
+
287
+ process.stdin.on('end', gracefulShutdown);
288
+ process.on('SIGTERM', gracefulShutdown);
289
+ process.on('SIGINT', gracefulShutdown);
279
290
  }
280
291
 
281
292
  main().catch(console.error);
@@ -295,6 +295,17 @@ async function main() {
295
295
  console.error(`VMOO生产环境MCP服务器已启动 - ${config.server.name} v${config.server.version}`);
296
296
  console.error(`安全级别: ${config.security.level} - ${config.security.description}`);
297
297
  console.error(`️ 防护功能: 频率限制防爬虫数据匿名化`);
298
+
299
+ // Graceful shutdown:当 MCP 通信管道关闭时,主动退出进程
300
+ const gracefulShutdown = async () => {
301
+ console.error('MCP服务器正在关闭...');
302
+ try { await pool.end(); } catch (e) { /* ignore */ }
303
+ process.exit(0);
304
+ };
305
+
306
+ process.stdin.on('end', gracefulShutdown);
307
+ process.on('SIGTERM', gracefulShutdown);
308
+ process.on('SIGINT', gracefulShutdown);
298
309
  }
299
310
 
300
311
  main().catch(console.error);