@@ -11,6 +11,7 @@ const EXPIRY_MS = 1000 * 60 * 60 * 12; // 12 hours
1111function sleep ( ms : number ) : Promise < void > {
1212 return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
1313}
14+
1415export class ConnectClusterTool extends AtlasToolBase {
1516 protected name = "atlas-connect-cluster" ;
1617 protected description = "Connect to MongoDB Atlas cluster" ;
@@ -20,9 +21,46 @@ export class ConnectClusterTool extends AtlasToolBase {
2021 clusterName : z . string ( ) . describe ( "Atlas cluster name" ) ,
2122 } ;
2223
23- protected async execute ( { projectId, clusterName } : ToolArgs < typeof this . argsShape > ) : Promise < CallToolResult > {
24- await this . session . disconnect ( ) ;
24+ private async queryConnection (
25+ projectId : string ,
26+ clusterName : string
27+ ) : Promise < "connected" | "disconnected" | "connecting" | "connected-to-other-cluster" | "unknown" > {
28+ if ( ! this . session . connectedAtlasCluster ) {
29+ if ( this . session . serviceProvider ) {
30+ return "connected-to-other-cluster" ;
31+ }
32+ return "disconnected" ;
33+ }
34+
35+ if (
36+ this . session . connectedAtlasCluster . projectId !== projectId ||
37+ this . session . connectedAtlasCluster . clusterName !== clusterName
38+ ) {
39+ return "connected-to-other-cluster" ;
40+ }
41+
42+ if ( ! this . session . serviceProvider ) {
43+ return "connecting" ;
44+ }
2545
46+ try {
47+ await this . session . serviceProvider . runCommand ( "admin" , {
48+ ping : 1 ,
49+ } ) ;
50+
51+ return "connected" ;
52+ } catch ( err : unknown ) {
53+ const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
54+ logger . debug (
55+ LogId . atlasConnectFailure ,
56+ "atlas-connect-cluster" ,
57+ `error querying cluster: ${ error . message } `
58+ ) ;
59+ return "unknown" ;
60+ }
61+ }
62+
63+ private async prepareClusterConnection ( projectId : string , clusterName : string ) : Promise < string > {
2664 const cluster = await inspectCluster ( this . session . apiClient , projectId , clusterName ) ;
2765
2866 if ( ! cluster . connectionString ) {
@@ -81,14 +119,32 @@ export class ConnectClusterTool extends AtlasToolBase {
81119 cn . username = username ;
82120 cn . password = password ;
83121 cn . searchParams . set ( "authSource" , "admin" ) ;
84- const connectionString = cn . toString ( ) ;
122+ return cn . toString ( ) ;
123+ }
85124
125+ private async connectToCluster ( projectId : string , clusterName : string , connectionString : string ) : Promise < void > {
86126 let lastError : Error | undefined = undefined ;
87127
88- for ( let i = 0 ; i < 20 ; i ++ ) {
128+ logger . debug (
129+ LogId . atlasConnectAttempt ,
130+ "atlas-connect-cluster" ,
131+ `attempting to connect to cluster: ${ this . session . connectedAtlasCluster ?. clusterName } `
132+ ) ;
133+
134+ // try to connect for about 5 minutes
135+ for ( let i = 0 ; i < 600 ; i ++ ) {
136+ if (
137+ ! this . session . connectedAtlasCluster ||
138+ this . session . connectedAtlasCluster . projectId != projectId ||
139+ this . session . connectedAtlasCluster . clusterName != clusterName
140+ ) {
141+ throw new Error ( "Cluster connection aborted" ) ;
142+ }
143+
89144 try {
90- await this . session . connectToMongoDB ( connectionString , this . config . connectOptions ) ;
91145 lastError = undefined ;
146+
147+ await this . session . connectToMongoDB ( connectionString , this . config . connectOptions ) ;
92148 break ;
93149 } catch ( err : unknown ) {
94150 const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
@@ -106,14 +162,94 @@ export class ConnectClusterTool extends AtlasToolBase {
106162 }
107163
108164 if ( lastError ) {
165+ if (
166+ this . session . connectedAtlasCluster ?. projectId == projectId &&
167+ this . session . connectedAtlasCluster ?. clusterName == clusterName &&
168+ this . session . connectedAtlasCluster ?. username
169+ ) {
170+ void this . session . apiClient
171+ . deleteDatabaseUser ( {
172+ params : {
173+ path : {
174+ groupId : this . session . connectedAtlasCluster . projectId ,
175+ username : this . session . connectedAtlasCluster . username ,
176+ databaseName : "admin" ,
177+ } ,
178+ } ,
179+ } )
180+ . catch ( ( err : unknown ) => {
181+ const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
182+ logger . debug (
183+ LogId . atlasConnectFailure ,
184+ "atlas-connect-cluster" ,
185+ `error deleting database user: ${ error . message } `
186+ ) ;
187+ } ) ;
188+ }
189+ this . session . connectedAtlasCluster = undefined ;
109190 throw lastError ;
110191 }
111192
193+ logger . debug (
194+ LogId . atlasConnectSucceeded ,
195+ "atlas-connect-cluster" ,
196+ `connected to cluster: ${ this . session . connectedAtlasCluster ?. clusterName } `
197+ ) ;
198+ }
199+
200+ protected async execute ( { projectId, clusterName } : ToolArgs < typeof this . argsShape > ) : Promise < CallToolResult > {
201+ for ( let i = 0 ; i < 60 ; i ++ ) {
202+ const state = await this . queryConnection ( projectId , clusterName ) ;
203+ switch ( state ) {
204+ case "connected" : {
205+ return {
206+ content : [
207+ {
208+ type : "text" ,
209+ text : `Connected to cluster "${ clusterName } ".` ,
210+ } ,
211+ ] ,
212+ } ;
213+ }
214+ case "connecting" : {
215+ break ;
216+ }
217+ case "connected-to-other-cluster" :
218+ case "disconnected" :
219+ case "unknown" :
220+ default : {
221+ await this . session . disconnect ( ) ;
222+ const connectionString = await this . prepareClusterConnection ( projectId , clusterName ) ;
223+
224+ // try to connect for about 5 minutes asynchronously
225+ void this . connectToCluster ( projectId , clusterName , connectionString ) . catch ( ( err : unknown ) => {
226+ const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
227+ logger . error (
228+ LogId . atlasConnectFailure ,
229+ "atlas-connect-cluster" ,
230+ `error connecting to cluster: ${ error . message } `
231+ ) ;
232+ } ) ;
233+ break ;
234+ }
235+ }
236+
237+ await sleep ( 500 ) ;
238+ }
239+
112240 return {
113241 content : [
114242 {
115- type : "text" ,
116- text : `Connected to cluster "${ clusterName } "` ,
243+ type : "text" as const ,
244+ text : `Attempting to connect to cluster "${ clusterName } "...` ,
245+ } ,
246+ {
247+ type : "text" as const ,
248+ text : `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.` ,
249+ } ,
250+ {
251+ type : "text" as const ,
252+ text : `Warning: Make sure your IP address was enabled in the allow list setting of the Atlas cluster.` ,
117253 } ,
118254 ] ,
119255 } ;
0 commit comments