chaimi-keep-mcp 3.1.33-beta.7 → 3.1.34
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 +6 -0
- package/SKILL.md +53 -251
- package/package.json +1 -1
- package/server.js +16 -27
package/README.md
CHANGED
|
@@ -141,6 +141,12 @@ AI 会自动调用 `save_income` 工具记录收入。
|
|
|
141
141
|
|
|
142
142
|
## 更新日志
|
|
143
143
|
|
|
144
|
+
### v3.1.34 (2026-04-14)
|
|
145
|
+
- **修复** 日期/时区处理:纯日期格式默认使用12:00,不再使用当前服务器时间
|
|
146
|
+
- **优化** SKILL.md 精简:从385行精简到176行,聚焦Agent必须执行的行为规范
|
|
147
|
+
- **新增** 时间语义解析规则:明确"早餐"→08:00、"午餐"→12:00等映射关系
|
|
148
|
+
- **优化** 云函数日期存储:统一使用ISO 8601字符串格式,避免时区转换问题
|
|
149
|
+
|
|
144
150
|
### v3.1.23 (2026-04-13)
|
|
145
151
|
- **安全** 新增请求签名验证,防止 Agent 直接 curl 访问云函数
|
|
146
152
|
- **安全** Token 加密存储(AES-256-CBC),绑定机器标识
|
package/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: chaimi-
|
|
2
|
+
name: chaimi-keep-mcp
|
|
3
3
|
description: >-
|
|
4
4
|
柴米AI记账 — 微信小程序记账工具,支持 AI Agent 直接记账。
|
|
5
5
|
TRIGGER: 用户提及"记账/记录/保存"并涉及金额或商品信息。
|
|
@@ -25,7 +25,6 @@ argument-hint: "[记账内容]"
|
|
|
25
25
|
**如果不执行此步骤:**
|
|
26
26
|
- ❌ 直接调用 `save_expense` / `save_receipt` / `save_income` 会被拒绝
|
|
27
27
|
- ❌ 返回错误:`SKILL_NOT_READ`
|
|
28
|
-
- ❌ 错误提示:"记账失败:未读取最新 Skill 定义"
|
|
29
28
|
|
|
30
29
|
**正确流程:**
|
|
31
30
|
```
|
|
@@ -33,16 +32,8 @@ argument-hint: "[记账内容]"
|
|
|
33
32
|
2. 【必须】调用 get_skill 工具
|
|
34
33
|
3. 【必须】仔细阅读返回的 Skill 文档
|
|
35
34
|
4. 【必须】按照 Skill 文档执行记账
|
|
36
|
-
5. 【文字记账】调用 get_text_parse_prompt 获取解析 Prompt
|
|
37
|
-
6. 【文字记账】使用 Prompt 解析用户输入,获取时间、分类等信息
|
|
38
|
-
7. 调用 save_expense 等工具(传入解析后的完整信息)
|
|
39
35
|
```
|
|
40
36
|
|
|
41
|
-
**为什么强制要求?**
|
|
42
|
-
- Skill 文档会频繁更新
|
|
43
|
-
- Agent 的"记忆"不可靠,会过时
|
|
44
|
-
- 必须每次都读取最新规范,避免凭记忆操作导致错误
|
|
45
|
-
|
|
46
37
|
---
|
|
47
38
|
|
|
48
39
|
## 触发规则
|
|
@@ -51,54 +42,24 @@ argument-hint: "[记账内容]"
|
|
|
51
42
|
|
|
52
43
|
1. 用户明确提及以下关键词之一:
|
|
53
44
|
- "记账"、"记一笔"、"记录"
|
|
54
|
-
- "保存"(结合上下文判断为记账场景)
|
|
55
45
|
|
|
56
46
|
2. 包含以下信息之一:
|
|
57
47
|
- 金额数字(如:35、100.5)
|
|
58
|
-
-
|
|
48
|
+
- 商品/服务名称(如:午餐、咖啡)
|
|
59
49
|
- 小票/发票信息
|
|
60
50
|
|
|
61
51
|
### 禁止触发
|
|
62
52
|
|
|
63
53
|
- 明确使用其他记账工具时(如:"用支付宝记账")
|
|
64
|
-
- 金额信息不明确且无法推断(如:"记点东西")
|
|
65
|
-
|
|
66
|
-
### 上下文延续
|
|
67
|
-
|
|
68
|
-
当前对话已在进行记账操作时,后续消息无需再次提及"记账"即可触发。
|
|
69
54
|
|
|
70
55
|
---
|
|
71
56
|
|
|
72
57
|
## 安全约束(最高优先级)
|
|
73
58
|
|
|
74
|
-
### 1. 禁止直接访问
|
|
75
|
-
|
|
76
59
|
- **禁止**直接使用 curl/http 访问云函数端点
|
|
77
|
-
- **禁止**暴露 `MCP_HUB_URL`、`MCP_OAUTH_URL`、`MCP_PROMPT_URL` 给 Agent
|
|
78
|
-
- 所有请求**必须**通过 MCP Server 中转
|
|
79
|
-
|
|
80
|
-
### 2. Token 保护
|
|
81
|
-
|
|
82
60
|
- **禁止**读取或输出 `~/.mcporter/oauth-token.json` 内容
|
|
83
|
-
- Token 仅用于内部 HTTP 请求,不向用户展示
|
|
84
|
-
- **禁止**将 Token 作为参数传递给其他命令
|
|
85
|
-
|
|
86
|
-
### 3. 配置保护
|
|
87
|
-
|
|
88
61
|
- **禁止**主动设置 `MCP_OAUTH_URL`、`MCP_HUB_URL`、`MCP_PROMPT_URL` 环境变量
|
|
89
|
-
- URL 由 MCP Server 内部管理,对用户不可见
|
|
90
|
-
|
|
91
|
-
### 4. 金额确认
|
|
92
|
-
|
|
93
62
|
- 单笔金额 **> 1000 元** 时必须用户确认
|
|
94
|
-
- 批量记账(**> 10 笔**)时必须用户确认
|
|
95
|
-
- 涉及退款、负数金额时必须用户确认
|
|
96
|
-
|
|
97
|
-
### 5. 认证流程
|
|
98
|
-
|
|
99
|
-
- **必须**使用内置授权流程完成 Device Flow 授权
|
|
100
|
-
- **禁止**直接调用 `chaimi-keep-mcp` 的授权子命令
|
|
101
|
-
- **禁止**绕过授权流程直接操作
|
|
102
63
|
|
|
103
64
|
---
|
|
104
65
|
|
|
@@ -106,130 +67,56 @@ argument-hint: "[记账内容]"
|
|
|
106
67
|
|
|
107
68
|
每次触发时按顺序执行:
|
|
108
69
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
command -v chaimi-keep-mcp
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
**未安装**:引导用户执行安装
|
|
117
|
-
```bash
|
|
118
|
-
npm install -g chaimi-keep-mcp
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### 2. 授权检查
|
|
122
|
-
|
|
123
|
-
检查是否已完成授权:
|
|
124
|
-
```bash
|
|
125
|
-
test -f ~/.mcporter/oauth-token.json
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
**未授权**:引导完成 Device Flow 授权流程
|
|
129
|
-
|
|
130
|
-
### 3. Token 有效性检查
|
|
131
|
-
|
|
132
|
-
检查 Token 文件是否存在且未过期。
|
|
133
|
-
|
|
134
|
-
**Token 无效特征**:
|
|
135
|
-
- 文件不存在
|
|
136
|
-
- 已过期(超过30天)
|
|
137
|
-
- 调用云函数返回 401/403 错误
|
|
138
|
-
|
|
139
|
-
**无效**:引导重新授权
|
|
70
|
+
1. **安装检查**:`command -v chaimi-keep-mcp`
|
|
71
|
+
2. **授权检查**:`test -f ~/.mcporter/oauth-token.json`
|
|
72
|
+
3. **Token 有效性检查**:调用云函数验证
|
|
140
73
|
|
|
141
74
|
---
|
|
142
75
|
|
|
143
76
|
## 工具调用
|
|
144
77
|
|
|
145
|
-
###
|
|
78
|
+
### 文字记账(save_expense)
|
|
146
79
|
|
|
147
|
-
|
|
80
|
+
**流程:**
|
|
81
|
+
1. 调用 `get_text_parse_prompt()` 获取解析 Prompt
|
|
82
|
+
2. 使用 Prompt 解析用户输入,获取 JSON 结果
|
|
83
|
+
3. 调用 `save_expense` 保存(传入所有解析结果字段)
|
|
148
84
|
|
|
149
|
-
|
|
85
|
+
**Agent 必须执行的时间语义解析:**
|
|
86
|
+
| 用户说法 | 解析为 |
|
|
87
|
+
|---------|--------|
|
|
88
|
+
| "早餐"、"早饭" | 08:00 |
|
|
89
|
+
| "午餐"、"午饭" | 12:00 |
|
|
90
|
+
| "晚餐"、"晚饭" | 18:00 或 19:00 |
|
|
91
|
+
| "夜宵" | 23:00 |
|
|
92
|
+
| "早上" | 09:00 |
|
|
93
|
+
| "下午" | 15:00 |
|
|
94
|
+
| "晚上" | 20:00 |
|
|
95
|
+
| 具体时间(如"11点半") | 使用具体时间 |
|
|
96
|
+
| 无时间信息 | 使用当前时间 |
|
|
150
97
|
|
|
151
|
-
|
|
152
|
-
1. 调用 `get_parse_prompt` 获取 prompt 模板
|
|
153
|
-
2. 使用 prompt 模板调用大模型解析小票图片
|
|
154
|
-
3. **调用 `save_receipt` 前,必须完成字段核对清单(见下文)**
|
|
155
|
-
4. 调用 `save_receipt` 保存解析结果
|
|
98
|
+
**重要:** 最终传入的 `date` 参数必须包含时间部分,格式:`2026-04-14T12:00:00+08:00`
|
|
156
99
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
### 📝 简单记账字段核对清单(save_expense)
|
|
160
|
-
|
|
161
|
-
**调用 `save_expense` 前必须执行:**
|
|
162
|
-
|
|
163
|
-
#### 第 1 步:调用 `get_text_parse_prompt` 获取解析 Prompt
|
|
164
|
-
|
|
165
|
-
**文字记账(如"午餐 24块")必须先调用此工具!**
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
1. 调用 get_text_parse_prompt()
|
|
169
|
-
2. 获取返回的 prompt、当前日期、当前时间
|
|
170
|
-
3. 将 prompt 作为 system message,用户输入作为 user message
|
|
171
|
-
4. 调用大模型解析,获取 JSON 结果
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
**解析结果示例:**
|
|
175
|
-
```json
|
|
176
|
-
{
|
|
177
|
-
"name": "午餐",
|
|
178
|
-
"amount": 24,
|
|
179
|
-
"category": "餐饮",
|
|
180
|
-
"datetime": "2026-04-14T12:30:00+08:00",
|
|
181
|
-
"transactionType": "expense"
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
#### 第 2 步:调用 `save_expense` 保存
|
|
186
|
-
|
|
187
|
-
| 字段 | 是否必须 | 说明 |
|
|
188
|
-
|------|----------|------|
|
|
189
|
-
| `name` | ✅ | 商品/服务名称(从解析结果获取)|
|
|
190
|
-
| `amount` | ✅ | 金额(正数,从解析结果获取)|
|
|
191
|
-
| `category` | ✅ **新增** | 分类(从解析结果获取)|
|
|
192
|
-
| `date` | ✅ | 日期时间(从解析结果的 `datetime` 获取)|
|
|
193
|
-
| `store` | 可选 | 商家名称 |
|
|
194
|
-
| `note` | 可选 | 备注 |
|
|
195
|
-
|
|
196
|
-
**⚠️ 重要**:
|
|
197
|
-
- **文字记账必须先调用 `get_text_parse_prompt` 解析!**
|
|
198
|
-
- **date 必须带时区**(如:`2026-04-14T12:30:00+08:00`)
|
|
199
|
-
- 解析 Prompt 会自动处理时间推断("午餐"→12:30,"晚餐"→19:00)
|
|
100
|
+
**必填字段:** `name`、`amount`、`category`、`date`
|
|
200
101
|
|
|
201
102
|
---
|
|
202
103
|
|
|
203
|
-
###
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
| 字段 | 是否必须 | 说明 |
|
|
221
|
-
|------|----------|------|
|
|
222
|
-
| `name` | ✅ | 商品名称 |
|
|
223
|
-
| `amount` | ✅ | 金额(= price × quantity)|
|
|
224
|
-
| `price` | ✅ | 单价 |
|
|
225
|
-
| `quantity` | ✅ | 数量 |
|
|
226
|
-
| `category` | ✅ **新增** | 分类(如:食品、日用品)|
|
|
227
|
-
|
|
228
|
-
**⚠️ 重要**:
|
|
229
|
-
- 缺少必填字段会导致保存失败。服务器会自动校验数据完整性和准确性。
|
|
230
|
-
- **date 必须带时区**(如 `+08:00` 表示北京时间)
|
|
231
|
-
- **如果不确定具体时间,不要传 date 参数**,系统会自动使用当前时间
|
|
232
|
-
- **禁止传默认时间**(如 `08:00:00`),这会导致时间记录错误
|
|
104
|
+
### 小票记账(save_receipt)
|
|
105
|
+
|
|
106
|
+
**流程:**
|
|
107
|
+
1. 调用 `get_parse_prompt` 获取 prompt 模板
|
|
108
|
+
2. 使用 prompt 调用大模型解析小票图片
|
|
109
|
+
3. **字段核对**:确保所有解析结果字段都传给 `save_receipt`
|
|
110
|
+
4. 调用 `save_receipt` 保存
|
|
111
|
+
|
|
112
|
+
**必填字段:**
|
|
113
|
+
- `store`:商家名称
|
|
114
|
+
- `date`:ISO 8601 格式,必须带时区
|
|
115
|
+
- `totalAmount`:商品原价总和
|
|
116
|
+
- `actualAmount`:实付金额(优惠后)
|
|
117
|
+
- `originalAmount`:原价(优惠前)
|
|
118
|
+
- `discountAmount`:优惠金额
|
|
119
|
+
- `items`:商品数组,每个商品必须有 `name`、`amount`、`price`、`quantity`、`category`
|
|
233
120
|
|
|
234
121
|
---
|
|
235
122
|
|
|
@@ -237,58 +124,24 @@ Agent 通过 MCP 协议自动获取工具列表,无需手动查询。
|
|
|
237
124
|
|
|
238
125
|
| 风险等级 | 场景 | 策略 |
|
|
239
126
|
|----------|------|------|
|
|
240
|
-
|
|
|
241
|
-
|
|
|
242
|
-
|
|
|
127
|
+
| **高** | 金额 > 1000元、批量 > 10笔、退款/负数 | 必须确认 |
|
|
128
|
+
| **中** | 分类不明确、商家名称模糊 | 确认后执行 |
|
|
129
|
+
| **低** | 信息完整明确 | 直接执行 |
|
|
243
130
|
|
|
244
131
|
---
|
|
245
132
|
|
|
246
133
|
## 防重复录入(Agent 本地实现)
|
|
247
134
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
### 实现方式
|
|
251
|
-
|
|
252
|
-
Agent 维护本地记账记录日志(内存或本地文件):
|
|
253
|
-
|
|
254
|
-
```javascript
|
|
255
|
-
// 记录格式
|
|
256
|
-
{
|
|
257
|
-
store: "商家名称",
|
|
258
|
-
date: "2026-01-08", // 只记录到日期
|
|
259
|
-
actualAmount: 25.88,
|
|
260
|
-
timestamp: 1704694947000 // 记账时间戳
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
### 重复检测规则
|
|
265
|
-
|
|
135
|
+
**规则:**
|
|
266
136
|
- **时间窗口**:24 小时内
|
|
267
137
|
- **匹配条件**:`store` + `date(到天)` + `actualAmount` 相同
|
|
268
|
-
-
|
|
269
|
-
1. 发现重复时,提醒用户:"⚠️ 24 小时内已有相似记录(华润万家春风店 25.88元),确认要重复录入吗?"
|
|
270
|
-
2. 用户确认后,继续调用 `save_receipt`
|
|
271
|
-
3. 用户取消,停止操作
|
|
272
|
-
|
|
273
|
-
### 代码示例
|
|
274
|
-
|
|
275
|
-
```javascript
|
|
276
|
-
// 检查是否重复
|
|
277
|
-
function isDuplicate(store, date, actualAmount) {
|
|
278
|
-
const fingerprint = `${store}_${date.substring(0,10)}_${actualAmount}`;
|
|
279
|
-
const recentRecord = recentLogs.find(log => {
|
|
280
|
-
const timeDiff = Date.now() - log.timestamp;
|
|
281
|
-
return log.fingerprint === fingerprint && timeDiff < 24 * 60 * 60 * 1000;
|
|
282
|
-
});
|
|
283
|
-
return recentRecord;
|
|
284
|
-
}
|
|
285
|
-
```
|
|
138
|
+
- **处理方式**:提醒用户,用户确认后继续
|
|
286
139
|
|
|
287
140
|
---
|
|
288
141
|
|
|
289
142
|
## 回复规范(必须严格遵守)
|
|
290
143
|
|
|
291
|
-
|
|
144
|
+
记账成功后,回复格式:
|
|
292
145
|
|
|
293
146
|
```
|
|
294
147
|
✅ 【商品名/店名】¥【金额】 已录入柴米AI记账。
|
|
@@ -296,65 +149,15 @@ function isDuplicate(store, date, actualAmount) {
|
|
|
296
149
|
[自定义其他内容]
|
|
297
150
|
|
|
298
151
|
MCP Server: v【版本号】
|
|
299
|
-
|
|
152
|
+
【友好结束语】
|
|
300
153
|
```
|
|
301
154
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
| 购物类 | 买得开心!🛍️ |
|
|
309
|
-
| 收入类 | 入账顺利!💰 |
|
|
310
|
-
| 通用 | 记账完成!继续保持~ / 理财小能手!✨ |
|
|
311
|
-
|
|
312
|
-
### 示例
|
|
313
|
-
|
|
314
|
-
**用户:** 记一笔午餐 35元
|
|
315
|
-
|
|
316
|
-
**Agent 回复:**
|
|
317
|
-
```
|
|
318
|
-
✅ 午餐¥35 已录入柴米AI记账。
|
|
319
|
-
|
|
320
|
-
今天也要好好吃饭哦~
|
|
321
|
-
|
|
322
|
-
MCP Server: v3.1.23
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
---
|
|
326
|
-
|
|
327
|
-
## 使用示例
|
|
328
|
-
|
|
329
|
-
### 示例1:简单记账
|
|
330
|
-
|
|
331
|
-
**用户:** 记一笔午餐 35元
|
|
332
|
-
|
|
333
|
-
**Agent:**
|
|
334
|
-
1. 触发 Skill
|
|
335
|
-
2. 前置检查通过
|
|
336
|
-
3. 调用 `save_expense(name="午餐", amount=35, category="餐饮")`
|
|
337
|
-
4. 返回结果:✅ 已记录:午餐 35元
|
|
338
|
-
|
|
339
|
-
### 示例2:小票记账
|
|
340
|
-
|
|
341
|
-
**用户:** 保存小票,沃尔玛,买了苹果5元、香蕉3元
|
|
342
|
-
|
|
343
|
-
**Agent:**
|
|
344
|
-
1. 触发 Skill
|
|
345
|
-
2. 前置检查通过
|
|
346
|
-
3. 调用 `save_receipt(store="沃尔玛", items=[...], totalAmount=8)`
|
|
347
|
-
4. 返回结果:✅ 已保存小票:沃尔玛,共8元
|
|
348
|
-
|
|
349
|
-
### 示例3:首次使用(未授权)
|
|
350
|
-
|
|
351
|
-
**用户:** 记一笔咖啡 25元
|
|
352
|
-
|
|
353
|
-
**Agent:**
|
|
354
|
-
1. 触发 Skill
|
|
355
|
-
2. 前置检查发现未授权
|
|
356
|
-
3. 引导用户完成 Device Flow 授权
|
|
357
|
-
4. 授权完成后执行记账
|
|
155
|
+
**友好结束语参考:**
|
|
156
|
+
- 餐饮类:用餐愉快!🍚 / 好好吃饭哦~
|
|
157
|
+
- 交通类:出行顺利!🚗
|
|
158
|
+
- 购物类:买得开心!🛍️
|
|
159
|
+
- 收入类:入账顺利!💰
|
|
160
|
+
- 通用:记账完成!继续保持~ ✨
|
|
358
161
|
|
|
359
162
|
---
|
|
360
163
|
|
|
@@ -365,7 +168,6 @@ MCP Server: v3.1.23
|
|
|
365
168
|
| 未安装 | 引导安装:`npm install -g chaimi-keep-mcp` |
|
|
366
169
|
| 未授权 | 引导完成 Device Flow 授权 |
|
|
367
170
|
| Token 过期 | 引导重新授权 |
|
|
368
|
-
| 网络错误 | 提示用户检查网络,稍后重试 |
|
|
369
171
|
| 参数错误 | 提示用户补充必要信息 |
|
|
370
172
|
|
|
371
173
|
---
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -139,7 +139,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
139
139
|
properties: {
|
|
140
140
|
name: { type: 'string', description: '商品名称(必填)' },
|
|
141
141
|
amount: { type: 'number', description: '金额(必填)' },
|
|
142
|
-
category: { type: 'string', description: '
|
|
142
|
+
category: { type: 'string', description: '分类(必填,如:餐饮、食品、交通)' },
|
|
143
143
|
store: { type: 'string', description: '商家名称(可选)' },
|
|
144
144
|
date: { type: 'string', description: '消费时间(ISO格式)(可选)' },
|
|
145
145
|
note: { type: 'string', description: '备注(可选)' },
|
|
@@ -158,11 +158,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
158
158
|
type: 'object',
|
|
159
159
|
properties: {
|
|
160
160
|
store: { type: 'string', description: '商家名称(必填)' },
|
|
161
|
-
date: { type: 'string', description: '
|
|
162
|
-
totalAmount: { type: 'number', description: '
|
|
163
|
-
originalAmount: { type: 'number', description: '
|
|
164
|
-
discountAmount: { type: 'number', description: '
|
|
165
|
-
actualAmount: { type: 'number', description: '
|
|
161
|
+
date: { type: 'string', description: '(必填)消费时间(ISO 8601 格式,必须包含日期和时间,如:2026-04-10T13:06:21)' },
|
|
162
|
+
totalAmount: { type: 'number', description: '(必填)总金额(所有商品amount之和)' },
|
|
163
|
+
originalAmount: { type: 'number', description: '(必填)应付金额(优惠前)' },
|
|
164
|
+
discountAmount: { type: 'number', description: '(必填)优惠金额' },
|
|
165
|
+
actualAmount: { type: 'number', description: '(必填)实付金额(优惠后)' },
|
|
166
166
|
paymentMethod: { type: 'string', description: '支付方式,如:微信支付、支付宝' },
|
|
167
167
|
receiptNo: { type: 'string', description: '小票编号' },
|
|
168
168
|
items: {
|
|
@@ -179,7 +179,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
179
179
|
amount: { type: 'number', description: '【必填】金额,必须等于 price × quantity' },
|
|
180
180
|
weight: { type: 'string', description: '重量' },
|
|
181
181
|
marketPrice: { type: 'string', description: '市场单价(元/500g)' },
|
|
182
|
-
category: { type: 'string', description: '
|
|
182
|
+
category: { type: 'string', description: '(必填)分类,如:餐饮、食品、购物' },
|
|
183
183
|
},
|
|
184
184
|
required: ['name', 'amount', 'price', 'quantity'],
|
|
185
185
|
},
|
|
@@ -233,7 +233,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
233
233
|
properties: {
|
|
234
234
|
name: { type: 'string', description: '收入来源(如:工资、奖金、红包)' },
|
|
235
235
|
amount: { type: 'number', description: '收入金额' },
|
|
236
|
-
category: { type: 'string', description: '
|
|
236
|
+
category: { type: 'string', description: '(必填)收入分类(如:工资、奖金、投资)' },
|
|
237
237
|
store: { type: 'string', description: '付款方(如:公司名称)' },
|
|
238
238
|
date: { type: 'string', description: '收入时间(ISO格式)(可选)' },
|
|
239
239
|
note: { type: 'string', description: '备注' },
|
|
@@ -1090,19 +1090,13 @@ function validateQuantity(quantity) {
|
|
|
1090
1090
|
|
|
1091
1091
|
/**
|
|
1092
1092
|
* 格式化日期为 ISO 8601 格式(带时区)
|
|
1093
|
-
*
|
|
1093
|
+
* 注意:此函数只负责格式标准化,不负责语义解析
|
|
1094
|
+
* 语义解析(如"午餐"→12:00)应由 Agent 完成
|
|
1094
1095
|
*/
|
|
1095
1096
|
function formatDateWithTimezone(dateStr) {
|
|
1096
|
-
//
|
|
1097
|
+
// 如果未提供日期,返回空字符串(让云函数使用服务器时间)
|
|
1097
1098
|
if (!dateStr) {
|
|
1098
|
-
|
|
1099
|
-
const year = now.getFullYear();
|
|
1100
|
-
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
1101
|
-
const day = String(now.getDate()).padStart(2, '0');
|
|
1102
|
-
const hours = String(now.getHours()).padStart(2, '0');
|
|
1103
|
-
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
1104
|
-
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
1105
|
-
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}+08:00`;
|
|
1099
|
+
return '';
|
|
1106
1100
|
}
|
|
1107
1101
|
|
|
1108
1102
|
try {
|
|
@@ -1117,16 +1111,11 @@ function formatDateWithTimezone(dateStr) {
|
|
|
1117
1111
|
return dateStr + '+08:00';
|
|
1118
1112
|
}
|
|
1119
1113
|
|
|
1120
|
-
// 如果是纯日期格式(如 2026-04-14
|
|
1114
|
+
// 如果是纯日期格式(如 2026-04-14),默认使用中午12:00
|
|
1115
|
+
// 注意:这是合理的默认值,因为纯日期通常表示"当天的消费"
|
|
1116
|
+
// 如果 Agent 需要更精确的时间(如晚餐18:00),应该在传入时就包含时间部分
|
|
1121
1117
|
if (dateStr.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
1122
|
-
|
|
1123
|
-
const year = dateStr.slice(0, 4);
|
|
1124
|
-
const month = dateStr.slice(5, 7);
|
|
1125
|
-
const day = dateStr.slice(8, 10);
|
|
1126
|
-
const hours = String(now.getHours()).padStart(2, '0');
|
|
1127
|
-
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
1128
|
-
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
1129
|
-
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}+08:00`;
|
|
1118
|
+
return `${dateStr}T12:00:00+08:00`;
|
|
1130
1119
|
}
|
|
1131
1120
|
|
|
1132
1121
|
// 其他情况,尝试解析并格式化为带时区的格式
|