actual-mcp-server 0.7.3 → 0.7.5

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
@@ -1,4 +1,8 @@
1
- # Actual MCP Server
1
+ <p align="center">
2
+ <img src="unraid/actual-mcp-icon.png" alt="Actual MCP Server icon" width="128" height="128">
3
+ </p>
4
+
5
+ <h1 align="center">Actual MCP Server</h1>
2
6
 
3
7
  [![npm version](https://img.shields.io/npm/v/actual-mcp-server)](https://www.npmjs.com/package/actual-mcp-server)
4
8
  [![npm downloads](https://img.shields.io/npm/dm/actual-mcp-server)](https://www.npmjs.com/package/actual-mcp-server)
@@ -51,10 +55,9 @@ Most Actual Budget MCP implementations are simple stdio bridges designed for sin
51
55
  - [Available Tools](#available-tools)
52
56
  - [Configuration](#configuration)
53
57
  - [Multi-Budget Switching](#multi-budget-switching)
54
- - [Transport & Authentication](#transport--authentication)
58
+ - [Transport & Authentication](#transport-authentication)
55
59
  - [Testing](#testing)
56
60
  - [Documentation](#documentation)
57
- - [Comparison with Similar Projects](#comparison-with-similar-projects)
58
61
  - [Contributing](#contributing)
59
62
  - [License](#license)
60
63
  - [Disclaimer](#disclaimer)
@@ -634,74 +637,14 @@ See [`tests/manual/README.md`](tests/manual/README.md) and [`tests/e2e/README.md
634
637
  | [docs/SECURITY_AND_PRIVACY.md](docs/SECURITY_AND_PRIVACY.md) | Auth models, threat model, hardening |
635
638
  | [docs/TESTING_AND_RELIABILITY.md](docs/TESTING_AND_RELIABILITY.md) | Test strategy, coverage, reliability patterns |
636
639
  | [docs/NEW_TOOL_CHECKLIST.md](docs/NEW_TOOL_CHECKLIST.md) | Step-by-step guide for adding a new MCP tool |
637
- | [CONTRIBUTING.md](CONTRIBUTING.md) | Development setup, code standards, PR process |
640
+ | [CONTRIBUTING.md](.github/CONTRIBUTING.md) | Development setup, code standards, PR process |
638
641
  | [.env.example](.env.example) | Fully annotated environment variable reference |
639
642
 
640
643
  ---
641
644
 
642
- ## Comparison with Similar Projects
643
-
644
- Several MCP servers exist for personal finance management. Here's how this project compares with the closest alternatives.
645
-
646
- > **Snapshot date**: 2026-03-20. Versions and features reflect each project's latest release at that date.
647
-
648
- ### Feature Comparison
649
-
650
- | Feature | **agigante80/actual-mcp-server** | [s-stefanov/actual-mcp](https://github.com/s-stefanov/actual-mcp) | [henfrydls/actual-budget-mcp](https://github.com/henfrydls/actual-budget-mcp) | [WGDevelopment/ynab-mcp-server](https://github.com/WGDevelopment/ynab-mcp-server) |
651
- |---|---|---|---|---|
652
- | **Version** | v0.4.26 | v1.11.1 | v0.2.0 | v0.1.0 |
653
- | **Budget App** | Actual Budget (self-hosted) | Actual Budget (self-hosted) | Actual Budget (self-hosted) | YNAB (cloud, subscription) |
654
- | **Language** | TypeScript / Node.js | TypeScript / Node.js | TypeScript / Node.js | Python |
655
- | **Tool Count** | **71** | ~22 | 18 | 9 |
656
- | **Setup & Distribution** |||||
657
- | **Transport** | HTTP + stdio | STDIO + SSE option | STDIO | STDIO |
658
- | **Docker support** | ✅ Full (image + Compose) | ✅ Image only | ❌ | ❌ |
659
- | **Published package (npx/pip)** | ✅ `npx actual-mcp-server` | ✅ `npx actual-mcp` | ✅ `npx actual-budget-mcp` | ✅ `pip install ynab-mcp` |
660
- | **Security & Access** |||||
661
- | **Authentication** | ✅ Bearer token + OIDC (JWKS) | ⚠️ Optional Bearer token | ❌ None (local only) | ✅ OS keyring / env var |
662
- | **Read-only mode** | ❌ All tools always available | ✅ Write requires `--enable-write` flag | ❌ | ✅ Most tools are read-only |
663
- | **Multi-budget switching** | ✅ Runtime switch via tool | ❌ | ❌ | ✅ (YNAB natively multi-budget) |
664
- | **Production & Reliability** |||||
665
- | **Connection pooling** | ✅ Up to 15 concurrent sessions | ❌ | ❌ | ❌ |
666
- | **Retry / backoff** | ✅ 3 attempts, exponential backoff | ❌ | ❌ | ❌ |
667
- | **Automated test suite** | ✅ Unit + E2E + integration | ❌ | ❌ | ❌ |
668
- | **Transactions** |||||
669
- | **Create / update / delete** | ✅ | ✅ | ✅ | ✅ |
670
- | **Import & reconcile** | ✅ `actual_transactions_import` | ❌ | ❌ | ❌ |
671
- | **Scheduled / recurring** | ❌ (planned) | ❌ | ❌ | ❌ |
672
- | **Analysis & Reporting** |||||
673
- | **ActualQL custom queries** | ✅ 6 exclusive tools + `actual_query_run` | ❌ | ❌ | N/A |
674
- | **Summary by category / payee** | ✅ | ✅ spending-by-category | ✅ | ❌ |
675
- | **Spending projections / forecast** | ❌ | ❌ | ✅ end-of-month forecast | ❌ |
676
- | **Budget vs actual comparison** | ✅ via `actual_budgets_getMonth` | ❌ | ✅ dedicated tool | ✅ month summary |
677
- | **Bank sync** | ✅ GoCardless / SimpleFIN | ❌ | ✅ | ❌ (YNAB handles sync natively) |
678
- | **Budget Management** |||||
679
- | **Set / transfer / carryover / hold** | ✅ Full (10 tools) | ❌ | ✅ Partial | ✅ Partial |
680
- | **Batch budget updates** | ✅ `actual_budget_updates_batch` | ❌ | ❌ | ❌ |
681
- | **Accounts, Payees & Rules** |||||
682
- | **Account lifecycle (close/reopen)** | ✅ | ❌ | ❌ | N/A |
683
- | **Payee merging** | ✅ `actual_payees_merge` | ❌ | ❌ | N/A |
684
- | **Payee rules management** | ✅ Full CRUD | ✅ Full CRUD | ❌ | N/A |
685
- | **UX & Usability** |||||
686
- | **Natural language date parsing** | ❌ YYYY-MM-DD required | ❌ | ✅ "last month", "yesterday" | ❌ |
687
- | **Bilingual support** | ❌ | ❌ | ✅ English + Spanish | ❌ |
688
- | **Auto name → UUID resolution** | ⚠️ Explicit tool (`actual_get_id_by_name`) | ❌ | ✅ Automatic in all tools | ❌ |
689
- | **AI prompt templates** | ❌ | ✅ financial-insights, budget-review | ❌ | ❌ |
690
- | **Tested AI clients** | LibreChat, LobeChat | Claude Desktop, Codex | Claude Desktop, Cursor, VS Code | Claude Code |
691
- | **License** | MIT | MIT | MIT | MIT |
692
-
693
- ### When to choose which project
694
-
695
- - **This project**: best for production deployments, multi-user environments (OIDC), LibreChat/LobeChat, Docker-native setup, or for Claude Desktop users who want a native stdio connection without any HTTP server overhead.
696
- - **s-stefanov/actual-mcp**: the original implementation; good for Claude Desktop with STDIO transport, AI-generated prompt templates, and built-in read-only mode.
697
- - **henfrydls/actual-budget-mcp**: best for Spanish-speaking users, Cursor/VS Code integration, or when you want natural-language dates, automatic name resolution, and spending forecasts without any server setup.
698
- - **WGDevelopment/ynab-mcp-server**: only option if you're a YNAB user; privacy-first design with OS keyring token storage and local-LLM focus.
699
-
700
- ---
701
-
702
645
  ## Contributing
703
646
 
704
- Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, code standards, and the PR process.
647
+ Contributions are welcome! See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for development setup, code standards, and the PR process.
705
648
 
706
649
  Quick flow:
707
650
  1. Fork → `git checkout -b feature/my-feature`
@@ -714,9 +657,9 @@ Quick flow:
714
657
  ## Architecture
715
658
 
716
659
  - **Runtime**: Node.js 22 (Alpine Linux in Docker)
717
- - **Language**: TypeScript 5.9 (ESM, NodeNext module resolution)
718
- - **MCP SDK**: `@modelcontextprotocol/sdk` ^1.25.2
719
- - **Actual API**: `@actual-app/api` ^26.3.0
660
+ - **Language**: TypeScript (ESM, NodeNext module resolution)
661
+ - **MCP SDK**: `@modelcontextprotocol/sdk`
662
+ - **Actual API**: `@actual-app/api`
720
663
  - **Validation**: Zod (runtime types + JSON Schema for tool inputs)
721
664
  - **Transports**: Express + StreamableHTTP (`--http`) · `StdioServerTransport` (`--stdio`)
722
665
  - **Logging**: Winston with daily rotation (all output routed to stderr in stdio mode)
@@ -755,4 +698,4 @@ The software is provided **as-is**, without warranty of any kind. The author acc
755
698
 
756
699
  ---
757
700
 
758
- **Version:** 0.7.3 | **Tool Count:** 71 (verified LibreChat-compatible)
701
+ **Version:** 0.7.5 | **Tool Count:** 71 (verified LibreChat-compatible)
package/dist/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "actual-mcp-server",
3
3
  "displayName": "Actual MCP Server",
4
- "version": "0.7.3",
4
+ "version": "0.7.5",
5
5
  "engines": {
6
6
  "node": ">=22.0.0",
7
7
  "npm": ">=10.0.0"
@@ -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_data_dir.test.js && node tests/unit/config_drift.test.js && node tests/unit/port_alignment.test.js && node tests/unit/dockerfile_data_dir_alignment.test.js && node tests/unit/compose_profile_sync.test.js && node tests/unit/advertised_tools_sync.test.js && node tests/unit/knip_config.test.js && node tests/unit/readme_stats_sync.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/transactions_import_schema.test.js && node tests/unit/tool_schema_shape.test.js && node tests/unit/zod_error_format.test.js && node tests/unit/tool_count_sync.test.js && node tests/unit/stdio_transport.test.js && node tests/unit/logger_structured.test.js && node tests/unit/logger_redaction.test.js && node tests/unit/logger_correlation.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_data_dir.test.js && node tests/unit/config_drift.test.js && node tests/unit/port_alignment.test.js && node tests/unit/dockerfile_data_dir_alignment.test.js && node tests/unit/unraid_template_alignment.test.js && node tests/unit/entrypoint_invariants.test.js && node tests/unit/compose_profile_sync.test.js && node tests/unit/advertised_tools_sync.test.js && node tests/unit/knip_config.test.js && node tests/unit/readme_stats_sync.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/httpServer_bind_host.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/transactions_import_schema.test.js && node tests/unit/tool_schema_shape.test.js && node tests/unit/zod_error_format.test.js && node tests/unit/tool_count_sync.test.js && node tests/unit/stdio_transport.test.js && node tests/unit/logger_structured.test.js && node tests/unit/logger_redaction.test.js && node tests/unit/logger_correlation.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",
package/dist/src/index.js CHANGED
@@ -245,8 +245,9 @@ Docs & source: https://github.com/agigante80/actual-mcp-server`);
245
245
  // If requested, run the MCP client-side tests now that all variables are ready
246
246
  if (useTestMcpClient) {
247
247
  logger.info('⚙️ --test-mcp-client specified, starting HTTP server and running client-side MCP tests...');
248
- // start server in http mode (bind to configured PORT)
249
- await startHttpServer(mcp, PORT, HTTP_PATH, capabilities, implementedTools, SERVER_DESCRIPTION, SERVER_INSTRUCTIONS, toolSchemas, version, process.env.MCP_BRIDGE_BIND_HOST || 'localhost', advertisedUrl);
248
+ // start server in http mode. Bind all interfaces by default (#239): advertisedUrl
249
+ // uses the LAN IP (getLocalIp), so the self-test client must be able to reach it.
250
+ await startHttpServer(mcp, PORT, HTTP_PATH, capabilities, implementedTools, SERVER_DESCRIPTION, SERVER_INSTRUCTIONS, toolSchemas, version, process.env.MCP_BRIDGE_BIND_HOST || '0.0.0.0', advertisedUrl);
250
251
  // dynamic import to avoid circular at top
251
252
  const { testMcpClient } = await import('./tests/testMcpClient.js');
252
253
  try {
@@ -37,7 +37,8 @@ serverDescription, // was passed by index.ts
37
37
  serverInstructions, // was passed by index.ts
38
38
  toolSchemas, // was passed by index.ts
39
39
  version, // server version from package.json
40
- _bindHost = 'localhost', advertisedUrl) {
40
+ bindHost = 'localhost', // #239: interface to bind; forwarded to listen()
41
+ advertisedUrl) {
41
42
  const app = express();
42
43
  // Explicit body-size cap (#168). An oversized payload is rejected with HTTP 413
43
44
  // rather than buffered unbounded. Tunable via MCP_HTTP_BODY_LIMIT (default 512kb).
@@ -580,11 +581,14 @@ _bindHost = 'localhost', advertisedUrl) {
580
581
  `MCP_HTTPS_CERT=${config.MCP_HTTPS_CERT}, MCP_HTTPS_KEY=${config.MCP_HTTPS_KEY}. Cause: ${cause}`);
581
582
  }
582
583
  }
583
- const listener = (tlsOptions ? https.createServer(tlsOptions, app) : app).listen(port, () => {
584
+ const listener = (tlsOptions ? https.createServer(tlsOptions, app) : app).listen(port, bindHost, () => {
584
585
  const advertised = advertisedUrl || `${scheme}://${serverIp}:${port}${httpPath}`;
586
+ // #239: when bound to a specific (non all-interfaces) host the server is NOT on
587
+ // loopback, so point the health hint at the actual bind host instead of localhost.
588
+ const healthHost = (!bindHost || bindHost === '0.0.0.0' || bindHost === '::') ? 'localhost' : bindHost;
585
589
  console.info(`MCP Streamable HTTP Server listening on ${port}`);
586
590
  console.info(`📨 MCP endpoint: ${advertised}`);
587
- console.info(`❤️ Health check: ${scheme}://localhost:${port}/health`);
591
+ console.info(`❤️ Health check: ${scheme}://${healthHost}:${port}/health`);
588
592
  if (config.AUTH_PROVIDER === 'oidc') {
589
593
  logger.info(`🔒 OIDC authentication enabled (JWT Bearer token required — issuer: ${config.OIDC_ISSUER})`);
590
594
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "actual-mcp-server",
3
3
  "displayName": "Actual MCP Server",
4
- "version": "0.7.3",
4
+ "version": "0.7.5",
5
5
  "engines": {
6
6
  "node": ">=22.0.0",
7
7
  "npm": ">=10.0.0"
@@ -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_data_dir.test.js && node tests/unit/config_drift.test.js && node tests/unit/port_alignment.test.js && node tests/unit/dockerfile_data_dir_alignment.test.js && node tests/unit/compose_profile_sync.test.js && node tests/unit/advertised_tools_sync.test.js && node tests/unit/knip_config.test.js && node tests/unit/readme_stats_sync.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/transactions_import_schema.test.js && node tests/unit/tool_schema_shape.test.js && node tests/unit/zod_error_format.test.js && node tests/unit/tool_count_sync.test.js && node tests/unit/stdio_transport.test.js && node tests/unit/logger_structured.test.js && node tests/unit/logger_redaction.test.js && node tests/unit/logger_correlation.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_data_dir.test.js && node tests/unit/config_drift.test.js && node tests/unit/port_alignment.test.js && node tests/unit/dockerfile_data_dir_alignment.test.js && node tests/unit/unraid_template_alignment.test.js && node tests/unit/entrypoint_invariants.test.js && node tests/unit/compose_profile_sync.test.js && node tests/unit/advertised_tools_sync.test.js && node tests/unit/knip_config.test.js && node tests/unit/readme_stats_sync.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/httpServer_bind_host.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/transactions_import_schema.test.js && node tests/unit/tool_schema_shape.test.js && node tests/unit/zod_error_format.test.js && node tests/unit/tool_count_sync.test.js && node tests/unit/stdio_transport.test.js && node tests/unit/logger_structured.test.js && node tests/unit/logger_redaction.test.js && node tests/unit/logger_correlation.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",