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

mongodb-js / mongodb-mcp-server / 17383501020

01 Sep 2025 04:59PM UTC coverage: 80.882% (-0.06%) from 80.945%
17383501020

Pull #500

github

web-flow
Merge 9ff88ed22 into d471cdd0d
Pull Request #500: feat: add more details about atlas connect flow - MCP-124

896 of 1192 branches covered (75.17%)

Branch coverage included in aggregate %.

38 of 51 new or added lines in 2 files covered. (74.51%)

1 existing line in 1 file now uncovered.

4549 of 5540 relevant lines covered (82.11%)

39.37 hits per line

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

76.47
/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
function sleep(ms: number): Promise<void> {
20✔
17
    return new Promise((resolve) => setTimeout(resolve, ms));
20✔
18
}
20✔
19

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

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

40
        const currentConectionState = this.session.connectionManager.currentConnectionState;
12✔
41
        if (
12✔
42
            this.session.connectedAtlasCluster.projectId !== projectId ||
12✔
43
            this.session.connectedAtlasCluster.clusterName !== clusterName
12✔
44
        ) {
14!
45
            return "connected-to-other-cluster";
×
46
        }
✔
47

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

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

70
        if (!cluster.connectionString) {
2!
71
            throw new Error("Connection string not available");
×
72
        }
×
73

74
        const username = `mcpUser${Math.floor(Math.random() * 100000)}`;
2✔
75
        const password = await generateSecurePassword();
2✔
76

77
        const expiryDate = new Date(Date.now() + EXPIRY_MS);
2✔
78
        const role = getDefaultRoleFromConfig(this.config);
2✔
79

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

103
        const connectedAtlasCluster = {
2✔
104
            username,
2✔
105
            projectId,
2✔
106
            clusterName,
2✔
107
            expiryDate,
2✔
108
        };
2✔
109

110
        const cn = new URL(cluster.connectionString);
2✔
111
        cn.username = username;
2✔
112
        cn.password = password;
2✔
113
        cn.searchParams.set("authSource", "admin");
2✔
114

115
        return { connectionString: cn.toString(), atlas: connectedAtlasCluster, userCreated: true };
2✔
116
    }
2✔
117

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

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

128
        // try to connect for about 5 minutes
129
        for (let i = 0; i < 600; i++) {
2✔
130
            try {
9✔
131
                lastError = undefined;
9✔
132

133
                await this.session.connectToMongoDB({ connectionString, atlas });
9✔
134
                break;
2✔
135
            } catch (err: unknown) {
9✔
136
                const error = err instanceof Error ? err : new Error(String(err));
7!
137

138
                lastError = error;
7✔
139

140
                this.session.logger.debug({
7✔
141
                    id: LogId.atlasConnectFailure,
7✔
142
                    context: "atlas-connect-cluster",
7✔
143
                    message: `error connecting to cluster: ${error.message}`,
7✔
144
                });
7✔
145

146
                await sleep(500); // wait for 500ms before retrying
7✔
147
            }
7✔
148

149
            if (
7✔
150
                !this.session.connectedAtlasCluster ||
7✔
151
                this.session.connectedAtlasCluster.projectId !== atlas.projectId ||
7✔
152
                this.session.connectedAtlasCluster.clusterName !== atlas.clusterName
7✔
153
            ) {
9!
154
                throw new Error("Cluster connection aborted");
×
155
            }
×
156
        }
9✔
157

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

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

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

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

209
                    // Add feedback about IP access list if it was updated
210
                    if (ipAccessListUpdated) {
1✔
211
                        content.push({
1✔
212
                            type: "text",
1✔
213
                            text: addedIpAccessListMessage,
1✔
214
                        });
1✔
215
                    }
1✔
216

217
                    return { content };
1✔
218
                }
1✔
219
                case "connecting":
14!
220
                case "unknown": {
14✔
221
                    break;
11✔
222
                }
11✔
223
                case "connected-to-other-cluster":
14!
224
                case "disconnected":
14✔
225
                default: {
14✔
226
                    await this.session.disconnect();
2✔
227
                    const { connectionString, atlas, userCreated } = await this.prepareClusterConnection(
2✔
228
                        projectId,
2✔
229
                        clusterName
2✔
230
                    );
2✔
231

232
                    createdUser = userCreated;
2✔
233
                    // try to connect for about 5 minutes asynchronously
234
                    void this.connectToCluster(connectionString, atlas).catch((err: unknown) => {
2✔
235
                        const error = err instanceof Error ? err : new Error(String(err));
×
236
                        this.session.logger.error({
×
237
                            id: LogId.atlasConnectFailure,
×
238
                            context: "atlas-connect-cluster",
×
239
                            message: `error connecting to cluster: ${error.message}`,
×
240
                        });
×
241
                    });
2✔
242
                    break;
2✔
243
                }
2✔
244
            }
14✔
245

246
            await sleep(500);
13✔
247
        }
13✔
248

249
        const content: CallToolResult["content"] = [
13✔
250
            {
13✔
251
                type: "text" as const,
13✔
252
                text: `Attempting to connect to cluster "${clusterName}"...`,
13✔
253
            },
13✔
254
            {
13✔
255
                type: "text" as const,
13✔
256
                text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`,
13✔
257
            },
13✔
258
        ];
13✔
259

260
        if (ipAccessListUpdated) {
1!
NEW
261
            content.push({
×
NEW
262
                type: "text" as const,
×
NEW
263
                text: addedIpAccessListMessage,
×
NEW
264
            });
×
NEW
265
        }
×
266

NEW
267
        if (createdUser) {
×
NEW
268
            content.push({
×
NEW
269
                type: "text" as const,
×
NEW
270
                text: `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`,
×
NEW
271
            });
×
NEW
272
        }
×
273

NEW
274
        return { content };
×
275
    }
1✔
276
}
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