@@ -4,21 +4,34 @@ import argv from "yargs-parser";
44
55import packageJson from "../package.json" with { type : "json" } ;
66import fs from "fs" ;
7+ import { ReadConcernLevel , ReadPreferenceMode , W } from "mongodb" ;
78const { localDataPath, configPath } = getLocalDataPath ( ) ;
89
910// If we decide to support non-string config options, we'll need to extend the mechanism for parsing
1011// env variables.
11- interface UserConfig extends Record < string , string | undefined > {
12+ interface UserConfig {
1213 apiBaseUrl : string ;
1314 clientId : string ;
1415 stateFile : string ;
1516 connectionString ?: string ;
17+ connectOptions : {
18+ readConcern : ReadConcernLevel ;
19+ readPreference : ReadPreferenceMode ;
20+ writeConcern : W ;
21+ timeoutMS : number ;
22+ } ;
1623}
1724
1825const defaults : UserConfig = {
1926 apiBaseUrl : "https://cloud.mongodb.com/" ,
2027 clientId : "0oabtxactgS3gHIR0297" ,
2128 stateFile : path . join ( localDataPath , "state.json" ) ,
29+ connectOptions : {
30+ readConcern : "local" ,
31+ readPreference : "secondaryPreferred" ,
32+ writeConcern : "majority" ,
33+ timeoutMS : 30_000 ,
34+ } ,
2235} ;
2336
2437const mergedUserConfig = {
@@ -66,21 +79,52 @@ function getLocalDataPath(): { localDataPath: string; configPath: string } {
6679// are prefixed with `MDB_MCP_` and the keys match the UserConfig keys, but are converted
6780// to SNAKE_UPPER_CASE.
6881function getEnvConfig ( ) : Partial < UserConfig > {
69- const camelCaseToSNAKE_UPPER_CASE = ( str : string ) : string => {
70- return str . replace ( / ( [ a - z ] ) ( [ A - Z ] ) / g, "$1_$2" ) . toUpperCase ( ) ;
71- } ;
82+ function setValue ( obj : Record < string , unknown > , path : string [ ] , value : string ) : void {
83+ const currentField = path . shift ( ) ! ;
84+ if ( path . length === 0 ) {
85+ const numberValue = Number ( value ) ;
86+ if ( ! isNaN ( numberValue ) ) {
87+ obj [ currentField ] = numberValue ;
88+ return ;
89+ }
90+
91+ const booleanValue = value . toLocaleLowerCase ( ) ;
92+ if ( booleanValue === "true" || booleanValue === "false" ) {
93+ obj [ currentField ] = booleanValue === "true" ;
94+ return ;
95+ }
96+
97+ obj [ currentField ] = value ;
98+ return ;
99+ }
72100
73- const result : Partial < UserConfig > = { } ;
74- for ( const key of Object . keys ( defaults ) ) {
75- const envVarName = `MDB_MCP_${ camelCaseToSNAKE_UPPER_CASE ( key ) } ` ;
76- if ( process . env [ envVarName ] ) {
77- result [ key ] = process . env [ envVarName ] ;
101+ if ( ! obj [ currentField ] ) {
102+ obj [ currentField ] = { } ;
78103 }
104+
105+ setValue ( obj [ currentField ] as Record < string , unknown > , path , value ) ;
106+ }
107+
108+ const result : Record < string , unknown > = { } ;
109+ const mcpVariables = Object . entries ( process . env ) . filter (
110+ ( [ key , value ] ) => value !== undefined && key . startsWith ( "MDB_MCP_" )
111+ ) as [ string , string ] [ ] ;
112+ for ( const [ key , value ] of mcpVariables ) {
113+ const fieldPath = key
114+ . replace ( "MDB_MCP_" , "" )
115+ . split ( "." )
116+ . map ( ( part ) => SNAKE_CASE_toCamelCase ( part ) ) ;
117+
118+ setValue ( result , fieldPath , value ) ;
79119 }
80120
81121 return result ;
82122}
83123
124+ function SNAKE_CASE_toCamelCase ( str : string ) : string {
125+ return str . toLowerCase ( ) . replace ( / ( [ - _ ] [ a - z ] ) / g, ( group ) => group . toUpperCase ( ) . replace ( "_" , "" ) ) ;
126+ }
127+
84128// Gets the config supplied by the user as a JSON file. The file is expected to be located in the local data path
85129// and named `config.json`.
86130function getFileConfig ( ) : Partial < UserConfig > {
0 commit comments