Skip to content

Commit 4274f1b

Browse files
committed
fix: move connect to always wait 30secs
1 parent 693d31c commit 4274f1b

File tree

3 files changed

+71
-86
lines changed

3 files changed

+71
-86
lines changed

src/tools/atlas/metadata/connectCluster.ts

Lines changed: 52 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ export class ConnectClusterTool extends AtlasToolBase {
2424
private async queryConnection(
2525
projectId: string,
2626
clusterName: string
27-
): Promise<"connected" | "disconnected" | "connecting" | "connected-to-other-cluster"> {
27+
): Promise<"connected" | "disconnected" | "connecting" | "connected-to-other-cluster" | "unknown"> {
2828
if (!this.session.connectedAtlasCluster) {
29+
if (this.session.serviceProvider) {
30+
return "connected-to-other-cluster";
31+
}
2932
return "disconnected";
3033
}
3134

@@ -40,10 +43,21 @@ export class ConnectClusterTool extends AtlasToolBase {
4043
return "connecting";
4144
}
4245

43-
await this.session.serviceProvider.runCommand("admin", {
44-
ping: 1,
45-
});
46-
return "connected";
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+
}
4761
}
4862

4963
private async prepareClusterConnection(projectId: string, clusterName: string): Promise<string> {
@@ -111,8 +125,7 @@ export class ConnectClusterTool extends AtlasToolBase {
111125
private async connectToCluster(
112126
projectId: string,
113127
clusterName: string,
114-
connectionString: string,
115-
tryCount: number
128+
connectionString: string
116129
): Promise<void> {
117130
let lastError: Error | undefined = undefined;
118131

@@ -122,7 +135,8 @@ export class ConnectClusterTool extends AtlasToolBase {
122135
`attempting to connect to cluster: ${this.session.connectedAtlasCluster?.clusterName}`
123136
);
124137

125-
for (let i = 0; i < tryCount; i++) {
138+
// try to connect for about 5 minutes
139+
for (let i = 0; i < 600; i++) {
126140
if (
127141
!this.session.connectedAtlasCluster ||
128142
this.session.connectedAtlasCluster.projectId != projectId ||
@@ -185,89 +199,53 @@ export class ConnectClusterTool extends AtlasToolBase {
185199
}
186200

187201
protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
188-
const connectingResult = {
189-
content: [
190-
{
191-
type: "text" as const,
192-
text: `Attempting to connect to cluster "${clusterName}"...`,
193-
},
194-
{
195-
type: "text" as const,
196-
text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`,
197-
},
198-
],
199-
};
200-
201-
try {
202+
for (let i = 0; i < 60; i++) {
202203
const state = await this.queryConnection(projectId, clusterName);
203204
switch (state) {
204205
case "connected":
205206
return {
206207
content: [
207208
{
208209
type: "text",
209-
text: "Cluster is already connected.",
210+
text: `Connected to cluster "${clusterName}".`,
210211
},
211212
],
212213
};
213214
case "connecting":
214-
return connectingResult;
215+
break;
215216
case "connected-to-other-cluster":
216217
case "disconnected":
218+
case "unknown":
217219
default:
218-
// fall through to create new connection
220+
await this.session.disconnect();
221+
const connectionString = await this.prepareClusterConnection(projectId, clusterName);
222+
223+
// try to connect for about 5 minutes asynchronously
224+
void this.connectToCluster(projectId, clusterName, connectionString).catch((err: unknown) => {
225+
const error = err instanceof Error ? err : new Error(String(err));
226+
logger.error(
227+
LogId.atlasConnectFailure,
228+
"atlas-connect-cluster",
229+
`error connecting to cluster: ${error.message}`
230+
);
231+
});
219232
break;
220233
}
221-
} catch (err: unknown) {
222-
const error = err instanceof Error ? err : new Error(String(err));
223-
logger.debug(
224-
LogId.atlasConnectFailure,
225-
"atlas-connect-cluster",
226-
`error querying cluster: ${error.message}`
227-
);
228-
// fall through to create new connection
229-
}
230-
231-
await this.session.disconnect();
232-
const connectionString = await this.prepareClusterConnection(projectId, clusterName);
233-
234-
try {
235-
// First, try to connect to the cluster within the current tool call.
236-
// We give it 60 attempts with 500 ms delay between each, so ~30 seconds
237-
await this.connectToCluster(projectId, clusterName, connectionString, 60);
238-
239-
return {
240-
content: [
241-
{
242-
type: "text",
243-
text: `Connected to cluster "${clusterName}".`,
244-
},
245-
],
246-
};
247-
} catch (err: unknown) {
248-
const error = err instanceof Error ? err : new Error(String(err));
249-
logger.debug(
250-
LogId.atlasConnectFailure,
251-
"atlas-connect-cluster",
252-
`error connecting to cluster: ${error.message}`
253-
);
254-
255-
// We couldn't connect in ~30 seconds, likely because user creation is taking longer.
256-
// Retry the connection with longer timeout (~5 minutes), while also returning a response
257-
// to the client. Many clients will have a 1 minute timeout for tool calls, so we want to
258-
// return well before that.
259-
//
260-
// Once we add support for streamable http, we'd want to use progress notifications here.
261-
void this.connectToCluster(projectId, clusterName, connectionString, 600).catch((err) => {
262-
const error = err instanceof Error ? err : new Error(String(err));
263-
logger.debug(
264-
LogId.atlasConnectFailure,
265-
"atlas-connect-cluster",
266-
`error connecting to cluster: ${error.message}`
267-
);
268-
});
269234

270-
return connectingResult;
235+
await sleep(500);
271236
}
237+
238+
return {
239+
content: [
240+
{
241+
type: "text" as const,
242+
text: `Attempting to connect to cluster "${clusterName}"...`,
243+
},
244+
{
245+
type: "text" as const,
246+
text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`,
247+
},
248+
],
249+
};
272250
}
273251
}

src/tools/mongodb/mongodbTool.ts

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,27 @@ export abstract class MongoDBToolBase extends ToolBase {
1414
protected category: ToolCategory = "mongodb";
1515

1616
protected async ensureConnected(): Promise<NodeDriverServiceProvider> {
17-
if (!this.session.serviceProvider && this.config.connectionString) {
18-
try {
19-
await this.connectToMongoDB(this.config.connectionString);
20-
} catch (error) {
21-
logger.error(
22-
LogId.mongodbConnectFailure,
23-
"mongodbTool",
24-
`Failed to connect to MongoDB instance using the connection string from the config: ${error as string}`
17+
if (!this.session.serviceProvider) {
18+
if (this.session.connectedAtlasCluster) {
19+
throw new MongoDBError(
20+
ErrorCodes.NotConnectedToMongoDB,
21+
`Attempting to connect to Atlas cluster "${this.session.connectedAtlasCluster.clusterName}", try again in a few seconds.`
2522
);
26-
throw new MongoDBError(ErrorCodes.MisconfiguredConnectionString, "Not connected to MongoDB.");
2723
}
28-
}
2924

30-
if (!this.session.serviceProvider) {
25+
if (this.config.connectionString) {
26+
try {
27+
await this.connectToMongoDB(this.config.connectionString);
28+
} catch (error) {
29+
logger.error(
30+
LogId.mongodbConnectFailure,
31+
"mongodbTool",
32+
`Failed to connect to MongoDB instance using the connection string from the config: ${error as string}`
33+
);
34+
throw new MongoDBError(ErrorCodes.MisconfiguredConnectionString, "Not connected to MongoDB.");
35+
}
36+
}
37+
3138
throw new MongoDBError(ErrorCodes.NotConnectedToMongoDB, "Not connected to MongoDB");
3239
}
3340

tests/integration/tools/atlas/clusters.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ describeWithAtlas("clusters", (integration) => {
183183
it("connects to cluster", async () => {
184184
const projectId = getProjectId();
185185

186-
for (let i = 0; i < 600; i++) {
186+
for (let i = 0; i < 10; i++) {
187187
const response = (await integration.mcpClient().callTool({
188188
name: "atlas-connect-cluster",
189189
arguments: { projectId, clusterName },

0 commit comments

Comments
 (0)