actual-mcp-server 0.5.1 → 0.5.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.
package/README.md CHANGED
@@ -668,4 +668,4 @@ The software is provided **as-is**, without warranty of any kind. The author acc
668
668
 
669
669
  ---
670
670
 
671
- **Version:** 0.5.1 | **Tool Count:** 62 (verified LibreChat-compatible)
671
+ **Version:** 0.5.4 | **Tool Count:** 62 (verified LibreChat-compatible)
package/dist/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "actual-mcp-server",
3
3
  "displayName": "Actual MCP Server",
4
- "version": "0.5.1",
4
+ "version": "0.5.4",
5
+ "engines": {
6
+ "node": ">=20.0.0",
7
+ "npm": ">=10.0.0"
8
+ },
5
9
  "description": "MCP server with 62 tools for AI-driven financial management with Actual Budget — HTTP and stdio transports, LibreChat, Claude Desktop, Cursor, VS Code, Gemini CLI",
6
10
  "repository": {
7
11
  "type": "git",
package/dist/src/index.js CHANGED
@@ -67,6 +67,10 @@ process.on('uncaughtException', (error) => {
67
67
  });
68
68
  // Minimal early help handling before any side-effectful modules (prevents dotenv from running on --help)
69
69
  const argsEarly = process.argv.slice(2);
70
+ // dotenv v17 outputs diagnostic text to stdout by default (console.log).
71
+ // Suppress it unconditionally — in stdio mode stdout is the JSON-RPC channel
72
+ // (non-JSON corrupts the framing), and in other modes the diagnostic is noise.
73
+ process.env.DOTENV_CONFIG_QUIET = 'true';
70
74
  // Set MCP_STDIO_MODE before the async IIFE so it is in place when src/logger.ts is first
71
75
  // imported. The Winston Console transport reads this env var at construction time to decide
72
76
  // whether to route all output to stderr (required in stdio mode — stdout writes corrupt JSON-RPC).
@@ -76,19 +80,28 @@ if (argsEarly.includes('--stdio')) {
76
80
  // Only load dotenv if we're not just showing help
77
81
  // dotenv will be loaded inside the async IIFE below via dynamic import
78
82
  // to avoid using require() in ESM and to keep the early --help fast exit.
79
- const usageEarly = `
80
- Usage: npm run dev -- [--http | --stdio | --test-actual-connection | --test-actual-tools] [--debug] [--help]
83
+ if (argsEarly.includes('--help') || argsEarly.includes('-h') ||
84
+ argsEarly.includes('--version') || argsEarly.includes('-v')) {
85
+ const pkg = await import('../package.json', { with: { type: 'json' } });
86
+ const version = pkg.default.version;
87
+ if (argsEarly.includes('--version') || argsEarly.includes('-v')) {
88
+ console.log(version);
89
+ }
90
+ else {
91
+ console.log(`actual-mcp-server v${version}
92
+
93
+ Usage: actual-mcp-server [--http | --stdio | --test-actual-connection] [--debug] [--help]
81
94
 
82
95
  Options:
83
96
  --http Start HTTP MCP server
84
97
  --stdio Start stdio MCP server (for Claude Desktop / local clients)
85
98
  --test-actual-connection Test connecting to Actual and exit
86
- --test-actual-tools Test connecting and run all tools, then exit
87
99
  --debug Enable debug logging
88
- --help Show this help message
89
- `;
90
- if (argsEarly.includes('--help')) {
91
- console.log(usageEarly);
100
+ --version, -v Print version and exit
101
+ --help, -h Show this help message
102
+
103
+ Docs & source: https://github.com/agigante80/actual-mcp-server`);
104
+ }
92
105
  process.exit(0);
93
106
  }
94
107
  (async () => {
@@ -108,9 +121,8 @@ if (argsEarly.includes('--help')) {
108
121
  console.log('Debug mode enabled: DEBUG=* LOG_LEVEL=debug');
109
122
  }
110
123
  // dynamic imports to avoid running side effects on module import
111
- const [{ connectToActual }, { testAllTools }, { ActualMCPConnection }] = await Promise.all([
124
+ const [{ connectToActual }, { ActualMCPConnection }] = await Promise.all([
112
125
  import('./actualConnection.js'),
113
- import('./tests/actualToolsTests.js'),
114
126
  import('./lib/ActualMCPConnection.js'),
115
127
  ]);
116
128
  const [{ startHttpServer }, { startStdioServer }, loggerModule, osModule, utilsModule, actualToolsManagerModule,] = await Promise.all([
@@ -153,12 +165,10 @@ if (argsEarly.includes('--help')) {
153
165
  const useHttp = args.includes('--http');
154
166
  const useStdio = args.includes('--stdio');
155
167
  const useTestActualConnection = args.includes('--test-actual-connection');
156
- const useTestActualTools = args.includes('--test-actual-tools');
157
168
  const useTestMcpClient = args.includes('--test-mcp-client');
158
169
  const SERVER_DESCRIPTION = 'Bridge MCP server exposing Actual finance API to LibreChat.';
159
170
  const SERVER_INSTRUCTIONS = 'Welcome to the Actual MCP server. The tools listed here are only the ones currently confirmed and tested, ' +
160
171
  'but the server can proxy any API call supported by Actual. As we expand coverage, more tools will be officially exposed.';
161
- const usage = usageEarly;
162
172
  async function main() {
163
173
  // Mutual exclusion — stdio and http are incompatible transports
164
174
  if (useHttp && useStdio) {
@@ -175,23 +185,6 @@ if (argsEarly.includes('--help')) {
175
185
  logger.info('⚙️ --test-actual-connection specified, connection to Actual Finance successful.');
176
186
  process.exit(0);
177
187
  }
178
- if (useTestActualTools) {
179
- logger.info('⚙️ --test-actual-tools specified, connecting and testing all tools...');
180
- try {
181
- await testAllTools();
182
- logger.info('✅ All tool tests completed.');
183
- }
184
- catch (err) {
185
- if (err instanceof Error) {
186
- logger.error('❌ Tool tests failed: %s', err.message);
187
- }
188
- else {
189
- logger.error('❌ Tool tests failed: %o', err);
190
- }
191
- process.exit(1);
192
- }
193
- process.exit(0);
194
- }
195
188
  // Initialize tools before usage
196
189
  await actualToolsManager.initialize();
197
190
  // Now get implemented tools after initialization
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "actual-mcp-server",
3
3
  "displayName": "Actual MCP Server",
4
- "version": "0.5.1",
4
+ "version": "0.5.4",
5
+ "engines": {
6
+ "node": ">=20.0.0",
7
+ "npm": ">=10.0.0"
8
+ },
5
9
  "description": "MCP server with 62 tools for AI-driven financial management with Actual Budget — HTTP and stdio transports, LibreChat, Claude Desktop, Cursor, VS Code, Gemini CLI",
6
10
  "repository": {
7
11
  "type": "git",
@@ -1,70 +0,0 @@
1
- // src/tests/actualToolsTests.ts
2
- import logger from '../logger.js';
3
- import actualToolsManager from '../actualToolsManager.js';
4
- // Test data mapping for each tool
5
- const getTestArgs = (toolName) => {
6
- switch (toolName) {
7
- case 'actual.accounts.create':
8
- return { name: 'Test Account', balance: 1000 };
9
- case 'actual.accounts.update':
10
- return { id: 'test-account-id', fields: { name: 'Updated Test Account' } };
11
- case 'actual.accounts.get.balance':
12
- return { id: 'test-account-id' };
13
- case 'actual.transactions.create':
14
- return { accountId: 'test-account-id', amount: 100, payee: 'Test Payee', date: '2025-11-08' };
15
- case 'actual.transactions.get':
16
- return { accountId: 'test-account-id', startDate: '2025-11-01', endDate: '2025-11-08' };
17
- case 'actual.transactions.import':
18
- return { accountId: 'test-account-id', txs: [{ amount: 50, payee: 'Import Test', date: '2025-11-08' }] };
19
- case 'actual.categories.create':
20
- // Try different field names that might be expected
21
- return { name: 'Test Category', group_id: 'fc3825fd-b982-4b72-b768-5b30844cf832', groupId: 'fc3825fd-b982-4b72-b768-5b30844cf832' };
22
- case 'actual.payees.create':
23
- return { name: 'Test Payee' };
24
- case 'actual.budgets.setAmount':
25
- // Use an existing category ID (Food category from the budget data)
26
- return { month: '2025-11', categoryId: '541836f1-e756-4473-a5d0-6c1d3f06c7fa', amount: 500 };
27
- case 'actual.budgets.getMonth':
28
- return { month: '2025-11' };
29
- // These tools don't require parameters
30
- case 'actual.accounts.list':
31
- case 'actual.categories.get':
32
- case 'actual.payees.get':
33
- case 'actual.budgets.getMonths':
34
- default:
35
- return {};
36
- }
37
- };
38
- export async function testAllTools() {
39
- await actualToolsManager.initialize();
40
- const toolNames = actualToolsManager.getToolNames();
41
- const results = [];
42
- for (const name of toolNames) {
43
- try {
44
- logger.info(`⚙️ Testing tool: ${name}`);
45
- const testArgs = getTestArgs(name);
46
- const result = await actualToolsManager.callTool(name, testArgs);
47
- logger.info(`✅ Tool ${name} output: ${JSON.stringify(result, null, 2)}`);
48
- results.push({ name, success: true });
49
- }
50
- catch (err) {
51
- const message = err && typeof err?.message === 'string' ? err.message : String(err);
52
- logger.error(`❌ Tool ${name} test failed: ${message}`);
53
- results.push({ name, success: false, error: message });
54
- }
55
- }
56
- // Summary
57
- const successful = results.filter(r => r.success).length;
58
- const failed = results.filter(r => !r.success).length;
59
- logger.info(`\n📊 Test Summary: ${successful} passed, ${failed} failed`);
60
- if (failed > 0) {
61
- logger.error(`❌ Failed tools:`);
62
- results.filter(r => !r.success).forEach(r => {
63
- logger.error(` - ${r.name}: ${r.error}`);
64
- });
65
- throw new Error(`${failed} tool tests failed`);
66
- }
67
- else {
68
- logger.info(`🎉 All ${successful} tools passed!`);
69
- }
70
- }