1
1
import path from "path" ;
2
2
import os from "os" ;
3
3
import argv from "yargs-parser" ;
4
-
4
+ import type { CliOptions } from "@mongosh/arg-parser" ;
5
5
import { ReadConcernLevel , ReadPreferenceMode , W } from "mongodb" ;
6
6
7
+ // From: https://github.com/mongodb-js/mongosh/blob/main/packages/cli-repl/src/arg-parser.ts
8
+ const OPTIONS = {
9
+ string : [
10
+ "apiBaseUrl" ,
11
+ "apiClientId" ,
12
+ "apiClientSecret" ,
13
+ "connectionString" ,
14
+ "httpHost" ,
15
+ "httpPort" ,
16
+ "idleTimeoutMs" ,
17
+ "logPath" ,
18
+ "notificationTimeoutMs" ,
19
+ "telemetry" ,
20
+ "transport" ,
21
+ "apiVersion" ,
22
+ "authenticationDatabase" ,
23
+ "authenticationMechanism" ,
24
+ "browser" ,
25
+ "db" ,
26
+ "gssapiHostName" ,
27
+ "gssapiServiceName" ,
28
+ "host" ,
29
+ "oidcFlows" ,
30
+ "oidcRedirectUri" ,
31
+ "password" ,
32
+ "port" ,
33
+ "sslCAFile" ,
34
+ "sslCRLFile" ,
35
+ "sslCertificateSelector" ,
36
+ "sslDisabledProtocols" ,
37
+ "sslPEMKeyFile" ,
38
+ "sslPEMKeyPassword" ,
39
+ "sspiHostnameCanonicalization" ,
40
+ "sspiRealmOverride" ,
41
+ "tlsCAFile" ,
42
+ "tlsCRLFile" ,
43
+ "tlsCertificateKeyFile" ,
44
+ "tlsCertificateKeyFilePassword" ,
45
+ "tlsCertificateSelector" ,
46
+ "tlsDisabledProtocols" ,
47
+ "username" ,
48
+ ] ,
49
+ boolean : [
50
+ "apiDeprecationErrors" ,
51
+ "apiStrict" ,
52
+ "help" ,
53
+ "indexCheck" ,
54
+ "ipv6" ,
55
+ "nodb" ,
56
+ "oidcIdTokenAsAccessToken" ,
57
+ "oidcNoNonce" ,
58
+ "oidcTrustedEndpoint" ,
59
+ "readOnly" ,
60
+ "retryWrites" ,
61
+ "ssl" ,
62
+ "sslAllowInvalidCertificates" ,
63
+ "sslAllowInvalidHostnames" ,
64
+ "sslFIPSMode" ,
65
+ "tls" ,
66
+ "tlsAllowInvalidCertificates" ,
67
+ "tlsAllowInvalidHostnames" ,
68
+ "tlsFIPSMode" ,
69
+ "tlsUseSystemCA" ,
70
+ "version" ,
71
+ ] ,
72
+ array : [ "disabledTools" , "loggers" ] ,
73
+ alias : {
74
+ h : "help" ,
75
+ p : "password" ,
76
+ u : "username" ,
77
+ "build-info" : "buildInfo" ,
78
+ browser : "browser" ,
79
+ oidcDumpTokens : "oidcDumpTokens" ,
80
+ oidcRedirectUrl : "oidcRedirectUri" ,
81
+ oidcIDTokenAsAccessToken : "oidcIdTokenAsAccessToken" ,
82
+ } ,
83
+ configuration : {
84
+ "camel-case-expansion" : false ,
85
+ "unknown-options-as-args" : true ,
86
+ "parse-positional-numbers" : false ,
87
+ "parse-numbers" : false ,
88
+ "greedy-arrays" : true ,
89
+ "short-option-groups" : false ,
90
+ } ,
91
+ } ;
92
+
93
+ function isConnectionSpecifier ( arg : string | undefined ) : boolean {
94
+ return (
95
+ arg !== undefined &&
96
+ ( arg . startsWith ( "mongodb://" ) ||
97
+ arg . startsWith ( "mongodb+srv://" ) ||
98
+ ! ( arg . endsWith ( ".js" ) || arg . endsWith ( ".mongodb" ) ) )
99
+ ) ;
100
+ }
101
+
7
102
export interface ConnectOptions {
8
103
readConcern : ReadConcernLevel ;
9
104
readPreference : ReadPreferenceMode ;
@@ -13,7 +108,7 @@ export interface ConnectOptions {
13
108
14
109
// If we decide to support non-string config options, we'll need to extend the mechanism for parsing
15
110
// env variables.
16
- export interface UserConfig {
111
+ export interface UserConfig extends CliOptions {
17
112
apiBaseUrl : string ;
18
113
apiClientId ?: string ;
19
114
apiClientSecret ?: string ;
@@ -59,11 +154,11 @@ const defaults: UserConfig = {
59
154
notificationTimeoutMs : 540000 , // 9 minutes
60
155
} ;
61
156
62
- export const config = {
63
- ... defaults ,
64
- ... getEnvConfig ( ) ,
65
- ... getCliConfig ( ) ,
66
- } ;
157
+ export const config = setupUserConfig ( {
158
+ defaults,
159
+ cli : process . argv ,
160
+ env : process . env ,
161
+ } ) ;
67
162
68
163
function getLocalDataPath ( ) : string {
69
164
return process . platform === "win32"
@@ -83,7 +178,7 @@ function getExportsPath(): string {
83
178
// Gets the config supplied by the user as environment variables. The variable names
84
179
// are prefixed with `MDB_MCP_` and the keys match the UserConfig keys, but are converted
85
180
// to SNAKE_UPPER_CASE.
86
- function getEnvConfig ( ) : Partial < UserConfig > {
181
+ function parseEnvConfig ( env : Record < string , unknown > ) : Partial < UserConfig > {
87
182
function setValue ( obj : Record < string , unknown > , path : string [ ] , value : string ) : void {
88
183
const currentField = path . shift ( ) ;
89
184
if ( ! currentField ) {
@@ -120,7 +215,7 @@ function getEnvConfig(): Partial<UserConfig> {
120
215
}
121
216
122
217
const result : Record < string , unknown > = { } ;
123
- const mcpVariables = Object . entries ( process . env ) . filter (
218
+ const mcpVariables = Object . entries ( env ) . filter (
124
219
( [ key , value ] ) => value !== undefined && key . startsWith ( "MDB_MCP_" )
125
220
) as [ string , string ] [ ] ;
126
221
for ( const [ key , value ] of mcpVariables ) {
@@ -139,9 +234,72 @@ function SNAKE_CASE_toCamelCase(str: string): string {
139
234
return str . toLowerCase ( ) . replace ( / ( [ - _ ] [ a - z ] ) / g, ( group ) => group . toUpperCase ( ) . replace ( "_" , "" ) ) ;
140
235
}
141
236
142
- // Reads the cli args and parses them into a UserConfig object.
143
- function getCliConfig ( ) : Partial < UserConfig > {
144
- return argv ( process . argv . slice ( 2 ) , {
145
- array : [ "disabledTools" , "loggers" ] ,
146
- } ) as unknown as Partial < UserConfig > ;
237
+ // Right now we have arguments that are not compatible with the format used in mongosh.
238
+ // An example is using --connectionString and positional arguments.
239
+ // We will consolidate them in a way where the mongosh format takes precedence.
240
+ // We will warn users that previous configuration is deprecated in favour of
241
+ // whatever is in mongosh.
242
+ function parseCliConfig ( args : string [ ] ) : CliOptions {
243
+ const programArgs = args . slice ( 2 ) ;
244
+ const parsed = argv ( programArgs , OPTIONS ) as unknown as CliOptions &
245
+ UserConfig & {
246
+ _ ?: string [ ] ;
247
+ } ;
248
+
249
+ const positionalArguments = parsed . _ ?? [ ] ;
250
+ if ( ! parsed . nodb && isConnectionSpecifier ( positionalArguments [ 0 ] ) ) {
251
+ parsed . connectionSpecifier = positionalArguments . shift ( ) ;
252
+ }
253
+
254
+ delete parsed . _ ;
255
+ return parsed ;
256
+ }
257
+
258
+ function commaSeparatedToArray < T extends string [ ] > ( str : string | string [ ] | undefined ) : T {
259
+ if ( str === undefined ) {
260
+ return [ ] as unknown as T ;
261
+ }
262
+
263
+ if ( ! Array . isArray ( str ) ) {
264
+ return [ str ] as T ;
265
+ }
266
+
267
+ if ( str . length === 0 ) {
268
+ return str as T ;
269
+ }
270
+
271
+ if ( str . length === 1 ) {
272
+ if ( str [ 0 ] ?. indexOf ( "," ) === - 1 ) {
273
+ return str as T ;
274
+ } else {
275
+ return str [ 0 ] ?. split ( "," ) . map ( ( e ) => e . trim ( ) ) as T ;
276
+ }
277
+ }
278
+
279
+ return str as T ;
280
+ }
281
+
282
+ export function setupUserConfig ( {
283
+ cli,
284
+ env,
285
+ defaults,
286
+ } : {
287
+ cli : string [ ] ;
288
+ env : Record < string , unknown > ;
289
+ defaults : Partial < UserConfig > ;
290
+ } ) : UserConfig {
291
+ const userConfig : UserConfig = {
292
+ ...defaults ,
293
+ ...parseEnvConfig ( env ) ,
294
+ ...parseCliConfig ( cli ) ,
295
+ } as UserConfig ;
296
+
297
+ userConfig . disabledTools = commaSeparatedToArray ( userConfig . disabledTools ) ;
298
+ userConfig . loggers = commaSeparatedToArray ( userConfig . loggers ) ;
299
+
300
+ if ( userConfig . connectionString && userConfig . connectionSpecifier ) {
301
+ delete userConfig . connectionString ;
302
+ }
303
+
304
+ return userConfig ;
147
305
}
0 commit comments