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

mongodb-js / mongodb-mcp-server / 17765271882

16 Sep 2025 12:10PM UTC coverage: 82.097% (-0.09%) from 82.188%
17765271882

Pull #565

github

web-flow
Merge 66570e984 into bb28bd6c4
Pull Request #565: chore: extend lib to provide configurable properties for VSCode

1022 of 1356 branches covered (75.37%)

Branch coverage included in aggregate %.

1 of 2 new or added lines in 2 files covered. (50.0%)

8 existing lines in 1 file now uncovered.

5022 of 6006 relevant lines covered (83.62%)

54.27 hits per line

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

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

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

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

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

22
export const ConnectClusterArgs = {
2✔
23
    projectId: AtlasArgs.projectId().describe("Atlas project ID"),
2✔
24
    clusterName: AtlasArgs.clusterName().describe("Atlas cluster name"),
2✔
25
};
2✔
26

27
export class ConnectClusterTool extends AtlasToolBase {
2✔
28
    public name = "atlas-connect-cluster";
66✔
29
    protected description = "Connect to MongoDB Atlas cluster";
66✔
30
    public operationType: OperationType = "connect";
66✔
31
    protected argsShape = {
66✔
32
        ...ConnectClusterArgs,
66✔
33
    };
66✔
34

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

46
        const currentConectionState = this.session.connectionManager.currentConnectionState;
7✔
47
        if (
7✔
48
            this.session.connectedAtlasCluster.projectId !== projectId ||
7✔
49
            this.session.connectedAtlasCluster.clusterName !== clusterName
7✔
50
        ) {
9!
51
            return "connected-to-other-cluster";
×
52
        }
✔
53

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

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

76
        if (!cluster.connectionString) {
2!
77
            throw new Error("Connection string not available");
×
78
        }
×
79

80
        const username = `mcpUser${Math.floor(Math.random() * 100000)}`;
2✔
81
        const password = await generateSecurePassword();
2✔
82

83
        const expiryDate = new Date(Date.now() + this.config.atlasTemporaryDatabaseUserLifetimeMs);
2✔
84
        const role = getDefaultRoleFromConfig(this.config);
2✔
85

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

109
        const connectedAtlasCluster = {
2✔
110
            username,
2✔
111
            projectId,
2✔
112
            clusterName,
2✔
113
            expiryDate,
2✔
114
        };
2✔
115

116
        const cn = new URL(cluster.connectionString);
2✔
117
        cn.username = username;
2✔
118
        cn.password = password;
2✔
119
        cn.searchParams.set("authSource", "admin");
2✔
120

121
        this.session.keychain.register(username, "user");
2✔
122
        this.session.keychain.register(password, "password");
2✔
123

124
        return { connectionString: cn.toString(), atlas: connectedAtlasCluster };
2✔
125
    }
2✔
126

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

130
        this.session.logger.debug({
2✔
131
            id: LogId.atlasConnectAttempt,
2✔
132
            context: "atlas-connect-cluster",
2✔
133
            message: `attempting to connect to cluster: ${this.session.connectedAtlasCluster?.clusterName}`,
2✔
134
            noRedaction: true,
2✔
135
        });
2✔
136

137
        // try to connect for about 5 minutes
138
        for (let i = 0; i < 600; i++) {
2✔
139
            try {
7✔
140
                lastError = undefined;
7✔
141

142
                await this.session.connectToMongoDB({ connectionString, atlas });
7✔
143
                break;
2✔
144
            } catch (err: unknown) {
7✔
145
                const error = err instanceof Error ? err : new Error(String(err));
5!
146

147
                lastError = error;
5✔
148

149
                this.session.logger.debug({
5✔
150
                    id: LogId.atlasConnectFailure,
5✔
151
                    context: "atlas-connect-cluster",
5✔
152
                    message: `error connecting to cluster: ${error.message}`,
5✔
153
                });
5✔
154

155
                await sleep(500); // wait for 500ms before retrying
5✔
156
            }
5✔
157

158
            if (
5✔
159
                !this.session.connectedAtlasCluster ||
5✔
160
                this.session.connectedAtlasCluster.projectId !== atlas.projectId ||
5✔
161
                this.session.connectedAtlasCluster.clusterName !== atlas.clusterName
5✔
162
            ) {
7!
UNCOV
163
                throw new Error("Cluster connection aborted");
×
UNCOV
164
            }
×
165
        }
7✔
166

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

195
        this.session.logger.debug({
2✔
196
            id: LogId.atlasConnectSucceeded,
2✔
197
            context: "atlas-connect-cluster",
2✔
198
            message: `connected to cluster: ${this.session.connectedAtlasCluster?.clusterName}`,
2✔
199
            noRedaction: true,
2✔
200
        });
2✔
201
    }
2✔
202

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

207
        for (let i = 0; i < 60; i++) {
1✔
208
            const state = this.queryConnection(projectId, clusterName);
9✔
209
            switch (state) {
9✔
210
                case "connected": {
9✔
211
                    const content: CallToolResult["content"] = [
1✔
212
                        {
1✔
213
                            type: "text",
1✔
214
                            text: `Connected to cluster "${clusterName}".`,
1✔
215
                        },
1✔
216
                    ];
1✔
217

218
                    if (ipAccessListUpdated) {
1✔
219
                        content.push({
1✔
220
                            type: "text",
1✔
221
                            text: addedIpAccessListMessage,
1✔
222
                        });
1✔
223
                    }
1✔
224

225
                    if (createdUser) {
1✔
226
                        content.push({
1✔
227
                            type: "text",
1✔
228
                            text: createdUserMessage,
1✔
229
                        });
1✔
230
                    }
1✔
231

232
                    return { content };
1✔
233
                }
1✔
234
                case "connecting":
9!
235
                case "unknown": {
9✔
236
                    break;
6✔
237
                }
6✔
238
                case "connected-to-other-cluster":
9!
239
                case "disconnected":
9✔
240
                default: {
9✔
241
                    await this.session.disconnect();
2✔
242
                    const { connectionString, atlas } = await this.prepareClusterConnection(projectId, clusterName);
2✔
243

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

258
            await sleep(500);
8✔
259
        }
8✔
260

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

272
        if (ipAccessListUpdated) {
1!
273
            content.push({
×
274
                type: "text" as const,
×
275
                text: addedIpAccessListMessage,
×
276
            });
×
277
        }
×
278

279
        if (createdUser) {
×
280
            content.push({
×
281
                type: "text" as const,
×
282
                text: createdUserMessage,
×
283
            });
×
284
        }
×
285

286
        return { content };
×
287
    }
1✔
288
}
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