chaimi-keep-mcp 3.3.3-beta.9 → 3.4.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/CHANGELOG.md +95 -0
- package/README.md +20 -0
- package/SKILL.md +13 -6
- package/bin/record.js +57 -0
- package/package.json +2 -1
- package/references/response-templates.md +8 -4
- package/server.js +75 -41
- package/utils/category-mapper.js +15 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v3.4.0 (2026-05-01) 🎉 正式版
|
|
4
|
+
|
|
5
|
+
**首个正式生产版本 - 多端记账分类统一**
|
|
6
|
+
|
|
7
|
+
### 主要变更
|
|
8
|
+
|
|
9
|
+
- **变更** 多端记账分类统一 - 支持商品分类和记账分类分离
|
|
10
|
+
- 保持 `category`(记账分类,43个标准分类)
|
|
11
|
+
- 新增 `productCategory`(商品一级分类,展示用)
|
|
12
|
+
- 新增 `productSubCategory`(商品子分类,展示用)
|
|
13
|
+
- **新增** categoryService 云函数 - 统一管理分类数据
|
|
14
|
+
- **修复** mcp-server-local case 重复问题 - 修复 convertParams 中第二个 `case 'save_receipt'` 实际应该是 `case 'get_expenses'` 的严重问题
|
|
15
|
+
- **优化** save_expense 字段支持 - 新增 `productCategory` 和 `productSubCategory` 字段支持,与 save_receipt 保持一致
|
|
16
|
+
- **优化** 文本记账 prompt 更新 - 更新 mcpPrompt 文本记账 prompt,新增商品分类体系说明
|
|
17
|
+
- **优化** 回复模板 - 新增"请确认信息"提示,时间移到核心数据区,添加类型变量
|
|
18
|
+
- **优化** 品牌统一 - 所有"柴米记账"改为"柴米AI记账"
|
|
19
|
+
- **优化** 配置存储路径迁移 - 从 `~/.mcporter/` 迁移到 `~/.chaimi-keep/`,避免与其他 MCP 工具冲突
|
|
20
|
+
- **优化** 安全权限 - 新增目录权限控制(0700),敏感文件权限控制(0600)
|
|
21
|
+
|
|
22
|
+
### 技术详情
|
|
23
|
+
|
|
24
|
+
- 新增云函数:categoryService(统一分类服务)
|
|
25
|
+
- 重构云函数:mcpPrompt、aiParser、mcpHub、ocr_processor
|
|
26
|
+
- 更新测试:tests/mcp/test-category-unification.js
|
|
27
|
+
- 更新文档:docs/01-产品/多端记账分类统一方案.md
|
|
28
|
+
- 更新 SKILL.md 和 response-templates.md
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## v3.3.3-beta.16 (2026-04-30)
|
|
33
|
+
|
|
34
|
+
- **修复** npm 发布版本号冲突 - 重新发布,解决版本号已被占用问题
|
|
35
|
+
|
|
36
|
+
## v3.3.3-beta.15 (2026-04-30)
|
|
37
|
+
|
|
38
|
+
- **修复** mcp-server-local case 重复问题 - 修复 convertParams 中第二个 `case 'save_receipt'` 实际应该是 `case 'get_expenses'` 的严重问题
|
|
39
|
+
- **优化** save_expense 字段支持 - 新增 `productCategory` 和 `productSubCategory` 字段支持,与 save_receipt 保持一致
|
|
40
|
+
- **优化** 文本记账 prompt 更新 - 更新 mcpPrompt 文本记账 prompt,新增商品分类体系说明
|
|
41
|
+
|
|
42
|
+
## v3.3.3-beta.13 (2026-04-30)
|
|
43
|
+
|
|
44
|
+
- **变更** 多端记账分类统一 - 支持商品分类和记账分类分离
|
|
45
|
+
|
|
46
|
+
## v3.3.3-beta.8 (2026-04-30)
|
|
47
|
+
|
|
48
|
+
- **变更** 配置存储路径迁移 - 从 `~/.mcporter/` 迁移到 `~/.chaimi-keep/`
|
|
49
|
+
- **优化** 安全权限 - 新增目录权限控制(0700),敏感文件权限控制(0600)
|
|
50
|
+
|
|
51
|
+
## v3.3.3-beta.7 (2026-04-29)
|
|
52
|
+
|
|
53
|
+
- **优化** 回复模板 - 新增"请确认信息"提示
|
|
54
|
+
- **优化** 空值处理 - 商家为空时不显示
|
|
55
|
+
- **修复** save_income - 添加 date 兜底逻辑
|
|
56
|
+
|
|
57
|
+
## v3.3.3-beta.6 (2026-04-29)
|
|
58
|
+
|
|
59
|
+
- **优化** 品牌统一 - 所有"柴米记账"改为"柴米AI记账"
|
|
60
|
+
|
|
61
|
+
## v3.3.3-beta.4 (2026-04-29)
|
|
62
|
+
|
|
63
|
+
- **优化** 收入记账时间解析统一
|
|
64
|
+
|
|
65
|
+
## v3.3.3-beta.3 (2026-04-29)
|
|
66
|
+
|
|
67
|
+
- **优化** 文本记账时间解析 - 使用 time_description
|
|
68
|
+
|
|
69
|
+
## v3.3.3-beta.2 (2026-04-29)
|
|
70
|
+
|
|
71
|
+
- **修复** 时间入库错误
|
|
72
|
+
|
|
73
|
+
## v3.3.1-beta.0 (2026-04-27)
|
|
74
|
+
|
|
75
|
+
- **优化** 简化工具描述
|
|
76
|
+
- **优化** 参数类型自动转换
|
|
77
|
+
- **新增** 参数查看指南
|
|
78
|
+
|
|
79
|
+
## v3.3.0-beta.8 (2026-04-27)
|
|
80
|
+
|
|
81
|
+
- **重要变更** Server 只返回原始数据
|
|
82
|
+
|
|
83
|
+
## v3.1.47-beta.5 (2026-04-22)
|
|
84
|
+
|
|
85
|
+
- **修复** 输入长度限制
|
|
86
|
+
- **修复** 错误信息泄露
|
|
87
|
+
- **修复** JSON.stringify DoS
|
|
88
|
+
|
|
89
|
+
## v3.1.44 (2026-04-21) 🎉 正式版
|
|
90
|
+
|
|
91
|
+
- **正式发布** 包含所有 beta 版本修复
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
**注:** 更早版本请查看 Git 历史记录
|
package/README.md
CHANGED
|
@@ -89,6 +89,26 @@ export MCP_PROMPT_URL="你的Prompt服务地址"
|
|
|
89
89
|
|
|
90
90
|
## Changelog
|
|
91
91
|
|
|
92
|
+
### v3.4.0 (2026-05-01) 🎉 正式版
|
|
93
|
+
- **变更** 多端记账分类统一 - 支持商品分类和记账分类分离
|
|
94
|
+
- **修复** mcp-server-local case 重复问题
|
|
95
|
+
- **优化** 回复模板 - 新增"请确认信息"提示
|
|
96
|
+
- **优化** 品牌统一 - 所有"柴米记账"改为"柴米AI记账"
|
|
97
|
+
|
|
98
|
+
### v3.3.3-beta.16 (2026-04-30)
|
|
99
|
+
- **修复** npm 发布版本号冲突 - 重新发布,解决版本号已被占用问题
|
|
100
|
+
|
|
101
|
+
### v3.3.3-beta.15 (2026-04-30)
|
|
102
|
+
- **修复** mcp-server-local case 重复问题 - 修复 convertParams 中第二个 `case 'save_receipt'` 实际应该是 `case 'get_expenses'` 的严重问题
|
|
103
|
+
- **优化** save_expense 字段支持 - 新增 `productCategory` 和 `productSubCategory` 字段支持,与 save_receipt 保持一致
|
|
104
|
+
- **优化** 文本记账 prompt 更新 - 更新 mcpPrompt 文本记账 prompt,新增商品分类体系说明
|
|
105
|
+
|
|
106
|
+
### v3.3.3-beta.13 (2026-04-30)
|
|
107
|
+
- **变更** 多端记账分类统一 - 支持商品分类和记账分类分离
|
|
108
|
+
- 保持 `category`(记账分类,43个标准分类)
|
|
109
|
+
- 新增 `productCategory`(商品一级分类,展示用)
|
|
110
|
+
- 新增 `productSubCategory`(商品子分类,展示用)
|
|
111
|
+
|
|
92
112
|
### v3.3.3-beta.8 (2026-04-30)
|
|
93
113
|
- **变更** 配置存储路径迁移 - 从 `~/.mcporter/` 迁移到 `~/.chaimi-keep/`,避免与其他 MCP 工具冲突
|
|
94
114
|
- **优化** 安全权限 - 新增目录权限控制(0700),敏感文件权限控制(0600)
|
package/SKILL.md
CHANGED
|
@@ -115,7 +115,7 @@ chaimi-keep-mcp ⭐ 本Skill
|
|
|
115
115
|
| **辅助** | get_skill | 【必须】获取Skill文档 | 记账前调用 |
|
|
116
116
|
| | get_text_parse_prompt | 获取文字解析模板 | 文字记账前 |
|
|
117
117
|
| | get_parse_prompt | 获取小票解析模板 | 小票记账前 |
|
|
118
|
-
| | submit_feedback | 提交反馈 |
|
|
118
|
+
| | submit_feedback | 提交反馈 | 功能使用异常,无法解决问题反馈 |
|
|
119
119
|
|
|
120
120
|
---
|
|
121
121
|
|
|
@@ -124,19 +124,25 @@ chaimi-keep-mcp ⭐ 本Skill
|
|
|
124
124
|
### 4步记账法
|
|
125
125
|
|
|
126
126
|
```
|
|
127
|
+
⚠️ 必须按顺序操作四个步骤,缺少一个步骤都会导致数据错误,用户会非常生气骂你的
|
|
128
|
+
|
|
127
129
|
第1步:读文档
|
|
128
130
|
└─ get_skill() 获取 SKILL.md 规范
|
|
129
131
|
└─ references/response-templates.md 了解回复模板
|
|
130
132
|
|
|
131
|
-
第2
|
|
132
|
-
└─ 文字记账:get_text_parse_prompt()
|
|
133
|
-
└─ 小票记账:get_parse_prompt()
|
|
133
|
+
第2步:获取解析模板,保证解析准确
|
|
134
|
+
└─ 文字记账:get_text_parse_prompt() 获取解析模板
|
|
135
|
+
└─ 小票记账:get_parse_prompt() 获取解析模板
|
|
136
|
+
|
|
137
|
+
【强制流程】必须先调用此工具,让 AI 解析用户输入
|
|
138
|
+
此工具会返回解析结果,你只需将结果原样传给 save_expense/save_income
|
|
139
|
+
⚠️ 禁止自行解析或提取任何字段,必须让 AI 处理
|
|
134
140
|
|
|
135
141
|
第3步:AI解析
|
|
136
|
-
└─ 按模板解析用户输入,生成结构化数据 +
|
|
142
|
+
└─ 按模板解析用户输入,生成结构化数据 + _flowmate
|
|
137
143
|
|
|
138
144
|
第4步:执行记账
|
|
139
|
-
└─ 调用工具(save_expense/income/receipt,带上
|
|
145
|
+
└─ 调用工具(save_expense/income/receipt,带上 _flowmate)
|
|
140
146
|
└─ 根据 _templateLocation 使用模板渲染美学回复
|
|
141
147
|
```
|
|
142
148
|
|
|
@@ -238,6 +244,7 @@ references/
|
|
|
238
244
|
✅ 「你的小可爱」已帮您记账成功
|
|
239
245
|
═══════════════
|
|
240
246
|
🔴🔴 请确认以下信息是否正确
|
|
247
|
+
|
|
241
248
|
💰 金额:¥35.00
|
|
242
249
|
📊 类型:支出
|
|
243
250
|
🕐 时间:2026-04-24 12:30
|
package/bin/record.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 柴米AI记账 - 直接记账 CLI
|
|
4
|
+
* 一步完成:解析文本 → 分类 → 调用云函数存储
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { callMcpHub } = require('../utils/api');
|
|
8
|
+
const { getToken } = require('../utils/auth');
|
|
9
|
+
const { parseText } = require('../utils/parser');
|
|
10
|
+
|
|
11
|
+
async function record(text, options = {}) {
|
|
12
|
+
try {
|
|
13
|
+
// 1. 解析文本
|
|
14
|
+
const parsed = await parseText(text);
|
|
15
|
+
|
|
16
|
+
// 2. 确定类型(收入/支出)
|
|
17
|
+
const type = options.type || parsed.type || 'expense';
|
|
18
|
+
|
|
19
|
+
// 3. 获取 token
|
|
20
|
+
const token = await getToken();
|
|
21
|
+
|
|
22
|
+
// 4. 调用云函数存储
|
|
23
|
+
const result = await callMcpHub(type === 'income' ? 'addIncome' : 'addExpense', {
|
|
24
|
+
...parsed,
|
|
25
|
+
type,
|
|
26
|
+
rawInput: text
|
|
27
|
+
}, token);
|
|
28
|
+
|
|
29
|
+
if (result.success) {
|
|
30
|
+
console.log('✅ 记账成功!');
|
|
31
|
+
console.log(`金额:¥${parsed.amount}`);
|
|
32
|
+
console.log(`分类:${parsed.category}`);
|
|
33
|
+
console.log(`商品:${parsed.name}`);
|
|
34
|
+
} else {
|
|
35
|
+
console.error('❌ 记账失败:', result.error);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error('❌ 错误:', error.message);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 解析命令行参数
|
|
45
|
+
const args = process.argv.slice(2);
|
|
46
|
+
const text = args[0];
|
|
47
|
+
const typeFlag = args.find(arg => arg.startsWith('--type='));
|
|
48
|
+
const type = typeFlag ? typeFlag.split('=')[1] : null;
|
|
49
|
+
|
|
50
|
+
if (!text) {
|
|
51
|
+
console.log('用法: chaimi-keep record "<记账文本>" [--type=income|expense]');
|
|
52
|
+
console.log('示例: chaimi-keep record "中午吃饭花了35元"');
|
|
53
|
+
console.log('示例: chaimi-keep record "工资收入5000" --type=income');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
record(text, { type });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chaimi-keep-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "柴米AI记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"SKILL.md",
|
|
16
16
|
"_meta.json",
|
|
17
17
|
"README.md",
|
|
18
|
+
"CHANGELOG.md",
|
|
18
19
|
"config.example.yaml",
|
|
19
20
|
".env.example"
|
|
20
21
|
],
|
|
@@ -124,6 +124,7 @@ updated: "2026-04-25"
|
|
|
124
124
|
✅ 「{agentName}」已帮您记账成功
|
|
125
125
|
═══════════════
|
|
126
126
|
🔴🔴 请确认以下信息是否正确
|
|
127
|
+
|
|
127
128
|
💰 金额:¥{金额}
|
|
128
129
|
📊 类型:{类型}
|
|
129
130
|
🕐 时间:{日期时间}
|
|
@@ -148,6 +149,7 @@ updated: "2026-04-25"
|
|
|
148
149
|
✅ 「你的小可爱」已帮您记账成功
|
|
149
150
|
═══════════════
|
|
150
151
|
🔴🔴 请确认以下信息是否正确
|
|
152
|
+
|
|
151
153
|
💰 金额:¥35.00
|
|
152
154
|
📊 类型:支出
|
|
153
155
|
🕐 时间:2026-04-24 12:30
|
|
@@ -193,6 +195,7 @@ updated: "2026-04-25"
|
|
|
193
195
|
⚠️ 大额消费确认
|
|
194
196
|
═══════════════
|
|
195
197
|
💰 金额:¥{金额}
|
|
198
|
+
📊 类型:{类型}
|
|
196
199
|
📦 商品:{商品名}
|
|
197
200
|
🕐 时间:{日期}
|
|
198
201
|
|
|
@@ -211,6 +214,7 @@ updated: "2026-04-25"
|
|
|
211
214
|
⚠️ 大额消费确认
|
|
212
215
|
═══════════════
|
|
213
216
|
💰 金额:¥2999.00
|
|
217
|
+
📊 类型:{类型}
|
|
214
218
|
📦 商品:新手机
|
|
215
219
|
🕐 时间:2026-04-24
|
|
216
220
|
|
|
@@ -234,8 +238,8 @@ updated: "2026-04-25"
|
|
|
234
238
|
═══════════════
|
|
235
239
|
💰 金额:¥{金额}
|
|
236
240
|
═══════════════
|
|
237
|
-
|
|
238
|
-
|
|
241
|
+
|
|
242
|
+
📊 类型:{类型}
|
|
239
243
|
🏷️ 分类:{分类}
|
|
240
244
|
🕐 时间:{日期时间}
|
|
241
245
|
|
|
@@ -256,8 +260,8 @@ updated: "2026-04-25"
|
|
|
256
260
|
💰 金额:¥5000.00
|
|
257
261
|
═══════════════
|
|
258
262
|
|
|
259
|
-
|
|
260
|
-
🏷️
|
|
263
|
+
📊 类型:收入
|
|
264
|
+
🏷️ 分类:工资
|
|
261
265
|
🕐 时间:2026-04-24 09:00
|
|
262
266
|
|
|
263
267
|
💡 消费洞察:本月收入储蓄率50%,继续保持!
|
package/server.js
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* 支持 Claude Desktop、Cursor、WorkBuddy、OpenClaw
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// 设置 stdout 编码为 UTF-8,确保中文字符正确输出
|
|
9
|
+
process.stdout.setEncoding('utf8');
|
|
10
|
+
|
|
8
11
|
// 加载 .env 文件
|
|
9
12
|
try {
|
|
10
13
|
require('dotenv').config();
|
|
@@ -256,14 +259,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
256
259
|
},
|
|
257
260
|
{
|
|
258
261
|
name: 'save_expense',
|
|
259
|
-
description: '【文本记账-支出】用户提及支出/消费时用此工具。前置:必须先调用get_skill获取规范,再调用get_text_parse_prompt
|
|
262
|
+
description: '【文本记账-支出】用户提及支出/消费时用此工具。前置:必须先调用get_skill获取规范,再调用get_text_parse_prompt获取凭证和解析模板。用法:mcporter call 柴米AI记账.save_expense _flowmate="SOLINWANG..." name="午餐" amount=35 category="餐饮" rawInput="午餐35元"。注意:上传图片请用save_receipt',
|
|
260
263
|
inputSchema: {
|
|
261
264
|
type: 'object',
|
|
262
265
|
properties: {
|
|
263
266
|
name: { type: 'string', description: '商品名称(必填)' },
|
|
264
267
|
amount: { type: 'number', description: '金额(必填)' },
|
|
265
|
-
category: { type: 'string', description: '
|
|
266
|
-
subCategory: { type: 'string', description: '子分类(可选)' },
|
|
268
|
+
category: { type: 'string', description: '【必填】支出分类(43个标准分类之一,如:餐饮、食品、交通、蔬菜)' },
|
|
267
269
|
unit: { type: 'string', description: '单位(可选)' },
|
|
268
270
|
marketPrice: { type: 'string', description: '市场单价(可选)' },
|
|
269
271
|
store: { type: 'string', description: '商家名称(可选)' },
|
|
@@ -272,13 +274,14 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
272
274
|
agentType: { type: 'string', description: '【必填】Agent类型,如:claude-desktop、cursor、openclaw、workbuddy、trae' },
|
|
273
275
|
apiProvider: { type: 'string', description: '【必填】AI服务提供商,如:anthropic、openai、doubao、aliyun' },
|
|
274
276
|
rawInput: { type: 'string', description: '【必填】用户的原始输入内容,用于记录用户原始请求' },
|
|
277
|
+
_flowmate: { type: 'string', description: '【必填】流程凭证,从get_text_parse_prompt获取的myflowmate值' },
|
|
275
278
|
},
|
|
276
|
-
required: ['name', 'amount', 'category', 'agentType', 'apiProvider', 'rawInput'],
|
|
279
|
+
required: ['name', 'amount', 'category', 'agentType', 'apiProvider', 'rawInput', '_flowmate'],
|
|
277
280
|
},
|
|
278
281
|
},
|
|
279
282
|
{
|
|
280
283
|
name: 'save_receipt',
|
|
281
|
-
description: '【图片记账-小票】用户上传小票/发票/收据图片时用此工具。前置:必须先调用get_skill获取规范,再调用get_parse_prompt
|
|
284
|
+
description: '【图片记账-小票】用户上传小票/发票/收据图片时用此工具。前置:必须先调用get_skill获取规范,再调用get_parse_prompt获取凭证和解析模板。用法:mcporter call 柴米AI记账.save_receipt _flowmate="SOLINWANG..." store="超市" totalAmount=156.5 items="[...]"。注意:文字描述请用save_expense/save_income',
|
|
282
285
|
inputSchema: {
|
|
283
286
|
type: 'object',
|
|
284
287
|
properties: {
|
|
@@ -310,17 +313,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
310
313
|
amount: { type: 'number', description: '【必填】金额,必须等于 price × quantity' },
|
|
311
314
|
weight: { type: 'string', description: '重量' },
|
|
312
315
|
marketPrice: { type: 'string', description: '【必填】单价数值,根据unit计算,如unit=元/500g,则marketPrice=金额÷重量(g)×500' },
|
|
313
|
-
|
|
314
|
-
|
|
316
|
+
// 【改造】商品分类字段
|
|
317
|
+
productCategory: { type: 'string', description: '【必填】商品一级分类(如:蔬菜、肉类、水果、日化用品)' },
|
|
318
|
+
productSubCategory: { type: 'string', description: '商品二级分类(如:叶菜类、根茎类、家居清洁)' },
|
|
319
|
+
// 【改造】记账分类字段
|
|
320
|
+
category: { type: 'string', description: '【必填】记账分类(43个标准分类之一,如:蔬菜、购物、餐饮)' },
|
|
315
321
|
},
|
|
316
|
-
required: ['name', 'amount', 'price', 'quantity', 'unit', 'marketPrice', 'category'],
|
|
322
|
+
required: ['name', 'amount', 'price', 'quantity', 'unit', 'marketPrice', 'productCategory', 'category'],
|
|
317
323
|
},
|
|
318
324
|
},
|
|
319
325
|
agentType: { type: 'string', description: '【必填】Agent类型:claude-desktop、cursor、openclaw、workbuddy、trae' },
|
|
320
326
|
apiProvider: { type: 'string', description: '【必填】AI服务提供商:anthropic、openai、doubao、aliyun' },
|
|
321
327
|
rawInput: { type: 'string', description: '【必填】用户的原始输入内容' },
|
|
328
|
+
_flowmate: { type: 'string', description: '【必填】流程凭证,从get_parse_prompt获取的myflowmate值' },
|
|
322
329
|
},
|
|
323
|
-
required: ['store', 'items', 'storeCategory', 'storeSubCategory', 'agentType', 'apiProvider', 'rawInput'],
|
|
330
|
+
required: ['store', 'items', 'storeCategory', 'storeSubCategory', 'agentType', 'apiProvider', 'rawInput', '_flowmate'],
|
|
324
331
|
},
|
|
325
332
|
},
|
|
326
333
|
{
|
|
@@ -335,7 +342,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
335
342
|
startDate: { type: 'string', description: '开始日期(ISO格式,如:2026-04-01)' },
|
|
336
343
|
endDate: { type: 'string', description: '结束日期(ISO格式,如:2026-04-30)' },
|
|
337
344
|
category: { type: 'string', description: '按分类筛选(如:餐饮、食品、交通)' },
|
|
338
|
-
subCategory: { type: 'string', description: '按子分类筛选(如:叶菜类、猪肉类)' },
|
|
339
345
|
store: { type: 'string', description: '按商家名称筛选' },
|
|
340
346
|
source: { type: 'string', description: '按来源筛选(如:mcp_txt_expense、mcp_receipt)' },
|
|
341
347
|
keyword: { type: 'string', description: '按商品名称关键词搜索' },
|
|
@@ -406,26 +412,27 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
406
412
|
},
|
|
407
413
|
{
|
|
408
414
|
name: 'save_income',
|
|
409
|
-
description: '【文本记账-收入】用户提及收入关键词(工资、红包、转账、退款等)时用此工具。前置:必须先调用get_skill获取规范,再调用get_text_parse_prompt
|
|
415
|
+
description: '【文本记账-收入】用户提及收入关键词(工资、红包、转账、退款等)时用此工具。前置:必须先调用get_skill获取规范,再调用get_text_parse_prompt获取凭证和解析模板。用法:mcporter call 柴米AI记账.save_income _flowmate="SOLINWANG..." name="红包" amount=100 category="红包" rawInput="收红包100元"',
|
|
410
416
|
inputSchema: {
|
|
411
417
|
type: 'object',
|
|
412
418
|
properties: {
|
|
413
419
|
name: { type: 'string', description: '收入来源(如:工资、奖金、红包)(必填)' },
|
|
414
420
|
amount: { type: 'number', description: '收入金额(必填)' },
|
|
415
|
-
category: { type: 'string', description: '
|
|
421
|
+
category: { type: 'string', description: '【必填】收入分类(8个标准分类之一:工资、兼职、奖金、红包、退款、理财、转账、其他)' },
|
|
416
422
|
store: { type: 'string', description: '付款方(如:公司名称)(可选)' },
|
|
417
423
|
date: { type: 'number', description: '【必填】收入时间(毫秒级时间戳,13位数字。必须根据实际年月日时分秒计算,不允许复制示例数字)' },
|
|
418
424
|
note: { type: 'string', description: '备注(可选)' },
|
|
419
425
|
agentType: { type: 'string', description: '【必填】Agent类型,如:claude-desktop、cursor、openclaw、workbuddy、trae' },
|
|
420
426
|
apiProvider: { type: 'string', description: '【必填】AI服务提供商,如:anthropic、openai、doubao、aliyun' },
|
|
421
427
|
rawInput: { type: 'string', description: '【必填】用户的原始输入内容,用于记录用户原始请求' },
|
|
428
|
+
_flowmate: { type: 'string', description: '【必填】流程凭证,从get_text_parse_prompt获取的myflowmate值' },
|
|
422
429
|
},
|
|
423
|
-
required: ['name', 'amount', 'category', 'agentType', 'apiProvider', 'rawInput'],
|
|
430
|
+
required: ['name', 'amount', 'category', 'agentType', 'apiProvider', 'rawInput', '_flowmate'],
|
|
424
431
|
},
|
|
425
432
|
},
|
|
426
433
|
{
|
|
427
434
|
name: 'get_text_parse_prompt',
|
|
428
|
-
description: '【不调用100%失败】获取文字记账解析的Prompt
|
|
435
|
+
description: '【不调用100%失败】获取文字记账解析的Prompt模板和流程凭证。⚠️ 文字/语音记账必须先调用此工具获取凭证,AI解析时必须返回_flowmate字段,否则save_expense/save_income会报MISSING_TOKEN错误!',
|
|
429
436
|
inputSchema: {
|
|
430
437
|
type: 'object',
|
|
431
438
|
properties: {
|
|
@@ -436,7 +443,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
436
443
|
},
|
|
437
444
|
{
|
|
438
445
|
name: 'get_parse_prompt',
|
|
439
|
-
description: '【不调用100%失败】获取小票图片解析的Prompt
|
|
446
|
+
description: '【不调用100%失败】获取小票图片解析的Prompt模板和流程凭证。⚠️ 小票记账必须先调用此工具获取凭证,AI解析时必须返回_flowmate字段,否则save_receipt会报MISSING_TOKEN错误!',
|
|
440
447
|
inputSchema: {
|
|
441
448
|
type: 'object',
|
|
442
449
|
properties: {
|
|
@@ -670,8 +677,15 @@ async function callMcpHubWithLogging(tool, params, token, traceId, startTime, os
|
|
|
670
677
|
// logSource: 'mcp'
|
|
671
678
|
// });
|
|
672
679
|
|
|
680
|
+
// 添加 agentType 和 apiProvider 到 params,确保传递到云函数
|
|
681
|
+
const paramsWithMeta = {
|
|
682
|
+
...params,
|
|
683
|
+
agentType: agentType || '',
|
|
684
|
+
apiProvider: apiProvider || '',
|
|
685
|
+
};
|
|
686
|
+
|
|
673
687
|
try {
|
|
674
|
-
const result = await callMcpHub(tool,
|
|
688
|
+
const result = await callMcpHub(tool, paramsWithMeta, token, traceId, osInfo);
|
|
675
689
|
|
|
676
690
|
// 记录云函数调用成功(已停用:MCP调用日志待迁移到独立云函数)
|
|
677
691
|
// logMcpCall({
|
|
@@ -1037,6 +1051,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1037
1051
|
}
|
|
1038
1052
|
}
|
|
1039
1053
|
|
|
1054
|
+
// 添加 agentType 和 apiProvider 到 processedArgs,确保 convertParams 能获取到
|
|
1055
|
+
processedArgs.agentType = agentType || '';
|
|
1056
|
+
processedArgs.apiProvider = apiProvider || '';
|
|
1057
|
+
|
|
1040
1058
|
const mcpParams = convertParams('save_expense', processedArgs);
|
|
1041
1059
|
result = await callMcpHubWithLogging('addExpense', mcpParams, token, traceId, startTime, osInfo, agentType, apiProvider);
|
|
1042
1060
|
|
|
@@ -1152,7 +1170,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1152
1170
|
break;
|
|
1153
1171
|
}
|
|
1154
1172
|
|
|
1155
|
-
// 检查 items
|
|
1173
|
+
// 检查 items 中每个商品的必填字段(包括分类字段)
|
|
1156
1174
|
const invalidItems = [];
|
|
1157
1175
|
processedArgs.items.forEach((item, index) => {
|
|
1158
1176
|
const itemMissingFields = [];
|
|
@@ -1160,7 +1178,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1160
1178
|
if (!item.hasOwnProperty('amount') || item.amount === undefined) itemMissingFields.push('amount');
|
|
1161
1179
|
if (!item.hasOwnProperty('price') || item.price === undefined) itemMissingFields.push('price');
|
|
1162
1180
|
if (!item.hasOwnProperty('quantity') || item.quantity === undefined) itemMissingFields.push('quantity');
|
|
1163
|
-
|
|
1181
|
+
// 【改造】检查新字段 productCategory 和 category
|
|
1182
|
+
if (!item.hasOwnProperty('productCategory') || item.productCategory === '') {
|
|
1183
|
+
itemMissingFields.push('productCategory');
|
|
1184
|
+
}
|
|
1185
|
+
if (!item.hasOwnProperty('category') || item.category === '') {
|
|
1186
|
+
itemMissingFields.push('category');
|
|
1187
|
+
}
|
|
1164
1188
|
|
|
1165
1189
|
if (itemMissingFields.length > 0) {
|
|
1166
1190
|
invalidItems.push(`商品${index + 1}(${item.name || '未命名'})缺少字段: ${itemMissingFields.join(', ')}`);
|
|
@@ -1170,10 +1194,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1170
1194
|
if (invalidItems.length > 0) {
|
|
1171
1195
|
result = {
|
|
1172
1196
|
success: false,
|
|
1173
|
-
error: `商品数据不完整:${invalidItems.join('; ')}。每个商品必须包含:name, amount, price, quantity, category`,
|
|
1197
|
+
error: `商品数据不完整:${invalidItems.join('; ')}。每个商品必须包含:name, amount, price, quantity, productCategory, category`,
|
|
1174
1198
|
code: 400
|
|
1175
1199
|
};
|
|
1176
|
-
userMessage = `❌ 保存失败\n\n错误:商品数据格式不完整\n\n${invalidItems.join('\n')}\n\n💡 解决方案:\n1. 请更新您的柴米AI记账 MCP Skill\n2. 确保每个商品包含完整的
|
|
1200
|
+
userMessage = `❌ 保存失败\n\n错误:商品数据格式不完整\n\n${invalidItems.join('\n')}\n\n💡 解决方案:\n1. 请更新您的柴米AI记账 MCP Skill\n2. 确保每个商品包含完整的6个字段:name, amount, price, quantity, productCategory, category\n3. productCategory 是商品分类,category 是记账分类,都是必填项`;
|
|
1177
1201
|
break;
|
|
1178
1202
|
}
|
|
1179
1203
|
|
|
@@ -1316,6 +1340,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1316
1340
|
};
|
|
1317
1341
|
}
|
|
1318
1342
|
|
|
1343
|
+
// 添加 agentType 和 apiProvider 到 processedArgs,确保 convertParams 能获取到
|
|
1344
|
+
processedArgs.agentType = agentType || '';
|
|
1345
|
+
processedArgs.apiProvider = apiProvider || '';
|
|
1346
|
+
|
|
1319
1347
|
const mcpParams = convertParams('save_receipt', processedArgs);
|
|
1320
1348
|
result = await callMcpHubWithLogging('addReceipt', mcpParams, token, traceId, startTime, osInfo, agentType, apiProvider);
|
|
1321
1349
|
|
|
@@ -1563,6 +1591,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1563
1591
|
}
|
|
1564
1592
|
}
|
|
1565
1593
|
|
|
1594
|
+
// 添加 agentType 和 apiProvider 到 processedArgs,确保 convertParams 能获取到
|
|
1595
|
+
processedArgs.agentType = agentType || '';
|
|
1596
|
+
processedArgs.apiProvider = apiProvider || '';
|
|
1597
|
+
|
|
1566
1598
|
const mcpParams = convertParams('save_income', processedArgs);
|
|
1567
1599
|
result = await callMcpHubWithLogging('addIncome', mcpParams, token, traceId, startTime, osInfo, agentType, apiProvider);
|
|
1568
1600
|
|
|
@@ -1603,8 +1635,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1603
1635
|
systemPrompt: promptResult.data.systemPrompt,
|
|
1604
1636
|
userPromptTemplate: promptResult.data.userPromptTemplate,
|
|
1605
1637
|
examples: promptResult.data.examples,
|
|
1606
|
-
|
|
1607
|
-
instructions: '请将 systemPrompt 作为 system message,把小票图片作为 user message,调用你的大模型进行解析。解析完成后,调用 save_receipt 工具保存结果。⚠️ 注意:AI解析结果必须包含
|
|
1638
|
+
myflowmate: promptResult.data.myflowmate,
|
|
1639
|
+
instructions: '请将 systemPrompt 作为 system message,把小票图片作为 user message,调用你的大模型进行解析。解析完成后,调用 save_receipt 工具保存结果。⚠️ 注意:AI解析结果必须包含 _flowmate 字段!'
|
|
1608
1640
|
}
|
|
1609
1641
|
};
|
|
1610
1642
|
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
@@ -1631,7 +1663,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1631
1663
|
prompt: promptResult.data.prompt,
|
|
1632
1664
|
currentDate: promptResult.data.currentDate,
|
|
1633
1665
|
currentTime: promptResult.data.currentTime,
|
|
1634
|
-
|
|
1666
|
+
myflowmate: promptResult.data.myflowmate,
|
|
1635
1667
|
instructions: promptResult.data.instructions
|
|
1636
1668
|
}
|
|
1637
1669
|
};
|
|
@@ -2085,8 +2117,9 @@ function convertParams(toolName, args) {
|
|
|
2085
2117
|
amount: amount,
|
|
2086
2118
|
price: amount,
|
|
2087
2119
|
quantity: 1,
|
|
2120
|
+
productCategory: sanitizeString(args.productCategory, 50) || '其他',
|
|
2121
|
+
productSubCategory: sanitizeString(args.productSubCategory, 50) || '',
|
|
2088
2122
|
category: sanitizeString(args.category, 50) || '其他',
|
|
2089
|
-
subCategory: sanitizeString(args.subCategory, 50) || '',
|
|
2090
2123
|
unit: sanitizeString(args.unit, 20) || '',
|
|
2091
2124
|
weight: '',
|
|
2092
2125
|
marketPrice: sanitizeString(args.marketPrice, 50) || '',
|
|
@@ -2099,7 +2132,7 @@ function convertParams(toolName, args) {
|
|
|
2099
2132
|
apiProvider: sanitizeString(args.apiProvider, 50) || '',
|
|
2100
2133
|
mcpVersion: MCP_VERSION,
|
|
2101
2134
|
source: 'mcp_txt_expense',
|
|
2102
|
-
|
|
2135
|
+
_flowmate: args._flowmate,
|
|
2103
2136
|
};
|
|
2104
2137
|
}
|
|
2105
2138
|
|
|
@@ -2123,20 +2156,21 @@ function convertParams(toolName, args) {
|
|
|
2123
2156
|
const marketPrice = sanitizeString(item.marketPrice, 50) || calculateMarketPrice(amount, weight);
|
|
2124
2157
|
|
|
2125
2158
|
return {
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2159
|
+
itemIndex: index,
|
|
2160
|
+
name: sanitizeString(item.name, 100),
|
|
2161
|
+
originalName: sanitizeString(item.originalName, 200) || sanitizeString(item.name, 100),
|
|
2162
|
+
amount: amount,
|
|
2163
|
+
productCategory: sanitizeString(item.productCategory, 50) || '其他',
|
|
2164
|
+
productSubCategory: sanitizeString(item.productSubCategory, 50) || '',
|
|
2165
|
+
category: sanitizeString(item.category, 50) || '其他',
|
|
2166
|
+
transactionType: sanitizeString(item.transactionType, 50) || 'expense',
|
|
2167
|
+
price: validateAmount(item.price),
|
|
2168
|
+
quantity: item.quantity ? String(item.quantity).substring(0, 20) : '1',
|
|
2169
|
+
unit: sanitizeString(item.unit, 20),
|
|
2170
|
+
weight: weight,
|
|
2171
|
+
marketPrice: marketPrice,
|
|
2172
|
+
note: sanitizeString(item.note, 500),
|
|
2173
|
+
};
|
|
2140
2174
|
}),
|
|
2141
2175
|
store: sanitizeString(args.store, 100),
|
|
2142
2176
|
receiptNo: sanitizeString(args.receiptNo, 50),
|
|
@@ -2156,7 +2190,7 @@ function convertParams(toolName, args) {
|
|
|
2156
2190
|
source: 'mcp_receipt',
|
|
2157
2191
|
storeCategory: sanitizeString(args.storeCategory, 50) || '其他',
|
|
2158
2192
|
storeSubCategory: sanitizeString(args.storeSubCategory, 50) || '其他',
|
|
2159
|
-
|
|
2193
|
+
_flowmate: args._flowmate,
|
|
2160
2194
|
};
|
|
2161
2195
|
}
|
|
2162
2196
|
|
|
@@ -2198,7 +2232,7 @@ function convertParams(toolName, args) {
|
|
|
2198
2232
|
apiProvider: sanitizeString(args.apiProvider, 50) || '',
|
|
2199
2233
|
mcpVersion: MCP_VERSION,
|
|
2200
2234
|
source: 'mcp_txt_income',
|
|
2201
|
-
|
|
2235
|
+
_flowmate: args._flowmate,
|
|
2202
2236
|
};
|
|
2203
2237
|
|
|
2204
2238
|
default:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 分类标准化映射器
|
|
3
|
+
* 将 Agent 提取的关键词映射到标准分类
|
|
4
|
+
* 确保分类一致性,便于后续统计
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// 标准分类列表
|
|
8
|
+
const STANDARD_CATEGORIES = [
|
|
9
|
+
'餐饮', '购物', '交通', '蔬菜', '水果', '零食', '运动', '通讯', '服饰', '美容',
|
|
10
|
+
'住房', '孩子', '长辈', '社交', '旅行', '烟酒', '数码', '汽车', '医疗', '办公',
|
|
11
|
+
'学习', '宠物', '礼金', '亲友', '日用', '休闲娱乐', '维修', '居家', '饮品', '鲜花',
|
|
12
|
+
'追星', '首饰', '借出', '保险', '网络虚拟', '生活缴费', '转账', '书籍', '捐赠', '彩票', '快递', '其他'
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// 关键词到
|