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

mongodb-js / mongodb-mcp-server / 19925638108

04 Dec 2025 10:23AM UTC coverage: 80.575% (-0.03%) from 80.607%
19925638108

Pull #779

github

web-flow
Merge 1e90969c7 into e12068a03
Pull Request #779: chore: bump mcp SDK, refactor tool arguments

1469 of 1905 branches covered (77.11%)

Branch coverage included in aggregate %.

49 of 66 new or added lines in 12 files covered. (74.24%)

2 existing lines in 2 files now uncovered.

6736 of 8278 relevant lines covered (81.37%)

78.78 hits per line

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

71.38
/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";
3✔
4
import { generateSecurePassword } from "../../../helpers/generatePassword.js";
3✔
5
import { LogId } from "../../../common/logger.js";
3✔
6
import { getConnectionString, inspectCluster } from "../../../common/atlas/cluster.js";
3✔
7
import { ensureCurrentIpInAccessList } from "../../../common/atlas/accessListUtils.js";
3✔
8
import type { AtlasClusterConnectionInfo } from "../../../common/connectionManager.js";
9
import { getDefaultRoleFromConfig } from "../../../common/atlas/roles.js";
3✔
10
import { AtlasArgs } from "../../args.js";
3✔
11
import type { ConnectionMetadata } from "../../../telemetry/types.js";
12

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

16
const createdUserMessage =
3✔
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";
3✔
18

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

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

31
export class ConnectClusterTool extends AtlasToolBase {
3✔
32
    public name = "atlas-connect-cluster";
100✔
33
    protected description = "Connect to MongoDB Atlas cluster";
100✔
34
    static operationType: OperationType = "connect";
100✔
35
    protected argsShape = ConnectClusterArgs;
100✔
36

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

48
        const currentConectionState = this.session.connectionManager.currentConnectionState;
79✔
49
        if (
79✔
50
            this.session.connectedAtlasCluster.projectId !== projectId ||
79✔
51
            this.session.connectedAtlasCluster.clusterName !== clusterName
78✔
52
        ) {
86✔
53
            return "connected-to-other-cluster";
1✔
54
        }
1✔
55

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

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

79
        if (cluster.connectionStrings === undefined) {
3!
80
            throw new Error("Connection strings not available");
×
81
        }
×
82
        const connectionString = getConnectionString(cluster.connectionStrings, connectionType);
3✔
83
        if (connectionString === undefined) {
3!
84
            throw new Error(
×
85
                `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.`
×
86
            );
×
87
        }
×
88

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

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

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

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

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

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

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

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

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

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

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

156
                lastError = error;
53✔
157

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

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

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

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

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

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

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

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

232
                createdUser = true;
3✔
233

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

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

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

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

278
                    return { content };
3✔
279
                }
3✔
280
                case "connecting":
83!
281
                case "unknown":
83✔
282
                case "connected-to-other-cluster":
83✔
283
                case "disconnected":
83✔
284
                default: {
83✔
285
                    break;
80✔
286
                }
80✔
287
            }
83✔
288

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

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

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

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

317
        return { content };
×
318
    }
3✔
319

320
    protected override resolveTelemetryMetadata(
3✔
NEW
321
        args: ToolArgs<typeof this.argsShape>,
×
NEW
322
        { result }: { result: CallToolResult }
×
323
    ): ConnectionMetadata {
×
NEW
324
        const parentMetadata = super.resolveTelemetryMetadata(args, { result });
×
325
        const connectionMetadata = this.getConnectionInfoMetadata();
×
326
        if (connectionMetadata && connectionMetadata.project_id !== undefined) {
×
327
            // delete the project_id from the parent metadata to avoid duplication
328
            delete parentMetadata.project_id;
×
329
        }
×
330
        return { ...parentMetadata, ...connectionMetadata };
×
331
    }
×
332
}
3✔
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