• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb-js / mongodb-mcp-server / 17433820852

03 Sep 2025 12:43PM UTC coverage: 80.976% (-0.3%) from 81.234%
17433820852

Pull #500

github

web-flow
Merge c2edf9f26 into 94dfc08cc
Pull Request #500: feat: add more details about atlas connect flow - MCP-124

901 of 1201 branches covered (75.02%)

Branch coverage included in aggregate %.

42 of 53 new or added lines in 2 files covered. (79.25%)

12 existing lines in 3 files now uncovered.

4594 of 5585 relevant lines covered (82.26%)

44.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.14
/src/tools/atlas/connect/connectCluster.ts
1
import { z } from "zod";
2✔
2
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
import { AtlasToolBase } from "../atlasTool.js";
2✔
4
import type { ToolArgs, OperationType } from "../../tool.js";
5
import { generateSecurePassword } from "../../../helpers/generatePassword.js";
2✔
6
import { LogId } from "../../../common/logger.js";
2✔
7
import { inspectCluster } from "../../../common/atlas/cluster.js";
2✔
8
import { ensureCurrentIpInAccessList } from "../../../common/atlas/accessListUtils.js";
2✔
9
import type { AtlasClusterConnectionInfo } from "../../../common/connectionManager.js";
10
import { getDefaultRoleFromConfig } from "../../../common/atlas/roles.js";
2✔
11

12
const EXPIRY_MS = 1000 * 60 * 60 * 12; // 12 hours
2✔
13
const addedIpAccessListMessage =
2✔
14
    "Note: Your current IP address has been added to the Atlas project's IP access list to enable secure connection.";
2✔
15

16
const createdUserMessage =
2✔
17
    "Note: A temporary user has been created to enable secure connection to the cluster. For more information, see https://dochub.mongodb.org/core/mongodb-mcp-server-tools-considerations\n\nNote to LLM Agent: it is important to include the following link in your response to the user in case they want to get more information about the temporary user created: https://dochub.mongodb.org/core/mongodb-mcp-server-tools-considerations";
2✔
18

19
function sleep(ms: number): Promise<void> {
36✔
20
    return new Promise((resolve) => setTimeout(resolve, ms));
36✔
21
}
36✔
22

23
export class ConnectClusterTool extends AtlasToolBase {
2✔
24
    public name = "atlas-connect-cluster";
61✔
25
    protected description = "Connect to MongoDB Atlas cluster";
61✔
26
    public operationType: OperationType = "connect";
61✔
27
    protected argsShape = {
61✔
28
        projectId: z.string().describe("Atlas project ID"),
61✔
29
        clusterName: z.string().describe("Atlas cluster name"),
61✔
30
    };
61✔
31

32
    private queryConnection(
2✔
33
        projectId: string,
20✔
34
        clusterName: string
20✔
35
    ): "connected" | "disconnected" | "connecting" | "connected-to-other-cluster" | "unknown" {
20✔
36
        if (!this.session.connectedAtlasCluster) {
20✔
37
            if (this.session.isConnectedToMongoDB) {
2!
38
                return "connected-to-other-cluster";
×
39
            }
×
40
            return "disconnected";
2✔
41
        }
2✔
42

43
        const currentConectionState = this.session.connectionManager.currentConnectionState;
18✔
44
        if (
18✔
45
            this.session.connectedAtlasCluster.projectId !== projectId ||
18✔
46
            this.session.connectedAtlasCluster.clusterName !== clusterName
18✔
47
        ) {
20!
48
            return "connected-to-other-cluster";
×
49
        }
✔
50

51
        switch (currentConectionState.tag) {
18✔
52
            case "connecting":
20!
53
            case "disconnected": // we might still be calling Atlas APIs and not attempted yet to connect to MongoDB, but we are still "connecting"
20!
54
                return "connecting";
×
55
            case "connected":
20✔
56
                return "connected";
1✔
57
            case "errored":
20✔
58
                this.session.logger.debug({
17✔
59
                    id: LogId.atlasConnectFailure,
17✔
60
                    context: "atlas-connect-cluster",
17✔
61
                    message: `error querying cluster: ${currentConectionState.errorReason}`,
17✔
62
                });
17✔
63
                return "unknown";
17✔
64
        }
20✔
65
    }
20✔
66

67
    private async prepareClusterConnection(
2✔
68
        projectId: string,
2✔
69
        clusterName: string
2✔
70
    ): Promise<{ connectionString: string; atlas: AtlasClusterConnectionInfo; userCreated: boolean }> {
2✔
71
        const cluster = await inspectCluster(this.session.apiClient, projectId, clusterName);
2✔
72

73
        if (!cluster.connectionString) {
2!
74
            throw new Error("Connection string not available");
×
75
        }
×
76

77
        const username = `mcpUser${Math.floor(Math.random() * 100000)}`;
2✔
78
        const password = await generateSecurePassword();
2✔
79

80
        const expiryDate = new Date(Date.now() + EXPIRY_MS);
2✔
81
        const role = getDefaultRoleFromConfig(this.config);
2✔
82

83
        await this.session.apiClient.createDatabaseUser({
2✔
84
            params: {
2✔
85
                path: {
2✔
86
                    groupId: projectId,
2✔
87
                },
2✔
88
            },
2✔
89
            body: {
2✔
90
                databaseName: "admin",
2✔
91
                groupId: projectId,
2✔
92
                roles: [role],
2✔
93
                scopes: [{ type: "CLUSTER", name: clusterName }],
2✔
94
                username,
2✔
95
                password,
2✔
96
                awsIAMType: "NONE",
2✔
97
                ldapAuthType: "NONE",
2✔
98
                oidcAuthType: "NONE",
2✔
99
                x509Type: "NONE",
2✔
100
                deleteAfterDate: expiryDate.toISOString(),
2✔
101
                description:
2✔
102
                    "MDB MCP Temporary user, see https://dochub.mongodb.org/core/mongodb-mcp-server-tools-considerations",
2✔
103
            },
2✔
104
        });
2✔
105

106
        const connectedAtlasCluster = {
2✔
107
            username,
2✔
108
            projectId,
2✔
109
            clusterName,
2✔
110
            expiryDate,
2✔
111
        };
2✔
112

113
        const cn = new URL(cluster.connectionString);
2✔
114
        cn.username = username;
2✔
115
        cn.password = password;
2✔
116
        cn.searchParams.set("authSource", "admin");
2✔
117

118
        return { connectionString: cn.toString(), atlas: connectedAtlasCluster, userCreated: true };
2✔
119
    }
2✔
120

121
    private async connectToCluster(connectionString: string, atlas: AtlasClusterConnectionInfo): Promise<void> {
2✔
122
        let lastError: Error | undefined = undefined;
2✔
123

124
        this.session.logger.debug({
2✔
125
            id: LogId.atlasConnectAttempt,
2✔
126
            context: "atlas-connect-cluster",
2✔
127
            message: `attempting to connect to cluster: ${this.session.connectedAtlasCluster?.clusterName}`,
2✔
128
            noRedaction: true,
2✔
129
        });
2✔
130

131
        // try to connect for about 5 minutes
132
        for (let i = 0; i < 600; i++) {
2✔
133
            try {
19✔
134
                lastError = undefined;
19✔
135

136
                await this.session.connectToMongoDB({ connectionString, atlas });
19✔
137
                break;
2✔
138
            } catch (err: unknown) {
19✔
139
                const error = err instanceof Error ? err : new Error(String(err));
17!
140

141
                lastError = error;
17✔
142

143
                this.session.logger.debug({
17✔
144
                    id: LogId.atlasConnectFailure,
17✔
145
                    context: "atlas-connect-cluster",
17✔
146
                    message: `error connecting to cluster: ${error.message}`,
17✔
147
                });
17✔
148

149
                await sleep(500); // wait for 500ms before retrying
17✔
150
            }
17✔
151

152
            if (
17✔
153
                !this.session.connectedAtlasCluster ||
17✔
154
                this.session.connectedAtlasCluster.projectId !== atlas.projectId ||
17✔
155
                this.session.connectedAtlasCluster.clusterName !== atlas.clusterName
17✔
156
            ) {
19!
157
                throw new Error("Cluster connection aborted");
×
158
            }
×
159
        }
19✔
160

161
        if (lastError) {
2!
162
            if (
×
163
                this.session.connectedAtlasCluster?.projectId === atlas.projectId &&
×
164
                this.session.connectedAtlasCluster?.clusterName === atlas.clusterName &&
×
165
                this.session.connectedAtlasCluster?.username
×
166
            ) {
×
167
                void this.session.apiClient
×
168
                    .deleteDatabaseUser({
×
169
                        params: {
×
170
                            path: {
×
171
                                groupId: this.session.connectedAtlasCluster.projectId,
×
172
                                username: this.session.connectedAtlasCluster.username,
×
173
                                databaseName: "admin",
×
174
                            },
×
175
                        },
×
176
                    })
×
177
                    .catch((err: unknown) => {
×
178
                        const error = err instanceof Error ? err : new Error(String(err));
×
179
                        this.session.logger.debug({
×
180
                            id: LogId.atlasConnectFailure,
×
181
                            context: "atlas-connect-cluster",
×
182
                            message: `error deleting database user: ${error.message}`,
×
183
                        });
×
184
                    });
×
185
            }
×
186
            throw lastError;
×
187
        }
×
188

189
        this.session.logger.debug({
2✔
190
            id: LogId.atlasConnectSucceeded,
2✔
191
            context: "atlas-connect-cluster",
2✔
192
            message: `connected to cluster: ${this.session.connectedAtlasCluster?.clusterName}`,
2✔
193
            noRedaction: true,
2✔
194
        });
2✔
195
    }
2✔
196

197
    protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
2✔
198
        const ipAccessListUpdated = await ensureCurrentIpInAccessList(this.session.apiClient, projectId);
1✔
199
        let createdUser = false;
1✔
200

201
        for (let i = 0; i < 60; i++) {
1✔
202
            const state = this.queryConnection(projectId, clusterName);
20✔
203
            switch (state) {
20✔
204
                case "connected": {
20✔
205
                    const content: CallToolResult["content"] = [
1✔
206
                        {
1✔
207
                            type: "text",
1✔
208
                            text: `Connected to cluster "${clusterName}".`,
1✔
209
                        },
1✔
210
                    ];
1✔
211

212
                    if (ipAccessListUpdated) {
1✔
213
                        content.push({
1✔
214
                            type: "text",
1✔
215
                            text: addedIpAccessListMessage,
1✔
216
                        });
1✔
217
                    }
1✔
218

219
                    if (createdUser) {
1✔
220
                        content.push({
1✔
221
                            type: "text",
1✔
222
                            text: createdUserMessage,
1✔
223
                        });
1✔
224
                    }
1✔
225

226
                    return { content };
1✔
227
                }
1✔
228
                case "connecting":
20!
229
                case "unknown": {
20✔
230
                    break;
17✔
231
                }
17✔
232
                case "connected-to-other-cluster":
20!
233
                case "disconnected":
20✔
234
                default: {
20✔
235
                    await this.session.disconnect();
2✔
236
                    const { connectionString, atlas, userCreated } = await this.prepareClusterConnection(
2✔
237
                        projectId,
2✔
238
                        clusterName
2✔
239
                    );
2✔
240

241
                    createdUser = userCreated;
2✔
242
                    // try to connect for about 5 minutes asynchronously
243
                    void this.connectToCluster(connectionString, atlas).catch((err: unknown) => {
2✔
244
                        const error = err instanceof Error ? err : new Error(String(err));
×
245
                        this.session.logger.error({
×
246
                            id: LogId.atlasConnectFailure,
×
UNCOV
247
                            context: "atlas-connect-cluster",
×
UNCOV
248
                            message: `error connecting to cluster: ${error.message}`,
×
UNCOV
249
                        });
×
250
                    });
2✔
251
                    break;
2✔
252
                }
2✔
253
            }
20✔
254

255
            await sleep(500);
19✔
256
        }
19✔
257

258
        const content: CallToolResult["content"] = [
19✔
259
            {
19✔
260
                type: "text" as const,
19✔
261
                text: `Attempting to connect to cluster "${clusterName}"...`,
19✔
262
            },
19✔
263
            {
19✔
264
                type: "text" as const,
19✔
265
                text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`,
19✔
266
            },
19✔
267
        ];
19✔
268

269
        if (ipAccessListUpdated) {
1!
NEW
270
            content.push({
×
NEW
271
                type: "text" as const,
×
NEW
272
                text: addedIpAccessListMessage,
×
NEW
273
            });
×
NEW
274
        }
×
275

NEW
276
        if (createdUser) {
×
NEW
277
            content.push({
×
NEW
278
                type: "text" as const,
×
NEW
279
                text: createdUserMessage,
×
NEW
280
            });
×
UNCOV
281
        }
×
282

UNCOV
283
        return { content };
×
284
    }
1✔
285
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc