mcp-adder-simple 1.1.2 → 1.1.3
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/cache.js +65 -0
- package/config.js +24 -0
- package/handlers/initialize.js +22 -0
- package/handlers/tools.js +47 -0
- package/package.json +8 -3
- package/tools/calculator.js +51 -0
- package/tools/index.js +72 -0
- package/tools/sup8.js +133 -0
- package/utils/dbhelper.js +116 -0
- package/utils/http.js +141 -0
package/cache.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 缓存模块
|
|
3
|
+
* 提供基于内存的缓存功能,支持过期时间
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { CACHE_DURATION } = require('./config');
|
|
7
|
+
|
|
8
|
+
const cache = new Map();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 获取缓存数据
|
|
12
|
+
* @param {string} key - 缓存键
|
|
13
|
+
* @returns {any|null} 缓存的数据,如果不存在或已过期则返回 null
|
|
14
|
+
*/
|
|
15
|
+
function getCache(key) {
|
|
16
|
+
const cached = cache.get(key);
|
|
17
|
+
if (!cached) return null;
|
|
18
|
+
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
if (now - cached.timestamp > CACHE_DURATION) {
|
|
21
|
+
// 缓存已过期,删除
|
|
22
|
+
cache.delete(key);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.error(`[CACHE] 命中缓存: ${key}`);
|
|
27
|
+
return cached.data;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 设置缓存数据
|
|
32
|
+
* @param {string} key - 缓存键
|
|
33
|
+
* @param {any} data - 要缓存的数据
|
|
34
|
+
*/
|
|
35
|
+
function setCache(key, data) {
|
|
36
|
+
cache.set(key, {
|
|
37
|
+
data: data,
|
|
38
|
+
timestamp: Date.now()
|
|
39
|
+
});
|
|
40
|
+
console.error(`[CACHE] 已缓存: ${key}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 清除指定缓存
|
|
45
|
+
* @param {string} key - 缓存键
|
|
46
|
+
*/
|
|
47
|
+
function clearCache(key) {
|
|
48
|
+
cache.delete(key);
|
|
49
|
+
console.error(`[CACHE] 已清除缓存: ${key}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 清除所有缓存
|
|
54
|
+
*/
|
|
55
|
+
function clearAllCache() {
|
|
56
|
+
cache.clear();
|
|
57
|
+
console.error(`[CACHE] 已清除所有缓存`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
getCache,
|
|
62
|
+
setCache,
|
|
63
|
+
clearCache,
|
|
64
|
+
clearAllCache
|
|
65
|
+
};
|
package/config.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置文件
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
sup8Host: "https://api-fat.yesno.com.cn",
|
|
7
|
+
sup8OpenHost: "https://open-fat.yesno.com.cn",
|
|
8
|
+
AK: "d913f38847214cd0a4169a959af001de",
|
|
9
|
+
SK: "1b555b531eafff1d7d6e34c5888f05e0",
|
|
10
|
+
CACHE_DURATION: 10 * 60 * 1000, // 10分钟缓存时间
|
|
11
|
+
|
|
12
|
+
// MySQL 数据库配置
|
|
13
|
+
MYSQL_HOST: "127.0.0.1",
|
|
14
|
+
MYSQL_PORT: "3306",
|
|
15
|
+
MYSQL_USER: "root",
|
|
16
|
+
MYSQL_PASS: "123456",
|
|
17
|
+
MYSQL_DB: "supercarrier_masterdata",
|
|
18
|
+
|
|
19
|
+
serverInfo: {
|
|
20
|
+
name: 'mcp-calculator-server',
|
|
21
|
+
version: '1.0.0'
|
|
22
|
+
},
|
|
23
|
+
protocolVersion: '2024-10-07'
|
|
24
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 初始化处理器
|
|
3
|
+
* 处理 MCP 的 initialize 请求
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { serverInfo, protocolVersion } = require('../config');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 处理 initialize 请求
|
|
10
|
+
* @returns {Object} 初始化响应
|
|
11
|
+
*/
|
|
12
|
+
function handleInitialize() {
|
|
13
|
+
return {
|
|
14
|
+
protocolVersion: protocolVersion,
|
|
15
|
+
serverInfo: serverInfo,
|
|
16
|
+
capabilities: {
|
|
17
|
+
tools: {} // 声明支持 tools 功能
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = { handleInitialize };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具调用处理器
|
|
3
|
+
* 根据工具名称分发到对应的工具函数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { addNumbers, multiplyNumbers } = require('../tools/calculator');
|
|
7
|
+
const { sup8Decode, sup8QueryCode, queryEnterpriseNo } = require('../tools/sup8');
|
|
8
|
+
const { queryEnterprise: queryEnterpriseDB, queryByEnterpriseNo } = require('../tools/database');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 处理工具调用请求
|
|
12
|
+
* @param {Object} params - 请求参数
|
|
13
|
+
* @param {string} params.name - 工具名称
|
|
14
|
+
* @param {Object} params.arguments - 工具参数
|
|
15
|
+
* @returns {Object} 工具执行结果
|
|
16
|
+
*/
|
|
17
|
+
async function handleToolCall(params) {
|
|
18
|
+
const { name, arguments: args } = params;
|
|
19
|
+
|
|
20
|
+
switch (name) {
|
|
21
|
+
case 'add_numbers':
|
|
22
|
+
return addNumbers(args.a, args.b);
|
|
23
|
+
|
|
24
|
+
case 'multiply_numbers':
|
|
25
|
+
return multiplyNumbers(args.a, args.b);
|
|
26
|
+
|
|
27
|
+
case 'sup8_decode':
|
|
28
|
+
return sup8Decode(args.code, args.enterpriseNo);
|
|
29
|
+
|
|
30
|
+
case 'sup8_query_code':
|
|
31
|
+
return sup8QueryCode(args.code, args.enterpriseNo);
|
|
32
|
+
|
|
33
|
+
case 'query_enterprise_no':
|
|
34
|
+
return queryEnterpriseNo(args.enter);
|
|
35
|
+
|
|
36
|
+
case 'query_enterprise_no_db':
|
|
37
|
+
return queryEnterpriseDB(args.name);
|
|
38
|
+
|
|
39
|
+
case 'query_by_enterprise_no':
|
|
40
|
+
return queryByEnterpriseNo(args.enterpriseNo);
|
|
41
|
+
|
|
42
|
+
default:
|
|
43
|
+
throw new Error(`未知工具: ${name}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = { handleToolCall };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-adder-simple",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"main": "mcp-adder.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mcp-adder-simple": "./bin/mcp-adder-simple.cmd"
|
|
@@ -26,9 +26,14 @@
|
|
|
26
26
|
"mcp-adder.js",
|
|
27
27
|
"bin/mcp-adder-simple.cmd",
|
|
28
28
|
"package.json",
|
|
29
|
-
"README.md"
|
|
29
|
+
"README.md",
|
|
30
|
+
"config.js",
|
|
31
|
+
"cache.js",
|
|
32
|
+
"handlers/**",
|
|
33
|
+
"tools/**",
|
|
34
|
+
"utils/**"
|
|
30
35
|
],
|
|
31
36
|
"dependencies": {
|
|
32
37
|
"mysql2": "^3.16.1"
|
|
33
38
|
}
|
|
34
|
-
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 计算器工具模块
|
|
3
|
+
* 提供加法和乘法功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 将两个数字相加
|
|
8
|
+
* @param {number} a - 第一个数
|
|
9
|
+
* @param {number} b - 第二个数
|
|
10
|
+
* @returns {Object} MCP 格式的响应
|
|
11
|
+
*/
|
|
12
|
+
function addNumbers(a, b) {
|
|
13
|
+
if (typeof a !== 'number' || typeof b !== 'number') {
|
|
14
|
+
throw new Error('参数必须是数字');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
content: [
|
|
19
|
+
{
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: `${a} + ${b} = ${a + b}`
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 将两个数字相乘
|
|
29
|
+
* @param {number} a - 第一个数
|
|
30
|
+
* @param {number} b - 第二个数
|
|
31
|
+
* @returns {Object} MCP 格式的响应
|
|
32
|
+
*/
|
|
33
|
+
function multiplyNumbers(a, b) {
|
|
34
|
+
if (typeof a !== 'number' || typeof b !== 'number') {
|
|
35
|
+
throw new Error('参数必须是数字');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: 'text',
|
|
42
|
+
text: `${a} × ${b} = ${a * b}`
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
addNumbers,
|
|
50
|
+
multiplyNumbers
|
|
51
|
+
};
|
package/tools/index.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具列表注册
|
|
3
|
+
* 定义所有可用的 MCP 工具及其参数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function handleToolsList() {
|
|
7
|
+
return {
|
|
8
|
+
tools: [
|
|
9
|
+
{
|
|
10
|
+
name: 'add_numbers',
|
|
11
|
+
description: '将两个数字相加',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
a: { type: 'number', description: '第一个数' },
|
|
16
|
+
b: { type: 'number', description: '第二个数' }
|
|
17
|
+
},
|
|
18
|
+
required: ['a', 'b']
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'multiply_numbers',
|
|
23
|
+
description: '将两个数字相乘',
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
a: { type: 'number', description: '第一个数' },
|
|
28
|
+
b: { type: 'number', description: '第二个数' }
|
|
29
|
+
},
|
|
30
|
+
required: ['a', 'b']
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'sup8_decode',
|
|
35
|
+
description: '解码(带10分钟缓存)',
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
code: { type: 'string', description: '数码' },
|
|
40
|
+
enterpriseNo: { type: 'string', description: '企业号' }
|
|
41
|
+
},
|
|
42
|
+
required: ['code', 'enterpriseNo']
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'sup8_query_code',
|
|
47
|
+
description: '查询数码信息(带10分钟缓存)',
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
properties: {
|
|
51
|
+
code: { type: 'string', description: '数码' },
|
|
52
|
+
enterpriseNo: { type: 'string', description: '企业号' }
|
|
53
|
+
},
|
|
54
|
+
required: ['code', 'enterpriseNo']
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: 'sup8_query_enterprise_no',
|
|
59
|
+
description: '查询企业号信息',
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: 'object',
|
|
62
|
+
properties: {
|
|
63
|
+
enterpriseNo: { type: 'string', description: '企业号或企业名称' }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { handleToolsList };
|
package/tools/sup8.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sup8 API 工具模块
|
|
3
|
+
* 提供解码和查询数码信息功能(带缓存)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { sup8Host, AK, SK, sup8OpenHost } = require('../config');
|
|
7
|
+
const { getCache, setCache } = require('../cache');
|
|
8
|
+
const { get, post } = require('../utils/http');
|
|
9
|
+
const db = require('../utils/dbhelper');
|
|
10
|
+
/**
|
|
11
|
+
* Sup8 解码
|
|
12
|
+
* @param {string} code - 数码
|
|
13
|
+
* @param {string} enterpriseNo - 企业号
|
|
14
|
+
* @returns {Object} MCP 格式的响应
|
|
15
|
+
*/
|
|
16
|
+
async function sup8Decode(code, enterpriseNo) {
|
|
17
|
+
if (typeof code !== 'string' || typeof enterpriseNo !== 'string') {
|
|
18
|
+
throw new Error('参数必须是字符串');
|
|
19
|
+
}
|
|
20
|
+
const url = `${sup8Host}/digit/v1/decode/decode?digit=${encodeURIComponent(code)}&enterpriseNo=${encodeURIComponent(enterpriseNo)}`;
|
|
21
|
+
|
|
22
|
+
const result = await get(url);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: 'text',
|
|
28
|
+
text: JSON.stringify(result, null, 2)
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 查询数码信息(带缓存)
|
|
36
|
+
* @param {string} code - 数码
|
|
37
|
+
* @param {string} enterpriseNo - 企业号
|
|
38
|
+
* @returns {Object} MCP 格式的响应
|
|
39
|
+
*/
|
|
40
|
+
async function sup8QueryCode(code, enterpriseNo) {
|
|
41
|
+
if (typeof code !== 'string' || typeof enterpriseNo !== 'string') {
|
|
42
|
+
throw new Error('参数必须是字符串');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 构建缓存键和 URL
|
|
46
|
+
const cacheKey = `query:${code}:${enterpriseNo}`;
|
|
47
|
+
const url = `${sup8Host}/digit/v2/digit/get-digit-info?code=${encodeURIComponent(code)}&enterpriseNo=${encodeURIComponent(enterpriseNo)}`;
|
|
48
|
+
|
|
49
|
+
// 先尝试从缓存获取
|
|
50
|
+
const cachedData = getCache(cacheKey);
|
|
51
|
+
if (cachedData) {
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: JSON.stringify(cachedData, null, 2)
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 缓存未命中,调用 API
|
|
63
|
+
const result = await get(url);
|
|
64
|
+
|
|
65
|
+
// 将结果存入缓存
|
|
66
|
+
setCache(cacheKey, result);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: JSON.stringify(result, null, 2)
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function queryEnterpriseNo(enter) {
|
|
79
|
+
|
|
80
|
+
const sql = 'SELECT * FROM t_sup8_cc_enterprisenumber';
|
|
81
|
+
const params = [];
|
|
82
|
+
|
|
83
|
+
if (enter && enter.trim()) {
|
|
84
|
+
params.push(`%${enter}%`, `%${enter}%`);
|
|
85
|
+
const finalSql = sql + ' WHERE name LIKE ? OR enterpriseno LIKE ? ';
|
|
86
|
+
const rows = await db.query(finalSql, params);
|
|
87
|
+
return formatMCPResponse(rows);
|
|
88
|
+
}
|
|
89
|
+
const rows = await db.query(sql, params);
|
|
90
|
+
return formatMCPResponse(rows);
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async function getSup8Token() {
|
|
97
|
+
const cacheKey = 'sup8Token';
|
|
98
|
+
const url = `${sup8OpenHost}/cncop/ca/v1/applications/get-token`;
|
|
99
|
+
setCache(cacheKey, 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhcHAiOiJ7XCJlbnRlcnByaXNlTm9cIjpcIjEzMTk4OTY1OTkxN1wiLFwiZW50ZXJwcmlzZVVzZXJJZFwiOlwiMjAwODM1MDA1NjgyNjU0NDEyOFwiLFwiYWNjZXNzS2V5XCI6XCJkOTEzZjM4ODQ3MjE0Y2QwYTQxNjlhOTU5YWYwMDFkZVwiLFwiYXBwSWRcIjpcIjIwMDkwOTMxNTg2NTQ0NTk5MDRcIixcImVudGVycHJpc2VVc2VyQWNjb3VudFwiOlwieHNfbHF4XCJ9Iiwic3ViIjoiMjAwOTA5MzE1ODY1NDQ1OTkwNCIsImlzcyI6Inllc25vLmNvbS5jbiIsImV4cCI6MTc2OTE3NjM5MDg0OSwiaWF0IjoxNzY5MTQ3NTkwODQ5LCJ2ZXJzaW9uIjoiMS4wIiwianRpIjoiMGJhZTM5NjIxYzA1NGI0ODk1MjQzYjIyMWZlZWM4MGYifQ.AapbK9QLI_pkiBBOZIaLl2ecH3vbrdNM17pmrybX1eA');
|
|
100
|
+
const cacheToken = getCache(cacheKey);
|
|
101
|
+
if (cacheToken) {
|
|
102
|
+
return cacheToken;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var body = {
|
|
106
|
+
"accessKey": AK,
|
|
107
|
+
"secretKeySign": SK
|
|
108
|
+
}
|
|
109
|
+
const result = await post(url, body)
|
|
110
|
+
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 格式化为 MCP 响应格式
|
|
115
|
+
* @param {*} data - 数据对象或数组
|
|
116
|
+
* @returns {Object} MCP 格式的响应
|
|
117
|
+
*/
|
|
118
|
+
function formatMCPResponse(data) {
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: 'text',
|
|
123
|
+
text: JSON.stringify(data, null, 2)
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = {
|
|
130
|
+
sup8Decode,
|
|
131
|
+
sup8QueryCode,
|
|
132
|
+
queryEnterpriseNo
|
|
133
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据库助手
|
|
3
|
+
* 提供通用的数据库操作方法
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const mysql = require('mysql2/promise');
|
|
7
|
+
const { MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB } = require('../config');
|
|
8
|
+
|
|
9
|
+
// 创建连接池
|
|
10
|
+
const pool = mysql.createPool({
|
|
11
|
+
host: MYSQL_HOST,
|
|
12
|
+
port: MYSQL_PORT,
|
|
13
|
+
user: MYSQL_USER,
|
|
14
|
+
password: MYSQL_PASS,
|
|
15
|
+
database: MYSQL_DB,
|
|
16
|
+
waitForConnections: true,
|
|
17
|
+
connectionLimit: 10,
|
|
18
|
+
queueLimit: 0
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 执行查询(SELECT)
|
|
23
|
+
* @param {string} sql - SQL 语句
|
|
24
|
+
* @param {Array} params - 查询参数
|
|
25
|
+
* @returns {Array} 查询结果行
|
|
26
|
+
*/
|
|
27
|
+
async function query(sql, params = []) {
|
|
28
|
+
try {
|
|
29
|
+
const [rows] = await pool.execute(sql, params);
|
|
30
|
+
console.log(`[DBHelper] 查询成功,返回 ${rows.length} 条记录`);
|
|
31
|
+
return rows;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(`[DBHelper] 查询失败:`, error.message);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 执行 INSERT/UPDATE/DELETE
|
|
40
|
+
* @param {string} sql - SQL 语句
|
|
41
|
+
* @param {Array} params - 查询参数
|
|
42
|
+
* @returns {Object} 结果信息 {affectedRows, insertId, etc.}
|
|
43
|
+
*/
|
|
44
|
+
async function execute(sql, params = []) {
|
|
45
|
+
try {
|
|
46
|
+
const [result] = await pool.execute(sql, params);
|
|
47
|
+
console.log(`[DBHelper] 执行成功,影响 ${result.affectedRows} 行`);
|
|
48
|
+
return result;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(`[DBHelper] 执行失败:`, error.message);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 查询单行数据
|
|
57
|
+
* @param {string} sql - SQL 语句
|
|
58
|
+
* @param {Array} params - 查询参数
|
|
59
|
+
* @returns {Object|null} 单行数据或 null
|
|
60
|
+
*/
|
|
61
|
+
async function queryOne(sql, params = []) {
|
|
62
|
+
const rows = await query(sql, params);
|
|
63
|
+
return rows.length > 0 ? rows[0] : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 开启事务
|
|
68
|
+
* @returns {Object} 事务连接对象
|
|
69
|
+
*/
|
|
70
|
+
async function beginTransaction() {
|
|
71
|
+
const conn = await pool.getConnection();
|
|
72
|
+
await conn.beginTransaction();
|
|
73
|
+
return conn;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 提交事务
|
|
78
|
+
* @param {Object} conn - 事务连接对象
|
|
79
|
+
*/
|
|
80
|
+
async function commit(conn) {
|
|
81
|
+
try {
|
|
82
|
+
await conn.commit();
|
|
83
|
+
} finally {
|
|
84
|
+
conn.release();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 回滚事务
|
|
90
|
+
* @param {Object} conn - 事务连接对象
|
|
91
|
+
*/
|
|
92
|
+
async function rollback(conn) {
|
|
93
|
+
try {
|
|
94
|
+
await conn.rollback();
|
|
95
|
+
} finally {
|
|
96
|
+
conn.release();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 关闭连接池
|
|
102
|
+
*/
|
|
103
|
+
async function close() {
|
|
104
|
+
await pool.end();
|
|
105
|
+
console.log('[DBHelper] 连接池已关闭');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
query,
|
|
110
|
+
execute,
|
|
111
|
+
queryOne,
|
|
112
|
+
beginTransaction,
|
|
113
|
+
commit,
|
|
114
|
+
rollback,
|
|
115
|
+
close
|
|
116
|
+
};
|
package/utils/http.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 工具模块
|
|
3
|
+
* 提供 GET 和 POST 请求方法
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const https = require('https');
|
|
7
|
+
const http = require('http');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 通用 HTTP 请求方法
|
|
11
|
+
* @param {string} url - 请求的 URL
|
|
12
|
+
* @param {Object} options - 请求选项
|
|
13
|
+
* @param {string} [options.method='GET'] - 请求方法 (GET/POST)
|
|
14
|
+
* @param {Object} [options.headers] - 请求头
|
|
15
|
+
* @param {Object|string} [options.body] - 请求体(用于 POST)
|
|
16
|
+
* @returns {Promise<Object>} 返回 JSON 格式的响应数据
|
|
17
|
+
*/
|
|
18
|
+
function request(url, options = {}) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const { method = 'GET', headers = {}, body } = options;
|
|
21
|
+
|
|
22
|
+
// 解析 URL
|
|
23
|
+
const urlObj = new URL(url);
|
|
24
|
+
const isHttps = urlObj.protocol === 'https:';
|
|
25
|
+
const requestFn = isHttps ? https.request : http.request;
|
|
26
|
+
|
|
27
|
+
// 设置默认请求头
|
|
28
|
+
const defaultHeaders = {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
'User-Agent': 'MCP-Server/1.0.0'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const mergedHeaders = { ...defaultHeaders, ...headers };
|
|
34
|
+
|
|
35
|
+
// 如果有 body,需要设置 Content-Length
|
|
36
|
+
let bodyString = '';
|
|
37
|
+
if (body) {
|
|
38
|
+
bodyString = typeof body === 'string' ? body : JSON.stringify(body);
|
|
39
|
+
mergedHeaders['Content-Length'] = Buffer.byteLength(bodyString);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 请求配置
|
|
43
|
+
const requestOptions = {
|
|
44
|
+
hostname: urlObj.hostname,
|
|
45
|
+
port: urlObj.port || (isHttps ? 443 : 80),
|
|
46
|
+
path: urlObj.pathname + urlObj.search,
|
|
47
|
+
method: method,
|
|
48
|
+
headers: mergedHeaders
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
console.error(`[HTTP] 发送请求头:`, JSON.stringify(requestOptions.headers, null, 2));
|
|
52
|
+
console.error(`[HTTP] ${method} ${url}`);
|
|
53
|
+
|
|
54
|
+
// 发起请求
|
|
55
|
+
const req = requestFn(requestOptions, (res) => {
|
|
56
|
+
let data = '';
|
|
57
|
+
|
|
58
|
+
res.on('data', (chunk) => {
|
|
59
|
+
data += chunk;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
res.on('end', () => {
|
|
63
|
+
try {
|
|
64
|
+
const jsonData = JSON.parse(data);
|
|
65
|
+
console.error(`[HTTP] 响应 (${res.statusCode}):`, JSON.stringify(jsonData, null, 2));
|
|
66
|
+
resolve(jsonData);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// 如果不是 JSON,返回原始数据
|
|
69
|
+
console.error(`[HTTP] 响应 (${res.statusCode}): ${data}`);
|
|
70
|
+
resolve({ data, statusCode: res.statusCode });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
req.on('error', (err) => {
|
|
76
|
+
console.error(`[HTTP] 请求失败:`, err.message);
|
|
77
|
+
reject(new Error(`HTTP请求失败: ${err.message}`));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// 对于 POST 等有 body 的请求,写入数据
|
|
81
|
+
if (bodyString) {
|
|
82
|
+
req.write(bodyString);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
req.end();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* HTTP GET 请求
|
|
91
|
+
* @param {string} url - 请求的 URL
|
|
92
|
+
* @param {Object} [options] - 请求选项
|
|
93
|
+
* @param {Object} [options.headers] - 请求头
|
|
94
|
+
* @returns {Promise<Object>} 返回 JSON 格式的响应数据
|
|
95
|
+
*/
|
|
96
|
+
function get(url, options = {}) {
|
|
97
|
+
return request(url, { ...options, method: 'GET' });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* HTTP POST 请求
|
|
102
|
+
* @param {string} url - 请求的 URL
|
|
103
|
+
* @param {Object|string} body - 请求体
|
|
104
|
+
* @param {Object} [options] - 请求选项
|
|
105
|
+
* @param {Object} [options.headers] - 请求头
|
|
106
|
+
* @returns {Promise<Object>} 返回 JSON 格式的响应数据
|
|
107
|
+
*/
|
|
108
|
+
function post(url, body, options = {}) {
|
|
109
|
+
return request(url, { ...options, method: 'POST', body });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* HTTP PUT 请求
|
|
114
|
+
* @param {string} url - 请求的 URL
|
|
115
|
+
* @param {Object|string} body - 请求体
|
|
116
|
+
* @param {Object} [options] - 请求选项
|
|
117
|
+
* @param {Object} [options.headers] - 请求头
|
|
118
|
+
* @returns {Promise<Object>} 返回 JSON 格式的响应数据
|
|
119
|
+
*/
|
|
120
|
+
function put(url, body, options = {}) {
|
|
121
|
+
return request(url, { ...options, method: 'PUT', body });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* HTTP DELETE 请求
|
|
126
|
+
* @param {string} url - 请求的 URL
|
|
127
|
+
* @param {Object} [options] - 请求选项
|
|
128
|
+
* @param {Object} [options.headers] - 请求头
|
|
129
|
+
* @returns {Promise<Object>} 返回 JSON 格式的响应数据
|
|
130
|
+
*/
|
|
131
|
+
function del(url, options = {}) {
|
|
132
|
+
return request(url, { ...options, method: 'DELETE' });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
request,
|
|
137
|
+
get,
|
|
138
|
+
post,
|
|
139
|
+
put,
|
|
140
|
+
delete: del
|
|
141
|
+
};
|