chaimi-keep-mcp 3.3.0-beta.8 → 3.3.1-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/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
- version: "3.2.0"
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
- ### 3步极简记账流程
124
+ ### 4步记账法
126
125
 
127
126
  ```
128
- 第1步:状态自检
129
- 检查依赖历史习惯
130
- 第2步:获取Skill
131
- ↓ 调用 get_skill 获取最新规范
132
- 3步:执行记账
133
- 调用对应工具(save_expense/income/receipt)
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
- 1. **完成授权** → 运行 `./scripts/auth-flow.sh`
140
- 2. **验证安装** → 运行 `./scripts/verify-setup.sh`
141
- 3. **开始记账** "午餐35元"
138
+ 第4步:执行记账
139
+ └─ 调用工具(save_expense/income/receipt,带上 _requestToken)
140
+ └─ 根据 _templateLocation 使用模板渲染美学回复
141
+ ```
142
142
 
143
143
  ---
144
144
 
145
145
  ## 七、回复规范
146
146
 
147
- ### 7.1 5层视觉结构
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.2 标准成功模板(方案1:双分隔线短版)
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.5 变量说明
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
- **详细正能量词库和模板** → 见 references/response-templates.md
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
- **文档版本:v3.2.0**
273
- **最后更新:2026-04-25**
288
+
289
+ **最后更新:2026-04-27**
274
290
  **Status:生产就绪**
package/bin/cli.js CHANGED
@@ -293,13 +293,13 @@ function configureAllAgents() {
293
293
  * 注意:使用 console.error,避免污染 stdout(MCP 协议通信使用 stdout)
294
294
  */
295
295
  function showWelcome() {
296
- console.error('╔════════════════════════════════════════════════════════╗');
297
- console.error('║ ║');
298
- console.error(`║ 柴米记账 MCP Server v${CURRENT_VERSION} ║`);
299
- console.error('║ ║');
300
- console.error('║ 支持: OpenClaw | WorkBuddy | Claude | Cursor ║');
301
- console.error('║ ║');
302
- console.error('╚════════════════════════════════════════════════════════╝');
296
+ console.error('╔══════════════════════════════════════════════════════════════════════════════════╗');
297
+ console.error('║ ║');
298
+ console.error(`║ 柴米记账 MCP Server v${CURRENT_VERSION} ║`);
299
+ console.error('║ ║');
300
+ console.error('║ 支持: OpenClaw|hermes | WorkBuddy等国产小龙虾 | Claude | Cursor等支持 mcp的agent ║');
301
+ console.error('║ ║');
302
+ console.error('╚══════════════════════════════════════════════════════════════════════════════════╝');
303
303
  console.error('');
304
304
  }
305
305
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-keep-mcp",
3
- "version": "3.3.0-beta.8",
3
+ "version": "3.3.1-beta.1",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -23,9 +23,18 @@ const os = require('os');
23
23
  const fs = require('fs');
24
24
  const crypto = require('crypto');
25
25
 
26
- // 读取 package.json 获取版本
27
- const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
28
- const MCP_VERSION = packageJson.version;
26
+ // 读取版本文件获取版本号
27
+ function getVersion() {
28
+ try {
29
+ const verFilePath = path.join(__dirname, 'VERSION');
30
+ return fs.readFileSync(verFilePath, 'utf8').trim();
31
+ } catch (e) {
32
+ // 如果版本文件不存在,从 package.json 兜底
33
+ const pkgJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
34
+ return pkgJson.version;
35
+ }
36
+ }
37
+ const MCP_VERSION = getVersion();
29
38
 
30
39
  // 导入 OAuth 模块
31
40
  const { OAuthManager, FileTokenStorage } = require('./oauth.js');
@@ -244,7 +253,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
244
253
  },
245
254
  {
246
255
  name: 'save_expense',
247
- description: '保存单商品消费记录(AI文字记账)。只需提供商品名称和金额,其他参数自动填充。示例:name="午餐", amount=35',
256
+ description: '【强制验证Token】保存单商品消费记录。前置要求:先调用 get_skill 和 get_text_parse_prompt。字段列表见 get_text_parse_prompt 返回的模板。',
248
257
  inputSchema: {
249
258
  type: 'object',
250
259
  properties: {
@@ -266,7 +275,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
266
275
  },
267
276
  {
268
277
  name: 'save_receipt',
269
- description: '【图片小票专用】保存购物小票/发票/收据。⚠️ 重要:items数组中每个商品必须包含完整的4个字段:name(商品名称)、amount(金额=单价×数量)、price(单价)、quantity(数量)。示例:[{"name":"苹果","amount":5.5,"price":5.5,"quantity":1}]',
278
+ description: '【强制验证Token】【必须上传图片】保存购物小票/发票/收据图片。⚠️ 注意:必须是图片才能使用此工具记账,文字描述请用 save_expense。前置要求:先调用 get_skill 和 get_parse_prompt。items为商品数组,字段列表见 get_parse_prompt 返回的模板.',
270
279
  inputSchema: {
271
280
  type: 'object',
272
281
  properties: {
@@ -309,7 +318,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
309
318
  },
310
319
  {
311
320
  name: 'get_expenses',
312
- description: '查询消费记录(支持周/月周期)。支持按周/月或日期范围筛选,可查看分类、商家、来源等多维度数据。新功能:新增 period 参数,以及消费模式分析(高频商家、消费时间等)',
321
+ description: '查询消费记录(支持周/月周期)。支持按周/月或日期范围筛选,可查看分类、商家、来源等多维度数据。新功能:新增 period 参数,以及消费模式分析(高频商家、消费时间等)。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
313
322
  inputSchema: {
314
323
  type: 'object',
315
324
  properties: {
@@ -330,7 +339,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
330
339
  },
331
340
  {
332
341
  name: 'get_receipt_list',
333
- description: '获取小票列表。支持按日期范围、商家、金额等多维度筛选',
342
+ description: '获取小票列表。支持按日期范围、商家、金额等多维度筛选。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
334
343
  inputSchema: {
335
344
  type: 'object',
336
345
  properties: {
@@ -347,7 +356,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
347
356
  },
348
357
  {
349
358
  name: 'get_statistics',
350
- description: '获取消费统计(支持周/月周期)。支持按周/月或日期范围统计,可查看分类占比、消费趋势等。新功能:新增 period 参数(this_week、last_week、this_month、last_month),以及 insights 洞察数据',
359
+ description: '获取消费统计(支持周/月周期)。支持按周/月或日期范围统计,可查看分类占比、消费趋势等。新功能:新增 period 参数(this_week、last_week、this_month、last_month),以及 insights 洞察数据。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
351
360
  inputSchema: {
352
361
  type: 'object',
353
362
  properties: {
@@ -365,7 +374,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
365
374
  },
366
375
  {
367
376
  name: 'get_insights',
368
- description: '【新功能】获取消费洞察线索(包含趋势、模式、健康、优化建议)。纯云端计算,零AI成本!返回洞察线索,供 Agent 结合用户大模型进行深度分析',
377
+ description: '【新功能】获取消费洞察线索(包含趋势、模式、健康、优化建议)。纯云端计算,零AI成本!返回洞察线索,供 Agent 结合用户大模型进行深度分析。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
369
378
  inputSchema: {
370
379
  type: 'object',
371
380
  properties: {
@@ -376,7 +385,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
376
385
  },
377
386
  {
378
387
  name: 'export_data',
379
- description: '【新功能】导出完整的消费数据,供 Agent 深度分析(零AI成本!)。支持导出完整的消费记录和小票数据,然后在用户侧使用大模型进行深度分析',
388
+ description: '【新功能】导出完整的消费数据,供 Agent 深度分析(零AI成本!)。支持导出完整的消费记录和小票数据,然后在用户侧使用大模型进行深度分析。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
380
389
  inputSchema: {
381
390
  type: 'object',
382
391
  properties: {
@@ -390,7 +399,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
390
399
  },
391
400
  {
392
401
  name: 'save_income',
393
- description: '保存收入记录(工资、奖金、红包等)。⚠️ 重要:必须先调用 get_text_parse_prompt 解析,然后传入所有解析结果字段',
402
+ description: '【强制验证Token】保存收入记录。前置要求:先调用 get_skill get_text_parse_prompt。字段列表见 get_text_parse_prompt 返回的模板.',
394
403
  inputSchema: {
395
404
  type: 'object',
396
405
  properties: {
@@ -409,7 +418,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
409
418
  },
410
419
  {
411
420
  name: 'get_text_parse_prompt',
412
- description: '获取文字记账解析的Prompt模板,用于指导大模型如何解析用户输入的记账文字。返回的Prompt应作为system message,配合用户输入的记账文字作为user message调用大模型',
421
+ description: '【不调用100%失败】获取文字记账解析的Prompt模板和requestToken。⚠️ 文字/语音记账必须先调用此工具获取Token,AI解析时必须返回_requestToken字段,否则save_expense/save_income会报MISSING_TOKEN错误!',
413
422
  inputSchema: {
414
423
  type: 'object',
415
424
  properties: {
@@ -420,7 +429,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
420
429
  },
421
430
  {
422
431
  name: 'get_parse_prompt',
423
- description: '获取小票图片解析的Prompt模板,用于指导大模型如何格式化输出小票信息。返回的Prompt应作为system message,配合小票图片作为user message调用大模型',
432
+ description: '【不调用100%失败】获取小票图片解析的Prompt模板和requestToken。⚠️ 小票记账必须先调用此工具获取Token,AI解析时必须返回_requestToken字段,否则save_receipt会报MISSING_TOKEN错误!',
424
433
  inputSchema: {
425
434
  type: 'object',
426
435
  properties: {
@@ -430,7 +439,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
430
439
  },
431
440
  {
432
441
  name: 'submit_feedback',
433
- description: '提交反馈、建议或问题报告。当用户遇到记账问题、有功能建议或发现异常时,可使用此工具提交反馈。反馈类型包括:bug(问题报告)、feature(功能建议)、improvement(优化建议)、other(其他)。提交成功后会返回反馈编号,可用于查询处理进度。注意:反馈内容不要超过150个字符,超过部分会被截断',
442
+ description: '提交反馈、建议或问题报告。当用户遇到记账问题、有功能建议或发现异常时,可使用此工具提交反馈。反馈类型包括:bug(问题报告)、feature(功能建议)、improvement(优化建议)、other(其他)。提交成功后会返回反馈编号,可用于查询处理进度。注意:反馈内容不要超过150个字符,超过部分会被截断。返回格式:本工具只返回原始数据,不返回格式化消息。Agent必须使用get_skill获取SKILL.md中的"七、回复规范",再读取references/response-templates.md中的模板自行渲染回复。',
434
443
  inputSchema: {
435
444
  type: 'object',
436
445
  properties: {
@@ -478,9 +487,7 @@ async function getToken() {
478
487
  const existingToken = await oauthManager.tokenStorage.load();
479
488
  if (existingToken && existingToken.accessToken && existingToken.expiresAt) {
480
489
  const expiresAt = new Date(existingToken.expiresAt).getTime();
481
- const now = Date.now();
482
- const threshold = now + 5 * 60 * 1000;
483
- if (expiresAt > threshold) {
490
+ if (expiresAt > Date.now() + 5 * 60 * 1000) {
484
491
  // Token 有效,直接使用
485
492
  cachedToken = existingToken.accessToken;
486
493
  tokenExpireTime = expiresAt;
@@ -886,8 +893,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
886
893
  }
887
894
  }
888
895
 
896
+ // 【新增】参数类型自动转换(兼容 CLI 工具的字符串传参)
897
+ function autoConvertTypes(toolName, args) {
898
+ const numberFields = {
899
+ 'save_expense': ['amount', 'date'],
900
+ 'save_receipt': ['totalAmount', 'actualAmount', 'originalAmount', 'discountAmount', 'date'],
901
+ 'save_income': ['amount', 'date'],
902
+ };
903
+
904
+ const fields = numberFields[toolName] || [];
905
+ for (const field of fields) {
906
+ if (field in args && typeof args[field] !== 'number') {
907
+ const parsed = parseFloat(args[field]);
908
+ if (!isNaN(parsed)) {
909
+ console.error(`[TypeConvert] ${field}: "${args[field]}" → ${parsed}`);
910
+ args[field] = parsed;
911
+ }
912
+ }
913
+ }
914
+
915
+ // 转换 items 数组中的数字字段
916
+ if (args.items && Array.isArray(args.items)) {
917
+ args.items.forEach(item => {
918
+ ['amount', 'price', 'quantity'].forEach(field => {
919
+ if (field in item && typeof item[field] !== 'number') {
920
+ const parsed = parseFloat(item[field]);
921
+ if (!isNaN(parsed)) item[field] = parsed;
922
+ }
923
+ });
924
+ });
925
+ }
926
+ }
927
+
928
+ autoConvertTypes(name, processedArgs);
929
+
889
930
  let result;
890
- let userMessage;
891
931
 
892
932
  switch (name) {
893
933
  case 'get_skill': {
@@ -931,8 +971,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
931
971
  if (missingFields.length > 0) {
932
972
  result = {
933
973
  success: false,
934
- error: `必填字段缺失:${missingFields.join(', ')}。请从解析结果中提取并传递所有字段。`,
935
- code: 400
974
+ error: `必填字段缺失:${missingFields.join(', ')}`,
975
+ code: 400,
976
+ hint: '解决方法:1.查看 SKILL.md 中"六.5 如何查看工具参数" 2.运行 mcporter list 柴米记账 --schema 查看完整参数结构',
977
+ debug: {
978
+ missing: missingFields,
979
+ received: Object.keys(processedArgs),
980
+ docs: '调用 get_skill() 获取详细使用指南'
981
+ }
936
982
  };
937
983
  userMessage = `❌ 记账失败\n\n错误:缺少必填字段:${missingFields.join(', ')}\n\n💡 解决方案:\n1. 请检查是否从用户输入中提取了所有信息\n2. 确保传递以下字段:name(商品名)、amount(金额)、category(分类)、rawInput(原始输入)\n3. 参考 SKILL.md 中的"调用前检查清单"`;
938
984
  break;
@@ -995,17 +1041,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
995
1041
  achievementsText = '\n\n🎉 成就解锁:' + result.newlyUnlocked.map(a => `${a.icon} ${a.title}(+${a.points}分)`).join(';');
996
1042
  }
997
1043
 
998
- // 【新增】获取 Agent 名称
999
- const agentName = await getAgentName();
1000
-
1001
- // SKILL 规范格式:新的回复模板
1002
- const displayStoreText = displayStore ? `【${displayStore}】` : '';
1003
- const dateStr = result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN');
1004
- userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n · 商品/店名:${displayName}${displayStoreText} 💰¥${displayAmount}\n · 收支类型: 支出\n · 分类:${result.data?.categoryName || displayCategory}\n · 时间:${dateStr}\n✅ ${friendlyEnding}${insightsText}${achievementsText}\n-------------\nchaimi-keep-mcp: v${MCP_VERSION}`;
1005
-
1006
- if (!processedArgs.agentType || !processedArgs.apiProvider) {
1007
- userMessage += '\n\n💡 提示:传递agentType和apiProvider参数可解锁小程序记录来源统计功能,示例:agentType="openclaw", apiProvider="doubao"';
1008
- }
1044
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1045
+ result._templateLocation = 'references/response-templates.md';
1046
+ result._templateHint = '请使用references/response-templates.md中的2.1标准成功模板渲染回复';
1047
+ result._templateVariables = {
1048
+ agentName: await getAgentName(),
1049
+ 商品名: displayName,
1050
+ 商家: displayStore || '',
1051
+ 金额: displayAmount,
1052
+ 分类: result.data?.categoryName || displayCategory,
1053
+ 日期: result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN'),
1054
+ 正能量祝福语: friendlyEnding,
1055
+ insightsText,
1056
+ achievementsText
1057
+ };
1009
1058
  }
1010
1059
  break;
1011
1060
  }
@@ -1024,13 +1073,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1024
1073
  if (missingFields.length > 0) {
1025
1074
  result = {
1026
1075
  success: false,
1027
- error: `必填字段缺失:${missingFields.join(', ')}。请从 get_parse_prompt 的解析结果中提取并传递所有字段。`,
1028
- code: 400
1076
+ error: `必填字段缺失:${missingFields.join(', ')}`,
1077
+ code: 400,
1078
+ hint: '解决方法:1.查看 SKILL.md 中"六.5 如何查看工具参数" 2.运行 mcporter list 柴米记账 --schema 查看完整参数结构',
1079
+ debug: {
1080
+ missing: missingFields,
1081
+ received: Object.keys(processedArgs),
1082
+ docs: '调用 get_skill() 获取详细使用指南'
1083
+ }
1029
1084
  };
1030
- userMessage = `❌ 保存失败\n\n错误:缺少必填字段:${missingFields.join(', ')}\n\n💡 解决方案:\n1. 请检查是否从 get_parse_prompt 的解析结果中提取了所有信息\n2. 确保传递以下字段:store、totalAmount、actualAmount、originalAmount、discountAmount、storeCategory、storeSubCategory\n3. 参考 SKILL.md 中的"小票字段核对清单"`;
1085
+ userMessage = `❌ 保存失败\n\n错误:缺少必填字段:${missingFields.join(', ')}\n\n💡 解决方案:\n1. 请检查是否从 get_parse_prompt 的解析结果中提取了所有信息\n2. 确保传递以下字段:store、totalAmount、actualAmount、originalAmount、discountAmount、storeCategory、storeSubCategory\n3. 参考 SKILL.md 中的小票字段核对清单`;
1031
1086
  break;
1032
1087
  }
1033
-
1088
+
1034
1089
  // 数值校验
1035
1090
  const amountFields = ['totalAmount', 'actualAmount', 'originalAmount'];
1036
1091
  for (const field of amountFields) {
@@ -1240,16 +1295,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1240
1295
  achievementsText = '\n\n🎉 成就解锁:' + result.newlyUnlocked.map(a => `${a.icon} ${a.title}(+${a.points}分)`).join(';');
1241
1296
  }
1242
1297
 
1243
- // 【新增】获取 Agent 名称
1244
- const agentName = await getAgentName();
1245
-
1246
- // SKILL 规范格式:新的回复模板
1247
- const dateStr = result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN');
1248
- userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n · 商品/店名:${storeName} 💰¥${totalAmount}\n · 收支类型: 支出\n · 分类:${result.data?.storeCategory || category}\n · 商品数量:${itemCount}件\n · 时间:${dateStr}\n✅ ${friendlyEnding}${insightsText}${achievementsText}\n-------------\nchaimi-keep-mcp: v${MCP_VERSION}`;
1249
-
1250
- if (!processedArgs.agentType || !processedArgs.apiProvider) {
1251
- userMessage += '\n\n💡 提示:传递agentType和apiProvider参数可解锁小程序记录来源统计功能,示例:agentType="openclaw", apiProvider="doubao"';
1252
- }
1298
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1299
+ result._templateLocation = 'references/response-templates.md';
1300
+ result._templateHint = '请使用references/response-templates.md中的2.3小票记账模板渲染回复';
1301
+ result._templateVariables = {
1302
+ agentName: await getAgentName(),
1303
+ store: storeName,
1304
+ totalAmount: totalAmount,
1305
+ category: result.data?.storeCategory || category,
1306
+ itemCount: itemCount,
1307
+ date: result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN'),
1308
+ 正能量祝福语: friendlyEnding,
1309
+ insightsText,
1310
+ achievementsText,
1311
+ items: processedArgs.items
1312
+ };
1253
1313
  }
1254
1314
  break;
1255
1315
  }
@@ -1265,33 +1325,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1265
1325
  const items = result.data?.items || [];
1266
1326
  const patterns = result.data?.patterns || {};
1267
1327
 
1268
- // 构建友好的摘要信息
1269
- let summary = `📊 消费记录查询成功\n\n`;
1270
- summary += `📋 共找到 ${total} 条记录\n`;
1271
-
1272
- // 高频商家
1273
- if (patterns.highFrequencyStores && patterns.highFrequencyStores.length > 0) {
1274
- const topStore = patterns.highFrequencyStores[0];
1275
- summary += `🏪 高频商家:${topStore.store}(${topStore.count}次,均价¥${topStore.avgAmount})\n`;
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;
1328
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1329
+ result._templateLocation = 'references/response-templates.md';
1330
+ result._templateHint = '请使用references/response-templates.md中的2.5查询结果模板渲染回复';
1331
+ result._templateVariables = {
1332
+ total: total,
1333
+ items: items,
1334
+ patterns: patterns
1335
+ };
1295
1336
  }
1296
1337
  break;
1297
1338
  }
@@ -1304,42 +1345,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1304
1345
  if (result.success) {
1305
1346
  const data = result.data || {};
1306
1347
 
1307
- // 构建友好的统计摘要
1308
- let summary = `📈 统计查询成功\n\n`;
1309
- summary += `📅 周期:${data.periodDisplay || data.period || '未知'}\n`;
1310
- summary += `💰 总支出:¥${data.totalAmount || 0}(${data.totalCount || 0}笔)\n`;
1311
-
1312
- // 环比对比
1313
- if (data.compare && data.compare.vsLastPeriod !== undefined) {
1314
- const change = data.compare.vsLastPeriod;
1315
- const emoji = change > 0 ? '📈' : change < 0 ? '📉' : '➡️';
1316
- summary += `${emoji} 环比:${change > 0 ? '+' : ''}${change}%\n`;
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;
1348
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1349
+ result._templateLocation = 'references/response-templates.md';
1350
+ result._templateHint = '请使用references/response-templates.md中的2.6统计结果模板渲染回复';
1351
+ result._templateVariables = {
1352
+ period: data.periodDisplay || data.period || '未知',
1353
+ totalAmount: data.totalAmount || 0,
1354
+ totalCount: data.totalCount || 0,
1355
+ compare: data.compare,
1356
+ budget: data.budget,
1357
+ categories: data.categories,
1358
+ insights: data.insights
1359
+ };
1343
1360
  }
1344
1361
  break;
1345
1362
  }
@@ -1383,9 +1400,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1383
1400
  }
1384
1401
  }
1385
1402
 
1386
- summary += `\n💡 提示:每条洞察都包含 suggestionForAgent 字段,可用于深度分析`;
1387
-
1388
- userMessage = summary;
1403
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1404
+ result._templateLocation = 'references/response-templates.md';
1405
+ result._templateHint = '请使用references/response-templates.md中的2.6智能洞察模板渲染回复';
1406
+ result._templateVariables = {
1407
+ insights: insights
1408
+ };
1389
1409
  }
1390
1410
  break;
1391
1411
  }
@@ -1401,15 +1421,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1401
1421
  const totalAmount = summary.totalAmount || 0;
1402
1422
  const periodDisplay = result.data?.periodDisplay || '';
1403
1423
 
1404
- let summaryText = `📦 数据导出完成\n\n`;
1405
- if (periodDisplay) {
1406
- summaryText += `📅 周期:${periodDisplay}\n`;
1407
- }
1408
- summaryText += `📊 导出记录数:${totalRecords}条\n`;
1409
- summaryText += `💰 总金额:¥${totalAmount.toFixed ? totalAmount.toFixed(2) : totalAmount}\n`;
1410
- summaryText += `\n✅ 完整数据已准备好,你可以使用自己的大模型进行深度分析了!\n(完整数据在下方的完整数据JSON中)`;
1411
-
1412
- userMessage = summaryText;
1424
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1425
+ result._templateLocation = 'references/response-templates.md';
1426
+ result._templateHint = '请使用references/response-templates.md中的模板渲染回复';
1427
+ result._templateVariables = {
1428
+ period: periodDisplay,
1429
+ totalRecords: totalRecords,
1430
+ totalAmount: totalAmount
1431
+ };
1413
1432
  }
1414
1433
  break;
1415
1434
  }
@@ -1428,8 +1447,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1428
1447
  if (missingFields.length > 0) {
1429
1448
  result = {
1430
1449
  success: false,
1431
- error: `必填字段缺失:${missingFields.join(', ')}。请从用户输入中提取并传递所有字段。`,
1432
- code: 400
1450
+ error: `必填字段缺失:${missingFields.join(', ')}`,
1451
+ code: 400,
1452
+ hint: '解决方法:1.查看 SKILL.md 中"六.5 如何查看工具参数" 2.运行 mcporter list 柴米记账 --schema 查看完整参数结构',
1453
+ debug: {
1454
+ missing: missingFields,
1455
+ received: Object.keys(processedArgs),
1456
+ docs: '调用 get_skill() 获取详细使用指南'
1457
+ }
1433
1458
  };
1434
1459
  userMessage = `❌ 收入记录失败\n\n错误:缺少必填字段:${missingFields.join(', ')}\n\n💡 解决方案:\n1. 请检查是否从用户输入中提取了所有信息\n2. 确保传递以下字段:name(收入来源)、amount(金额)、category(分类)、date(日期)、rawInput(原始输入)\n3. 参考 SKILL.md 中的"调用前检查清单"`;
1435
1460
  break;
@@ -1468,14 +1493,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1468
1493
  const displayAmount = processedArgs.amount || 0;
1469
1494
  const displayCategory = processedArgs.category || '其他';
1470
1495
  const displayStore = processedArgs.store || '';
1471
- const displayStoreText = displayStore ? `【${displayStore}】` : '';
1472
1496
 
1473
- // 【新增】获取 Agent 名称
1474
- const agentName = await getAgentName();
1475
-
1476
- // SKILL 规范格式:新的回复模板
1477
- const dateStr = result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN');
1478
- userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n · 商品/店名:${displayName}${displayStoreText} 💰¥${displayAmount}\n · 收支类型: 收入\n · 分类:${result.data?.categoryName || displayCategory}\n · 时间:${dateStr}\n✅ 入账顺利!💰\n-------------\nchaimi-keep-mcp: v${MCP_VERSION}`;
1497
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1498
+ result._templateLocation = 'references/response-templates.md';
1499
+ result._templateHint = '请使用references/response-templates.md中的2.1标准成功模板渲染回复';
1500
+ result._templateVariables = {
1501
+ agentName: await getAgentName(),
1502
+ 商品名: displayName,
1503
+ 商家: displayStore,
1504
+ 金额: displayAmount,
1505
+ 分类: result.data?.categoryName || displayCategory,
1506
+ 日期: result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN'),
1507
+ 正能量祝福语: '入账顺利!💰'
1508
+ };
1479
1509
  }
1480
1510
  break;
1481
1511
  }
@@ -1493,13 +1523,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1493
1523
  systemPrompt: promptResult.data.systemPrompt,
1494
1524
  userPromptTemplate: promptResult.data.userPromptTemplate,
1495
1525
  examples: promptResult.data.examples,
1496
- instructions: '请将 systemPrompt 作为 system message,把小票图片作为 user message,调用你的大模型进行解析。解析完成后,调用 save_receipt 工具保存结果。'
1526
+ requestToken: promptResult.data.requestToken,
1527
+ instructions: '请将 systemPrompt 作为 system message,把小票图片作为 user message,调用你的大模型进行解析。解析完成后,调用 save_receipt 工具保存结果。⚠️ 注意:AI解析结果必须包含 _requestToken 字段!'
1497
1528
  }
1498
1529
  };
1499
- userMessage = `✅ 获取解析Prompt成功\n\n版本: ${promptResult.data.version}\n\n## System Prompt\n\n请将以下内容作为 system message 发送给大模型:\n\n\`\`\`\n${promptResult.data.systemPrompt}\n\`\`\`\n\n## 使用说明\n\n1. 将上面的 System Prompt 作为 system message\n2. 将小票图片作为 user message\n3. 调用大模型解析\n4. 解析完成后,调用 save_receipt 工具保存结果\n\n## 示例\n\n${JSON.stringify(promptResult.data.examples, null, 2)}`;
1530
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1531
+ result._templateLocation = 'references/response-templates.md';
1532
+ result._templateHint = '请使用references/response-templates.md中的2.1标准成功模板渲染回复';
1533
+ result._templateVariables = {
1534
+ version: promptResult.data.version,
1535
+ examples: promptResult.data.examples
1536
+ };
1500
1537
  } else {
1501
1538
  result = { success: false, error: promptResult.error };
1502
- userMessage = `❌ 获取Prompt失败:${promptResult.error}`;
1503
1539
  }
1504
1540
  break;
1505
1541
  }
@@ -1515,13 +1551,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1515
1551
  prompt: promptResult.data.prompt,
1516
1552
  currentDate: promptResult.data.currentDate,
1517
1553
  currentTime: promptResult.data.currentTime,
1554
+ requestToken: promptResult.data.requestToken,
1518
1555
  instructions: promptResult.data.instructions
1519
1556
  }
1520
1557
  };
1521
- userMessage = `✅ 获取文字记账解析 Prompt 成功\n\n当前日期: ${promptResult.data.currentDate}\n当前时间: ${promptResult.data.currentTime}\n\n## Prompt\n\n请将以下内容作为 system message 发送给大模型:\n\n\`\`\`\n${promptResult.data.prompt}\n\`\`\`\n\n## 使用说明\n\n1. 将上面的 Prompt 作为 system message\n2. 将用户输入的记账文字(如"午餐 24块")作为 user message\n3. 调用大模型解析,获取 JSON 结果\n4. 解析完成后,调用 save_expense 或 save_income 工具保存结果`;
1558
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1559
+ result._templateLocation = 'references/response-templates.md';
1560
+ result._templateHint = '请使用references/response-templates.md中的2.1标准成功模板渲染回复';
1561
+ result._templateVariables = {
1562
+ currentDate: promptResult.data.currentDate,
1563
+ currentTime: promptResult.data.currentTime
1564
+ };
1522
1565
  } else {
1523
1566
  result = { success: false, error: promptResult.error };
1524
- userMessage = `❌ 获取文字记账解析 Prompt 失败:${promptResult.error}`;
1525
1567
  }
1526
1568
  break;
1527
1569
  }
@@ -1548,9 +1590,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1548
1590
  other: '其他反馈'
1549
1591
  }[feedbackData.feedType] || '其他反馈';
1550
1592
 
1551
- userMessage = `✅ 反馈提交成功!\n\n感谢您的反馈,我们会尽快处理。\n\n━━━━━━━━━━━━━━\n📋 反馈编号:${result.data.feedbackId}\n📂 类型:${typeText}\n⏰ 时间:${new Date().toLocaleString('zh-CN')}\n━━━━━━━━━━━━━━\n\n💡 提示:\n- 反馈编号可用于查询处理进度\n- 如需补充信息,可再次提交并备注原编号\n- 我们会在小程序客服中回复您`;
1593
+ // 【新增】返回模板位置信息(Server只返回原始数据,Agent使用模板渲染)
1594
+ result._templateLocation = 'references/response-templates.md';
1595
+ result._templateHint = '请使用references/response-templates.md中的模板渲染回复';
1596
+ result._templateVariables = {
1597
+ feedbackId: result.data.feedbackId,
1598
+ type: typeText,
1599
+ time: new Date().toLocaleString('zh-CN')
1600
+ };
1552
1601
  } else {
1553
- userMessage = `❌ 反馈提交失败\n\n错误信息:${result.error || '未知错误'}\n\n💡 建议:\n- 检查网络连接后重试\n- 如持续失败,请稍后重试或联系客服`;
1602
+ result._templateLocation = 'references/response-templates.md';
1603
+ result._templateHint = '请使用references/response-templates.md中的模板渲染回复';
1604
+ result._templateVariables = {
1605
+ error: result.error || '未知错误'
1606
+ };
1554
1607
  }
1555
1608
  break;
1556
1609
  }
@@ -1559,21 +1612,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1559
1612
  throw new Error(`未知工具: ${name}`);
1560
1613
  }
1561
1614
 
1562
- if (!result.success) {
1563
- userMessage = `❌ 操作失败:${result.error || '未知错误'}`;
1564
- }
1565
-
1566
1615
  // 【新增】在结果中添加 agentName,让 Agent 可以使用
1567
1616
  const agentName = await getAgentName();
1568
1617
  if (result && typeof result === 'object') {
1569
1618
  result.agentName = agentName;
1570
1619
  }
1571
1620
 
1621
+ // 【修改】Server 只返回原始数据,不再构建格式化消息
1622
+ // Agent 必须使用 references/response-templates.md 中的模板自行渲染回复
1623
+ const responseData = {
1624
+ success: result.success,
1625
+ data: result.data,
1626
+ error: result.error,
1627
+ _templateLocation: result._templateLocation,
1628
+ _templateHint: result._templateHint,
1629
+ _templateVariables: result._templateVariables,
1630
+ agentName: agentName
1631
+ };
1632
+
1572
1633
  return {
1573
1634
  content: [
1574
1635
  {
1575
1636
  type: 'text',
1576
- text: `${userMessage}\n\n---\n📦 柴米记账 MCP v${MCP_VERSION}\n\n## 完整数据\n\`\`\`json\n${safeStringify(result)}\n\`\`\``,
1637
+ text: `⚠️ 【注意】Server 只返回原始数据,不返回格式化消息。\n请使用 _templateLocation 指定的模板自行渲染回复。\n\n---\n📦 柴米记账 MCP v${MCP_VERSION}\n\n## 完整数据\n\`\`\`json\n${safeStringify(responseData)}\n\`\`\``,
1577
1638
  },
1578
1639
  ],
1579
1640
  };
@@ -2185,23 +2246,11 @@ async function pollForAuthInBackground(deviceCode, interval) {
2185
2246
  authState.isWaiting = false;
2186
2247
 
2187
2248
  // 保存token到文件
2188
- try {
2189
- await oauthManager.tokenStorage.save(token);
2190
- console.error('🔍 [DEBUG] Token 保存完成');
2191
- } catch (error) {
2192
- console.error('❌ Token 保存失败:', error.message);
2193
- throw error;
2194
- }
2249
+ await oauthManager.tokenStorage.save(token);
2195
2250
 
2196
2251
  // 【新增】保存默认 Agent 名称和 deviceCode(首次记账时会获取真实名称)
2197
- try {
2198
- await oauthManager.tokenStorage.saveAgentName('柴米AI助手');
2199
- await oauthManager.tokenStorage.saveDeviceCode(authState.deviceCode);
2200
- console.error('🔍 [DEBUG] Agent 名称和 deviceCode 保存完成');
2201
- } catch (error) {
2202
- console.error('❌ Agent 信息保存失败:', error.message);
2203
- // 这个失败不影响授权,只记录日志
2204
- }
2252
+ await oauthManager.tokenStorage.saveAgentName('柴米AI助手');
2253
+ await oauthManager.tokenStorage.saveDeviceCode(authState.deviceCode);
2205
2254
 
2206
2255
  // 清除授权状态
2207
2256
  await oauthManager.clearAuthState();