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

mongodb-js / mongodb-mcp-server / 19293756954

12 Nov 2025 10:08AM UTC coverage: 80.169% (-0.2%) from 80.331%
19293756954

Pull #721

github

web-flow
Merge 8e0a29ad9 into 2a499f4f3
Pull Request #721: fix: creating multiple users on atlas connect [MCP-280]

1375 of 1828 branches covered (75.22%)

Branch coverage included in aggregate %.

22 of 30 new or added lines in 1 file covered. (73.33%)

11 existing lines in 1 file now uncovered.

6488 of 7980 relevant lines covered (81.3%)

140.84 hits per line

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

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

12
const addedIpAccessListMessage =
6✔
13
    "Note: Your current IP address has been added to the Atlas project's IP access list to enable secure connection.";
6✔
14

15
const createdUserMessage =
6✔
16
    "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";
6✔
17

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

22
export const ConnectClusterArgs = {
6✔
23
    projectId: AtlasArgs.projectId().describe("Atlas project ID"),
6✔
24
    clusterName: AtlasArgs.clusterName().describe("Atlas cluster name"),
6✔
25
    connectionType: AtlasArgs.connectionType().describe(
6✔
26
        "Type of connection (standard, private, or privateEndpoint) to an Atlas cluster"
6✔
27
    ),
6✔
28
};
6✔
29

30
export class ConnectClusterTool extends AtlasToolBase {
6✔
31
    public name = "atlas-connect-cluster";
170✔
32
    protected description = "Connect to MongoDB Atlas cluster";
170✔
33
    public operationType: OperationType = "connect";
170✔
34
    protected argsShape = {
170✔
35
        ...ConnectClusterArgs,
170✔
36
    };
170✔
37

38
    private queryConnection(
6✔
39
        projectId: string,
126✔
40
        clusterName: string
126✔
41
    ): "connected" | "disconnected" | "connecting" | "connected-to-other-cluster" | "unknown" {
126✔
42
        if (!this.session.connectedAtlasCluster) {
126✔
43
            if (this.session.isConnectedToMongoDB) {
16!
44
                return "connected-to-other-cluster";
×
45
            }
×
46
            return "disconnected";
16✔
47
        }
16✔
48

49
        const currentConectionState = this.session.connectionManager.currentConnectionState;
110✔
50
        if (
110✔
51
            this.session.connectedAtlasCluster.projectId !== projectId ||
110✔
52
            this.session.connectedAtlasCluster.clusterName !== clusterName
108✔
53
        ) {
126✔
54
            return "connected-to-other-cluster";
2✔
55
        }
2✔
56

57
        switch (currentConectionState.tag) {
108✔
58
            case "connecting":
126!
59
            case "disconnected": // we might still be calling Atlas APIs and not attempted yet to connect to MongoDB, but we are still "connecting"
126!
60
                return "connecting";
×
61
            case "connected":
126✔
62
                return "connected";
6✔
63
            case "errored":
126✔
64
                this.session.logger.debug({
102✔
65
                    id: LogId.atlasConnectFailure,
102✔
66
                    context: "atlas-connect-cluster",
102✔
67
                    message: `error querying cluster: ${currentConectionState.errorReason}`,
102✔
68
                });
102✔
69
                return "unknown";
102✔
70
        }
126✔
71
    }
126✔
72

73
    private async prepareClusterConnection(
6✔
74
        projectId: string,
6✔
75
        clusterName: string,
6✔
76
        connectionType: "standard" | "private" | "privateEndpoint" | undefined = "standard"
6✔
77
    ): Promise<{ connectionString: string; atlas: AtlasClusterConnectionInfo }> {
6✔
78
        const cluster = await inspectCluster(this.session.apiClient, projectId, clusterName);
6✔
79

80
        if (cluster.connectionStrings === undefined) {
6!
81
            throw new Error("Connection strings not available");
×
82
        }
×
83
        const connectionString = getConnectionString(cluster.connectionStrings, connectionType);
6✔
84
        if (connectionString === undefined) {
6!
85
            throw new Error(
×
86
                `Connection string for connection type "${connectionType}" is not available. Please ensure this connection type is set up in Atlas. See https://www.mongodb.com/docs/atlas/connect-to-database-deployment/#connect-to-an-atlas-cluster.`
×
87
            );
×
88
        }
×
89

90
        const username = `mcpUser${Math.floor(Math.random() * 100000)}`;
6✔
91
        const password = await generateSecurePassword();
6✔
92

93
        const expiryDate = new Date(Date.now() + this.config.atlasTemporaryDatabaseUserLifetimeMs);
6✔
94
        const role = getDefaultRoleFromConfig(this.config);
6✔
95

96
        await this.session.apiClient.createDatabaseUser({
6✔
97
            params: {
6✔
98
                path: {
6✔
99
                    groupId: projectId,
6✔
100
                },
6✔
101
            },
6✔
102
            body: {
6✔
103
                databaseName: "admin",
6✔
104
                groupId: projectId,
6✔
105
                roles: [role],
6✔
106
                scopes: [{ type: "CLUSTER", name: clusterName }],
6✔
107
                username,
6✔
108
                password,
6✔
109
                awsIAMType: "NONE",
6✔
110
                ldapAuthType: "NONE",
6✔
111
                oidcAuthType: "NONE",
6✔
112
                x509Type: "NONE",
6✔
113
                deleteAfterDate: expiryDate.toISOString(),
6✔
114
                description:
6✔
115
                    "MDB MCP Temporary user, see https://dochub.mongodb.org/core/mongodb-mcp-server-tools-considerations",
6✔
116
            },
6✔
117
        });
6✔
118

119
        const connectedAtlasCluster = {
6✔
120
            username,
6✔
121
            projectId,
6✔
122
            clusterName,
6✔
123
            expiryDate,
6✔
124
        };
6✔
125

126
        const cn = new URL(connectionString);
6✔
127
        cn.username = username;
6✔
128
        cn.password = password;
6✔
129
        cn.searchParams.set("authSource", "admin");
6✔
130

131
        this.session.keychain.register(username, "user");
6✔
132
        this.session.keychain.register(password, "password");
6✔
133

134
        return { connectionString: cn.toString(), atlas: connectedAtlasCluster };
6✔
135
    }
6✔
136

137
    private async connectToCluster(connectionString: string, atlas: AtlasClusterConnectionInfo): Promise<void> {
6✔
138
        let lastError: Error | undefined = undefined;
6✔
139

140
        this.session.logger.debug({
6✔
141
            id: LogId.atlasConnectAttempt,
6✔
142
            context: "atlas-connect-cluster",
6✔
143
            message: `attempting to connect to cluster: ${this.session.connectedAtlasCluster?.clusterName}`,
6!
144
            noRedaction: true,
6✔
145
        });
6✔
146

147
        // try to connect for about 5 minutes
148
        for (let i = 0; i < 600; i++) {
6✔
149
            try {
50✔
150
                lastError = undefined;
50✔
151

152
                await this.session.connectToMongoDB({ connectionString, atlas });
50✔
153
                break;
6✔
154
            } catch (err: unknown) {
50✔
155
                const error = err instanceof Error ? err : new Error(String(err));
44!
156

157
                lastError = error;
44✔
158

159
                this.session.logger.debug({
44✔
160
                    id: LogId.atlasConnectFailure,
44✔
161
                    context: "atlas-connect-cluster",
44✔
162
                    message: `error connecting to cluster: ${error.message}`,
44✔
163
                });
44✔
164

165
                await sleep(500); // wait for 500ms before retrying
44✔
166
            }
44✔
167

168
            if (
44✔
169
                !this.session.connectedAtlasCluster ||
44✔
170
                this.session.connectedAtlasCluster.projectId !== atlas.projectId ||
44✔
171
                this.session.connectedAtlasCluster.clusterName !== atlas.clusterName
44✔
172
            ) {
50!
173
                throw new Error("Cluster connection aborted");
×
174
            }
×
175
        }
50✔
176

177
        if (lastError) {
6!
178
            if (
×
179
                this.session.connectedAtlasCluster?.projectId === atlas.projectId &&
×
180
                this.session.connectedAtlasCluster?.clusterName === atlas.clusterName &&
×
181
                this.session.connectedAtlasCluster?.username
×
182
            ) {
×
183
                void this.session.apiClient
×
184
                    .deleteDatabaseUser({
×
185
                        params: {
×
186
                            path: {
×
187
                                groupId: this.session.connectedAtlasCluster.projectId,
×
188
                                username: this.session.connectedAtlasCluster.username,
×
189
                                databaseName: "admin",
×
190
                            },
×
191
                        },
×
192
                    })
×
193
                    .catch((err: unknown) => {
×
194
                        const error = err instanceof Error ? err : new Error(String(err));
×
195
                        this.session.logger.debug({
×
196
                            id: LogId.atlasConnectFailure,
×
197
                            context: "atlas-connect-cluster",
×
198
                            message: `error deleting database user: ${error.message}`,
×
199
                        });
×
200
                    });
×
201
            }
×
202
            throw lastError;
×
203
        }
×
204

205
        this.session.logger.debug({
6✔
206
            id: LogId.atlasConnectSucceeded,
6✔
207
            context: "atlas-connect-cluster",
6✔
208
            message: `connected to cluster: ${this.session.connectedAtlasCluster?.clusterName}`,
6✔
209
            noRedaction: true,
6✔
210
        });
6✔
211
    }
6✔
212

213
    protected async execute({
6✔
214
        projectId,
6✔
215
        clusterName,
6✔
216
        connectionType,
6✔
217
    }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
6✔
218
        const ipAccessListUpdated = await ensureCurrentIpInAccessList(this.session.apiClient, projectId);
6✔
219
        let createdUser = false;
6✔
220

221
        const state = this.queryConnection(projectId, clusterName);
6✔
222
        switch (state) {
6✔
223
            case "connected-to-other-cluster":
6✔
224
            case "disconnected": {
6✔
225
                await this.session.disconnect();
6✔
226

227
                const { connectionString, atlas } = await this.prepareClusterConnection(
6✔
228
                    projectId,
6✔
229
                    clusterName,
6✔
230
                    connectionType
6✔
231
                );
6✔
232

233
                createdUser = true;
6✔
234

235
                // try to connect for about 5 minutes asynchronously
236
                void this.connectToCluster(connectionString, atlas).catch((err: unknown) => {
6✔
NEW
237
                    const error = err instanceof Error ? err : new Error(String(err));
×
NEW
238
                    this.session.logger.error({
×
NEW
239
                        id: LogId.atlasConnectFailure,
×
NEW
240
                        context: "atlas-connect-cluster",
×
NEW
241
                        message: `error connecting to cluster: ${error.message}`,
×
NEW
242
                    });
×
243
                });
6✔
244
                break;
6✔
245
            }
6✔
246
            case "connecting":
6!
247
            case "connected":
6!
248
            case "unknown":
6!
249
            default: {
6!
NEW
250
                break;
×
NEW
251
            }
×
252
        }
6✔
253

254
        for (let i = 0; i < 60; i++) {
6✔
255
            const state = this.queryConnection(projectId, clusterName);
120✔
256
            switch (state) {
120✔
257
                case "connected": {
120✔
258
                    const content: CallToolResult["content"] = [
6✔
259
                        {
6✔
260
                            type: "text",
6✔
261
                            text: `Connected to cluster "${clusterName}".`,
6✔
262
                        },
6✔
263
                    ];
6✔
264

265
                    if (ipAccessListUpdated) {
6✔
266
                        content.push({
6✔
267
                            type: "text",
6✔
268
                            text: addedIpAccessListMessage,
6✔
269
                        });
6✔
270
                    }
6✔
271

272
                    if (createdUser) {
6✔
273
                        content.push({
6✔
274
                            type: "text",
6✔
275
                            text: createdUserMessage,
6✔
276
                        });
6✔
277
                    }
6✔
278

279
                    return { content };
6✔
280
                }
6✔
281
                case "connecting":
120!
282
                case "unknown":
120✔
283
                case "connected-to-other-cluster":
120✔
284
                case "disconnected":
120✔
285
                default: {
120✔
286
                    break;
114✔
287
                }
114✔
288
            }
120✔
289

290
            await sleep(500); // wait 500ms before checking the connection state again
114✔
291
        }
114!
292

UNCOV
293
        const content: CallToolResult["content"] = [
×
UNCOV
294
            {
×
UNCOV
295
                type: "text" as const,
×
UNCOV
296
                text: `Attempting to connect to cluster "${clusterName}"...`,
×
UNCOV
297
            },
×
UNCOV
298
            {
×
UNCOV
299
                type: "text" as const,
×
UNCOV
300
                text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`,
×
UNCOV
301
            },
×
UNCOV
302
        ];
×
303

UNCOV
304
        if (ipAccessListUpdated) {
×
305
            content.push({
×
306
                type: "text" as const,
×
307
                text: addedIpAccessListMessage,
×
308
            });
×
309
        }
×
310

311
        if (createdUser) {
×
312
            content.push({
×
313
                type: "text" as const,
×
314
                text: createdUserMessage,
×
315
            });
×
316
        }
×
317

318
        return { content };
×
319
    }
6✔
320
}
6✔
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