chaimi-keep-mcp 3.3.2-beta.1 → 3.3.3-beta.1
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/bin/cli.js +15 -3
- package/config.example.yaml +1 -1
- package/package.json +3 -2
- package/server.js +27 -2
- package/utils/validators.js +67 -1
package/bin/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* 柴米AI记账 MCP Server CLI 入口
|
|
5
5
|
* 智能创建/更新多个 Agent 配置,保护用户已有配置
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -20,7 +20,7 @@ const serverPath = path.join(__dirname, '..', 'server.js');
|
|
|
20
20
|
// 默认的柴米记账配置
|
|
21
21
|
const DEFAULT_CHAIMI_CONFIG = {
|
|
22
22
|
command: 'chaimi-keep-mcp',
|
|
23
|
-
description: '
|
|
23
|
+
description: '柴米AI记账 MCP Server - 支持 AI 工具直接记账',
|
|
24
24
|
env: {
|
|
25
25
|
MCP_OAUTH_URL: 'https://cloud1-2gfe5jhjef06b85d-1412172089.ap-shanghai.app.tcloudbase.com/mcpOAuth',
|
|
26
26
|
MCP_HUB_URL: 'https://cloud1-2gfe5jhjef06b85d-1412172089.ap-shanghai.app.tcloudbase.com/mcpHub-mcp',
|
|
@@ -38,6 +38,18 @@ const SUPPORTED_AGENTS = [
|
|
|
38
38
|
configPath: path.join(os.homedir(), '.mcporter', 'mcporter.json'),
|
|
39
39
|
format: 'json'
|
|
40
40
|
},
|
|
41
|
+
{
|
|
42
|
+
name: 'OpenClaw MCP Config',
|
|
43
|
+
configPath: path.join(os.homedir(), '.openclaw', 'workspace', 'config', 'mcporter.json'),
|
|
44
|
+
format: 'json',
|
|
45
|
+
platform: 'darwin'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: 'OpenClaw MCP Config (Linux)',
|
|
49
|
+
configPath: path.join(os.homedir(), '.openclaw', 'workspace', 'config', 'mcporter.json'),
|
|
50
|
+
format: 'json',
|
|
51
|
+
platform: 'linux'
|
|
52
|
+
},
|
|
41
53
|
{
|
|
42
54
|
name: 'WorkBuddy (Windows)',
|
|
43
55
|
configPath: path.join(os.homedir(), 'workbuddy', 'mcp.json'),
|
|
@@ -297,7 +309,7 @@ function showWelcome() {
|
|
|
297
309
|
|
|
298
310
|
console.error('+--------------------------------------------------------+');
|
|
299
311
|
console.error('| |');
|
|
300
|
-
console.error('|
|
|
312
|
+
console.error('| 柴米AI记账 MCP Server v' + ver + ' |');
|
|
301
313
|
console.error('| |');
|
|
302
314
|
console.error('| 支持: OpenClaw/hermes/WorkBuddy 等国产小龙虾 |');
|
|
303
315
|
console.error('| Claude/Cursor 等支持 MCP 的 Agent |');
|
package/config.example.yaml
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chaimi-keep-mcp",
|
|
3
|
-
"version": "3.3.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.3.3-beta.1",
|
|
4
|
+
"description": "柴米AI记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"chaimi-keep-mcp": "bin/cli.js"
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"license": "MIT",
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
47
|
+
"chaimi-keep-mcp": "^3.3.2-beta.1",
|
|
47
48
|
"dotenv": "^17.3.1",
|
|
48
49
|
"node-fetch": "^2.7.0"
|
|
49
50
|
},
|
package/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* 柴米AI记账 MCP Server (Node.js 版本)
|
|
4
4
|
* 适配微信云函数 mcpHub
|
|
5
5
|
* 支持 Claude Desktop、Cursor、WorkBuddy、OpenClaw
|
|
6
6
|
*/
|
|
@@ -280,7 +280,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
280
280
|
type: 'object',
|
|
281
281
|
properties: {
|
|
282
282
|
store: { type: 'string', description: '商家名称(必填)' },
|
|
283
|
-
date: { type: 'number', description: '
|
|
283
|
+
date: { type: 'number', description: '消费时间(毫秒级时间戳,13位数字。如提供 date_components 可不传此字段)' },
|
|
284
|
+
date_components: {
|
|
285
|
+
type: 'object',
|
|
286
|
+
description: '【推荐】消费时间组件。包含:year、month、day、hour、minute、second。服务端会自动转换为时间戳'
|
|
287
|
+
},
|
|
284
288
|
totalAmount: { type: 'number', description: '(必填)总金额(所有商品amount之和)' },
|
|
285
289
|
originalAmount: { type: 'number', description: '(必填)应付金额(优惠前)' },
|
|
286
290
|
discountAmount: { type: 'number', description: '(必填)优惠金额' },
|
|
@@ -1147,6 +1151,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1147
1151
|
break;
|
|
1148
1152
|
}
|
|
1149
1153
|
|
|
1154
|
+
// 【新增】转换 date_components 为 date
|
|
1155
|
+
if (processedArgs.date_components && !processedArgs.date) {
|
|
1156
|
+
const { year, month, day, hour, minute, second } = processedArgs.date_components;
|
|
1157
|
+
|
|
1158
|
+
// 校验时间组件完整性
|
|
1159
|
+
if (!year || !month || !day || hour === undefined || minute === undefined) {
|
|
1160
|
+
result = {
|
|
1161
|
+
success: false,
|
|
1162
|
+
error: 'date_components 不完整',
|
|
1163
|
+
code: 400,
|
|
1164
|
+
hint: '必须包含:year、month、day、hour、minute'
|
|
1165
|
+
};
|
|
1166
|
+
userMessage = '❌ 保存失败:时间信息不完整\n\n请确保 date_components 包含完整的年月日时分秒';
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// 转换为时间戳
|
|
1171
|
+
processedArgs.date = new Date(year, month - 1, day, hour, minute, second || 0).getTime();
|
|
1172
|
+
console.log(`[save_receipt] 时间组件转换: ${JSON.stringify(processedArgs.date_components)} → ${processedArgs.date}`);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1150
1175
|
// P1: 日期合理性检查
|
|
1151
1176
|
if (processedArgs.date) {
|
|
1152
1177
|
const validation = validateDate(processedArgs.date, '小票日期');
|
package/utils/validators.js
CHANGED
|
@@ -44,6 +44,72 @@ function validateDate(dateInput, fieldName = '日期') {
|
|
|
44
44
|
return { valid: true };
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* 校验时间组件合理性
|
|
49
|
+
* @param {Object} components - 时间组件对象
|
|
50
|
+
* @param {string} fieldName - 字段名称(用于错误提示)
|
|
51
|
+
* @returns {Object} { valid: boolean, error?: string, message?: string }
|
|
52
|
+
*/
|
|
53
|
+
function validateDateComponents(components, fieldName = '时间') {
|
|
54
|
+
if (!components || typeof components !== 'object') {
|
|
55
|
+
return {
|
|
56
|
+
valid: false,
|
|
57
|
+
error: '时间组件格式无效',
|
|
58
|
+
message: `❌ ${fieldName}组件必须是对象`
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { year, month, day, hour, minute, second } = components;
|
|
63
|
+
|
|
64
|
+
// 校验必填字段
|
|
65
|
+
const requiredFields = ['year', 'month', 'day', 'hour', 'minute'];
|
|
66
|
+
const missingFields = requiredFields.filter(f => components[f] === undefined || components[f] === null);
|
|
67
|
+
|
|
68
|
+
if (missingFields.length > 0) {
|
|
69
|
+
return {
|
|
70
|
+
valid: false,
|
|
71
|
+
error: '时间组件缺少必填字段',
|
|
72
|
+
message: `❌ ${fieldName}组件缺少: ${missingFields.join(', ')}`
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 校验数值范围
|
|
77
|
+
if (year < 1965 || year > 2100) {
|
|
78
|
+
return { valid: false, error: '年份不合法', message: `❌ ${fieldName}年份必须在 1965-2100 之间` };
|
|
79
|
+
}
|
|
80
|
+
if (month < 1 || month > 12) {
|
|
81
|
+
return { valid: false, error: '月份不合法', message: `❌ ${fieldName}月份必须在 1-12 之间` };
|
|
82
|
+
}
|
|
83
|
+
if (day < 1 || day > 31) {
|
|
84
|
+
return { valid: false, error: '日期不合法', message: `❌ ${fieldName}日期必须在 1-31 之间` };
|
|
85
|
+
}
|
|
86
|
+
if (hour < 0 || hour > 23) {
|
|
87
|
+
return { valid: false, error: '小时不合法', message: `❌ ${fieldName}小时必须在 0-23 之间` };
|
|
88
|
+
}
|
|
89
|
+
if (minute < 0 || minute > 59) {
|
|
90
|
+
return { valid: false, error: '分钟不合法', message: `❌ ${fieldName}分钟必须在 0-59 之间` };
|
|
91
|
+
}
|
|
92
|
+
if (second !== undefined && (second < 0 || second > 59)) {
|
|
93
|
+
return { valid: false, error: '秒数不合法', message: `❌ ${fieldName}秒数必须在 0-59 之间` };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 校验日期有效性(如 2025-02-30 是无效日期)
|
|
97
|
+
const testDate = new Date(year, month - 1, day);
|
|
98
|
+
if (testDate.getMonth() !== month - 1 || testDate.getDate() !== day) {
|
|
99
|
+
return { valid: false, error: '日期不存在', message: `❌ ${fieldName}日期不存在(如 2月30日)` };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 校验不能是未来时间
|
|
103
|
+
const inputDate = new Date(year, month - 1, day, hour, minute, second || 0);
|
|
104
|
+
const now = new Date();
|
|
105
|
+
if (inputDate > now) {
|
|
106
|
+
return { valid: false, error: '不能是未来时间', message: `❌ ${fieldName}不能是未来时间` };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { valid: true };
|
|
110
|
+
}
|
|
111
|
+
|
|
47
112
|
module.exports = {
|
|
48
|
-
validateDate
|
|
113
|
+
validateDate,
|
|
114
|
+
validateDateComponents
|
|
49
115
|
};
|