chaimi-keep-mcp 3.3.0-beta.8 → 3.3.1-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/README.md +10 -0
- package/SKILL.md +66 -50
- package/package.json +1 -1
- package/server.js +192 -156
package/README.md
CHANGED
|
@@ -89,6 +89,16 @@ export MCP_PROMPT_URL="你的Prompt服务地址"
|
|
|
89
89
|
|
|
90
90
|
## Changelog
|
|
91
91
|
|
|
92
|
+
### v3.3.1-beta.0 (2026-04-27)
|
|
93
|
+
- **优化** 简化工具描述 - save_expense/save_receipt/save_income 描述更简洁,不暴露业务逻辑
|
|
94
|
+
- **优化** 参数类型自动转换 - 支持 CLI 工具的字符串传参自动转为数字
|
|
95
|
+
- **优化** 改进错误提示 - 添加 `hint` 和 `debug` 字段,帮助 Agent 自查参数问题
|
|
96
|
+
- **新增** 参数查看指南 - SKILL.md 添加"六.5 如何查看工具参数"章节
|
|
97
|
+
- **优化** 更新记账流程 - 简化为"4步记账法",更易于 Agent 执行
|
|
98
|
+
|
|
99
|
+
### v3.3.0-beta.8 (2026-04-27)
|
|
100
|
+
- **重要变更** Server 只返回原始数据 - 不再构建 userMessage,由 Agent 使用模板渲染
|
|
101
|
+
|
|
92
102
|
### v3.1.47-beta.5 (2026-04-22)
|
|
93
103
|
- **修复** 输入长度限制 - 添加硬性长度检查,防止超大字符串攻击
|
|
94
104
|
- **修复** 错误信息泄露 - 生产环境只显示友好提示,不暴露内部错误
|
package/SKILL.md
CHANGED
|
@@ -4,8 +4,7 @@ description: >-
|
|
|
4
4
|
柴米AI记账MCP Server,连接微信小程序记账功能。
|
|
5
5
|
TRIGGER: 用户提及"记账/记录/保存"并涉及金额或商品信息。
|
|
6
6
|
DO NOT TRIGGER: 非记账操作,或明确使用其他记账工具时。
|
|
7
|
-
|
|
8
|
-
updated: "2026-04-25"
|
|
7
|
+
updated: "2026-04-27"
|
|
9
8
|
---
|
|
10
9
|
|
|
11
10
|
# 柴米AI记账 Skill
|
|
@@ -122,29 +121,71 @@ chaimi-keep-mcp ⭐ 本Skill
|
|
|
122
121
|
|
|
123
122
|
## 六、快速开始
|
|
124
123
|
|
|
125
|
-
###
|
|
124
|
+
### 4步记账法
|
|
126
125
|
|
|
127
126
|
```
|
|
128
|
-
第1
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
第
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
```
|
|
127
|
+
第1步:读文档
|
|
128
|
+
└─ get_skill() 获取 SKILL.md 规范
|
|
129
|
+
└─ references/response-templates.md 了解回复模板
|
|
130
|
+
|
|
131
|
+
第2步:拿Token
|
|
132
|
+
└─ 文字记账:get_text_parse_prompt() 获取 Token 和解析模板
|
|
133
|
+
└─ 小票记账:get_parse_prompt() 获取 Token 和解析模板
|
|
136
134
|
|
|
137
|
-
|
|
135
|
+
第3步:AI解析
|
|
136
|
+
└─ 按模板解析用户输入,生成结构化数据 + _requestToken
|
|
138
137
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
第4步:执行记账
|
|
139
|
+
└─ 调用工具(save_expense/income/receipt,带上 _requestToken)
|
|
140
|
+
└─ 根据 _templateLocation 使用模板渲染美学回复
|
|
141
|
+
```
|
|
142
142
|
|
|
143
143
|
---
|
|
144
144
|
|
|
145
145
|
## 七、回复规范
|
|
146
146
|
|
|
147
|
-
### 7.1
|
|
147
|
+
### 7.1 核心原则
|
|
148
|
+
|
|
149
|
+
**Server 只返回原始数据,Agent 必须使用模板自行渲染回复。**
|
|
150
|
+
|
|
151
|
+
所有工具调用成功后:
|
|
152
|
+
- ❌ 不再返回 `userMessage` 字段
|
|
153
|
+
- ✅ 返回 `_templateLocation` 指向模板文件位置
|
|
154
|
+
- ✅ 返回 `_templateHint` 提示使用哪个模板
|
|
155
|
+
- ✅ 返回 `_templateVariables` 提供模板变量数据
|
|
156
|
+
|
|
157
|
+
### 7.2 模板文件位置
|
|
158
|
+
|
|
159
|
+
**主模板文件**: `references/response-templates.md`
|
|
160
|
+
|
|
161
|
+
**模板文件结构**:
|
|
162
|
+
```
|
|
163
|
+
references/
|
|
164
|
+
└── response-templates.md # 完整回复模板库(包含所有工具的回复模板)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 7.3 各工具对应的回复模板
|
|
168
|
+
|
|
169
|
+
| 工具 | 模板位置 | 模板章节 | 说明 |
|
|
170
|
+
|------|---------|---------|------|
|
|
171
|
+
| `save_expense` | `references/response-templates.md` | 2.1 标准成功模板 | 文字记账成功 |
|
|
172
|
+
| `save_income` | `references/response-templates.md` | 2.1 标准成功模板 | 收入记账成功 |
|
|
173
|
+
| `save_receipt` | `references/response-templates.md` | 2.3 小票记账模板 | 小票记账成功 |
|
|
174
|
+
| `get_expenses` | `references/response-templates.md` | 2.5 查询结果模板 | 消费记录查询 |
|
|
175
|
+
| `get_receipt_list` | `references/response-templates.md` | 2.5 查询结果模板 | 小票列表查询 |
|
|
176
|
+
| `get_statistics` | `references/response-templates.md` | 2.6 统计结果模板 | 消费统计分析 |
|
|
177
|
+
|
|
178
|
+
### 7.4 回复模板使用流程
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
1. 调用工具 → 返回原始数据 + _templateLocation + _templateHint + _templateVariables
|
|
182
|
+
2. 读取 _templateLocation 指定的模板文件
|
|
183
|
+
3. 根据 _templateHint 选择对应模板章节
|
|
184
|
+
4. 使用 _templateVariables 填充模板变量
|
|
185
|
+
5. 渲染最终回复给用户
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 7.5 5层视觉结构
|
|
148
189
|
|
|
149
190
|
```
|
|
150
191
|
【第1层:成功标识】✅ 「Agent名称」已帮您记账成功
|
|
@@ -164,7 +205,7 @@ chaimi-keep-mcp ⭐ 本Skill
|
|
|
164
205
|
───────────────
|
|
165
206
|
```
|
|
166
207
|
|
|
167
|
-
### 7.
|
|
208
|
+
### 7.6 标准成功模板
|
|
168
209
|
|
|
169
210
|
```markdown
|
|
170
211
|
✅ 「{agentName}」已帮您记账成功
|
|
@@ -180,40 +221,13 @@ chaimi-keep-mcp ⭐ 本Skill
|
|
|
180
221
|
💡 消费洞察:{洞察内容}(可选)
|
|
181
222
|
|
|
182
223
|
───────────────
|
|
183
|
-
🎉 {
|
|
184
|
-
柴米AI记账
|
|
185
|
-
chaimi-keep-mcp v{版本号}
|
|
186
|
-
───────────────
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### 7.3 极简模板(连续记账)
|
|
190
|
-
|
|
191
|
-
```markdown
|
|
192
|
-
✅ 已记录:{商品名} ¥{金额} · {分类}
|
|
193
|
-
{正能量祝福语}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### 7.4 高金额模板(>1000元)
|
|
197
|
-
|
|
198
|
-
```markdown
|
|
199
|
-
⚠️ 大额消费确认
|
|
200
|
-
═══════════════
|
|
201
|
-
💰 金额:¥{金额}
|
|
202
|
-
📦 商品:{商品名}
|
|
203
|
-
🕐 时间:{日期}
|
|
204
|
-
|
|
205
|
-
✅ 已确认录入「柴米AI记账」
|
|
206
|
-
|
|
207
|
-
💡 消费洞察:{洞察内容}(可选)
|
|
208
|
-
|
|
209
|
-
───────────────
|
|
210
|
-
💡 {消费建议}
|
|
224
|
+
🎉 {正能量情绪词}!
|
|
211
225
|
柴米AI记账
|
|
212
226
|
chaimi-keep-mcp v{版本号}
|
|
213
227
|
───────────────
|
|
214
228
|
```
|
|
215
229
|
|
|
216
|
-
### 7.
|
|
230
|
+
### 7.7 模板变量说明
|
|
217
231
|
|
|
218
232
|
| 变量 | 说明 | 示例 |
|
|
219
233
|
|:-----|:-----|:-----|
|
|
@@ -224,10 +238,12 @@ chaimi-keep-mcp ⭐ 本Skill
|
|
|
224
238
|
| {商家} | 商家名称(可选) | 麦当劳 |
|
|
225
239
|
| {日期时间} | 格式化时间 | 2026-04-24 12:30 |
|
|
226
240
|
| {洞察内容} | 消费洞察建议(可选) | 本月餐饮支出占比30%,建议控制 |
|
|
227
|
-
| {
|
|
241
|
+
| {正能量情绪词} | 分类对应正能量祝福语 | 美食为梦想充电,继续向前冲!🍚 |
|
|
228
242
|
| {版本号} | MCP版本 | 3.2.0 |
|
|
229
243
|
|
|
230
|
-
|
|
244
|
+
**详细正能量情绪词库和完整模板** → 见 references/response-templates.md
|
|
245
|
+
|
|
246
|
+
**历史变更记录** → 见 CHANGELOG.md
|
|
231
247
|
|
|
232
248
|
---
|
|
233
249
|
|
|
@@ -269,6 +285,6 @@ chaimi-keep-mcp ⭐ 本Skill
|
|
|
269
285
|
|
|
270
286
|
---
|
|
271
287
|
|
|
272
|
-
|
|
273
|
-
**最后更新:2026-04-
|
|
288
|
+
|
|
289
|
+
**最后更新:2026-04-27**
|
|
274
290
|
**Status:生产就绪**
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -244,7 +244,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
244
244
|
},
|
|
245
245
|
{
|
|
246
246
|
name: 'save_expense',
|
|
247
|
-
description: '
|
|
247
|
+
description: '【强制验证Token】保存单商品消费记录。前置要求:先调用 get_skill 和 get_text_parse_prompt。字段列表见 get_text_parse_prompt 返回的模板。',
|
|
248
248
|
inputSchema: {
|
|
249
249
|
type: 'object',
|
|
250
250
|
properties: {
|
|
@@ -266,7 +266,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
266
266
|
},
|
|
267
267
|
{
|
|
268
268
|
name: 'save_receipt',
|
|
269
|
-
description: '
|
|
269
|
+
description: '【强制验证Token】【必须上传图片】保存购物小票/发票/收据图片。⚠️ 注意:必须是图片才能使用此工具记账,文字描述请用 save_expense。前置要求:先调用 get_skill 和 get_parse_prompt。items为商品数组,字段列表见 get_parse_prompt 返回的模板.',返回的模板。',
|
|
270
270
|
inputSchema: {
|
|
271
271
|
type: 'object',
|
|
272
272
|
properties: {
|
|
@@ -309,7 +309,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
309
309
|
},
|
|
310
310
|
{
|
|
311
311
|
name: 'get_expenses',
|
|
312
|
-
description: '查询消费记录(支持周/月周期)。支持按周/月或日期范围筛选,可查看分类、商家、来源等多维度数据。新功能:新增 period
|
|
312
|
+
description: '查询消费记录(支持周/月周期)。支持按周/月或日期范围筛选,可查看分类、商家、来源等多维度数据。新功能:新增 period 参数,以及消费模式分析(高频商家、消费时间等)。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
|
|
313
313
|
inputSchema: {
|
|
314
314
|
type: 'object',
|
|
315
315
|
properties: {
|
|
@@ -330,7 +330,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
330
330
|
},
|
|
331
331
|
{
|
|
332
332
|
name: 'get_receipt_list',
|
|
333
|
-
description: '
|
|
333
|
+
description: '获取小票列表。支持按日期范围、商家、金额等多维度筛选。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
|
|
334
334
|
inputSchema: {
|
|
335
335
|
type: 'object',
|
|
336
336
|
properties: {
|
|
@@ -347,7 +347,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
347
347
|
},
|
|
348
348
|
{
|
|
349
349
|
name: 'get_statistics',
|
|
350
|
-
description: '获取消费统计(支持周/月周期)。支持按周/月或日期范围统计,可查看分类占比、消费趋势等。新功能:新增 period 参数(this_week、last_week、this_month、last_month),以及 insights
|
|
350
|
+
description: '获取消费统计(支持周/月周期)。支持按周/月或日期范围统计,可查看分类占比、消费趋势等。新功能:新增 period 参数(this_week、last_week、this_month、last_month),以及 insights 洞察数据。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
|
|
351
351
|
inputSchema: {
|
|
352
352
|
type: 'object',
|
|
353
353
|
properties: {
|
|
@@ -365,7 +365,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
365
365
|
},
|
|
366
366
|
{
|
|
367
367
|
name: 'get_insights',
|
|
368
|
-
description: '【新功能】获取消费洞察线索(包含趋势、模式、健康、优化建议)。纯云端计算,零AI成本!返回洞察线索,供 Agent
|
|
368
|
+
description: '【新功能】获取消费洞察线索(包含趋势、模式、健康、优化建议)。纯云端计算,零AI成本!返回洞察线索,供 Agent 结合用户大模型进行深度分析。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
|
|
369
369
|
inputSchema: {
|
|
370
370
|
type: 'object',
|
|
371
371
|
properties: {
|
|
@@ -376,7 +376,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
name: 'export_data',
|
|
379
|
-
description: '【新功能】导出完整的消费数据,供 Agent 深度分析(零AI
|
|
379
|
+
description: '【新功能】导出完整的消费数据,供 Agent 深度分析(零AI成本!)。支持导出完整的消费记录和小票数据,然后在用户侧使用大模型进行深度分析。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
|
|
380
380
|
inputSchema: {
|
|
381
381
|
type: 'object',
|
|
382
382
|
properties: {
|
|
@@ -390,7 +390,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
390
390
|
},
|
|
391
391
|
{
|
|
392
392
|
name: 'save_income',
|
|
393
|
-
description: '
|
|
393
|
+
description: '【强制验证Token】保存收入记录。前置要求:先调用 get_skill 和 get_text_parse_prompt。字段列表见 get_text_parse_prompt 返回的模板.',
|
|
394
394
|
inputSchema: {
|
|
395
395
|
type: 'object',
|
|
396
396
|
properties: {
|
|
@@ -409,7 +409,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
409
409
|
},
|
|
410
410
|
{
|
|
411
411
|
name: 'get_text_parse_prompt',
|
|
412
|
-
description: '
|
|
412
|
+
description: '【不调用100%失败】获取文字记账解析的Prompt模板和requestToken。⚠️ 文字/语音记账必须先调用此工具获取Token,AI解析时必须返回_requestToken字段,否则save_expense/save_income会报MISSING_TOKEN错误!',
|
|
413
413
|
inputSchema: {
|
|
414
414
|
type: 'object',
|
|
415
415
|
properties: {
|
|
@@ -420,7 +420,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
420
420
|
},
|
|
421
421
|
{
|
|
422
422
|
name: 'get_parse_prompt',
|
|
423
|
-
description: '
|
|
423
|
+
description: '【不调用100%失败】获取小票图片解析的Prompt模板和requestToken。⚠️ 小票记账必须先调用此工具获取Token,AI解析时必须返回_requestToken字段,否则save_receipt会报MISSING_TOKEN错误!',
|
|
424
424
|
inputSchema: {
|
|
425
425
|
type: 'object',
|
|
426
426
|
properties: {
|
|
@@ -430,7 +430,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
430
430
|
},
|
|
431
431
|
{
|
|
432
432
|
name: 'submit_feedback',
|
|
433
|
-
description: '提交反馈、建议或问题报告。当用户遇到记账问题、有功能建议或发现异常时,可使用此工具提交反馈。反馈类型包括:bug(问题报告)、feature(功能建议)、improvement(优化建议)、other(其他)。提交成功后会返回反馈编号,可用于查询处理进度。注意:反馈内容不要超过150
|
|
433
|
+
description: '提交反馈、建议或问题报告。当用户遇到记账问题、有功能建议或发现异常时,可使用此工具提交反馈。反馈类型包括:bug(问题报告)、feature(功能建议)、improvement(优化建议)、other(其他)。提交成功后会返回反馈编号,可用于查询处理进度。注意:反馈内容不要超过150个字符,超过部分会被截断。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
|
|
434
434
|
inputSchema: {
|
|
435
435
|
type: 'object',
|
|
436
436
|
properties: {
|
|
@@ -478,9 +478,7 @@ async function getToken() {
|
|
|
478
478
|
const existingToken = await oauthManager.tokenStorage.load();
|
|
479
479
|
if (existingToken && existingToken.accessToken && existingToken.expiresAt) {
|
|
480
480
|
const expiresAt = new Date(existingToken.expiresAt).getTime();
|
|
481
|
-
|
|
482
|
-
const threshold = now + 5 * 60 * 1000;
|
|
483
|
-
if (expiresAt > threshold) {
|
|
481
|
+
if (expiresAt > Date.now() + 5 * 60 * 1000) {
|
|
484
482
|
// Token 有效,直接使用
|
|
485
483
|
cachedToken = existingToken.accessToken;
|
|
486
484
|
tokenExpireTime = expiresAt;
|
|
@@ -886,8 +884,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
886
884
|
}
|
|
887
885
|
}
|
|
888
886
|
|
|
887
|
+
// 【新增】参数类型自动转换(兼容 CLI 工具的字符串传参)
|
|
888
|
+
function autoConvertTypes(toolName, args) {
|
|
889
|
+
const numberFields = {
|
|
890
|
+
'save_expense': ['amount', 'date'],
|
|
891
|
+
'save_receipt': ['totalAmount', 'actualAmount', 'originalAmount', 'discountAmount', 'date'],
|
|
892
|
+
'save_income': ['amount', 'date'],
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
const fields = numberFields[toolName] || [];
|
|
896
|
+
for (const field of fields) {
|
|
897
|
+
if (field in args && typeof args[field] !== 'number') {
|
|
898
|
+
const parsed = parseFloat(args[field]);
|
|
899
|
+
if (!isNaN(parsed)) {
|
|
900
|
+
console.error(`[TypeConvert] ${field}: "${args[field]}" → ${parsed}`);
|
|
901
|
+
args[field] = parsed;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// 转换 items 数组中的数字字段
|
|
907
|
+
if (args.items && Array.isArray(args.items)) {
|
|
908
|
+
args.items.forEach(item => {
|
|
909
|
+
['amount', 'price', 'quantity'].forEach(field => {
|
|
910
|
+
if (field in item && typeof item[field] !== 'number') {
|
|
911
|
+
const parsed = parseFloat(item[field]);
|
|
912
|
+
if (!isNaN(parsed)) item[field] = parsed;
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
autoConvertTypes(name, processedArgs);
|
|
920
|
+
|
|
889
921
|
let result;
|
|
890
|
-
let userMessage;
|
|
891
922
|
|
|
892
923
|
switch (name) {
|
|
893
924
|
case 'get_skill': {
|
|
@@ -931,8 +962,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
931
962
|
if (missingFields.length > 0) {
|
|
932
963
|
result = {
|
|
933
964
|
success: false,
|
|
934
|
-
error: `必填字段缺失:${missingFields.join(', ')}
|
|
935
|
-
code: 400
|
|
965
|
+
error: `必填字段缺失:${missingFields.join(', ')}`,
|
|
966
|
+
code: 400,
|
|
967
|
+
hint: '解决方法:1.查看 SKILL.md 中"六.5 如何查看工具参数" 2.运行 mcporter list 柴米记账 --schema 查看完整参数结构',
|
|
968
|
+
debug: {
|
|
969
|
+
missing: missingFields,
|
|
970
|
+
received: Object.keys(processedArgs),
|
|
971
|
+
docs: '调用 get_skill() 获取详细使用指南'
|
|
972
|
+
}
|
|
936
973
|
};
|
|
937
974
|
userMessage = `❌ 记账失败\n\n错误:缺少必填字段:${missingFields.join(', ')}\n\n💡 解决方案:\n1. 请检查是否从用户输入中提取了所有信息\n2. 确保传递以下字段:name(商品名)、amount(金额)、category(分类)、rawInput(原始输入)\n3. 参考 SKILL.md 中的"调用前检查清单"`;
|
|
938
975
|
break;
|
|
@@ -995,17 +1032,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
995
1032
|
achievementsText = '\n\n🎉 成就解锁:' + result.newlyUnlocked.map(a => `${a.icon} ${a.title}(+${a.points}分)`).join(';');
|
|
996
1033
|
}
|
|
997
1034
|
|
|
998
|
-
//
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1035
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1036
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1037
|
+
result._templateHint = '请使用references/response-templates.md中的2.1标准成功模板渲染回复';
|
|
1038
|
+
result._templateVariables = {
|
|
1039
|
+
agentName: await getAgentName(),
|
|
1040
|
+
商品名: displayName,
|
|
1041
|
+
商家: displayStore || '',
|
|
1042
|
+
金额: displayAmount,
|
|
1043
|
+
分类: result.data?.categoryName || displayCategory,
|
|
1044
|
+
日期: result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN'),
|
|
1045
|
+
正能量祝福语: friendlyEnding,
|
|
1046
|
+
insightsText,
|
|
1047
|
+
achievementsText
|
|
1048
|
+
};
|
|
1009
1049
|
}
|
|
1010
1050
|
break;
|
|
1011
1051
|
}
|
|
@@ -1024,14 +1064,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1024
1064
|
if (missingFields.length > 0) {
|
|
1025
1065
|
result = {
|
|
1026
1066
|
success: false,
|
|
1027
|
-
error: `必填字段缺失:${missingFields.join(', ')}
|
|
1028
|
-
code: 400
|
|
1067
|
+
error: `必填字段缺失:${missingFields.join(', ')}`,
|
|
1068
|
+
code: 400,
|
|
1069
|
+
hint: '解决方法:1.查看 SKILL.md 中"六.5 如何查看工具参数" 2.运行 mcporter list 柴米记账 --schema 查看完整参数结构',
|
|
1070
|
+
debug: {
|
|
1071
|
+
missing: missingFields,
|
|
1072
|
+
received: Object.keys(processedArgs),
|
|
1073
|
+
docs: '调用 get_skill() 获取详细使用指南'
|
|
1074
|
+
}
|
|
1029
1075
|
};
|
|
1030
|
-
userMessage = `❌ 保存失败\n\n错误:缺少必填字段:${missingFields.join(', ')}\n\n💡 解决方案:\n1. 请检查是否从 get_parse_prompt 的解析结果中提取了所有信息\n2. 确保传递以下字段:store、totalAmount、actualAmount、originalAmount、discountAmount、storeCategory、storeSubCategory\n3. 参考 SKILL.md 中的"小票字段核对清单"
|
|
1031
|
-
break;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
// 数值校验
|
|
1076
|
+
userMessage = `❌ 保存失败\n\n错误:缺少必填字段:${missingFields.join(', ')}\n\n💡 解决方案:\n1. 请检查是否从 get_parse_prompt 的解析结果中提取了所有信息\n2. 确保传递以下字段:store、totalAmount、actualAmount、originalAmount、discountAmount、storeCategory、storeSubCategory\n3. 参考 SKILL.md 中的"小票字段核对清单"`;\n break;\n }\n \n // 数值校验
|
|
1035
1077
|
const amountFields = ['totalAmount', 'actualAmount', 'originalAmount'];
|
|
1036
1078
|
for (const field of amountFields) {
|
|
1037
1079
|
if (typeof processedArgs[field] !== 'number' || processedArgs[field] <= 0) {
|
|
@@ -1240,16 +1282,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1240
1282
|
achievementsText = '\n\n🎉 成就解锁:' + result.newlyUnlocked.map(a => `${a.icon} ${a.title}(+${a.points}分)`).join(';');
|
|
1241
1283
|
}
|
|
1242
1284
|
|
|
1243
|
-
//
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1285
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1286
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1287
|
+
result._templateHint = '请使用references/response-templates.md中的2.3小票记账模板渲染回复';
|
|
1288
|
+
result._templateVariables = {
|
|
1289
|
+
agentName: await getAgentName(),
|
|
1290
|
+
store: storeName,
|
|
1291
|
+
totalAmount: totalAmount,
|
|
1292
|
+
category: result.data?.storeCategory || category,
|
|
1293
|
+
itemCount: itemCount,
|
|
1294
|
+
date: result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN'),
|
|
1295
|
+
正能量祝福语: friendlyEnding,
|
|
1296
|
+
insightsText,
|
|
1297
|
+
achievementsText,
|
|
1298
|
+
items: processedArgs.items
|
|
1299
|
+
};
|
|
1253
1300
|
}
|
|
1254
1301
|
break;
|
|
1255
1302
|
}
|
|
@@ -1265,33 +1312,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1265
1312
|
const items = result.data?.items || [];
|
|
1266
1313
|
const patterns = result.data?.patterns || {};
|
|
1267
1314
|
|
|
1268
|
-
//
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
// 消费时间模式
|
|
1279
|
-
if (patterns.timePattern) {
|
|
1280
|
-
summary += `⏰ 消费高峰:${patterns.timePattern.peakTime}\n`;
|
|
1281
|
-
if (patterns.timePattern.lateNightCount > 0) {
|
|
1282
|
-
summary += `🌙 深夜消费:${patterns.timePattern.lateNightCount}次\n`;
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
// 异常消费
|
|
1287
|
-
if (patterns.unusualItems && patterns.unusualItems.length > 0) {
|
|
1288
|
-
const topUnusual = patterns.unusualItems[0];
|
|
1289
|
-
summary += `💡 大额消费:${topUnusual.name} ¥${topUnusual.amount}\n`;
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
summary += `\n💡 提示:完整数据(含每笔明细、flags标记)请查看下方 JSON 数据`;
|
|
1293
|
-
|
|
1294
|
-
userMessage = summary;
|
|
1315
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1316
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1317
|
+
result._templateHint = '请使用references/response-templates.md中的2.5查询结果模板渲染回复';
|
|
1318
|
+
result._templateVariables = {
|
|
1319
|
+
total: total,
|
|
1320
|
+
items: items,
|
|
1321
|
+
patterns: patterns
|
|
1322
|
+
};
|
|
1295
1323
|
}
|
|
1296
1324
|
break;
|
|
1297
1325
|
}
|
|
@@ -1304,42 +1332,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1304
1332
|
if (result.success) {
|
|
1305
1333
|
const data = result.data || {};
|
|
1306
1334
|
|
|
1307
|
-
//
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
// 预算进度
|
|
1320
|
-
if (data.budget) {
|
|
1321
|
-
summary += `🎯 预算:已用¥${data.budget.used}/¥${data.budget.total}(${data.budget.progress}%)\n`;
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
// Top 3 分类
|
|
1325
|
-
if (data.categories && data.categories.length > 0) {
|
|
1326
|
-
summary += `\n📂 消费分类 Top3:\n`;
|
|
1327
|
-
data.categories.slice(0, 3).forEach((cat, idx) => {
|
|
1328
|
-
summary += ` ${idx + 1}. ${cat.category}:¥${cat.amount}(${cat.percentage}%)\n`;
|
|
1329
|
-
});
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
// 洞察摘要
|
|
1333
|
-
if (data.insights && data.insights.length > 0) {
|
|
1334
|
-
summary += `\n💡 洞察线索(${data.insights.length}条):\n`;
|
|
1335
|
-
data.insights.slice(0, 3).forEach(insight => {
|
|
1336
|
-
summary += ` ${insight.emoji || '•'} ${insight.title}:${insight.message}\n`;
|
|
1337
|
-
});
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
summary += `\n💡 提示:完整数据(含每日统计、所有洞察)请查看下方 JSON 数据`;
|
|
1341
|
-
|
|
1342
|
-
userMessage = summary;
|
|
1335
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1336
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1337
|
+
result._templateHint = '请使用references/response-templates.md中的2.6统计结果模板渲染回复';
|
|
1338
|
+
result._templateVariables = {
|
|
1339
|
+
period: data.periodDisplay || data.period || '未知',
|
|
1340
|
+
totalAmount: data.totalAmount || 0,
|
|
1341
|
+
totalCount: data.totalCount || 0,
|
|
1342
|
+
compare: data.compare,
|
|
1343
|
+
budget: data.budget,
|
|
1344
|
+
categories: data.categories,
|
|
1345
|
+
insights: data.insights
|
|
1346
|
+
};
|
|
1343
1347
|
}
|
|
1344
1348
|
break;
|
|
1345
1349
|
}
|
|
@@ -1383,9 +1387,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1383
1387
|
}
|
|
1384
1388
|
}
|
|
1385
1389
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1390
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1391
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1392
|
+
result._templateHint = '请使用references/response-templates.md中的2.6智能洞察模板渲染回复';
|
|
1393
|
+
result._templateVariables = {
|
|
1394
|
+
insights: insights
|
|
1395
|
+
};
|
|
1389
1396
|
}
|
|
1390
1397
|
break;
|
|
1391
1398
|
}
|
|
@@ -1401,15 +1408,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1401
1408
|
const totalAmount = summary.totalAmount || 0;
|
|
1402
1409
|
const periodDisplay = result.data?.periodDisplay || '';
|
|
1403
1410
|
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
userMessage = summaryText;
|
|
1411
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1412
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1413
|
+
result._templateHint = '请使用references/response-templates.md中的模板渲染回复';
|
|
1414
|
+
result._templateVariables = {
|
|
1415
|
+
period: periodDisplay,
|
|
1416
|
+
totalRecords: totalRecords,
|
|
1417
|
+
totalAmount: totalAmount
|
|
1418
|
+
};
|
|
1413
1419
|
}
|
|
1414
1420
|
break;
|
|
1415
1421
|
}
|
|
@@ -1428,8 +1434,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1428
1434
|
if (missingFields.length > 0) {
|
|
1429
1435
|
result = {
|
|
1430
1436
|
success: false,
|
|
1431
|
-
error: `必填字段缺失:${missingFields.join(', ')}
|
|
1432
|
-
code: 400
|
|
1437
|
+
error: `必填字段缺失:${missingFields.join(', ')}`,
|
|
1438
|
+
code: 400,
|
|
1439
|
+
hint: '解决方法:1.查看 SKILL.md 中"六.5 如何查看工具参数" 2.运行 mcporter list 柴米记账 --schema 查看完整参数结构',
|
|
1440
|
+
debug: {
|
|
1441
|
+
missing: missingFields,
|
|
1442
|
+
received: Object.keys(processedArgs),
|
|
1443
|
+
docs: '调用 get_skill() 获取详细使用指南'
|
|
1444
|
+
}
|
|
1433
1445
|
};
|
|
1434
1446
|
userMessage = `❌ 收入记录失败\n\n错误:缺少必填字段:${missingFields.join(', ')}\n\n💡 解决方案:\n1. 请检查是否从用户输入中提取了所有信息\n2. 确保传递以下字段:name(收入来源)、amount(金额)、category(分类)、date(日期)、rawInput(原始输入)\n3. 参考 SKILL.md 中的"调用前检查清单"`;
|
|
1435
1447
|
break;
|
|
@@ -1468,14 +1480,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1468
1480
|
const displayAmount = processedArgs.amount || 0;
|
|
1469
1481
|
const displayCategory = processedArgs.category || '其他';
|
|
1470
1482
|
const displayStore = processedArgs.store || '';
|
|
1471
|
-
const displayStoreText = displayStore ? `【${displayStore}】` : '';
|
|
1472
|
-
|
|
1473
|
-
// 【新增】获取 Agent 名称
|
|
1474
|
-
const agentName = await getAgentName();
|
|
1475
1483
|
|
|
1476
|
-
//
|
|
1477
|
-
|
|
1478
|
-
|
|
1484
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1485
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1486
|
+
result._templateHint = '请使用references/response-templates.md中的2.1标准成功模板渲染回复';
|
|
1487
|
+
result._templateVariables = {
|
|
1488
|
+
agentName: await getAgentName(),
|
|
1489
|
+
商品名: displayName,
|
|
1490
|
+
商家: displayStore,
|
|
1491
|
+
金额: displayAmount,
|
|
1492
|
+
分类: result.data?.categoryName || displayCategory,
|
|
1493
|
+
日期: result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN'),
|
|
1494
|
+
正能量祝福语: '入账顺利!💰'
|
|
1495
|
+
};
|
|
1479
1496
|
}
|
|
1480
1497
|
break;
|
|
1481
1498
|
}
|
|
@@ -1493,13 +1510,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1493
1510
|
systemPrompt: promptResult.data.systemPrompt,
|
|
1494
1511
|
userPromptTemplate: promptResult.data.userPromptTemplate,
|
|
1495
1512
|
examples: promptResult.data.examples,
|
|
1496
|
-
|
|
1513
|
+
requestToken: promptResult.data.requestToken,
|
|
1514
|
+
instructions: '请将 systemPrompt 作为 system message,把小票图片作为 user message,调用你的大模型进行解析。解析完成后,调用 save_receipt 工具保存结果。⚠️ 注意:AI解析结果必须包含 _requestToken 字段!'
|
|
1497
1515
|
}
|
|
1498
1516
|
};
|
|
1499
|
-
|
|
1517
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1518
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1519
|
+
result._templateHint = '请使用references/response-templates.md中的2.1标准成功模板渲染回复';
|
|
1520
|
+
result._templateVariables = {
|
|
1521
|
+
version: promptResult.data.version,
|
|
1522
|
+
examples: promptResult.data.examples
|
|
1523
|
+
};
|
|
1500
1524
|
} else {
|
|
1501
1525
|
result = { success: false, error: promptResult.error };
|
|
1502
|
-
userMessage = `❌ 获取Prompt失败:${promptResult.error}`;
|
|
1503
1526
|
}
|
|
1504
1527
|
break;
|
|
1505
1528
|
}
|
|
@@ -1515,13 +1538,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1515
1538
|
prompt: promptResult.data.prompt,
|
|
1516
1539
|
currentDate: promptResult.data.currentDate,
|
|
1517
1540
|
currentTime: promptResult.data.currentTime,
|
|
1541
|
+
requestToken: promptResult.data.requestToken,
|
|
1518
1542
|
instructions: promptResult.data.instructions
|
|
1519
1543
|
}
|
|
1520
1544
|
};
|
|
1521
|
-
|
|
1545
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1546
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1547
|
+
result._templateHint = '请使用references/response-templates.md中的2.1标准成功模板渲染回复';
|
|
1548
|
+
result._templateVariables = {
|
|
1549
|
+
currentDate: promptResult.data.currentDate,
|
|
1550
|
+
currentTime: promptResult.data.currentTime
|
|
1551
|
+
};
|
|
1522
1552
|
} else {
|
|
1523
1553
|
result = { success: false, error: promptResult.error };
|
|
1524
|
-
userMessage = `❌ 获取文字记账解析 Prompt 失败:${promptResult.error}`;
|
|
1525
1554
|
}
|
|
1526
1555
|
break;
|
|
1527
1556
|
}
|
|
@@ -1548,9 +1577,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1548
1577
|
other: '其他反馈'
|
|
1549
1578
|
}[feedbackData.feedType] || '其他反馈';
|
|
1550
1579
|
|
|
1551
|
-
|
|
1580
|
+
// 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
|
|
1581
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1582
|
+
result._templateHint = '请使用references/response-templates.md中的模板渲染回复';
|
|
1583
|
+
result._templateVariables = {
|
|
1584
|
+
feedbackId: result.data.feedbackId,
|
|
1585
|
+
type: typeText,
|
|
1586
|
+
time: new Date().toLocaleString('zh-CN')
|
|
1587
|
+
};
|
|
1552
1588
|
} else {
|
|
1553
|
-
|
|
1589
|
+
result._templateLocation = 'references/response-templates.md';
|
|
1590
|
+
result._templateHint = '请使用references/response-templates.md中的模板渲染回复';
|
|
1591
|
+
result._templateVariables = {
|
|
1592
|
+
error: result.error || '未知错误'
|
|
1593
|
+
};
|
|
1554
1594
|
}
|
|
1555
1595
|
break;
|
|
1556
1596
|
}
|
|
@@ -1559,21 +1599,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1559
1599
|
throw new Error(`未知工具: ${name}`);
|
|
1560
1600
|
}
|
|
1561
1601
|
|
|
1562
|
-
if (!result.success) {
|
|
1563
|
-
userMessage = `❌ 操作失败:${result.error || '未知错误'}`;
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
1602
|
// 【新增】在结果中添加 agentName,让 Agent 可以使用
|
|
1567
1603
|
const agentName = await getAgentName();
|
|
1568
1604
|
if (result && typeof result === 'object') {
|
|
1569
1605
|
result.agentName = agentName;
|
|
1570
1606
|
}
|
|
1571
1607
|
|
|
1608
|
+
// 【修改】Server 只返回原始数据,不再构建格式化消息
|
|
1609
|
+
// Agent 必须使用 references/response-templates.md 中的模板自行渲染回复
|
|
1610
|
+
const responseData = {
|
|
1611
|
+
success: result.success,
|
|
1612
|
+
data: result.data,
|
|
1613
|
+
error: result.error,
|
|
1614
|
+
_templateLocation: result._templateLocation,
|
|
1615
|
+
_templateHint: result._templateHint,
|
|
1616
|
+
_templateVariables: result._templateVariables,
|
|
1617
|
+
agentName: agentName
|
|
1618
|
+
};
|
|
1619
|
+
|
|
1572
1620
|
return {
|
|
1573
1621
|
content: [
|
|
1574
1622
|
{
|
|
1575
1623
|
type: 'text',
|
|
1576
|
-
text:
|
|
1624
|
+
text: `⚠️ 【注意】Server 只返回原始数据,不返回格式化消息。\n请使用 _templateLocation 指定的模板自行渲染回复。\n\n---\n📦 柴米记账 MCP v${MCP_VERSION}\n\n## 完整数据\n\`\`\`json\n${safeStringify(responseData)}\n\`\`\``,
|
|
1577
1625
|
},
|
|
1578
1626
|
],
|
|
1579
1627
|
};
|
|
@@ -2185,23 +2233,11 @@ async function pollForAuthInBackground(deviceCode, interval) {
|
|
|
2185
2233
|
authState.isWaiting = false;
|
|
2186
2234
|
|
|
2187
2235
|
// 保存token到文件
|
|
2188
|
-
|
|
2189
|
-
await oauthManager.tokenStorage.save(token);
|
|
2190
|
-
console.error('🔍 [DEBUG] Token 保存完成');
|
|
2191
|
-
} catch (error) {
|
|
2192
|
-
console.error('❌ Token 保存失败:', error.message);
|
|
2193
|
-
throw error;
|
|
2194
|
-
}
|
|
2236
|
+
await oauthManager.tokenStorage.save(token);
|
|
2195
2237
|
|
|
2196
2238
|
// 【新增】保存默认 Agent 名称和 deviceCode(首次记账时会获取真实名称)
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
await oauthManager.tokenStorage.saveDeviceCode(authState.deviceCode);
|
|
2200
|
-
console.error('🔍 [DEBUG] Agent 名称和 deviceCode 保存完成');
|
|
2201
|
-
} catch (error) {
|
|
2202
|
-
console.error('❌ Agent 信息保存失败:', error.message);
|
|
2203
|
-
// 这个失败不影响授权,只记录日志
|
|
2204
|
-
}
|
|
2239
|
+
await oauthManager.tokenStorage.saveAgentName('柴米AI助手');
|
|
2240
|
+
await oauthManager.tokenStorage.saveDeviceCode(authState.deviceCode);
|
|
2205
2241
|
|
|
2206
2242
|
// 清除授权状态
|
|
2207
2243
|
await oauthManager.clearAuthState();
|