• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb-js / mongodb-mcp-server / 19462524052

18 Nov 2025 10:20AM UTC coverage: 80.328% (+0.1%) from 80.187%
19462524052

Pull #729

github

web-flow
Merge 8e32474a4 into 53143eecd
Pull Request #729: chore: refactor config initialisation for CLI to allow easy extension from other config sources as well MCP-288

1328 of 1750 branches covered (75.89%)

Branch coverage included in aggregate %.

332 of 346 new or added lines in 12 files covered. (95.95%)

15 existing lines in 2 files now uncovered.

6406 of 7878 relevant lines covered (81.32%)

72.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.82
/src/common/config/createUserConfig.ts
1
import argv from "yargs-parser";
1!
2
import { generateConnectionInfoFromCliArgs } from "@mongosh/arg-parser";
1✔
3
import { Keychain } from "../keychain.js";
1✔
4
import type { Secret } from "../keychain.js";
5
import { isConnectionSpecifier, matchingConfigKey } from "./configUtils.js";
1✔
6
import { OPTIONS } from "./argsParserOptions.js";
1✔
7
import { UserConfigSchema, type UserConfig } from "./userConfig.js";
1✔
8

9
export type CreateUserConfigHelpers = {
10
    onWarning: (message: string) => void;
11
    onError: (message: string) => void;
12
    closeProcess: (exitCode: number) => never;
13
    cliArguments: string[];
14
};
15

16
export const defaultUserConfigHelpers: CreateUserConfigHelpers = {
1✔
17
    onWarning(message) {
1✔
18
        console.warn(message);
6✔
19
    },
6✔
20
    onError(message) {
1✔
NEW
21
        console.error(message);
×
NEW
22
    },
×
23
    closeProcess(exitCode) {
1✔
NEW
24
        process.exit(exitCode);
×
NEW
25
    },
×
26
    cliArguments: process.argv.slice(2),
1✔
27
};
1✔
28

29
export function createUserConfig({
1✔
30
    onWarning = defaultUserConfigHelpers.onWarning,
125✔
31
    onError = defaultUserConfigHelpers.onError,
125✔
32
    closeProcess = defaultUserConfigHelpers.closeProcess,
125✔
33
    cliArguments = defaultUserConfigHelpers.cliArguments,
125✔
34
}: Partial<CreateUserConfigHelpers> = defaultUserConfigHelpers): UserConfig {
125✔
35
    const { unknownCliArgumentErrors, deprecatedCliArgumentWarning, userAndArgsParserConfig, connectionSpecifier } =
125✔
36
        parseUserConfigSources(cliArguments);
125✔
37

38
    if (unknownCliArgumentErrors.length) {
125✔
39
        const errorMessage = `
3✔
40
${unknownCliArgumentErrors.join("\n")}
3✔
41
- Refer to https://www.mongodb.com/docs/mcp-server/get-started/ for setting up the MCP Server.
42
`;
43
        onError(errorMessage);
3✔
44
        return closeProcess(1);
3✔
45
    }
3✔
46

47
    if (deprecatedCliArgumentWarning) {
125✔
48
        const deprecatedMessages = `
9✔
49
${deprecatedCliArgumentWarning}
9✔
50
- Refer to https://www.mongodb.com/docs/mcp-server/get-started/ for setting up the MCP Server.
51
`;
52
        onWarning(deprecatedMessages);
9✔
53
    }
9✔
54

55
    // If we have a connectionSpecifier, which can only appear as the positional
56
    // argument, then that has to be used on priority to construct the
57
    // connection string. In this case, if there is a connection string provided
58
    // by the env variable or config file, that will be overridden.
59
    if (connectionSpecifier) {
125✔
60
        const connectionInfo = generateConnectionInfoFromCliArgs({ ...userAndArgsParserConfig, connectionSpecifier });
4✔
61
        userAndArgsParserConfig.connectionString = connectionInfo.connectionString;
4✔
62
    }
4✔
63

64
    const configParseResult = UserConfigSchema.safeParse(userAndArgsParserConfig);
122✔
65
    if (configParseResult.error) {
125✔
66
        onError(
10✔
67
            `Invalid configuration for the following fields:\n${configParseResult.error.issues.map((issue) => `${issue.path.join(".")} - ${issue.message}`).join("\n")}`
10✔
68
        );
10✔
69
        return closeProcess(1);
10✔
70
    }
10✔
71

72
    // TODO: Separate correctly parsed user config from all other valid
73
    // arguments relevant to mongosh's args-parser.
74
    const userConfig: UserConfig = { ...userAndArgsParserConfig, ...configParseResult.data };
112✔
75
    warnIfVectorSearchNotEnabledCorrectly(userConfig, onWarning);
112✔
76
    registerKnownSecretsInRootKeychain(userConfig);
112✔
77
    return userConfig;
112✔
78
}
112✔
79

80
function parseUserConfigSources(cliArguments: string[]): {
125✔
81
    unknownCliArgumentErrors: string[];
82
    deprecatedCliArgumentWarning: string | undefined;
83
    userAndArgsParserConfig: Record<string, unknown>;
84
    connectionSpecifier: string | undefined;
85
} {
125✔
86
    const {
125✔
87
        _: positionalAndUnknownArguments,
125✔
88
        // We don't make use of end of flag arguments but also don't want them to
89
        // end up alongside unknown arguments so we are extracting them and having a
90
        // no-op statement so ESLint does not complain.
91
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
92
        "--": _endOfFlagArguments,
125✔
93
        ...parsedUserAndArgsParserConfig
125✔
94
    } = argv(cliArguments, {
125✔
95
        ...OPTIONS,
125✔
96
        // This helps parse the relevant environment variables.
97
        envPrefix: "MDB_MCP_",
125✔
98
        configuration: {
125✔
99
            ...OPTIONS.configuration,
125✔
100
            // Setting this to true will populate `_` variable which is
101
            // originally used for positional arguments, now with the unknown
102
            // arguments as well. The order of arguments are maintained.
103
            "unknown-options-as-args": true,
125✔
104
            // To avoid populating `_` with end-of-flag arguments we explicitly
105
            // populate `--` variable and altogether ignore them later.
106
            "populate--": true,
125✔
107
        },
125✔
108
    });
125✔
109

110
    // A connectionSpecifier can be one of:
111
    // - database name
112
    // - host name
113
    // - ip address
114
    // - replica set specifier
115
    // - complete connection string
116
    let connectionSpecifier: string | undefined = undefined;
125✔
117
    const [maybeConnectionSpecifier, ...unknownArguments] = positionalAndUnknownArguments;
125✔
118

119
    if (typeof maybeConnectionSpecifier === "string" && isConnectionSpecifier(maybeConnectionSpecifier)) {
125✔
120
        connectionSpecifier = maybeConnectionSpecifier;
4✔
121
    } else if (maybeConnectionSpecifier !== undefined) {
125✔
122
        // If the extracted connection specifier is not a connection specifier
123
        // indeed, then we push it back to the unknown arguments list. This might
124
        // happen for example when an unknown argument is provided without ever
125
        // specifying a positional argument.
126
        unknownArguments.unshift(maybeConnectionSpecifier);
3✔
127
    }
3✔
128

129
    return {
125✔
130
        unknownCliArgumentErrors: unknownArguments
125✔
131
            .filter((argument): argument is string => typeof argument === "string" && argument.startsWith("--"))
125✔
132
            .map((argument) => {
125✔
133
                const argumentKey = argument.replace(/^(--)/, "");
3✔
134
                const matchingKey = matchingConfigKey(argumentKey);
3✔
135
                if (matchingKey) {
3✔
136
                    return `Error: Invalid command line argument '${argument}'. Did you mean '--${matchingKey}'?`;
2✔
137
                }
2✔
138

139
                return `Error: Invalid command line argument '${argument}'.`;
1✔
140
            }),
125✔
141
        deprecatedCliArgumentWarning: cliArguments.find((argument) => argument.startsWith("--connectionString"))
125✔
142
            ? "Warning: The --connectionString argument is deprecated. Prefer using the MDB_MCP_CONNECTION_STRING environment variable or the first positional argument for the connection string."
9✔
143
            : undefined,
116✔
144
        userAndArgsParserConfig: parsedUserAndArgsParserConfig,
125✔
145
        connectionSpecifier,
125✔
146
    };
125✔
147
}
125✔
148

149
function registerKnownSecretsInRootKeychain(userConfig: Partial<UserConfig>): void {
112✔
150
    const keychain = Keychain.root;
112✔
151

152
    const maybeRegister = (value: string | undefined, kind: Secret["kind"]): void => {
112✔
153
        if (value) {
1,344✔
154
            keychain.register(value, kind);
20✔
155
        }
20✔
156
    };
1,344✔
157

158
    maybeRegister(userConfig.apiClientId, "user");
112✔
159
    maybeRegister(userConfig.apiClientSecret, "password");
112✔
160
    maybeRegister(userConfig.awsAccessKeyId, "password");
112✔
161
    maybeRegister(userConfig.awsIamSessionToken, "password");
112✔
162
    maybeRegister(userConfig.awsSecretAccessKey, "password");
112✔
163
    maybeRegister(userConfig.awsSessionToken, "password");
112✔
164
    maybeRegister(userConfig.password, "password");
112✔
165
    maybeRegister(userConfig.tlsCAFile, "url");
112✔
166
    maybeRegister(userConfig.tlsCRLFile, "url");
112✔
167
    maybeRegister(userConfig.tlsCertificateKeyFile, "url");
112✔
168
    maybeRegister(userConfig.tlsCertificateKeyFilePassword, "password");
112✔
169
    maybeRegister(userConfig.username, "user");
112✔
170
}
112✔
171

172
function warnIfVectorSearchNotEnabledCorrectly(config: UserConfig, warn: (message: string) => void): void {
112✔
173
    const vectorSearchEnabled = config.previewFeatures.includes("vectorSearch");
112✔
174
    const embeddingsProviderConfigured = !!config.voyageApiKey;
112✔
175
    if (vectorSearchEnabled && !embeddingsProviderConfigured) {
112✔
176
        warn(`\
1✔
177
Warning: Vector search is enabled but no embeddings provider is configured.
178
- Set an embeddings provider configuration option to enable auto-embeddings during document insertion and text-based queries with $vectorSearch.\
179
`);
1✔
180
    }
1✔
181

182
    if (!vectorSearchEnabled && embeddingsProviderConfigured) {
112✔
183
        warn(`\
1✔
184
Warning: An embeddings provider is configured but the 'vectorSearch' preview feature is not enabled.
185
- Enable vector search by adding 'vectorSearch' to the 'previewFeatures' configuration option, or remove the embeddings provider configuration if not needed.\
186
`);
1✔
187
    }
1✔
188
}
112✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc