From 83c9639c2e861e25e2df05768dcee5bdd983ac97 Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Thu, 11 Sep 2025 09:08:13 +0100 Subject: [PATCH 1/9] feat: Allow configuration of temporary user when connecting to atlas cluster --- src/common/config.ts | 3 +++ src/tools/atlas/connect/connectCluster.ts | 3 +-- tests/unit/common/config.test.ts | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 9132a6c6f..b11f52b2e 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -48,6 +48,7 @@ const OPTIONS = { "tlsCertificateSelector", "tlsDisabledProtocols", "username", + "temporaryDatabaseUserLifetimeSeconds", ], boolean: [ "apiDeprecationErrors", @@ -161,6 +162,7 @@ export interface UserConfig extends CliOptions { loggers: Array<"stderr" | "disk" | "mcp">; idleTimeoutMs: number; notificationTimeoutMs: number; + temporaryDatabaseUserLifetimeSeconds: number; } export const defaultUserConfig: UserConfig = { @@ -180,6 +182,7 @@ export const defaultUserConfig: UserConfig = { idleTimeoutMs: 600000, // 10 minutes notificationTimeoutMs: 540000, // 9 minutes httpHeaders: {}, + temporaryDatabaseUserLifetimeSeconds: 14400, // 4 hours }; export const config = setupUserConfig({ diff --git a/src/tools/atlas/connect/connectCluster.ts b/src/tools/atlas/connect/connectCluster.ts index 618f5483b..5ed89a1e1 100644 --- a/src/tools/atlas/connect/connectCluster.ts +++ b/src/tools/atlas/connect/connectCluster.ts @@ -9,7 +9,6 @@ import { ensureCurrentIpInAccessList } from "../../../common/atlas/accessListUti import type { AtlasClusterConnectionInfo } from "../../../common/connectionManager.js"; import { getDefaultRoleFromConfig } from "../../../common/atlas/roles.js"; -const EXPIRY_MS = 1000 * 60 * 60 * 12; // 12 hours const addedIpAccessListMessage = "Note: Your current IP address has been added to the Atlas project's IP access list to enable secure connection."; @@ -77,7 +76,7 @@ export class ConnectClusterTool extends AtlasToolBase { const username = `mcpUser${Math.floor(Math.random() * 100000)}`; const password = await generateSecurePassword(); - const expiryDate = new Date(Date.now() + EXPIRY_MS); + const expiryDate = new Date(Date.now() + this.config.temporaryDatabaseUserLifetimeSeconds * 1000); const role = getDefaultRoleFromConfig(this.config); await this.session.apiClient.createDatabaseUser({ diff --git a/tests/unit/common/config.test.ts b/tests/unit/common/config.test.ts index a497cb331..cff3a3b75 100644 --- a/tests/unit/common/config.test.ts +++ b/tests/unit/common/config.test.ts @@ -41,6 +41,11 @@ describe("config", () => { { envVar: "MDB_MCP_HTTP_HOST", property: "httpHost", value: "localhost" }, { envVar: "MDB_MCP_IDLE_TIMEOUT_MS", property: "idleTimeoutMs", value: 5000 }, { envVar: "MDB_MCP_NOTIFICATION_TIMEOUT_MS", property: "notificationTimeoutMs", value: 5000 }, + { + envVar: "MDB_MCP_TEMPORARY_DATABASE_USER_LIFETIME_SECONDS", + property: "temporaryDatabaseUserLifetimeSeconds", + value: 12345, + }, ] as const; for (const { envVar, property, value } of testCases) { @@ -129,6 +134,10 @@ describe("config", () => { cli: ["--notificationTimeoutMs", "42"], expected: { notificationTimeoutMs: "42" }, }, + { + cli: ["--temporaryDatabaseUserLifetimeSeconds", "12345"], + expected: { temporaryDatabaseUserLifetimeSeconds: "12345" }, + }, { cli: ["--telemetry", "enabled"], expected: { telemetry: "enabled" }, From 4a9d8798d7de4fff1eeb9194c18f716bbf814dac Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Thu, 11 Sep 2025 10:35:39 +0100 Subject: [PATCH 2/9] renamed temporaryDatabaseUserLifetimeSeconds -> atlasTemporaryDatabaseUserLifetimeMs --- src/common/config.ts | 6 +++--- src/tools/atlas/connect/connectCluster.ts | 2 +- tests/unit/common/config.test.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index b11f52b2e..9bdcbbe51 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -48,7 +48,7 @@ const OPTIONS = { "tlsCertificateSelector", "tlsDisabledProtocols", "username", - "temporaryDatabaseUserLifetimeSeconds", + "atlasTemporaryDatabaseUserLifetimeMs", ], boolean: [ "apiDeprecationErrors", @@ -162,7 +162,7 @@ export interface UserConfig extends CliOptions { loggers: Array<"stderr" | "disk" | "mcp">; idleTimeoutMs: number; notificationTimeoutMs: number; - temporaryDatabaseUserLifetimeSeconds: number; + atlasTemporaryDatabaseUserLifetimeMs: number; } export const defaultUserConfig: UserConfig = { @@ -182,7 +182,7 @@ export const defaultUserConfig: UserConfig = { idleTimeoutMs: 600000, // 10 minutes notificationTimeoutMs: 540000, // 9 minutes httpHeaders: {}, - temporaryDatabaseUserLifetimeSeconds: 14400, // 4 hours + atlasTemporaryDatabaseUserLifetimeMs: 14400000, // 4 hours }; export const config = setupUserConfig({ diff --git a/src/tools/atlas/connect/connectCluster.ts b/src/tools/atlas/connect/connectCluster.ts index 5ed89a1e1..a7680b3f6 100644 --- a/src/tools/atlas/connect/connectCluster.ts +++ b/src/tools/atlas/connect/connectCluster.ts @@ -76,7 +76,7 @@ export class ConnectClusterTool extends AtlasToolBase { const username = `mcpUser${Math.floor(Math.random() * 100000)}`; const password = await generateSecurePassword(); - const expiryDate = new Date(Date.now() + this.config.temporaryDatabaseUserLifetimeSeconds * 1000); + const expiryDate = new Date(Date.now() + this.config.atlasTemporaryDatabaseUserLifetimeMs); const role = getDefaultRoleFromConfig(this.config); await this.session.apiClient.createDatabaseUser({ diff --git a/tests/unit/common/config.test.ts b/tests/unit/common/config.test.ts index cff3a3b75..9e0a739f1 100644 --- a/tests/unit/common/config.test.ts +++ b/tests/unit/common/config.test.ts @@ -42,8 +42,8 @@ describe("config", () => { { envVar: "MDB_MCP_IDLE_TIMEOUT_MS", property: "idleTimeoutMs", value: 5000 }, { envVar: "MDB_MCP_NOTIFICATION_TIMEOUT_MS", property: "notificationTimeoutMs", value: 5000 }, { - envVar: "MDB_MCP_TEMPORARY_DATABASE_USER_LIFETIME_SECONDS", - property: "temporaryDatabaseUserLifetimeSeconds", + envVar: "MDB_MCP_ATLAS_TEMPORARY_DATABASE_USER_LIFETIME_MS", + property: "atlasTemporaryDatabaseUserLifetimeMs", value: 12345, }, ] as const; @@ -135,8 +135,8 @@ describe("config", () => { expected: { notificationTimeoutMs: "42" }, }, { - cli: ["--temporaryDatabaseUserLifetimeSeconds", "12345"], - expected: { temporaryDatabaseUserLifetimeSeconds: "12345" }, + cli: ["--atlasTemporaryDatabaseUserLifetimeMs", "12345"], + expected: { atlasTemporaryDatabaseUserLifetimeMs: "12345" }, }, { cli: ["--telemetry", "enabled"], From 9581337168ef4d7ec65e3eb115eca67879938931 Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Thu, 11 Sep 2025 12:12:03 +0100 Subject: [PATCH 3/9] split options --- src/common/config.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 9bdcbbe51..9dc024a90 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -8,7 +8,7 @@ import type { Secret } from "./keychain.js"; import levenshtein from "ts-levenshtein"; // From: https://github.com/mongodb-js/mongosh/blob/main/packages/cli-repl/src/arg-parser.ts -const OPTIONS = { +const MONGOSH_OPTIONS = { string: [ "apiBaseUrl", "apiClientId", @@ -47,8 +47,7 @@ const OPTIONS = { "tlsCertificateKeyFilePassword", "tlsCertificateSelector", "tlsDisabledProtocols", - "username", - "atlasTemporaryDatabaseUserLifetimeMs", + "username" ], boolean: [ "apiDeprecationErrors", @@ -93,6 +92,17 @@ const OPTIONS = { }, } as const; +const MCP_SERVER_OPTIONS = { + string: [ + "atlasTemporaryDatabaseUserLifetimeMs", + ], +} as const; + +const OPTIONS = { + ...MONGOSH_OPTIONS, + ...MCP_SERVER_OPTIONS, +} as const; + const ALL_CONFIG_KEYS = new Set( (OPTIONS.string as readonly string[]) .concat(OPTIONS.array) From ce905bd905f20834e8720f6a1bf81cfe51093358 Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Thu, 11 Sep 2025 12:23:33 +0100 Subject: [PATCH 4/9] fixed shallow merge issue --- src/common/config.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 9dc024a90..87378d26d 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -99,8 +99,11 @@ const MCP_SERVER_OPTIONS = { } as const; const OPTIONS = { - ...MONGOSH_OPTIONS, - ...MCP_SERVER_OPTIONS, + string: { ...MONGOSH_OPTIONS.string, ...MCP_SERVER_OPTIONS.string }, + boolean: { ...MONGOSH_OPTIONS.boolean }, + array: { ...MONGOSH_OPTIONS.array }, + alias: { ...MONGOSH_OPTIONS.alias }, + configuration: { ...MONGOSH_OPTIONS.configuration }, } as const; const ALL_CONFIG_KEYS = new Set( From 90123d34a96c8cc84e55e152d1f13f08ab346c70 Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Thu, 11 Sep 2025 12:28:44 +0100 Subject: [PATCH 5/9] fixed typing issue not caught by typescript --- src/common/config.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 87378d26d..4dd0cdcdb 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -47,7 +47,7 @@ const MONGOSH_OPTIONS = { "tlsCertificateKeyFilePassword", "tlsCertificateSelector", "tlsDisabledProtocols", - "username" + "username", ], boolean: [ "apiDeprecationErrors", @@ -93,15 +93,13 @@ const MONGOSH_OPTIONS = { } as const; const MCP_SERVER_OPTIONS = { - string: [ - "atlasTemporaryDatabaseUserLifetimeMs", - ], + string: ["atlasTemporaryDatabaseUserLifetimeMs"], } as const; const OPTIONS = { - string: { ...MONGOSH_OPTIONS.string, ...MCP_SERVER_OPTIONS.string }, - boolean: { ...MONGOSH_OPTIONS.boolean }, - array: { ...MONGOSH_OPTIONS.array }, + string: [...MONGOSH_OPTIONS.string, ...MCP_SERVER_OPTIONS.string], + boolean: [...MONGOSH_OPTIONS.boolean], + array: [...MONGOSH_OPTIONS.array], alias: { ...MONGOSH_OPTIONS.alias }, configuration: { ...MONGOSH_OPTIONS.configuration }, } as const; From b7e0d1ba99d9ac7fc98526a47e1163b6cbda5f5f Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Thu, 11 Sep 2025 12:43:19 +0100 Subject: [PATCH 6/9] updated the readme --- README.md | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d508b755d..10573bf35 100644 --- a/README.md +++ b/README.md @@ -323,25 +323,26 @@ The MongoDB MCP Server can be configured using multiple methods, with the follow ### Configuration Options -| CLI Option | Environment Variable | Default | Description | -| ------------------------- | ------------------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `apiClientId` | `MDB_MCP_API_CLIENT_ID` | | Atlas API client ID for authentication. Required for running Atlas tools. | -| `apiClientSecret` | `MDB_MCP_API_CLIENT_SECRET` | | Atlas API client secret for authentication. Required for running Atlas tools. | -| `connectionString` | `MDB_MCP_CONNECTION_STRING` | | MongoDB connection string for direct database connections. Optional, if not set, you'll need to call the `connect` tool before interacting with MongoDB data. | -| `loggers` | `MDB_MCP_LOGGERS` | disk,mcp | Comma separated values, possible values are `mcp`, `disk` and `stderr`. See [Logger Options](#logger-options) for details. | -| `logPath` | `MDB_MCP_LOG_PATH` | see note\* | Folder to store logs. | -| `disabledTools` | `MDB_MCP_DISABLED_TOOLS` | | An array of tool names, operation types, and/or categories of tools that will be disabled. | -| `readOnly` | `MDB_MCP_READ_ONLY` | false | When set to true, only allows read, connect, and metadata operation types, disabling create/update/delete operations. | -| `indexCheck` | `MDB_MCP_INDEX_CHECK` | false | When set to true, enforces that query operations must use an index, rejecting queries that perform a collection scan. | -| `telemetry` | `MDB_MCP_TELEMETRY` | enabled | When set to disabled, disables telemetry collection. | -| `transport` | `MDB_MCP_TRANSPORT` | stdio | Either 'stdio' or 'http'. | -| `httpPort` | `MDB_MCP_HTTP_PORT` | 3000 | Port number. | -| `httpHost` | `MDB_MCP_HTTP_HOST` | 127.0.0.1 | Host to bind the http server. | -| `idleTimeoutMs` | `MDB_MCP_IDLE_TIMEOUT_MS` | 600000 | Idle timeout for a client to disconnect (only applies to http transport). | -| `notificationTimeoutMs` | `MDB_MCP_NOTIFICATION_TIMEOUT_MS` | 540000 | Notification timeout for a client to be aware of diconnect (only applies to http transport). | -| `exportsPath` | `MDB_MCP_EXPORTS_PATH` | see note\* | Folder to store exported data files. | -| `exportTimeoutMs` | `MDB_MCP_EXPORT_TIMEOUT_MS` | 300000 | Time in milliseconds after which an export is considered expired and eligible for cleanup. | -| `exportCleanupIntervalMs` | `MDB_MCP_EXPORT_CLEANUP_INTERVAL_MS` | 120000 | Time in milliseconds between export cleanup cycles that remove expired export files. | +| CLI Option | Environment Variable | Default | Description | +| -------------------------------------- | --------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `apiClientId` | `MDB_MCP_API_CLIENT_ID` | | Atlas API client ID for authentication. Required for running Atlas tools. | +| `apiClientSecret` | `MDB_MCP_API_CLIENT_SECRET` | | Atlas API client secret for authentication. Required for running Atlas tools. | +| `connectionString` | `MDB_MCP_CONNECTION_STRING` | | MongoDB connection string for direct database connections. Optional, if not set, you'll need to call the `connect` tool before interacting with MongoDB data. | +| `loggers` | `MDB_MCP_LOGGERS` | disk,mcp | Comma separated values, possible values are `mcp`, `disk` and `stderr`. See [Logger Options](#logger-options) for details. | +| `logPath` | `MDB_MCP_LOG_PATH` | see note\* | Folder to store logs. | +| `disabledTools` | `MDB_MCP_DISABLED_TOOLS` | | An array of tool names, operation types, and/or categories of tools that will be disabled. | +| `readOnly` | `MDB_MCP_READ_ONLY` | false | When set to true, only allows read, connect, and metadata operation types, disabling create/update/delete operations. | +| `indexCheck` | `MDB_MCP_INDEX_CHECK` | false | When set to true, enforces that query operations must use an index, rejecting queries that perform a collection scan. | +| `telemetry` | `MDB_MCP_TELEMETRY` | enabled | When set to disabled, disables telemetry collection. | +| `transport` | `MDB_MCP_TRANSPORT` | stdio | Either 'stdio' or 'http'. | +| `httpPort` | `MDB_MCP_HTTP_PORT` | 3000 | Port number. | +| `httpHost` | `MDB_MCP_HTTP_HOST` | 127.0.0.1 | Host to bind the http server. | +| `idleTimeoutMs` | `MDB_MCP_IDLE_TIMEOUT_MS` | 600000 | Idle timeout for a client to disconnect (only applies to http transport). | +| `notificationTimeoutMs` | `MDB_MCP_NOTIFICATION_TIMEOUT_MS` | 540000 | Notification timeout for a client to be aware of diconnect (only applies to http transport). | +| `exportsPath` | `MDB_MCP_EXPORTS_PATH` | see note\* | Folder to store exported data files. | +| `exportTimeoutMs` | `MDB_MCP_EXPORT_TIMEOUT_MS` | 300000 | Time in milliseconds after which an export is considered expired and eligible for cleanup. | +| `exportCleanupIntervalMs` | `MDB_MCP_EXPORT_CLEANUP_INTERVAL_MS` | 120000 | Time in milliseconds between export cleanup cycles that remove expired export files. | +| `atlasTemporaryDatabaseUserLifetimeMs` | `MDB_MCP_ATLAS_TEMPORARY_DATABASE_USER_LIFETIME_MS` | 14400000 | Time in milliseconds that temporary database users created when connecting to MongoDB Atlas clusters will remain active before being automatically deleted. | #### Logger Options From b0e8b9403a49681e529712b9fccf72cf42948ee7 Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Thu, 11 Sep 2025 14:21:55 +0100 Subject: [PATCH 7/9] converted duration numbers to multiplications --- src/common/config.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 4dd0cdcdb..7778c0375 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -180,8 +180,8 @@ export const defaultUserConfig: UserConfig = { apiBaseUrl: "https://cloud.mongodb.com/", logPath: getLogPath(), exportsPath: getExportsPath(), - exportTimeoutMs: 300000, // 5 minutes - exportCleanupIntervalMs: 120000, // 2 minutes + exportTimeoutMs: 5 * 60 * 1000, // 5 minutes + exportCleanupIntervalMs: 2 * 60 * 1000, // 2 minutes disabledTools: [], telemetry: "enabled", readOnly: false, @@ -190,10 +190,10 @@ export const defaultUserConfig: UserConfig = { httpPort: 3000, httpHost: "127.0.0.1", loggers: ["disk", "mcp"], - idleTimeoutMs: 600000, // 10 minutes - notificationTimeoutMs: 540000, // 9 minutes + idleTimeoutMs: 10 * 60 * 1000, // 10 minutes + notificationTimeoutMs: 9 * 60 * 1000, // 9 minutes httpHeaders: {}, - atlasTemporaryDatabaseUserLifetimeMs: 14400000, // 4 hours + atlasTemporaryDatabaseUserLifetimeMs: 4 * 60 * 60 * 1000, // 4 hours }; export const config = setupUserConfig({ From 17c15bb05956308434c076eac3aa2610feb70573 Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Thu, 11 Sep 2025 14:30:42 +0100 Subject: [PATCH 8/9] added mergeOptions and options interface --- src/common/config.ts | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 7778c0375..82a97d984 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -90,19 +90,34 @@ const MONGOSH_OPTIONS = { "greedy-arrays": true, "short-option-groups": false, }, -} as const; +} as Readonly; const MCP_SERVER_OPTIONS = { string: ["atlasTemporaryDatabaseUserLifetimeMs"], -} as const; - -const OPTIONS = { - string: [...MONGOSH_OPTIONS.string, ...MCP_SERVER_OPTIONS.string], - boolean: [...MONGOSH_OPTIONS.boolean], - array: [...MONGOSH_OPTIONS.array], - alias: { ...MONGOSH_OPTIONS.alias }, - configuration: { ...MONGOSH_OPTIONS.configuration }, -} as const; +} as Readonly>; + +interface Options { + string: string[]; + boolean: string[]; + array: string[]; + alias: Record; + configuration: Record; +} + +function mergeOptions(...optionSources: Array>): Readonly { + return { + string: optionSources.flatMap((opts) => opts.string ?? []), + boolean: optionSources.flatMap((opts) => opts.boolean ?? []), + array: optionSources.flatMap((opts) => opts.array ?? []), + alias: Object.assign({}, ...optionSources.map((opts) => opts.alias ?? {})) as Record, + configuration: Object.assign({}, ...optionSources.map((opts) => opts.configuration ?? {})) as Record< + string, + boolean + >, + }; +} + +const OPTIONS = mergeOptions(MONGOSH_OPTIONS, MCP_SERVER_OPTIONS); const ALL_CONFIG_KEYS = new Set( (OPTIONS.string as readonly string[]) From 67008787cf503ca2de75eadbf6af052dcafd3c35 Mon Sep 17 00:00:00 2001 From: Jeroen Vervaeke Date: Fri, 12 Sep 2025 08:49:32 +0100 Subject: [PATCH 9/9] undo requested PR change requests --- src/common/config.ts | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 82a97d984..90d1fc807 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -8,7 +8,7 @@ import type { Secret } from "./keychain.js"; import levenshtein from "ts-levenshtein"; // From: https://github.com/mongodb-js/mongosh/blob/main/packages/cli-repl/src/arg-parser.ts -const MONGOSH_OPTIONS = { +const OPTIONS = { string: [ "apiBaseUrl", "apiClientId", @@ -48,6 +48,7 @@ const MONGOSH_OPTIONS = { "tlsCertificateSelector", "tlsDisabledProtocols", "username", + "atlasTemporaryDatabaseUserLifetimeMs", ], boolean: [ "apiDeprecationErrors", @@ -92,10 +93,6 @@ const MONGOSH_OPTIONS = { }, } as Readonly; -const MCP_SERVER_OPTIONS = { - string: ["atlasTemporaryDatabaseUserLifetimeMs"], -} as Readonly>; - interface Options { string: string[]; boolean: string[]; @@ -104,21 +101,6 @@ interface Options { configuration: Record; } -function mergeOptions(...optionSources: Array>): Readonly { - return { - string: optionSources.flatMap((opts) => opts.string ?? []), - boolean: optionSources.flatMap((opts) => opts.boolean ?? []), - array: optionSources.flatMap((opts) => opts.array ?? []), - alias: Object.assign({}, ...optionSources.map((opts) => opts.alias ?? {})) as Record, - configuration: Object.assign({}, ...optionSources.map((opts) => opts.configuration ?? {})) as Record< - string, - boolean - >, - }; -} - -const OPTIONS = mergeOptions(MONGOSH_OPTIONS, MCP_SERVER_OPTIONS); - const ALL_CONFIG_KEYS = new Set( (OPTIONS.string as readonly string[]) .concat(OPTIONS.array)