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

mongodb-js / mongodb-mcp-server / 16123662219

07 Jul 2025 05:25PM UTC coverage: 60.879% (-13.3%) from 74.187%
16123662219

Pull #343

github

web-flow
Merge 24298edad into 5b7ba55e0
Pull Request #343: fix: turn atlas-connect-cluster async

183 of 411 branches covered (44.53%)

Branch coverage included in aggregate %.

0 of 34 new or added lines in 1 file covered. (0.0%)

129 existing lines in 15 files now uncovered.

704 of 1046 relevant lines covered (67.3%)

59.03 hits per line

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

5.56
/src/tools/atlas/metadata/connectCluster.ts
1
import { z } from "zod";
2
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
import { AtlasToolBase } from "../atlasTool.js";
4
import { ToolArgs, OperationType } from "../../tool.js";
5
import { generateSecurePassword } from "../../../common/atlas/generatePassword.js";
6
import logger, { LogId } from "../../../logger.js";
7
import { inspectCluster } from "../../../common/atlas/cluster.js";
8

9
const EXPIRY_MS = 1000 * 60 * 60 * 12; // 12 hours
34✔
10

11
function sleep(ms: number): Promise<void> {
12
    return new Promise((resolve) => setTimeout(resolve, ms));
×
13
}
14

15
export class ConnectClusterTool extends AtlasToolBase {
16
    protected name = "atlas-connect-cluster";
33✔
17
    protected description = "Connect to MongoDB Atlas cluster";
33✔
18
    protected operationType: OperationType = "metadata";
33✔
19
    protected argsShape = {
33✔
20
        projectId: z.string().describe("Atlas project ID"),
21
        clusterName: z.string().describe("Atlas cluster name"),
22
    };
23

24
    private async queryConnection(projectId: string, clusterName: string) : Promise<"connected" | "disconnected" | "connecting" | "connected-to-other-cluster"> {
NEW
25
        if (!this.session.connectedAtlasCluster) {
×
NEW
26
            return "disconnected";
×
27
        }
28

NEW
29
        if (this.session.connectedAtlasCluster.projectId !== projectId || this.session.connectedAtlasCluster.clusterName !== clusterName) {
×
NEW
30
            return "connected-to-other-cluster";
×
31
        }
32

NEW
33
        if (!this.session.serviceProvider) {
×
NEW
34
            return "connecting";
×
35
        }
36

NEW
37
        await this.session.serviceProvider.runCommand("admin", {
×
38
            ping: 1,
39
        });
NEW
40
        return "connected";
×
41
    }
42

43
    private async prepareClusterConnection(projectId: string, clusterName: string) : Promise<string> {
NEW
44
        await this.session.disconnect();
×
45

NEW
46
        const cluster = await inspectCluster(this.session.apiClient, projectId, clusterName);
×
47

NEW
48
        if (!cluster.connectionString) {
×
NEW
49
            throw new Error("Connection string not available");
×
50
        }
51

UNCOV
52
        const username = `mcpUser${Math.floor(Math.random() * 100000)}`;
×
UNCOV
53
        const password = await generateSecurePassword();
×
54

UNCOV
55
        const expiryDate = new Date(Date.now() + EXPIRY_MS);
×
56

57
        const readOnly =
58
            this.config.readOnly ||
×
59
            (this.config.disabledTools?.includes("create") &&
60
                this.config.disabledTools?.includes("update") &&
61
                this.config.disabledTools?.includes("delete") &&
62
                !this.config.disabledTools?.includes("read") &&
63
                !this.config.disabledTools?.includes("metadata"));
64

UNCOV
65
        const roleName = readOnly ? "readAnyDatabase" : "readWriteAnyDatabase";
×
66

UNCOV
67
        await this.session.apiClient.createDatabaseUser({
×
68
            params: {
69
                path: {
70
                    groupId: projectId,
71
                },
72
            },
73
            body: {
74
                databaseName: "admin",
75
                groupId: projectId,
76
                roles: [
77
                    {
78
                        roleName,
79
                        databaseName: "admin",
80
                    },
81
                ],
82
                scopes: [{ type: "CLUSTER", name: clusterName }],
83
                username,
84
                password,
85
                awsIAMType: "NONE",
86
                ldapAuthType: "NONE",
87
                oidcAuthType: "NONE",
88
                x509Type: "NONE",
89
                deleteAfterDate: expiryDate.toISOString(),
90
            },
91
        });
92

UNCOV
93
        this.session.connectedAtlasCluster = {
×
94
            username,
95
            projectId,
96
            clusterName,
97
            expiryDate,
98
        };
99

UNCOV
100
        const cn = new URL(cluster.connectionString);
×
UNCOV
101
        cn.username = username;
×
UNCOV
102
        cn.password = password;
×
UNCOV
103
        cn.searchParams.set("authSource", "admin");
×
UNCOV
104
        const connectionString = cn.toString();
×
105

106
        return connectionString;
×
107
    }
108

109
    private async connectToCluster(connectionString: string): Promise<void> {
110
        let lastError: Error | undefined = undefined;
×
111

NEW
112
        for (let i = 0; i < 600; i++) { // try for 5 minutes
×
NEW
113
            try {
×
NEW
114
                await this.session.connectToMongoDB(connectionString, this.config.connectOptions);
×
NEW
115
                lastError = undefined;
×
UNCOV
116
                break;
×
117
            } catch (err: unknown) {
NEW
118
                const error = err instanceof Error ? err : new Error(String(err));
×
119

120
                lastError = error;
×
121

122
                logger.debug(
×
123
                    LogId.atlasConnectFailure,
124
                    "atlas-connect-cluster",
125
                    `error connecting to cluster: ${error.message}`
126
                );
127

UNCOV
128
                await sleep(500); // wait for 500ms before retrying
×
129
            }
130
        }
131
    
UNCOV
132
        if (lastError) {
×
UNCOV
133
            void this.session.apiClient.deleteDatabaseUser({
×
134
                params: {
135
                    path: {
136
                        groupId: this.session.connectedAtlasCluster?.projectId || "",
×
137
                        username: this.session.connectedAtlasCluster?.username || "",
×
138
                        databaseName: "admin",
139
                    },
140
                },
141
            }).catch((err: unknown) => {
NEW
142
                const error = err instanceof Error ? err : new Error(String(err));
×
NEW
143
                logger.debug(
×
144
                    LogId.atlasConnectFailure,
145
                    "atlas-connect-cluster",
146
                    `error deleting database user: ${error.message}`
147
                );
148
            });
NEW
149
            this.session.connectedAtlasCluster = undefined;
×
NEW
150
            throw lastError;
×
151
        }
152
    }
153
    
154
    protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
NEW
155
        try {
×
NEW
156
            const state = await this.queryConnection(projectId, clusterName);
×
NEW
157
            switch (state) {
×
158
                case "connected":
UNCOV
159
                    return {
×
160
                        content: [
161
                            {
162
                                type: "text",
163
                                text: "Cluster is already connected.",
164
                            },
165
                        ],
166
                    };
167
                case "connecting":
NEW
168
                    return {
×
169
                        content: [
170
                            {
171
                                type: "text",
172
                                text: "Cluster is connecting...",
173
                            },
174
                        ],
175
                    };
176
            }
177
        } catch (err: unknown) {
NEW
178
            const error = err instanceof Error ? err : new Error(String(err));
×
NEW
179
            logger.debug(
×
180
                LogId.atlasConnectFailure,
181
                "atlas-connect-cluster",
182
                `error querying cluster: ${error.message}`
183
            );
184
            // fall through to create new connection
185
        }
186

NEW
187
        const connectionString = await this.prepareClusterConnection(projectId, clusterName);
×
NEW
188
        process.nextTick(async () => {
×
NEW
189
            try {
×
NEW
190
                await this.connectToCluster(connectionString);
×
191
            } catch (err: unknown) {
NEW
192
                const error = err instanceof Error ? err : new Error(String(err));
×
NEW
193
                logger.debug(
×
194
                    LogId.atlasConnectFailure,
195
                    "atlas-connect-cluster",
196
                    `error connecting to cluster: ${error.message}`
197
                );
198
            }
199
        });
200

NEW
201
        return {
×
202
            content: [
203
                {
204
                    type: "text",
205
                    text: `Connecting to cluster "${clusterName}"...`,
206
                },
207
            ],
208
        };
209
    }
210
}
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