chaimi-bookkeeping-mcp 2.3.2 → 2.3.4

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.
Files changed (4) hide show
  1. package/2.3.4 +0 -0
  2. package/README.md +11 -0
  3. package/package.json +1 -1
  4. package/server.js +45 -31
package/2.3.4 ADDED
File without changes
package/README.md CHANGED
@@ -141,6 +141,17 @@ AI 会自动调用 `save_income` 工具记录收入。
141
141
 
142
142
  ## 更新日志
143
143
 
144
+ ### v2.3.3 (2026-04-08)
145
+ - **修正** 版本号统一为 v2.3.3(包含 v2.3.2 的所有修复)
146
+
147
+ ### v2.3.2 (2026-04-08)
148
+ - **修复** `server.js` 中 `CURRENT_VERSION` 未定义错误,统一使用 `MCP_VERSION`
149
+ - **修复** 所有工具的 `userMessage` 添加默认值,防止显示 `undefined`
150
+
151
+ ### v2.3.1 (2026-04-08)
152
+ - **修复** `bin/cli.js` 版本号显示问题,改为动态读取 package.json
153
+ - **优化** OAuth 授权流程,轮询间隔缩短为 2 秒
154
+
144
155
  ### v2.3.0 (2026-04-08)
145
156
  - **修复** `save_receipt` 的 `items` 参数解析 bug,支持 JSON 字符串自动转换
146
157
  - **优化** `source` 字段规范:`save_expense`→`mcp_txt_expense`、`save_income`→`mcp_txt_income`、`save_receipt`→`mcp_receipt`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-bookkeeping-mcp",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -72,7 +72,7 @@ function initOAuthManager() {
72
72
  const server = new Server(
73
73
  {
74
74
  name: 'chaimi-bookkeeping-mcp',
75
- version: CURRENT_VERSION,
75
+ version: MCP_VERSION,
76
76
  },
77
77
  {
78
78
  capabilities: {
@@ -108,23 +108,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
108
108
  */
109
109
  {
110
110
  name: 'save_expense',
111
- description: '保存单商品消费记录(AI文字记账)',
111
+ description: '保存单商品消费记录(AI文字记账)。只需提供商品名称和金额,其他参数自动填充。示例:name="午餐", amount=35',
112
112
  inputSchema: {
113
113
  type: 'object',
114
114
  properties: {
115
- name: { type: 'string', description: '商品名称' },
116
- amount: { type: 'number', description: '金额' },
117
- price: { type: 'number', description: '单价' },
118
- quantity: { type: 'number', description: '数量' },
119
- unit: { type: 'string', description: '单位(如:斤、个、瓶)' },
120
- category: { type: 'string', description: '分类(如:餐饮、食品、交通)' },
121
- store: { type: 'string', description: '商家名称' },
122
- agentType: { type: 'string', description: 'Agent 类型(如:workbuddy、claude、cursor)' },
123
- apiProvider: { type: 'string', description: 'AI 提供商' },
124
- rawInput: { type: 'string', description: '用户原始输入' },
115
+ name: { type: 'string', description: '商品名称(必填)' },
116
+ amount: { type: 'number', description: '金额(必填)' },
117
+ category: { type: 'string', description: '分类(可选,如:餐饮、食品、交通)' },
118
+ store: { type: 'string', description: '商家名称(可选)' },
119
+ note: { type: 'string', description: '备注(可选)' },
120
+ agentType: { type: 'string', description: 'Agent 类型(可选,如:workbuddy、claude、cursor)' },
121
+ apiProvider: { type: 'string', description: 'AI 提供商(可选)' },
122
+ rawInput: { type: 'string', description: '用户原始输入(可选)' },
125
123
  mcp_version: { type: 'string', description: 'MCP Server 版本号(自动填充)' },
126
124
  },
127
- required: ['name', 'amount', 'price', 'quantity'],
125
+ required: ['name', 'amount'],
128
126
  },
129
127
  },
130
128
  {
@@ -491,7 +489,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
491
489
 
492
490
  // 格式化输出
493
491
  if (result.success) {
494
- userMessage = `✅ 记账成功\n\n| 商品 | 金额 | 分类 | 商家 |\n|------|------|------|------|\n| ${processedArgs.name} | ${processedArgs.amount}元 | ${processedArgs.category || '其他'} | ${processedArgs.store || '-'} |\n\n已保存到你的柴米记账小程序数据库。`;
492
+ const displayName = processedArgs.name || '未知商品';
493
+ const displayAmount = processedArgs.amount || 0;
494
+ const displayCategory = processedArgs.category || '其他';
495
+ const displayStore = processedArgs.store || '-';
496
+ userMessage = `✅ 记账成功\n\n| 商品 | 金额 | 分类 | 商家 |\n|------|------|------|------|\n| ${displayName} | ${displayAmount}元 | ${displayCategory} | ${displayStore} |\n\n已保存到你的柴米记账小程序数据库。`;
495
497
  }
496
498
  break;
497
499
  }
@@ -533,8 +535,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
533
535
  // 格式化输出
534
536
  if (result.success) {
535
537
  const totalAmount = processedArgs.totalAmount || processedArgs.items?.reduce((sum, item) => sum + (item.amount || 0), 0) || 0;
536
- const itemsList = processedArgs.items?.map(item => `• ${item.name} - ${item.amount}元`).join('\n');
537
- userMessage = `✅ 小票记录成功\n\n| 项目 | 内容 |\n|------|------|\n| 商家 | ${processedArgs.store || '-'} |\n| 金额 | ${totalAmount}元 |\n| 分类 | ${processedArgs.items?.[0]?.category || '其他'} |\n\n商品明细:\n${itemsList}\n\n已保存到你的柴米记账小程序数据库。`;
538
+ const itemsList = processedArgs.items?.map(item => `• ${item.name || '未知商品'} - ${item.amount || 0}元`).join('\n') || '暂无商品明细';
539
+ const storeName = processedArgs.store || '-';
540
+ const category = processedArgs.items?.[0]?.category || '其他';
541
+ userMessage = `✅ 小票记录成功\n\n| 项目 | 内容 |\n|------|------|\n| 商家 | ${storeName} |\n| 金额 | ${totalAmount}元 |\n| 分类 | ${category} |\n\n商品明细:\n${itemsList}\n\n已保存到你的柴米记账小程序数据库。`;
538
542
  }
539
543
  break;
540
544
  }
@@ -571,7 +575,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
571
575
 
572
576
  // 格式化输出
573
577
  if (result.success) {
574
- userMessage = `✅ 收入记录成功\n\n| 来源 | 金额 | 分类 | 付款方 |\n|------|------|------|--------|\n| ${processedArgs.name} | ${processedArgs.amount}元 | ${processedArgs.category || '其他'} | ${processedArgs.store || '-'} |`;
578
+ const displayName = processedArgs.name || '未知来源';
579
+ const displayAmount = processedArgs.amount || 0;
580
+ const displayCategory = processedArgs.category || '其他';
581
+ const displayStore = processedArgs.store || '-';
582
+ userMessage = `✅ 收入记录成功\n\n| 来源 | 金额 | 分类 | 付款方 |\n|------|------|------|--------|\n| ${displayName} | ${displayAmount}元 | ${displayCategory} | ${displayStore} |`;
575
583
  }
576
584
  break;
577
585
  }
@@ -586,7 +594,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
586
594
  }
587
595
 
588
596
  // 添加版本信息提示
589
- userMessage += `\n\n---\n📦 柴米记账 MCP v${CURRENT_VERSION}`;
597
+ userMessage += `\n\n---\n📦 柴米记账 MCP v${MCP_VERSION}`;
590
598
 
591
599
  return {
592
600
  content: [
@@ -671,26 +679,32 @@ function calculateMarketPrice(amount, weightStr) {
671
679
  function convertParams(toolName, args) {
672
680
  switch (toolName) {
673
681
  case 'save_expense': {
674
- const weight = sanitizeString(args.weight, 50);
682
+ // 必填参数校验
683
+ if (!args.name || typeof args.name !== 'string' || args.name.trim() === '') {
684
+ throw new Error('缺少必填参数:name(商品名称)');
685
+ }
686
+ if (args.amount === undefined || args.amount === null) {
687
+ throw new Error('缺少必填参数:amount(金额)');
688
+ }
689
+
690
+ const name = sanitizeString(args.name, 100);
675
691
  const amount = validateAmount(args.amount);
676
- // 优先使用传入的 marketPrice,如果没有则自动计算
677
- const marketPrice = sanitizeString(args.marketPrice, 50) || calculateMarketPrice(amount, weight);
678
692
 
679
693
  return {
694
+ name: name,
695
+ originalName: name,
680
696
  amount: amount,
697
+ price: amount, // 单价默认等于金额
698
+ quantity: 1, // 数量默认为1
681
699
  category: sanitizeString(args.category, 50) || '其他',
682
- name: sanitizeString(args.name, 100),
683
- originalName: sanitizeString(args.originalName, 200) || sanitizeString(args.name, 100),
684
- store: sanitizeString(args.store, 100),
700
+ store: sanitizeString(args.store, 100) || '',
685
701
  type: 'expense',
686
- unit: sanitizeString(args.unit, 20),
687
- weight: weight,
688
- marketPrice: marketPrice,
689
- price: validateAmount(args.price),
690
- quantity: validateQuantity(args.quantity),
702
+ unit: '',
703
+ weight: '',
704
+ marketPrice: '',
691
705
  date: args.date,
692
- note: sanitizeString(args.note, 500),
693
- rawInput: sanitizeString(args.rawInput, 1000),
706
+ note: sanitizeString(args.note, 500) || '',
707
+ rawInput: sanitizeString(args.rawInput, 1000) || '',
694
708
  agentType: args.agentType || '',
695
709
  apiProvider: args.apiProvider || '',
696
710
  mcp_version: args.mcp_version || MCP_VERSION,