chaimi-bookkeeping-mcp 2.2.5 → 2.2.7

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 (3) hide show
  1. package/bin/cli.js +1 -5
  2. package/package.json +1 -1
  3. package/server.js +112 -8
package/bin/cli.js CHANGED
@@ -10,10 +10,6 @@ const fs = require('fs');
10
10
  const os = require('os');
11
11
  const { spawn } = require('child_process');
12
12
 
13
- // 读取 package.json 获取版本
14
- const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
15
- const CURRENT_VERSION = packageJson.version;
16
-
17
13
  // 获取 server.js 的绝对路径
18
14
  const serverPath = path.join(__dirname, '..', 'server.js');
19
15
 
@@ -187,7 +183,7 @@ function configureAllAgents() {
187
183
  function showWelcome() {
188
184
  console.log('╔════════════════════════════════════════════════════════╗');
189
185
  console.log('║ ║');
190
- console.log(`║ 柴米记账 MCP Server v${CURRENT_VERSION} ║`);
186
+ console.log('║ 柴米记账 MCP Server v2.1.0 ║');
191
187
  console.log('║ ║');
192
188
  console.log('║ 支持: OpenClaw | WorkBuddy | Claude | Cursor ║');
193
189
  console.log('║ ║');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-bookkeeping-mcp",
3
- "version": "2.2.5",
3
+ "version": "2.2.7",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -25,6 +25,8 @@ const fs = require('fs');
25
25
  // 读取 package.json 获取版本
26
26
  const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json'), 'utf8'));
27
27
  const CURRENT_VERSION = packageJson.version;
28
+ const MCP_VERSION = packageJson.version;
29
+ console.log('柴米记账 MCP Server 版本:', MCP_VERSION);
28
30
 
29
31
  // 导入 OAuth 模块
30
32
  const { OAuthManager, FileTokenStorage } = require('./oauth.js');
@@ -84,6 +86,25 @@ const server = new Server(
84
86
  server.setRequestHandler(ListToolsRequestSchema, async () => {
85
87
  return {
86
88
  tools: [
89
+ {
90
+ name: 'quick_book',
91
+ description: '【推荐首选】极简快捷记账 - 自动识别支出/收入,智能匹配分类。仅需 name 和 amount 两个参数,其他自动补全。输入如:"午餐 30 元"、"工资 8000 元"、"木屋烧烤 35 元"',
92
+ inputSchema: {
93
+ type: 'object',
94
+ properties: {
95
+ name: { type: 'string', description: '商品/服务名称或自然语言描述(必填)' },
96
+ amount: { type: 'number', description: '金额(必填)' },
97
+ category: { type: 'string', description: '分类(可选,系统自动推荐)' },
98
+ store: { type: 'string', description: '商家名称(可选)' },
99
+ note: { type: 'string', description: '备注(可选)' },
100
+ agentType: { type: 'string', description: 'Agent 类型(如:workbuddy、claude、cursor)' },
101
+ apiProvider: { type: 'string', description: 'AI 提供商' },
102
+ rawInput: { type: 'string', description: '用户原始输入' },
103
+ mcp_version: { type: 'string', description: 'MCP Server 版本号(自动填充)' },
104
+ },
105
+ required: ['name', 'amount'],
106
+ },
107
+ },
87
108
  {
88
109
  name: 'save_expense',
89
110
  description: '保存单商品消费记录(AI文字记账)',
@@ -97,9 +118,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
97
118
  unit: { type: 'string', description: '单位(如:斤、个、瓶)' },
98
119
  category: { type: 'string', description: '分类(如:餐饮、食品、交通)' },
99
120
  store: { type: 'string', description: '商家名称' },
100
- agentType: { type: 'string', description: 'Agent类型(如:workbuddy、claude、cursor)' },
101
- apiProvider: { type: 'string', description: 'AI提供商' },
121
+ agentType: { type: 'string', description: 'Agent 类型(如:workbuddy、claude、cursor)' },
122
+ apiProvider: { type: 'string', description: 'AI 提供商' },
102
123
  rawInput: { type: 'string', description: '用户原始输入' },
124
+ mcp_version: { type: 'string', description: 'MCP Server 版本号(自动填充)' },
103
125
  },
104
126
  required: ['name', 'amount', 'price', 'quantity'],
105
127
  },
@@ -137,9 +159,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
137
159
  required: ['name', 'amount', 'price', 'quantity'],
138
160
  },
139
161
  },
140
- agentType: { type: 'string', description: 'Agent类型(如:workbuddy、claude、cursor)' },
141
- apiProvider: { type: 'string', description: 'AI提供商' },
162
+ agentType: { type: 'string', description: 'Agent 类型(如:workbuddy、claude、cursor)' },
163
+ apiProvider: { type: 'string', description: 'AI 提供商' },
142
164
  rawInput: { type: 'string', description: '用户原始输入' },
165
+ mcp_version: { type: 'string', description: 'MCP Server 版本号(自动填充)' },
143
166
  },
144
167
  required: ['store', 'items'],
145
168
  },
@@ -188,9 +211,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
188
211
  category: { type: 'string', description: '收入分类(如:工资、奖金、投资)' },
189
212
  store: { type: 'string', description: '付款方(如:公司名称)' },
190
213
  note: { type: 'string', description: '备注' },
191
- agentType: { type: 'string', description: 'Agent类型(如:workbuddy、claude、cursor)' },
192
- apiProvider: { type: 'string', description: 'AI提供商' },
214
+ agentType: { type: 'string', description: 'Agent 类型(如:workbuddy、claude、cursor)' },
215
+ apiProvider: { type: 'string', description: 'AI 提供商' },
193
216
  rawInput: { type: 'string', description: '用户原始输入' },
217
+ mcp_version: { type: 'string', description: 'MCP Server 版本号(自动填充)' },
194
218
  },
195
219
  required: ['name', 'amount'],
196
220
  },
@@ -201,6 +225,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
201
225
 
202
226
  // 工具名称映射:SCF action -> mcpHub tool
203
227
  const toolMapping = {
228
+ 'quick_book': 'addExpense', // quick_book 内部会判断支出/收入
204
229
  'save_expense': 'addExpense',
205
230
  'save_receipt': 'addReceipt',
206
231
  'save_income': 'addIncome',
@@ -223,6 +248,10 @@ async function getToken() {
223
248
  }
224
249
  // 超过2小时,尝试刷新
225
250
  console.log('Token 超过2小时,尝试自动刷新...');
251
+ } else if (cachedToken && tokenExpireTime <= Date.now() + 5 * 60 * 1000) {
252
+ // Token 已过期,添加友好提示
253
+ console.log('🔐 Token 已过期,需要重新授权');
254
+ console.log('请使用 mcporter 调用 柴米记账.save_expense() 开始授权');
226
255
  }
227
256
 
228
257
  // 使用 OAuth 获取有效 Token
@@ -235,14 +264,32 @@ async function getToken() {
235
264
  cachedToken = oauthToken.accessToken;
236
265
  tokenExpireTime = oauthToken.expiresAt;
237
266
  lastRefreshTime = Date.now();
267
+ // 授权成功,清除授权状态
268
+ await oauthManager.clearAuthState();
238
269
  return cachedToken;
239
270
  } catch (err) {
240
- // OAuth Token 无效,尝试重新授权
241
- console.log('OAuth Token 无效,启动授权流程...');
271
+ // OAuth Token 无效,检查是否有未完成的授权
272
+ console.log('🔐 Token 已过期,需要重新授权');
273
+ console.log('请使用 mcporter 调用 柴米记账.save_expense() 开始授权');
274
+ console.log('OAuth Token 无效,检查授权状态...');
275
+
276
+ // 检查是否有未完成的授权
277
+ const authState = await oauthManager.loadAuthState();
278
+ if (authState && !oauthManager.isAuthStateExpired(authState)) {
279
+ console.log(`检测到未完成的授权,验证码: ${authState.userCode}`);
280
+ console.log('请在微信柴米记账小程序中输入此验证码完成授权');
281
+ console.log('授权完成后,请重新调用工具');
282
+ throw new Error(`授权进行中,请使用验证码 ${authState.userCode} 完成授权`);
283
+ }
284
+
285
+ // 没有未完成的授权,启动新的授权流程
286
+ console.log('启动新的授权流程...');
242
287
  const newToken = await oauthManager.startAuthFlow();
243
288
  cachedToken = newToken.accessToken;
244
289
  tokenExpireTime = newToken.expiresAt;
245
290
  lastRefreshTime = Date.now();
291
+ // 授权成功,清除授权状态
292
+ await oauthManager.clearAuthState();
246
293
  return cachedToken;
247
294
  }
248
295
  }
@@ -351,6 +398,60 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
351
398
 
352
399
  // 根据工具类型选择处理流程
353
400
  switch (name) {
401
+ case 'quick_book': {
402
+ // 极简快捷记账:自动识别支出/收入,智能匹配分类
403
+ console.log('处理极简快捷记账...');
404
+
405
+ // 1. 从环境变量补充参数(确保上报)
406
+ const agentType = process.env.AGENT_TYPE || process.env.MCP_AGENT_TYPE || args.agentType || '';
407
+ const apiProvider = process.env.API_PROVIDER || process.env.MCP_API_PROVIDER || args.apiProvider || '';
408
+ const rawInput = args.rawInput || args.name;
409
+
410
+ // 2. 判断是支出还是收入
411
+ const isIncome = isIncomeName(args.name);
412
+
413
+ // 3. 智能匹配分类
414
+ const category = args.category || getCategory(args.name);
415
+
416
+ // 4. 自动补全参数
417
+ const completedArgs = {
418
+ name: sanitizeString(args.name, 100),
419
+ amount: validateAmount(args.amount),
420
+ price: args.price || validateAmount(args.amount),
421
+ quantity: args.quantity || 1,
422
+ unit: args.unit || '个',
423
+ category: category,
424
+ store: sanitizeString(args.store, 100) || '未知商家',
425
+ note: sanitizeString(args.note, 500),
426
+ agentType: agentType,
427
+ apiProvider: apiProvider,
428
+ rawInput: rawInput,
429
+ mcp_version: MCP_VERSION,
430
+ };
431
+
432
+ // 5. 根据类型选择接口
433
+ if (isIncome) {
434
+ // 收入记账
435
+ console.log('识别为收入,调用 save_income...');
436
+ const mcpParams = convertParams('save_income', completedArgs);
437
+ result = await callMcpHub('addIncome', mcpParams, token);
438
+
439
+ if (result.success) {
440
+ userMessage = `✅ 记账成功\n\n| 收入来源 | 金额 | 分类 |\n|------|------|------|\n| ${completedArgs.name} | ${completedArgs.amount}元 | ${completedArgs.category || '其他收入'} |\n\n已保存到你的柴米记账小程序数据库。`;
441
+ }
442
+ } else {
443
+ // 支出记账
444
+ console.log('识别为支出,调用 save_expense...');
445
+ const mcpParams = convertParams('save_expense', completedArgs);
446
+ result = await callMcpHub('addExpense', mcpParams, token);
447
+
448
+ if (result.success) {
449
+ userMessage = `✅ 记账成功\n\n| 商品 | 金额 | 分类 | 商家 |\n|------|------|------|------|\n| ${completedArgs.name} | ${completedArgs.amount}元 | ${completedArgs.category || '其他'} | ${completedArgs.store} |\n\n已保存到你的柴米记账小程序数据库。`;
450
+ }
451
+ }
452
+ break;
453
+ }
454
+
354
455
  case 'save_expense': {
355
456
  // 步骤1:校验数据完整性
356
457
  console.log('校验消费数据完整性...');
@@ -589,6 +690,7 @@ function convertParams(toolName, args) {
589
690
  rawInput: sanitizeString(args.rawInput, 1000),
590
691
  agentType: args.agentType || '',
591
692
  apiProvider: args.apiProvider || '',
693
+ mcp_version: args.mcp_version || MCP_VERSION,
592
694
  };
593
695
  }
594
696
 
@@ -630,6 +732,7 @@ function convertParams(toolName, args) {
630
732
  rawInput: sanitizeString(args.rawInput, 2000),
631
733
  agentType: args.agentType || '',
632
734
  apiProvider: args.apiProvider || '',
735
+ mcp_version: args.mcp_version || MCP_VERSION,
633
736
  };
634
737
 
635
738
  case 'get_expenses':
@@ -656,6 +759,7 @@ function convertParams(toolName, args) {
656
759
  rawInput: sanitizeString(args.rawInput, 1000),
657
760
  agentType: args.agentType || '',
658
761
  apiProvider: args.apiProvider || '',
762
+ mcp_version: args.mcp_version || MCP_VERSION,
659
763
  };
660
764
 
661
765
  default: