actual-mcp-server 0.6.50 → 0.6.54
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 +20 -16
- package/dist/package.json +4 -3
- package/dist/src/config.js +1 -1
- package/dist/src/lib/config-registry.js +102 -0
- package/dist/src/lib/constants.js +4 -2
- package/dist/src/lib/loggerFactory.js +2 -2
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -84,12 +84,12 @@ docker run -d \
|
|
|
84
84
|
-e ACTUAL_PASSWORD=your_password \
|
|
85
85
|
-e ACTUAL_BUDGET_SYNC_ID=your_sync_id \
|
|
86
86
|
-e MCP_SSE_AUTHORIZATION=your_secret_token \
|
|
87
|
-
-v actual-mcp-data:/data \ # required, see note below
|
|
87
|
+
-v actual-mcp-data:/app/data \ # required, see note below
|
|
88
88
|
-v actual-mcp-logs:/app/logs \
|
|
89
89
|
ghcr.io/agigante80/actual-mcp-server:latest
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
> **Why the `/data` volume is required:** Actual Budget does not expose a REST API. The official `@actual-app/api` library (used internally by this server) works by downloading a local copy of your budget data, running all queries on that local copy, then syncing changes back. The `/data` volume gives the container a persistent, writable place to store that local copy. Without it the container has nowhere to write and will fail on startup. See the [Actual API docs](https://actualbudget.org/docs/api/) for details.
|
|
92
|
+
> **Why the `/app/data` volume is required:** Actual Budget does not expose a REST API. The official `@actual-app/api` library (used internally by this server) works by downloading a local copy of your budget data, running all queries on that local copy, then syncing changes back. The `/app/data` volume gives the container a persistent, writable place to store that local copy (it is the directory the image creates and owns as the runtime user). Without it the container has nowhere to write and will fail on startup. See the [Actual API docs](https://actualbudget.org/docs/api/) for details.
|
|
93
93
|
>
|
|
94
94
|
> **actual-mcp does not need to run on the same machine as Actual Budget.** You can have Actual Budget on one server and actual-mcp on another - as long as `ACTUAL_SERVER_URL` points to your Actual Budget instance, everything works.
|
|
95
95
|
|
|
@@ -121,13 +121,13 @@ git clone https://github.com/agigante80/actual-mcp-server.git
|
|
|
121
121
|
cd actual-mcp-server
|
|
122
122
|
cp .env.example .env # fill in ACTUAL_SERVER_URL, ACTUAL_PASSWORD, ACTUAL_BUDGET_SYNC_ID
|
|
123
123
|
|
|
124
|
-
docker compose --profile production up -d #
|
|
124
|
+
docker compose --profile production up -d # production: MCP server listens on :3600
|
|
125
125
|
# or
|
|
126
126
|
docker compose --profile dev up -d # dev mode with hot-reload
|
|
127
|
-
# or
|
|
128
|
-
docker compose --profile fullstack up -d # includes Actual Budget server on :5006
|
|
129
127
|
```
|
|
130
128
|
|
|
129
|
+
> The compose file defines only the `dev` and `production` profiles. The MCP server listens on `:3600` directly: there is no bundled reverse proxy and no bundled Actual Budget server, so point `ACTUAL_SERVER_URL` at your own Actual instance. For TLS, enable native HTTPS with `MCP_ENABLE_HTTPS=true` (plus `MCP_HTTPS_CERT` and `MCP_HTTPS_KEY`), or front the server with your own reverse proxy.
|
|
130
|
+
|
|
131
131
|
### Option C: npm (HTTP server)
|
|
132
132
|
|
|
133
133
|
```bash
|
|
@@ -147,7 +147,7 @@ npm run build
|
|
|
147
147
|
npm run dev -- --http
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
Server starts at `http://localhost:
|
|
150
|
+
Server starts at `http://localhost:3600/http` by default (the listen port is `MCP_BRIDGE_PORT`, default `3600`).
|
|
151
151
|
|
|
152
152
|
### Option D: stdio (Claude Desktop native, no Docker or HTTP server needed)
|
|
153
153
|
|
|
@@ -424,25 +424,27 @@ All configuration is via environment variables. Copy `.env.example` to `.env` to
|
|
|
424
424
|
| Variable | Default | Required | Description |
|
|
425
425
|
|----------|---------|----------|-------------|
|
|
426
426
|
| **Actual Budget Connection** ||||
|
|
427
|
-
| `ACTUAL_SERVER_URL` |
|
|
427
|
+
| `ACTUAL_SERVER_URL` | _(none)_ | Yes | URL of your Actual Budget server. Use the same URL you type in your browser: `http://localhost:5006` (local), `http://192.168.1.x:5006` (network), `https://actual.yourdomain.com` (domain), or `http://actual:5006` (container name if on the same Docker network) |
|
|
428
428
|
| `ACTUAL_PASSWORD` | _(none)_ | Yes | Password for Actual Budget server |
|
|
429
429
|
| `ACTUAL_BUDGET_SYNC_ID` | _(none)_ | Yes | Budget Sync ID from Actual (Settings then Sync ID) |
|
|
430
430
|
| `ACTUAL_BUDGET_PASSWORD` | _(none)_ | No | Optional encryption password for encrypted budgets |
|
|
431
|
+
| `ALLOW_INSECURE_UPSTREAM` | `false` | No | Allow an `http://` upstream even when `ACTUAL_BUDGET_PASSWORD` is set (#161). Off by default so a plaintext upstream plus an encryption password is refused |
|
|
431
432
|
| **MCP Server Settings** ||||
|
|
432
|
-
| `MCP_BRIDGE_PORT` | `
|
|
433
|
+
| `MCP_BRIDGE_PORT` | `3600` | No | Port for MCP server to listen on |
|
|
433
434
|
| `MCP_BRIDGE_BIND_HOST` | `0.0.0.0` | No | Host address to bind server to (`0.0.0.0` = all interfaces) |
|
|
434
435
|
| `MCP_BRIDGE_DATA_DIR` | `./actual-data` | No | Directory to store Actual Budget local data (SQLite). **Required to be a persistent path.** The `@actual-app/api` library downloads a local copy of your budget here to run queries; use a volume mount in Docker to persist it across restarts |
|
|
435
436
|
| `MCP_BRIDGE_PUBLIC_HOST` | auto-detected | No | Public hostname/IP for server (shown in logs) |
|
|
436
437
|
| `MCP_BRIDGE_PUBLIC_SCHEME` | auto-detected | No | Public scheme (`http` or `https`) |
|
|
437
438
|
| `MCP_BRIDGE_USE_TLS` | `false` | No | Set to `true` to advertise `https://` in the server URL (for reverse-proxy setups where TLS is terminated upstream) |
|
|
438
439
|
| **Transport Configuration** ||||
|
|
439
|
-
| `MCP_TRANSPORT_MODE` | `--http` | No | Transport mode
|
|
440
|
+
| `MCP_TRANSPORT_MODE` | `--http` | No | Transport mode. Only `--http` is a valid value; stdio is selected via the `--stdio` CLI flag, not this var |
|
|
440
441
|
| `MCP_HTTP_PATH` | `/http` | No | HTTP endpoint routing path |
|
|
441
442
|
| `MCP_BRIDGE_HTTP_PATH` | same as `MCP_HTTP_PATH` | No | Advertised HTTP path shown to clients (set when a reverse proxy rewrites the path) |
|
|
443
|
+
| `MCP_HTTP_BODY_LIMIT` | `512kb` | No | Maximum accepted JSON-RPC request body size (e.g. `512kb`, `1mb`) |
|
|
442
444
|
| **Session Management** ||||
|
|
443
445
|
| `USE_CONNECTION_POOL` | `true` | No | Enable session-based connection pooling |
|
|
444
446
|
| `MAX_CONCURRENT_SESSIONS` | `15` | No | Maximum concurrent MCP sessions allowed |
|
|
445
|
-
| `SESSION_IDLE_TIMEOUT_MINUTES` | `5`
|
|
447
|
+
| `SESSION_IDLE_TIMEOUT_MINUTES` | `5` | No | Minutes before idle session cleanup |
|
|
446
448
|
| **Security & Authentication** ||||
|
|
447
449
|
| `AUTH_PROVIDER` | `none` | No | Auth mode: `none` (static Bearer) or `oidc` (JWKS-validated JWT) |
|
|
448
450
|
| `MCP_SSE_AUTHORIZATION` | _(none)_ | No | Static Bearer token (`AUTH_PROVIDER=none`; highly recommended in production) |
|
|
@@ -455,19 +457,21 @@ All configuration is via environment variables. Copy `.env.example` to `.env` to
|
|
|
455
457
|
| `MCP_HTTPS_KEY` | _(none)_ | No | Path to PEM private key file (required when `MCP_ENABLE_HTTPS=true`) |
|
|
456
458
|
| **Logging Configuration** ||||
|
|
457
459
|
| `MCP_BRIDGE_STORE_LOGS` | `false` | No | Enable file logging (vs console only) |
|
|
458
|
-
| `MCP_BRIDGE_LOG_DIR` |
|
|
459
|
-
| `MCP_BRIDGE_LOG_LEVEL` | `debug` | No | Log level: `error`, `warn`, `info`, `debug` |
|
|
460
|
+
| `MCP_BRIDGE_LOG_DIR` | `app/logs` (beside the install) | No | Directory for log files (if `STORE_LOGS=true`). `.env.example` and Docker set it explicitly (e.g. `./logs`, `/app/logs`) |
|
|
461
|
+
| `MCP_BRIDGE_LOG_LEVEL` | `debug` (dev) / `info` (prod) | No | Log level: `error`, `warn`, `info`, `debug` |
|
|
462
|
+
| `LOG_FORMAT` | auto | No | Log output format: `json` or `pretty`. Precedence: explicit `LOG_FORMAT` wins, else `NODE_ENV=production` selects `json`, else `pretty` |
|
|
463
|
+
| `MCP_SERVICE_NAME` | `actual-mcp-server` | No | Service name stamped on every structured (json) log record |
|
|
460
464
|
| **Log Rotation** (when `MCP_BRIDGE_STORE_LOGS=true`) ||||
|
|
461
465
|
| `MCP_BRIDGE_MAX_FILES` | `14d` | No | Keep rotated logs for N days (e.g., `14d`, `30d`) |
|
|
462
466
|
| `MCP_BRIDGE_MAX_LOG_SIZE` | `20m` | No | Rotate when file reaches size (e.g., `20m`, `100m`) |
|
|
463
467
|
| `MCP_BRIDGE_ROTATE_DATEPATTERN` | `YYYY-MM-DD` | No | Date pattern for rotated log filenames |
|
|
464
468
|
| **Development & Debugging** ||||
|
|
465
|
-
| `DEBUG` |
|
|
466
|
-
| `LOG_LEVEL` |
|
|
469
|
+
| `DEBUG` | _(none)_ | No | Enable debug mode (verbose logging) when set to any truthy value |
|
|
470
|
+
| `LOG_LEVEL` | _(none)_ | No | Debug-detection toggle: set to `debug` to enable extra transport debug output. Distinct from `MCP_BRIDGE_LOG_LEVEL` (the winston level); has no default and is not itself a log level |
|
|
467
471
|
| `MCP_BRIDGE_DEBUG_TRANSPORT` | `false` | No | Enable transport-level debug logging |
|
|
468
472
|
| **Advanced/Internal** ||||
|
|
469
473
|
| `ACTUAL_API_CONCURRENCY` | `5` | No | Max concurrent Actual API operations |
|
|
470
|
-
| `NODE_ENV` | `production` | No | Node environment; `production
|
|
474
|
+
| `NODE_ENV` | _(none)_ / `production` | No | Node environment. No app default; the Docker image sets `production`, which selects json logs and hides stack traces in error responses |
|
|
471
475
|
| `VERSION` | auto-detected | No | Server version (auto-set by build/Docker) |
|
|
472
476
|
| `TZ` | `UTC` | No | Timezone for timestamps (e.g., `America/New_York`) |
|
|
473
477
|
|
|
@@ -751,4 +755,4 @@ The software is provided **as-is**, without warranty of any kind. The author acc
|
|
|
751
755
|
|
|
752
756
|
---
|
|
753
757
|
|
|
754
|
-
**Version:** 0.6.
|
|
758
|
+
**Version:** 0.6.54 | **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.6.
|
|
4
|
+
"version": "0.6.54",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.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_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/compose_profile_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",
|
|
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",
|
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
"release:major": "npm run version:bump -- major",
|
|
56
56
|
"release:minor": "npm run version:bump -- minor",
|
|
57
57
|
"release:patch": "npm run version:bump -- patch",
|
|
58
|
-
"tool-count": "node scripts/tool-count.mjs"
|
|
58
|
+
"tool-count": "node scripts/tool-count.mjs",
|
|
59
|
+
"config-drift": "npm run build && node scripts/config-drift.mjs"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
61
62
|
"@actual-app/api": "^26.6.0",
|
package/dist/src/config.js
CHANGED
|
@@ -10,7 +10,7 @@ export const configSchema = z.object({
|
|
|
10
10
|
// Off by default so a plaintext upstream + encryption password is refused.
|
|
11
11
|
ALLOW_INSECURE_UPSTREAM: z.string().optional().transform(val => val === 'true'),
|
|
12
12
|
MCP_BRIDGE_DATA_DIR: z.string().default('./actual-data'),
|
|
13
|
-
MCP_BRIDGE_PORT: z.string().default('
|
|
13
|
+
MCP_BRIDGE_PORT: z.string().default('3600'),
|
|
14
14
|
MCP_TRANSPORT_MODE: z.enum(['--http']).default('--http'),
|
|
15
15
|
MCP_SSE_AUTHORIZATION: z.string().optional(),
|
|
16
16
|
MCP_ENABLE_HTTPS: z.string().optional().transform(val => val === 'true'),
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/lib/config-registry.ts (#231)
|
|
2
|
+
//
|
|
3
|
+
// The single in-code record of every configuration variable that is NOT a Zod
|
|
4
|
+
// schema key in src/config.ts but is still read from process.env somewhere in
|
|
5
|
+
// src/. Together with `Object.keys(configSchema.shape)`, this allowlist is the
|
|
6
|
+
// canonical set of configuration variables the server understands.
|
|
7
|
+
//
|
|
8
|
+
// Why an allowlist instead of promoting everything into the schema: most of these
|
|
9
|
+
// are read before src/config.ts loads (src/logger.ts, see logger.ts:53) or in the
|
|
10
|
+
// deferred-config entry src/index.ts (which reads process.env directly to keep the
|
|
11
|
+
// fast `--help` exit, the decision recorded for #230). Promoting them risks a
|
|
12
|
+
// load-order regression, so they stay raw but DOCUMENTED here, with a reason and
|
|
13
|
+
// read site. Migrating any raw read into the validated config object is deliberately
|
|
14
|
+
// out of scope for #231 (a separate, behaviour-sensitive change).
|
|
15
|
+
//
|
|
16
|
+
// This file is data only. It imports nothing from src/config.ts so it carries no
|
|
17
|
+
// process.env requirement and can be imported by the drift guard and its unit test.
|
|
18
|
+
/**
|
|
19
|
+
* Variables read from process.env in src/ that are NOT configSchema keys.
|
|
20
|
+
* Grouped by reason in the comments; the array is the machine-readable source.
|
|
21
|
+
*/
|
|
22
|
+
export const RAW_ENV_ALLOWLIST = [
|
|
23
|
+
// Logger bootstrap: read in src/logger.ts before src/config.ts loads.
|
|
24
|
+
{ name: 'MCP_BRIDGE_STORE_LOGS', reason: 'logger bootstrap (before config loads)', readSite: 'logger.ts:14' },
|
|
25
|
+
{ name: 'MCP_BRIDGE_LOG_DIR', reason: 'logger bootstrap (before config loads)', readSite: 'logger.ts:15' },
|
|
26
|
+
{ name: 'MCP_BRIDGE_ROTATE_DATEPATTERN', reason: 'logger bootstrap (before config loads)', readSite: 'logger.ts:21' },
|
|
27
|
+
{ name: 'MCP_BRIDGE_MAX_LOG_SIZE', reason: 'logger bootstrap (before config loads)', readSite: 'logger.ts:22' },
|
|
28
|
+
{ name: 'MCP_BRIDGE_MAX_FILES', reason: 'logger bootstrap (before config loads)', readSite: 'logger.ts:23' },
|
|
29
|
+
{ name: 'LOG_FORMAT', reason: 'logger bootstrap, read via resolveLogConfig(env)', readSite: 'logger.ts:74' },
|
|
30
|
+
{ name: 'MCP_BRIDGE_LOG_LEVEL', reason: 'logger bootstrap, read via resolveLogConfig(env)', readSite: 'logger.ts:77' },
|
|
31
|
+
{ name: 'MCP_SERVICE_NAME', reason: 'logger bootstrap, read via resolveLogConfig(env)', readSite: 'logger.ts:78' },
|
|
32
|
+
{ name: 'MCP_STDIO_MODE', reason: 'mirrors the --stdio CLI flag, set in-process before the logger import; not a .env knob', readSite: 'logger.ts:345, index.ts', documented: false },
|
|
33
|
+
// Deferred-config entry: read in src/index.ts to preserve the fast --help exit (#230).
|
|
34
|
+
{ name: 'MCP_BRIDGE_BIND_HOST', reason: 'deferred-config entry (index.ts reads process.env directly, #230)', readSite: 'index.ts' },
|
|
35
|
+
{ name: 'MCP_HTTP_PATH', reason: 'deferred-config entry; the path the server LISTENS on', readSite: 'index.ts:195' },
|
|
36
|
+
{ name: 'MCP_BRIDGE_HTTP_PATH', reason: 'deferred-config entry; the path ADVERTISED to clients, falls back to MCP_HTTP_PATH', readSite: 'index.ts:281' },
|
|
37
|
+
{ name: 'MCP_BRIDGE_PUBLIC_HOST', reason: 'deferred-config entry; advertised public host', readSite: 'index.ts:269, httpServer.ts:581' },
|
|
38
|
+
{ name: 'MCP_BRIDGE_PUBLIC_SCHEME', reason: 'deferred-config entry; advertised scheme override', readSite: 'index.ts' },
|
|
39
|
+
{ name: 'MCP_BRIDGE_USE_TLS', reason: 'deprecated alias of MCP_ENABLE_HTTPS; affects only advertised-scheme detection', readSite: 'index.ts:277' },
|
|
40
|
+
{ name: 'MCP_BRIDGE_DEBUG_TRANSPORT', reason: 'transport debug flag, read pre-config', readSite: 'index.ts, utils.ts' },
|
|
41
|
+
// Framework / internal: not user-facing config knobs.
|
|
42
|
+
{ name: 'NODE_ENV', reason: 'framework var; selects prod log format and prod behaviours', readSite: 'httpServer.ts, logger.ts (resolveLogConfig)' },
|
|
43
|
+
{ name: 'DEBUG', reason: 'framework debug toggle', readSite: 'utils.ts, index.ts, httpServer_testing.ts' },
|
|
44
|
+
{ name: 'LOG_LEVEL', reason: 'debug-detection toggle (distinct from MCP_BRIDGE_LOG_LEVEL, the winston level)', readSite: 'utils.ts' },
|
|
45
|
+
{ name: 'DOTENV_CONFIG_QUIET', reason: 'dotenv internal flag, not an operator knob', readSite: 'index.ts', documented: false },
|
|
46
|
+
{ name: 'VERSION', reason: 'build-time version arg, injected by the Docker build, not an operator knob', readSite: 'index.ts, server_info.ts', documented: false },
|
|
47
|
+
// Domain-time: read after config loads; future candidates for schema promotion.
|
|
48
|
+
{ name: 'ACTUAL_API_CONCURRENCY', reason: 'adapter concurrency tuning; future schema-promotion candidate', readSite: 'lib/actual-adapter/concurrency.ts' },
|
|
49
|
+
{ name: 'SESSION_IDLE_TIMEOUT_MINUTES', reason: 'pool idle timeout; future schema-promotion candidate', readSite: 'lib/ActualConnectionPool.ts' },
|
|
50
|
+
{ name: 'USE_CONNECTION_POOL', reason: 'pool toggle; future schema-promotion candidate', readSite: 'actualConnection.ts' },
|
|
51
|
+
];
|
|
52
|
+
/**
|
|
53
|
+
* Schema keys that are ALSO read directly from process.env at a second site. These
|
|
54
|
+
* are validated by configSchema (so they are canonical via schema membership), but
|
|
55
|
+
* the matrix records their raw read site for completeness. Migrating these reads to
|
|
56
|
+
* config.X is the out-of-scope follow-up.
|
|
57
|
+
*/
|
|
58
|
+
export const SCHEMA_VARS_ALSO_READ_RAW = [
|
|
59
|
+
{ name: 'MCP_BRIDGE_PORT', readSite: 'index.ts:194' },
|
|
60
|
+
{ name: 'MCP_ENABLE_HTTPS', readSite: 'index.ts:277,286' },
|
|
61
|
+
{ name: 'MCP_HTTPS_CERT', readSite: 'index.ts:287' },
|
|
62
|
+
{ name: 'MCP_HTTPS_KEY', readSite: 'index.ts:288' },
|
|
63
|
+
{ name: 'MAX_CONCURRENT_SESSIONS', readSite: 'ActualConnectionPool.ts:60' },
|
|
64
|
+
{ name: 'ACTUAL_BUDGET_PASSWORD', readSite: 'actualConnection.ts:33, ActualConnectionPool.ts:255,338' },
|
|
65
|
+
];
|
|
66
|
+
/**
|
|
67
|
+
* Prefixes of dynamic variable families enumerated at runtime, not declared one by
|
|
68
|
+
* one. `.env.example` documents example members (BUDGET_1_*, BUDGET_2_*, ...); the
|
|
69
|
+
* drift guard collapses any name starting with one of these to the family.
|
|
70
|
+
*/
|
|
71
|
+
export const DYNAMIC_ENV_FAMILIES = ['BUDGET_'];
|
|
72
|
+
/**
|
|
73
|
+
* Variables documented for the operator (e.g. in Docker) but read by the OS or
|
|
74
|
+
* runtime, not by the app. The drift guard allows these in docs without requiring a
|
|
75
|
+
* schema/allowlist entry.
|
|
76
|
+
*/
|
|
77
|
+
export const OS_LEVEL_ENV = ['TZ'];
|
|
78
|
+
/**
|
|
79
|
+
* The canonical set of configuration variable names the server understands:
|
|
80
|
+
* every Zod schema key united with every raw-read allowlist name. Pass the schema
|
|
81
|
+
* keys in (e.g. `Object.keys(configSchema.shape)`) so this module stays free of any
|
|
82
|
+
* import from src/config.ts and its process.env requirement.
|
|
83
|
+
*/
|
|
84
|
+
export function canonicalConfigVars(schemaKeys) {
|
|
85
|
+
return new Set([...schemaKeys, ...RAW_ENV_ALLOWLIST.map((v) => v.name)]);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The subset of canonical vars that MUST appear in the user-facing docs
|
|
89
|
+
* (.env.example and the README env table): every schema key, plus every allowlist
|
|
90
|
+
* entry not flagged `documented: false`. Internal vars (build arg, dotenv flag,
|
|
91
|
+
* CLI-flag mirror) are accounted for by the enumeration but exempt from the docs.
|
|
92
|
+
*/
|
|
93
|
+
export function documentedConfigVars(schemaKeys) {
|
|
94
|
+
return new Set([
|
|
95
|
+
...schemaKeys,
|
|
96
|
+
...RAW_ENV_ALLOWLIST.filter((v) => v.documented !== false).map((v) => v.name),
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
99
|
+
/** True if `name` belongs to a dynamic family (e.g. BUDGET_2_NAME). */
|
|
100
|
+
export function isDynamicFamilyVar(name) {
|
|
101
|
+
return DYNAMIC_ENV_FAMILIES.some((prefix) => name.startsWith(prefix));
|
|
102
|
+
}
|
|
@@ -62,9 +62,11 @@ export const WRITE_SESSION_DELAY_MS = 100;
|
|
|
62
62
|
*/
|
|
63
63
|
export const DEFAULT_BIND_HOST = 'localhost';
|
|
64
64
|
/**
|
|
65
|
-
* Default HTTP port for MCP server
|
|
65
|
+
* Default HTTP port for MCP server. Canonical default is 3600 (#230): it matches
|
|
66
|
+
* the listen-port fallback in src/index.ts, the Dockerfile EXPOSE/HEALTHCHECK, the
|
|
67
|
+
* published image, .env.example, and every deployment guide.
|
|
66
68
|
*/
|
|
67
|
-
export const DEFAULT_HTTP_PORT =
|
|
69
|
+
export const DEFAULT_HTTP_PORT = 3600;
|
|
68
70
|
/**
|
|
69
71
|
* Default HTTP path for MCP server endpoint
|
|
70
72
|
*/
|
|
@@ -17,8 +17,8 @@ import logger from '../logger.js';
|
|
|
17
17
|
* import { createModuleLogger } from '../lib/loggerFactory.js';
|
|
18
18
|
*
|
|
19
19
|
* const log = createModuleLogger('HTTP');
|
|
20
|
-
* log.info('Server started', { port:
|
|
21
|
-
* // Output: [HTTP] Server started { port:
|
|
20
|
+
* log.info('Server started', { port: 3600 });
|
|
21
|
+
* // Output: [HTTP] Server started { port: 3600 }
|
|
22
22
|
*
|
|
23
23
|
* log.error('Connection failed', new Error('Timeout'), { retries: 3 });
|
|
24
24
|
* // Output: [HTTP] Connection failed { error: 'Timeout', stack: '...', retries: 3 }
|
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.6.
|
|
4
|
+
"version": "0.6.54",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.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_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/compose_profile_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",
|
|
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",
|
|
@@ -55,7 +55,8 @@
|
|
|
55
55
|
"release:major": "npm run version:bump -- major",
|
|
56
56
|
"release:minor": "npm run version:bump -- minor",
|
|
57
57
|
"release:patch": "npm run version:bump -- patch",
|
|
58
|
-
"tool-count": "node scripts/tool-count.mjs"
|
|
58
|
+
"tool-count": "node scripts/tool-count.mjs",
|
|
59
|
+
"config-drift": "npm run build && node scripts/config-drift.mjs"
|
|
59
60
|
},
|
|
60
61
|
"dependencies": {
|
|
61
62
|
"@actual-app/api": "^26.6.0",
|