chaimi-keep-mcp 3.3.2-beta.1 → 3.3.3-beta.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-keep-mcp",
3
- "version": "3.3.2-beta.1",
3
+ "version": "3.3.3-beta.0",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -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
@@ -280,7 +280,19 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
280
280
  type: 'object',
281
281
  properties: {
282
282
  store: { type: 'string', description: '商家名称(必填)' },
283
- date: { type: 'number', description: '【必填】消费时间(毫秒级时间戳,13位数字。必须根据实际年月日时分秒计算,不允许复制示例数字)' },
283
+ date: { type: 'number', description: '消费时间(毫秒级时间戳,13位数字。如提供 date_components 可不传此字段)' },
284
+ date_components: {
285
+ type: 'object',
286
+ description: '【推荐】消费时间组件。包含:year、month、day、hour、minute、second。服务端会自动转换为时间戳',
287
+ properties: {
288
+ year: { type: 'number', description: '4位年份,如 2025' },
289
+ month: { type: 'number', description: '1-12,1月=1' },
290
+ day: { type: 'number', description: '1-31' },
291
+ hour: { type: 'number', description: '0-23' },
292
+ minute: { type: 'number', description: '0-59' },
293
+ second: { type: 'number', description: '0-59,默认0' }
294
+ }
295
+ },
284
296
  totalAmount: { type: 'number', description: '(必填)总金额(所有商品amount之和)' },
285
297
  originalAmount: { type: 'number', description: '(必填)应付金额(优惠前)' },
286
298
  discountAmount: { type: 'number', description: '(必填)优惠金额' },
@@ -1147,6 +1159,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1147
1159
  break;
1148
1160
  }
1149
1161
 
1162
+ // 【新增】转换 date_components 为 date
1163
+ if (processedArgs.date_components && !processedArgs.date) {
1164
+ const { year, month, day, hour, minute, second } = processedArgs.date_components;
1165
+
1166
+ // 校验时间组件完整性
1167
+ if (!year || !month || !day || hour === undefined || minute === undefined) {
1168
+ result = {
1169
+ success: false,
1170
+ error: 'date_components 不完整',
1171
+ code: 400,
1172
+ hint: '必须包含:year、month、day、hour、minute'
1173
+ };
1174
+ userMessage = '❌ 保存失败:时间信息不完整\n\n请确保 date_components 包含完整的年月日时分秒';
1175
+ break;
1176
+ }
1177
+
1178
+ // 转换为时间戳
1179
+ processedArgs.date = new Date(year, month - 1, day, hour, minute, second || 0).getTime();
1180
+ console.log(`[save_receipt] 时间组件转换: ${JSON.stringify(processedArgs.date_components)} → ${processedArgs.date}`);
1181
+ }
1182
+
1150
1183
  // P1: 日期合理性检查
1151
1184
  if (processedArgs.date) {
1152
1185
  const validation = validateDate(processedArgs.date, '小票日期');
@@ -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
  };