@@ -11,16 +11,36 @@ 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" ;
16- protected description = "Connect to MongoDB Atlas cluster" ;
17+ protected description = "Connect to/Query status of MongoDB Atlas cluster" ;
1718 protected operationType : OperationType = "metadata" ;
1819 protected argsShape = {
1920 projectId : z . string ( ) . describe ( "Atlas project ID" ) ,
2021 clusterName : z . string ( ) . describe ( "Atlas cluster name" ) ,
2122 } ;
2223
23- protected async execute ( { projectId, clusterName } : ToolArgs < typeof this . argsShape > ) : Promise < CallToolResult > {
24+ private async queryConnection ( projectId : string , clusterName : string ) : Promise < "connected" | "disconnected" | "connecting" | "connected-to-other-cluster" > {
25+ if ( ! this . session . connectedAtlasCluster ) {
26+ return "disconnected" ;
27+ }
28+
29+ if ( this . session . connectedAtlasCluster . projectId !== projectId || this . session . connectedAtlasCluster . clusterName !== clusterName ) {
30+ return "connected-to-other-cluster" ;
31+ }
32+
33+ if ( ! this . session . serviceProvider ) {
34+ return "connecting" ;
35+ }
36+
37+ await this . session . serviceProvider . runCommand ( "admin" , {
38+ ping : 1 ,
39+ } ) ;
40+ return "connected" ;
41+ }
42+
43+ private async prepareClusterConnection ( projectId : string , clusterName : string ) : Promise < string > {
2444 await this . session . disconnect ( ) ;
2545
2646 const cluster = await inspectCluster ( this . session . apiClient , projectId , clusterName ) ;
@@ -83,9 +103,13 @@ export class ConnectClusterTool extends AtlasToolBase {
83103 cn . searchParams . set ( "authSource" , "admin" ) ;
84104 const connectionString = cn . toString ( ) ;
85105
106+ return connectionString ;
107+ }
108+
109+ private async connectToCluster ( connectionString : string ) : Promise < void > {
86110 let lastError : Error | undefined = undefined ;
87111
88- for ( let i = 0 ; i < 20 ; i ++ ) {
112+ for ( let i = 0 ; i < 600 ; i ++ ) { // try for 5 minutes
89113 try {
90114 await this . session . connectToMongoDB ( connectionString , this . config . connectOptions ) ;
91115 lastError = undefined ;
@@ -104,16 +128,81 @@ export class ConnectClusterTool extends AtlasToolBase {
104128 await sleep ( 500 ) ; // wait for 500ms before retrying
105129 }
106130 }
107-
131+
108132 if ( lastError ) {
133+ void this . session . apiClient . deleteDatabaseUser ( {
134+ params : {
135+ path : {
136+ groupId : this . session . connectedAtlasCluster ?. projectId || "" ,
137+ username : this . session . connectedAtlasCluster ?. username || "" ,
138+ databaseName : "admin" ,
139+ } ,
140+ } ,
141+ } ) . catch ( ( err : unknown ) => {
142+ const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
143+ logger . debug (
144+ LogId . atlasConnectFailure ,
145+ "atlas-connect-cluster" ,
146+ `error deleting database user: ${ error . message } `
147+ ) ;
148+ } ) ;
149+ this . session . connectedAtlasCluster = undefined ;
109150 throw lastError ;
110151 }
152+ }
153+
154+ protected async execute ( { projectId, clusterName } : ToolArgs < typeof this . argsShape > ) : Promise < CallToolResult > {
155+ try {
156+ const state = await this . queryConnection ( projectId , clusterName ) ;
157+ switch ( state ) {
158+ case "connected" :
159+ return {
160+ content : [
161+ {
162+ type : "text" ,
163+ text : "Cluster is already connected." ,
164+ } ,
165+ ] ,
166+ } ;
167+ case "connecting" :
168+ return {
169+ content : [
170+ {
171+ type : "text" ,
172+ text : "Cluster is connecting..." ,
173+ } ,
174+ ] ,
175+ } ;
176+ }
177+ } catch ( err : unknown ) {
178+ const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
179+ logger . debug (
180+ LogId . atlasConnectFailure ,
181+ "atlas-connect-cluster" ,
182+ `error querying cluster: ${ error . message } `
183+ ) ;
184+ // fall through to create new connection
185+ }
186+
187+ const connectionString = await this . prepareClusterConnection ( projectId , clusterName ) ;
188+ process . nextTick ( async ( ) => {
189+ try {
190+ await this . connectToCluster ( connectionString ) ;
191+ } catch ( err : unknown ) {
192+ const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
193+ logger . debug (
194+ LogId . atlasConnectFailure ,
195+ "atlas-connect-cluster" ,
196+ `error connecting to cluster: ${ error . message } `
197+ ) ;
198+ }
199+ } ) ;
111200
112201 return {
113202 content : [
114203 {
115204 type : "text" ,
116- text : `Connected to cluster "${ clusterName } "` ,
205+ text : `Connecting to cluster "${ clusterName } "... ` ,
117206 } ,
118207 ] ,
119208 } ;
0 commit comments