actual-mcp-server 0.6.34 → 0.6.36

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
@@ -33,14 +33,14 @@ Actual MCP Server is a [Model Context Protocol](https://modelcontextprotocol.io/
33
33
 
34
34
  Most Actual Budget MCP implementations are simple stdio bridges designed for single-user, local use with Claude Desktop. This project goes further:
35
35
 
36
- - **67 tools, the most comprehensive coverage available.** Accounts, transactions, categories, payees, tags, rules, budgets, batch operations, bank sync, and more. Covers 87% of the Actual Budget API.
36
+ - **70 tools, the most comprehensive coverage available.** Accounts, transactions, categories, payees, tags, notes, rules, budgets, batch operations, bank sync, and more. Covers 87% of the Actual Budget API.
37
37
  - **HTTP and stdio transport.** Runs as a real remote server for LibreChat/LobeChat (`--http`), or as a direct local process for Claude Desktop (`--stdio`). No Docker or HTTP server is needed for local use.
38
38
  - **6 exclusive ActualQL-powered tools.** Search and summarise transactions by month, amount, category, or payee using Actual Budget's native query engine. Aggregated results, no raw data dumped into the AI context window.
39
39
  - **Multi-budget switching at runtime.** Configure multiple budget files and let the AI switch between them mid-conversation with `actual_budgets_switch`.
40
40
  - **Multi-user ready with OIDC.** Secure every session with JWKS-validated JWTs and per-user budget ACLs. No shared tokens required.
41
41
  - **Production-grade reliability.** Connection pooling (up to 15 concurrent sessions), automatic retry with exponential backoff, and a full test suite (unit + E2E + integration).
42
42
 
43
- > **Verified working** with [LibreChat](https://www.librechat.ai/), [LobeChat](https://lobehub.com/home), and [Claude Desktop](https://claude.ai/download). All 67 tools tested end-to-end. Any MCP-compatible client should work.
43
+ > **Verified working** with [LibreChat](https://www.librechat.ai/), [LobeChat](https://lobehub.com/home), and [Claude Desktop](https://claude.ai/download). All 70 tools tested end-to-end. Any MCP-compatible client should work.
44
44
 
45
45
  ---
46
46
 
@@ -342,9 +342,9 @@ For Claude Desktop (stdio), restart Claude after upgrading.
342
342
 
343
343
  `actual_category_groups_get` · `actual_category_groups_create` · `actual_category_groups_update` · `actual_category_groups_delete`
344
344
 
345
- ### Payees (6)
345
+ ### Payees (7)
346
346
 
347
- `actual_payees_get` · `actual_payees_create` · `actual_payees_update` · `actual_payees_delete` · `actual_payees_merge` · `actual_payee_rules_get`
347
+ `actual_payees_get` · `actual_payees_common_list` · `actual_payees_create` · `actual_payees_update` · `actual_payees_delete` · `actual_payees_merge` · `actual_payee_rules_get`
348
348
 
349
349
  ### Tags (4)
350
350
 
@@ -357,6 +357,15 @@ For Claude Desktop (stdio), restart Claude after upgrading.
357
357
  | `actual_tags_update` | Update tag name, color, or description by UUID |
358
358
  | `actual_tags_delete` | Soft-delete a tag by UUID |
359
359
 
360
+ ### Notes (2)
361
+
362
+ `actual_notes_get` · `actual_notes_update`
363
+
364
+ | Tool | Description |
365
+ |------|-------------|
366
+ | `actual_notes_get` | Get the note for any entity (account/category/category-group/payee UUID, or budget-YYYY-MM) |
367
+ | `actual_notes_update` | Set or clear the note for any entity; validates entity exists or matches budget-YYYY-MM pattern |
368
+
360
369
  ### Budgets (10)
361
370
 
362
371
  | Tool | Description |
@@ -638,7 +647,7 @@ Several MCP servers exist for personal finance management. Here's how this proje
638
647
  | **Version** | v0.4.26 | v1.11.1 | v0.2.0 | v0.1.0 |
639
648
  | **Budget App** | Actual Budget (self-hosted) | Actual Budget (self-hosted) | Actual Budget (self-hosted) | YNAB (cloud, subscription) |
640
649
  | **Language** | TypeScript / Node.js | TypeScript / Node.js | TypeScript / Node.js | Python |
641
- | **Tool Count** | **63** | ~22 | 18 | 9 |
650
+ | **Tool Count** | **70** | ~22 | 18 | 9 |
642
651
  | **Setup & Distribution** |||||
643
652
  | **Transport** | HTTP + stdio | STDIO + SSE option | STDIO | STDIO |
644
653
  | **Docker support** | ✅ Full (image + Compose) | ✅ Image only | ❌ | ❌ |
@@ -741,4 +750,4 @@ The software is provided **as-is**, without warranty of any kind. The author acc
741
750
 
742
751
  ---
743
752
 
744
- **Version:** 0.6.34 | **Tool Count:** 67 (verified LibreChat-compatible)
753
+ **Version:** 0.6.36 | **Tool Count:** 70 (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.34",
4
+ "version": "0.6.36",
5
5
  "engines": {
6
6
  "node": ">=20.0.0",
7
7
  "npm": ">=10.0.0"
8
8
  },
9
- "description": "MCP server with 67 tools for AI-driven financial management with Actual Budget. HTTP and stdio transports for LibreChat, Claude Desktop, Cursor, VS Code, Gemini CLI",
9
+ "description": "MCP server with 70 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",
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",
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",
@@ -30,6 +30,7 @@ const IMPLEMENTED_TOOLS = [
30
30
  'actual_category_groups_delete',
31
31
  'actual_category_groups_get',
32
32
  'actual_category_groups_update',
33
+ 'actual_payees_common_list',
33
34
  'actual_payees_create',
34
35
  'actual_payees_delete',
35
36
  'actual_payees_get',
@@ -70,6 +71,8 @@ const IMPLEMENTED_TOOLS = [
70
71
  'actual_tags_create',
71
72
  'actual_tags_update',
72
73
  'actual_tags_delete',
74
+ 'actual_notes_get',
75
+ 'actual_notes_update',
73
76
  ];
74
77
  // 🔑 Mapping of Actual API function names → your MCP tool names
75
78
  // This allows us to compare what exists in the API vs. what it has been wrapped.
@@ -6,7 +6,7 @@ import api from '@actual-app/api';
6
6
  // cannot expose its named exports via static import syntax. At runtime the default import
7
7
  // IS module.exports, so all methods are accessible as properties.
8
8
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
- const { addTransactions: rawAddTransactions, getAccounts: rawGetAccounts, importTransactions: rawImportTransactions, getTransactions: rawGetTransactions, getCategories: rawGetCategories, createCategory: rawCreateCategory, getPayees: rawGetPayees, createPayee: rawCreatePayee, getBudgetMonths: rawGetBudgetMonths, getBudgetMonth: rawGetBudgetMonth, setBudgetAmount: rawSetBudgetAmount, createAccount: rawCreateAccount, updateAccount: rawUpdateAccount, getAccountBalance: rawGetAccountBalance, updateTransaction: rawUpdateTransaction, deleteTransaction: rawDeleteTransaction, updateCategory: rawUpdateCategory, deleteCategory: rawDeleteCategory, updatePayee: rawUpdatePayee, deletePayee: rawDeletePayee, deleteAccount: rawDeleteAccount, getRules: rawGetRules, createRule: rawCreateRule, updateRule: rawUpdateRule, deleteRule: rawDeleteRule, setBudgetCarryover: rawSetBudgetCarryover, closeAccount: rawCloseAccount, reopenAccount: rawReopenAccount, getCategoryGroups: rawGetCategoryGroups, createCategoryGroup: rawCreateCategoryGroup, updateCategoryGroup: rawUpdateCategoryGroup, deleteCategoryGroup: rawDeleteCategoryGroup, mergePayees: rawMergePayees, getPayeeRules: rawGetPayeeRules, batchBudgetUpdates: rawBatchBudgetUpdates, holdBudgetForNextMonth: rawHoldBudgetForNextMonth, resetBudgetHold: rawResetBudgetHold, runQuery: rawRunQuery, runBankSync: rawRunBankSync, getBudgets: rawGetBudgets, getIDByName: rawGetIDByName, getServerVersion: rawGetServerVersion, getSchedules: rawGetSchedules, createSchedule: rawCreateSchedule, updateSchedule: rawUpdateSchedule, deleteSchedule: rawDeleteSchedule, getTags: rawGetTags, createTag: rawCreateTag, updateTag: rawUpdateTag, deleteTag: rawDeleteTag,
9
+ const { addTransactions: rawAddTransactions, getAccounts: rawGetAccounts, importTransactions: rawImportTransactions, getTransactions: rawGetTransactions, getCategories: rawGetCategories, createCategory: rawCreateCategory, getPayees: rawGetPayees, getCommonPayees: rawGetCommonPayees, createPayee: rawCreatePayee, getBudgetMonths: rawGetBudgetMonths, getBudgetMonth: rawGetBudgetMonth, setBudgetAmount: rawSetBudgetAmount, createAccount: rawCreateAccount, updateAccount: rawUpdateAccount, getAccountBalance: rawGetAccountBalance, updateTransaction: rawUpdateTransaction, deleteTransaction: rawDeleteTransaction, updateCategory: rawUpdateCategory, deleteCategory: rawDeleteCategory, updatePayee: rawUpdatePayee, deletePayee: rawDeletePayee, deleteAccount: rawDeleteAccount, getRules: rawGetRules, createRule: rawCreateRule, updateRule: rawUpdateRule, deleteRule: rawDeleteRule, setBudgetCarryover: rawSetBudgetCarryover, closeAccount: rawCloseAccount, reopenAccount: rawReopenAccount, getCategoryGroups: rawGetCategoryGroups, createCategoryGroup: rawCreateCategoryGroup, updateCategoryGroup: rawUpdateCategoryGroup, deleteCategoryGroup: rawDeleteCategoryGroup, mergePayees: rawMergePayees, getPayeeRules: rawGetPayeeRules, batchBudgetUpdates: rawBatchBudgetUpdates, holdBudgetForNextMonth: rawHoldBudgetForNextMonth, resetBudgetHold: rawResetBudgetHold, runQuery: rawRunQuery, runBankSync: rawRunBankSync, getBudgets: rawGetBudgets, getIDByName: rawGetIDByName, getServerVersion: rawGetServerVersion, getSchedules: rawGetSchedules, createSchedule: rawCreateSchedule, updateSchedule: rawUpdateSchedule, deleteSchedule: rawDeleteSchedule, getTags: rawGetTags, createTag: rawCreateTag, updateTag: rawUpdateTag, deleteTag: rawDeleteTag, getNote: rawGetNote, updateNote: rawUpdateNote,
10
10
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
11
  } = api;
12
12
  import { EventEmitter } from 'events';
@@ -880,6 +880,12 @@ export async function getPayees() {
880
880
  return await withConcurrency(() => retry(() => rawGetPayees(), { retries: 2, backoffMs: 200 }));
881
881
  });
882
882
  }
883
+ export async function getCommonPayees() {
884
+ return withActualApi(async () => {
885
+ observability.incrementToolCall('actual.payees.commonList').catch(() => { });
886
+ return await withConcurrency(() => retry(() => rawGetCommonPayees(), { retries: 2, backoffMs: 200 }));
887
+ });
888
+ }
883
889
  export async function createPayee(payee) {
884
890
  observability.incrementToolCall('actual.payees.create').catch(() => { });
885
891
  return queueWriteOperation(async () => {
@@ -1792,6 +1798,18 @@ export async function deleteTag(id) {
1792
1798
  await withConcurrency(() => retry(() => rawDeleteTag(id), { retries: 0, backoffMs: 200 }));
1793
1799
  });
1794
1800
  }
1801
+ export async function getNote(id) {
1802
+ return withActualApi(async () => {
1803
+ observability.incrementToolCall('actual.notes.get').catch(() => { });
1804
+ return await withConcurrency(() => retry(() => rawGetNote(id), { retries: 2, backoffMs: 200 }));
1805
+ });
1806
+ }
1807
+ export async function updateNote(id, note) {
1808
+ observability.incrementToolCall('actual.notes.update').catch(() => { });
1809
+ return queueWriteOperation(async () => {
1810
+ await withConcurrency(() => retry(() => rawUpdateNote(id, note), { retries: 0, backoffMs: 200, isRetryable: isRetryableError }));
1811
+ });
1812
+ }
1795
1813
  export default {
1796
1814
  getAccounts,
1797
1815
  getAccountsWithBalances,
@@ -1802,11 +1820,14 @@ export default {
1802
1820
  getCategories,
1803
1821
  createCategory,
1804
1822
  getPayees,
1823
+ getCommonPayees,
1805
1824
  createPayee,
1806
1825
  getTags,
1807
1826
  createTag,
1808
1827
  updateTag,
1809
1828
  deleteTag,
1829
+ getNote,
1830
+ updateNote,
1810
1831
  getBudgetMonths,
1811
1832
  getBudgetMonth,
1812
1833
  setBudgetAmount,
@@ -27,6 +27,7 @@ export { default as category_groups_delete } from './category_groups_delete.js';
27
27
  export { default as category_groups_get } from './category_groups_get.js';
28
28
  export { default as category_groups_update } from './category_groups_update.js';
29
29
  export { default as payee_rules_get } from './payee_rules_get.js';
30
+ export { default as payees_common_list } from './payees_common_list.js';
30
31
  export { default as payees_create } from './payees_create.js';
31
32
  export { default as payees_delete } from './payees_delete.js';
32
33
  export { default as payees_get } from './payees_get.js';
@@ -66,3 +67,5 @@ export { default as tags_list } from './tags_list.js';
66
67
  export { default as tags_create } from './tags_create.js';
67
68
  export { default as tags_update } from './tags_update.js';
68
69
  export { default as tags_delete } from './tags_delete.js';
70
+ export { default as notes_get } from './notes_get.js';
71
+ export { default as notes_update } from './notes_update.js';
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ import { createTool } from '../lib/toolFactory.js';
3
+ import adapter from '../lib/actual-adapter.js';
4
+ export default createTool({
5
+ name: 'actual_notes_get',
6
+ description: 'Get the note attached to an entity in Actual Budget. ' +
7
+ 'The id can be any entity UUID (account, category, category-group, payee) ' +
8
+ 'or the synthetic budget-month id in the form "budget-YYYY-MM" ' +
9
+ '(e.g. "budget-2026-01" for January 2026). ' +
10
+ 'Returns the note text when one exists. ' +
11
+ 'Returns a clear "no note" result (not null) when no note has been set for the given id.',
12
+ schema: z.object({
13
+ id: z.string().min(1).describe('Entity id: a UUID for an account/category/category-group/payee, ' +
14
+ 'or "budget-YYYY-MM" for a budget month note'),
15
+ }),
16
+ handler: async ({ id }) => {
17
+ const note = await adapter.getNote(id);
18
+ if (note === null) {
19
+ return { found: false, id, note: null, message: `No note set for ${id}` };
20
+ }
21
+ return { found: true, id: note.id, note: note.note };
22
+ },
23
+ examples: [
24
+ {
25
+ description: 'Get note for an account',
26
+ input: { id: '00000000-0000-0000-0000-000000000001' },
27
+ },
28
+ {
29
+ description: 'Get note for a budget month',
30
+ input: { id: 'budget-2026-01' },
31
+ },
32
+ ],
33
+ });
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod';
2
+ import { createTool } from '../lib/toolFactory.js';
3
+ import adapter from '../lib/actual-adapter.js';
4
+ const BUDGET_MONTH_RE = /^budget-\d{4}-\d{2}$/;
5
+ export default createTool({
6
+ name: 'actual_notes_update',
7
+ description: 'Set or clear the note attached to an entity in Actual Budget. ' +
8
+ 'This is an upsert: creates the note if none exists, updates it if one does. ' +
9
+ 'Pass an empty string for note to clear it. ' +
10
+ 'The id must resolve to a known entity (account, category, category-group, payee) ' +
11
+ 'or match the pattern "budget-YYYY-MM" for a budget month note. ' +
12
+ 'Unknown ids are rejected to prevent orphan notes. ' +
13
+ 'Budget month notes support template directives such as "#template 250" and "#goal 1000".',
14
+ schema: z.object({
15
+ id: z.string().min(1).describe('Entity id: a UUID for an account/category/category-group/payee, ' +
16
+ 'or "budget-YYYY-MM" for a budget month note'),
17
+ note: z.string().describe('Note text to set. Pass an empty string to clear the note.'),
18
+ }),
19
+ handler: async ({ id, note }) => {
20
+ // Fast path: budget-YYYY-MM synthetic ids need no entity lookup.
21
+ if (!BUDGET_MONTH_RE.test(id)) {
22
+ // Validate that the id resolves to a known entity.
23
+ // Fetch in parallel to minimise latency.
24
+ const [accounts, categories, categoryGroups, payees] = await Promise.all([
25
+ adapter.getAccounts(),
26
+ adapter.getCategories(),
27
+ adapter.getCategoryGroups(),
28
+ adapter.getPayees(),
29
+ ]);
30
+ const known = (Array.isArray(accounts) && accounts.some((e) => e.id === id)) ||
31
+ (Array.isArray(categories) && categories.some((e) => e.id === id)) ||
32
+ (Array.isArray(categoryGroups) && categoryGroups.some((e) => e.id === id)) ||
33
+ (Array.isArray(payees) && payees.some((e) => e.id === id));
34
+ if (!known) {
35
+ return {
36
+ error: `Entity "${id}" not found. ` +
37
+ 'The id must be a UUID from actual_accounts_list, actual_categories_get, ' +
38
+ 'actual_category_groups_get, or actual_payees_get, ' +
39
+ 'or a budget month id like "budget-2026-01".',
40
+ };
41
+ }
42
+ }
43
+ await adapter.updateNote(id, note);
44
+ return {
45
+ success: true,
46
+ id,
47
+ note,
48
+ cleared: note === '',
49
+ };
50
+ },
51
+ examples: [
52
+ {
53
+ description: 'Set a budget template note for January 2026',
54
+ input: { id: 'budget-2026-01', note: '#template 250' },
55
+ },
56
+ {
57
+ description: 'Clear a note',
58
+ input: { id: 'budget-2026-01', note: '' },
59
+ },
60
+ {
61
+ description: 'Set a note on an account',
62
+ input: { id: '00000000-0000-0000-0000-000000000001', note: 'Reconcile monthly' },
63
+ },
64
+ ],
65
+ });
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+ import { createTool } from '../lib/toolFactory.js';
3
+ import adapter from '../lib/actual-adapter.js';
4
+ export default createTool({
5
+ name: 'actual_payees_common_list',
6
+ description: 'Return the most frequently used payees from recent transaction history (last ~12 weeks, top 10, ' +
7
+ 'non-transfer payees only). ' +
8
+ 'Distinct from actual_payees_get, which lists ALL payees with no recency or frequency filter. ' +
9
+ 'An empty list is a normal, successful result: it means no non-transfer payees appear in recent ' +
10
+ 'transactions, not that an error occurred. ' +
11
+ 'Each item has the shape { id, name, transfer_acct? }.',
12
+ schema: z.object({}),
13
+ handler: async () => {
14
+ return await adapter.getCommonPayees();
15
+ },
16
+ examples: [
17
+ { description: 'List recently frequent payees', input: {} },
18
+ ],
19
+ });
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.34",
4
+ "version": "0.6.36",
5
5
  "engines": {
6
6
  "node": ">=20.0.0",
7
7
  "npm": ">=10.0.0"
8
8
  },
9
- "description": "MCP server with 67 tools for AI-driven financial management with Actual Budget. HTTP and stdio transports for LibreChat, Claude Desktop, Cursor, VS Code, Gemini CLI",
9
+ "description": "MCP server with 70 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",
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",
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",