@@ -11,6 +11,7 @@ const EXPIRY_MS = 1000 * 60 * 60 * 12; // 12 hours
11
11
function sleep ( ms : number ) : Promise < void > {
12
12
return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
13
13
}
14
+
14
15
export class ConnectClusterTool extends AtlasToolBase {
15
16
protected name = "atlas-connect-cluster" ;
16
17
protected description = "Connect to MongoDB Atlas cluster" ;
@@ -20,9 +21,46 @@ export class ConnectClusterTool extends AtlasToolBase {
20
21
clusterName : z . string ( ) . describe ( "Atlas cluster name" ) ,
21
22
} ;
22
23
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
+ }
25
45
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 > {
26
64
const cluster = await inspectCluster ( this . session . apiClient , projectId , clusterName ) ;
27
65
28
66
if ( ! cluster . connectionString ) {
@@ -81,14 +119,32 @@ export class ConnectClusterTool extends AtlasToolBase {
81
119
cn . username = username ;
82
120
cn . password = password ;
83
121
cn . searchParams . set ( "authSource" , "admin" ) ;
84
- const connectionString = cn . toString ( ) ;
122
+ return cn . toString ( ) ;
123
+ }
85
124
125
+ private async connectToCluster ( projectId : string , clusterName : string , connectionString : string ) : Promise < void > {
86
126
let lastError : Error | undefined = undefined ;
87
127
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
+
89
144
try {
90
- await this . session . connectToMongoDB ( connectionString , this . config . connectOptions ) ;
91
145
lastError = undefined ;
146
+
147
+ await this . session . connectToMongoDB ( connectionString , this . config . connectOptions ) ;
92
148
break ;
93
149
} catch ( err : unknown ) {
94
150
const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
@@ -106,14 +162,94 @@ export class ConnectClusterTool extends AtlasToolBase {
106
162
}
107
163
108
164
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 ;
109
190
throw lastError ;
110
191
}
111
192
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
+
112
240
return {
113
241
content : [
114
242
{
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.` ,
117
253
} ,
118
254
] ,
119
255
} ;
0 commit comments