actual-mcp-server 0.6.42 → 0.6.43
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 +4 -3
- package/dist/package.json +4 -3
- package/dist/src/actualToolsManager.js +1 -0
- package/dist/src/tools/entities_search.js +100 -0
- package/dist/src/tools/index.js +1 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -396,13 +396,14 @@ For Claude Desktop (stdio), restart Claude after upgrading.
|
|
|
396
396
|
|
|
397
397
|
`actual_budget_updates_batch`: batch multiple budget updates in one call
|
|
398
398
|
|
|
399
|
-
### Server Information & Lookup (
|
|
399
|
+
### Server Information & Lookup (4)
|
|
400
400
|
|
|
401
401
|
| Tool | Description |
|
|
402
402
|
|------|-------------|
|
|
403
403
|
| `actual_server_info` | Server status, version, build info |
|
|
404
404
|
| `actual_server_get_version` | Actual Budget server version |
|
|
405
|
-
| `actual_get_id_by_name` | Resolve name → UUID for accounts, categories, payees |
|
|
405
|
+
| `actual_get_id_by_name` | Resolve an exact name → UUID for accounts, categories, payees |
|
|
406
|
+
| `actual_entities_search` | Find accounts/categories/payees by a name pattern (contains/startsWith/endsWith/exact/fuzzy). Fixes "payee not found" from a partial or mistyped name |
|
|
406
407
|
|
|
407
408
|
### Session Management (2)
|
|
408
409
|
|
|
@@ -750,4 +751,4 @@ The software is provided **as-is**, without warranty of any kind. The author acc
|
|
|
750
751
|
|
|
751
752
|
---
|
|
752
753
|
|
|
753
|
-
**Version:** 0.6.
|
|
754
|
+
**Version:** 0.6.43 | **Tool Count:** 71 (verified LibreChat-compatible)
|
package/dist/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "actual-mcp-server",
|
|
3
3
|
"displayName": "Actual MCP Server",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.43",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
7
7
|
"npm": ">=10.0.0"
|
|
8
8
|
},
|
|
9
|
-
"description": "MCP server with
|
|
9
|
+
"description": "MCP server with 71 tools for AI-driven financial management with Actual Budget. HTTP and stdio transports for LibreChat, Claude Desktop, Cursor, VS Code, Gemini CLI",
|
|
10
10
|
"homepage": "https://github.com/agigante80/actual-mcp-server#readme",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"verify-tools": "npm run build && node scripts/verify-tools.js",
|
|
31
31
|
"check:coverage": "node scripts/list-actual-api-methods.mjs",
|
|
32
32
|
"direct-sync": "node scripts/direct-sync/bank-sync-direct.mjs",
|
|
33
|
-
"test:unit-js": "node tests/unit/transactions_create.test.js && node tests/unit/generated_tools.smoke.test.js && node tests/unit/schema_validation.test.js && node tests/unit/config_https_validation.test.js && node tests/unit/config_insecure_upstream.test.js && node tests/unit/auth-acl.test.js && node tests/unit/bug76.test.js && node tests/unit/budgets_setAmount.test.js && node tests/unit/budgets_transfer.test.js && node tests/unit/transactions_uncategorized.test.js && node tests/unit/httpServer_session_init.test.js && node tests/unit/httpServer_session_not_found.test.js && node tests/unit/manual_mcp_client_retry.test.js && node tests/unit/manual_mcp_client_session.test.js && node tests/unit/manual_mcp_client_circuit.test.js && node tests/unit/manual_runner_killswitch.test.js && node tests/unit/adapter_auth_rate_limit.test.js && node tests/unit/adapter_session_reuse.test.js && node tests/unit/retry_classifier.test.js && node tests/unit/adapter_module_surface.test.js && node tests/unit/adapter_nonidempotent_no_retry.test.js && node tests/unit/pool_liveness.test.js && node tests/unit/pool_shutdown_all.test.js && node tests/unit/query_where_operators.test.js && node tests/unit/query_run_validation.test.js && node tests/unit/adapter_with_write_session.test.js && node tests/unit/category_groups_delete.test.js && node tests/unit/rules_delete.test.js && node tests/unit/schedules_delete.test.js && node tests/unit/payees_delete.test.js && node tests/unit/rules_create_or_update.test.js && node tests/unit/unhandled-rejection.test.js && node tests/unit/rejection-allowlist-purity.test.js && node tests/unit/httpServer_bearer_auth.test.js && node tests/unit/httpServer_oidc_audience.test.js && node tests/unit/httpServer_oidc_auth_verification.test.js && node tests/unit/httpServer_body_limit.test.js && node tests/unit/adapter_write_pool_cooperation.test.js && node tests/unit/budget_acl_enforcement.test.js && node tests/unit/budget_preference_store.test.js && node tests/unit/budget_preference_restore.test.js && node tests/unit/tags_list.test.js && node tests/unit/tags_create.test.js && node tests/unit/tags_update.test.js && node tests/unit/tags_delete.test.js && node tests/unit/notes_get.test.js && node tests/unit/notes_update.test.js && node tests/unit/payees_common_list.test.js && node tests/unit/transactions_update_guard.test.js && node tests/unit/check_coverage.test.js",
|
|
33
|
+
"test:unit-js": "node tests/unit/transactions_create.test.js && node tests/unit/generated_tools.smoke.test.js && node tests/unit/schema_validation.test.js && node tests/unit/config_https_validation.test.js && node tests/unit/config_insecure_upstream.test.js && node tests/unit/auth-acl.test.js && node tests/unit/bug76.test.js && node tests/unit/budgets_setAmount.test.js && node tests/unit/budgets_transfer.test.js && node tests/unit/transactions_uncategorized.test.js && node tests/unit/httpServer_session_init.test.js && node tests/unit/httpServer_session_not_found.test.js && node tests/unit/manual_mcp_client_retry.test.js && node tests/unit/manual_mcp_client_session.test.js && node tests/unit/manual_mcp_client_circuit.test.js && node tests/unit/manual_runner_killswitch.test.js && node tests/unit/adapter_auth_rate_limit.test.js && node tests/unit/adapter_session_reuse.test.js && node tests/unit/retry_classifier.test.js && node tests/unit/adapter_module_surface.test.js && node tests/unit/adapter_nonidempotent_no_retry.test.js && node tests/unit/pool_liveness.test.js && node tests/unit/pool_shutdown_all.test.js && node tests/unit/query_where_operators.test.js && node tests/unit/query_run_validation.test.js && node tests/unit/adapter_with_write_session.test.js && node tests/unit/category_groups_delete.test.js && node tests/unit/rules_delete.test.js && node tests/unit/schedules_delete.test.js && node tests/unit/payees_delete.test.js && node tests/unit/rules_create_or_update.test.js && node tests/unit/unhandled-rejection.test.js && node tests/unit/rejection-allowlist-purity.test.js && node tests/unit/httpServer_bearer_auth.test.js && node tests/unit/httpServer_oidc_audience.test.js && node tests/unit/httpServer_oidc_auth_verification.test.js && node tests/unit/httpServer_body_limit.test.js && node tests/unit/adapter_write_pool_cooperation.test.js && node tests/unit/budget_acl_enforcement.test.js && node tests/unit/budget_preference_store.test.js && node tests/unit/budget_preference_restore.test.js && node tests/unit/tags_list.test.js && node tests/unit/tags_create.test.js && node tests/unit/tags_update.test.js && node tests/unit/tags_delete.test.js && node tests/unit/notes_get.test.js && node tests/unit/notes_update.test.js && node tests/unit/payees_common_list.test.js && node tests/unit/transactions_update_guard.test.js && node tests/unit/entities_search.test.js && node tests/unit/check_coverage.test.js",
|
|
34
34
|
"test:adapter": "npm run build && node dist/src/tests_adapter_runner.js",
|
|
35
35
|
"test:e2e": "npx playwright test",
|
|
36
36
|
"test:e2e:docker": "./tests/e2e/run-docker-e2e.sh",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"debug": "^4.4.3",
|
|
63
63
|
"dotenv": "^17.4.2",
|
|
64
64
|
"express": "^5.2.1",
|
|
65
|
+
"fuse.js": "^7.4.2",
|
|
65
66
|
"jose": "^6.2.3",
|
|
66
67
|
"mcp-auth": "^0.2.0",
|
|
67
68
|
"winston": "^3.18.3",
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import Fuse from 'fuse.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { createTool } from '../lib/toolFactory.js';
|
|
4
|
+
import adapter from '../lib/actual-adapter.js';
|
|
5
|
+
/**
|
|
6
|
+
* actual_entities_search (#204)
|
|
7
|
+
*
|
|
8
|
+
* Find payees, categories, or accounts by a NAME pattern, so an AI can resolve a
|
|
9
|
+
* partial or mistyped name (the common "payee not found" failure) without knowing
|
|
10
|
+
* the exact string. Read-only.
|
|
11
|
+
*
|
|
12
|
+
* matchType: contains (default) | startsWith | endsWith | exact are case-insensitive
|
|
13
|
+
* native string ops; fuzzy is typo-tolerant via Fuse.js (a maintained, zero-dependency
|
|
14
|
+
* library, chosen over a hand-rolled Levenshtein). Regex is intentionally out of scope
|
|
15
|
+
* (separate Phase 2 ticket: ReDoS attack surface).
|
|
16
|
+
*
|
|
17
|
+
* On no match the tool returns `{ matches: [], count: 0 }` (never an error): an empty
|
|
18
|
+
* result is a valid "found nothing" signal that lets the AI retry with a broader
|
|
19
|
+
* matchType (e.g. switch `contains` to `fuzzy`).
|
|
20
|
+
*/
|
|
21
|
+
const ENTITY_TYPES = ['accounts', 'categories', 'payees'];
|
|
22
|
+
const FETCH = {
|
|
23
|
+
accounts: () => adapter.getAccounts(),
|
|
24
|
+
categories: () => adapter.getCategories(),
|
|
25
|
+
payees: () => adapter.getPayees(),
|
|
26
|
+
};
|
|
27
|
+
const schema = z.object({
|
|
28
|
+
type: z.enum(ENTITY_TYPES).describe('Entity type to search: accounts, categories, or payees.'),
|
|
29
|
+
query: z
|
|
30
|
+
.union([z.string().trim().min(1), z.array(z.string().trim().min(1)).min(1)])
|
|
31
|
+
.describe('Name pattern to match. A single string, or an array of strings (matches if ANY pattern matches).'),
|
|
32
|
+
matchType: z
|
|
33
|
+
.enum(['contains', 'startsWith', 'endsWith', 'exact', 'fuzzy'])
|
|
34
|
+
.default('contains')
|
|
35
|
+
.describe('contains (default), startsWith, endsWith, exact (all case-insensitive), or fuzzy (typo-tolerant). Regex is not supported.'),
|
|
36
|
+
limit: z.number().int().min(1).max(100).default(10).describe('Maximum number of matches to return (1 to 100, default 10).'),
|
|
37
|
+
});
|
|
38
|
+
export default createTool({
|
|
39
|
+
name: 'actual_entities_search',
|
|
40
|
+
description: 'Find accounts, categories, or payees by a name pattern. Use this to resolve a partial or mistyped name to its entity (the fix for "payee not found"). ' +
|
|
41
|
+
'matchType: contains (default), startsWith, endsWith, exact (all case-insensitive), or fuzzy (typo-tolerant, e.g. "amzon" matches "Amazon.com"). ' +
|
|
42
|
+
'query may be a single string or an array (matches if ANY matches). Read-only. ' +
|
|
43
|
+
'On no match it returns { matches: [], count: 0 } (not an error): retry with a broader matchType such as fuzzy.',
|
|
44
|
+
schema,
|
|
45
|
+
examples: [
|
|
46
|
+
{ description: 'Find the payee for a partial name', input: { type: 'payees', query: 'amazon', matchType: 'contains', limit: 10 } },
|
|
47
|
+
{ description: 'Typo-tolerant payee lookup', input: { type: 'payees', query: 'amzon', matchType: 'fuzzy', limit: 10 } },
|
|
48
|
+
{ description: 'Categories starting with "Gro"', input: { type: 'categories', query: 'gro', matchType: 'startsWith', limit: 10 } },
|
|
49
|
+
],
|
|
50
|
+
handler: async (input) => {
|
|
51
|
+
const { type, matchType, limit } = input;
|
|
52
|
+
const patterns = (Array.isArray(input.query) ? input.query : [input.query]).map((p) => p.trim());
|
|
53
|
+
const raw = (await FETCH[type]());
|
|
54
|
+
// Only entities with a usable string name can be matched (some payees have null names).
|
|
55
|
+
const entities = raw.filter((e) => typeof e?.name === 'string' && e.name.length > 0);
|
|
56
|
+
let matches;
|
|
57
|
+
if (matchType === 'fuzzy') {
|
|
58
|
+
const fuse = new Fuse(entities, {
|
|
59
|
+
keys: ['name'],
|
|
60
|
+
includeScore: true,
|
|
61
|
+
threshold: 0.4,
|
|
62
|
+
ignoreLocation: true,
|
|
63
|
+
minMatchCharLength: 1,
|
|
64
|
+
});
|
|
65
|
+
// Multi-pattern OR: search each pattern, keep the best (lowest) score per entity.
|
|
66
|
+
const bestById = new Map();
|
|
67
|
+
for (const pattern of patterns) {
|
|
68
|
+
for (const r of fuse.search(pattern)) {
|
|
69
|
+
const score = typeof r.score === 'number' ? r.score : 1;
|
|
70
|
+
const prev = bestById.get(r.item.id);
|
|
71
|
+
if (!prev || score < (prev.score ?? 1)) {
|
|
72
|
+
bestById.set(r.item.id, { id: r.item.id, name: r.item.name, score: Math.round(score * 1000) / 1000 });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
matches = [...bestById.values()].sort((a, b) => (a.score ?? 1) - (b.score ?? 1));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const needles = patterns.map((p) => p.toLowerCase());
|
|
80
|
+
const test = (name) => {
|
|
81
|
+
const n = name.toLowerCase();
|
|
82
|
+
return needles.some((q) => {
|
|
83
|
+
switch (matchType) {
|
|
84
|
+
case 'startsWith': return n.startsWith(q);
|
|
85
|
+
case 'endsWith': return n.endsWith(q);
|
|
86
|
+
case 'exact': return n === q;
|
|
87
|
+
case 'contains':
|
|
88
|
+
default: return n.includes(q);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
matches = entities
|
|
93
|
+
.filter((e) => test(e.name))
|
|
94
|
+
.map((e) => ({ id: e.id, name: e.name }))
|
|
95
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
96
|
+
}
|
|
97
|
+
const limited = matches.slice(0, limit);
|
|
98
|
+
return { matches: limited, count: limited.length, type, matchType };
|
|
99
|
+
},
|
|
100
|
+
});
|
package/dist/src/tools/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Auto-generated index for all tool modules
|
|
2
|
+
export { default as entities_search } from './entities_search.js';
|
|
2
3
|
export { default as accounts_close } from './accounts_close.js';
|
|
3
4
|
export { default as accounts_create } from './accounts_create.js';
|
|
4
5
|
export { default as accounts_delete } from './accounts_delete.js';
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "actual-mcp-server",
|
|
3
3
|
"displayName": "Actual MCP Server",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.43",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
7
7
|
"npm": ">=10.0.0"
|
|
8
8
|
},
|
|
9
|
-
"description": "MCP server with
|
|
9
|
+
"description": "MCP server with 71 tools for AI-driven financial management with Actual Budget. HTTP and stdio transports for LibreChat, Claude Desktop, Cursor, VS Code, Gemini CLI",
|
|
10
10
|
"homepage": "https://github.com/agigante80/actual-mcp-server#readme",
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"verify-tools": "npm run build && node scripts/verify-tools.js",
|
|
31
31
|
"check:coverage": "node scripts/list-actual-api-methods.mjs",
|
|
32
32
|
"direct-sync": "node scripts/direct-sync/bank-sync-direct.mjs",
|
|
33
|
-
"test:unit-js": "node tests/unit/transactions_create.test.js && node tests/unit/generated_tools.smoke.test.js && node tests/unit/schema_validation.test.js && node tests/unit/config_https_validation.test.js && node tests/unit/config_insecure_upstream.test.js && node tests/unit/auth-acl.test.js && node tests/unit/bug76.test.js && node tests/unit/budgets_setAmount.test.js && node tests/unit/budgets_transfer.test.js && node tests/unit/transactions_uncategorized.test.js && node tests/unit/httpServer_session_init.test.js && node tests/unit/httpServer_session_not_found.test.js && node tests/unit/manual_mcp_client_retry.test.js && node tests/unit/manual_mcp_client_session.test.js && node tests/unit/manual_mcp_client_circuit.test.js && node tests/unit/manual_runner_killswitch.test.js && node tests/unit/adapter_auth_rate_limit.test.js && node tests/unit/adapter_session_reuse.test.js && node tests/unit/retry_classifier.test.js && node tests/unit/adapter_module_surface.test.js && node tests/unit/adapter_nonidempotent_no_retry.test.js && node tests/unit/pool_liveness.test.js && node tests/unit/pool_shutdown_all.test.js && node tests/unit/query_where_operators.test.js && node tests/unit/query_run_validation.test.js && node tests/unit/adapter_with_write_session.test.js && node tests/unit/category_groups_delete.test.js && node tests/unit/rules_delete.test.js && node tests/unit/schedules_delete.test.js && node tests/unit/payees_delete.test.js && node tests/unit/rules_create_or_update.test.js && node tests/unit/unhandled-rejection.test.js && node tests/unit/rejection-allowlist-purity.test.js && node tests/unit/httpServer_bearer_auth.test.js && node tests/unit/httpServer_oidc_audience.test.js && node tests/unit/httpServer_oidc_auth_verification.test.js && node tests/unit/httpServer_body_limit.test.js && node tests/unit/adapter_write_pool_cooperation.test.js && node tests/unit/budget_acl_enforcement.test.js && node tests/unit/budget_preference_store.test.js && node tests/unit/budget_preference_restore.test.js && node tests/unit/tags_list.test.js && node tests/unit/tags_create.test.js && node tests/unit/tags_update.test.js && node tests/unit/tags_delete.test.js && node tests/unit/notes_get.test.js && node tests/unit/notes_update.test.js && node tests/unit/payees_common_list.test.js && node tests/unit/transactions_update_guard.test.js && node tests/unit/check_coverage.test.js",
|
|
33
|
+
"test:unit-js": "node tests/unit/transactions_create.test.js && node tests/unit/generated_tools.smoke.test.js && node tests/unit/schema_validation.test.js && node tests/unit/config_https_validation.test.js && node tests/unit/config_insecure_upstream.test.js && node tests/unit/auth-acl.test.js && node tests/unit/bug76.test.js && node tests/unit/budgets_setAmount.test.js && node tests/unit/budgets_transfer.test.js && node tests/unit/transactions_uncategorized.test.js && node tests/unit/httpServer_session_init.test.js && node tests/unit/httpServer_session_not_found.test.js && node tests/unit/manual_mcp_client_retry.test.js && node tests/unit/manual_mcp_client_session.test.js && node tests/unit/manual_mcp_client_circuit.test.js && node tests/unit/manual_runner_killswitch.test.js && node tests/unit/adapter_auth_rate_limit.test.js && node tests/unit/adapter_session_reuse.test.js && node tests/unit/retry_classifier.test.js && node tests/unit/adapter_module_surface.test.js && node tests/unit/adapter_nonidempotent_no_retry.test.js && node tests/unit/pool_liveness.test.js && node tests/unit/pool_shutdown_all.test.js && node tests/unit/query_where_operators.test.js && node tests/unit/query_run_validation.test.js && node tests/unit/adapter_with_write_session.test.js && node tests/unit/category_groups_delete.test.js && node tests/unit/rules_delete.test.js && node tests/unit/schedules_delete.test.js && node tests/unit/payees_delete.test.js && node tests/unit/rules_create_or_update.test.js && node tests/unit/unhandled-rejection.test.js && node tests/unit/rejection-allowlist-purity.test.js && node tests/unit/httpServer_bearer_auth.test.js && node tests/unit/httpServer_oidc_audience.test.js && node tests/unit/httpServer_oidc_auth_verification.test.js && node tests/unit/httpServer_body_limit.test.js && node tests/unit/adapter_write_pool_cooperation.test.js && node tests/unit/budget_acl_enforcement.test.js && node tests/unit/budget_preference_store.test.js && node tests/unit/budget_preference_restore.test.js && node tests/unit/tags_list.test.js && node tests/unit/tags_create.test.js && node tests/unit/tags_update.test.js && node tests/unit/tags_delete.test.js && node tests/unit/notes_get.test.js && node tests/unit/notes_update.test.js && node tests/unit/payees_common_list.test.js && node tests/unit/transactions_update_guard.test.js && node tests/unit/entities_search.test.js && node tests/unit/check_coverage.test.js",
|
|
34
34
|
"test:adapter": "npm run build && node dist/src/tests_adapter_runner.js",
|
|
35
35
|
"test:e2e": "npx playwright test",
|
|
36
36
|
"test:e2e:docker": "./tests/e2e/run-docker-e2e.sh",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
"debug": "^4.4.3",
|
|
63
63
|
"dotenv": "^17.4.2",
|
|
64
64
|
"express": "^5.2.1",
|
|
65
|
+
"fuse.js": "^7.4.2",
|
|
65
66
|
"jose": "^6.2.3",
|
|
66
67
|
"mcp-auth": "^0.2.0",
|
|
67
68
|
"winston": "^3.18.3",
|