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

mongodb-js / mongodb-mcp-server / 17074835080

19 Aug 2025 03:48PM UTC coverage: 82.111% (+0.1%) from 81.985%
17074835080

push

github

web-flow
chore(deps): bump @mongosh/service-provider-node-driver from 3.10.2 to 3.12.0 (#444)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

807 of 1010 branches covered (79.9%)

Branch coverage included in aggregate %.

4242 of 5139 relevant lines covered (82.55%)

68.47 hits per line

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

90.15
/src/common/connectionManager.ts
1
import { UserConfig, DriverOptions } from "./config.js";
2
import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
2✔
3
import EventEmitter from "events";
2✔
4
import { setAppNameParamIfMissing } from "../helpers/connectionOptions.js";
2✔
5
import { packageInfo } from "./packageInfo.js";
2✔
6
import ConnectionString from "mongodb-connection-string-url";
2✔
7
import { MongoClientOptions } from "mongodb";
8
import { ErrorCodes, MongoDBError } from "./errors.js";
2✔
9
import { CompositeLogger, LogId } from "./logger.js";
2✔
10
import { ConnectionInfo, generateConnectionInfoFromCliArgs } from "@mongosh/arg-parser";
2✔
11

12
export interface AtlasClusterConnectionInfo {
13
    username: string;
14
    projectId: string;
15
    clusterName: string;
16
    expiryDate: Date;
17
}
18

19
export interface ConnectionSettings {
20
    connectionString: string;
21
    atlas?: AtlasClusterConnectionInfo;
22
}
23

24
type ConnectionTag = "connected" | "connecting" | "disconnected" | "errored";
25
type OIDCConnectionAuthType = "oidc-auth-flow" | "oidc-device-flow";
26
export type ConnectionStringAuthType = "scram" | "ldap" | "kerberos" | OIDCConnectionAuthType | "x.509";
27

28
export interface ConnectionState {
29
    tag: ConnectionTag;
30
    connectionStringAuthType?: ConnectionStringAuthType;
31
    connectedAtlasCluster?: AtlasClusterConnectionInfo;
32
}
33

34
export interface ConnectionStateConnected extends ConnectionState {
35
    tag: "connected";
36
    serviceProvider: NodeDriverServiceProvider;
37
}
38

39
export interface ConnectionStateConnecting extends ConnectionState {
40
    tag: "connecting";
41
    serviceProvider: NodeDriverServiceProvider;
42
    oidcConnectionType: OIDCConnectionAuthType;
43
    oidcLoginUrl?: string;
44
    oidcUserCode?: string;
45
}
46

47
export interface ConnectionStateDisconnected extends ConnectionState {
48
    tag: "disconnected";
49
}
50

51
export interface ConnectionStateErrored extends ConnectionState {
52
    tag: "errored";
53
    errorReason: string;
54
}
55

56
export type AnyConnectionState =
57
    | ConnectionStateConnected
58
    | ConnectionStateConnecting
59
    | ConnectionStateDisconnected
60
    | ConnectionStateErrored;
61

62
export interface ConnectionManagerEvents {
63
    "connection-requested": [AnyConnectionState];
64
    "connection-succeeded": [ConnectionStateConnected];
65
    "connection-timed-out": [ConnectionStateErrored];
66
    "connection-closed": [ConnectionStateDisconnected];
67
    "connection-errored": [ConnectionStateErrored];
68
}
69

70
export class ConnectionManager extends EventEmitter<ConnectionManagerEvents> {
2✔
71
    private state: AnyConnectionState;
72
    private bus: EventEmitter;
73

74
    constructor(
2✔
75
        private userConfig: UserConfig,
92✔
76
        private driverOptions: DriverOptions,
92✔
77
        private logger: CompositeLogger,
92✔
78
        bus?: EventEmitter
92✔
79
    ) {
92✔
80
        super();
92✔
81

82
        this.bus = bus ?? new EventEmitter();
92✔
83
        this.state = { tag: "disconnected" };
92✔
84

85
        this.bus.on("mongodb-oidc-plugin:auth-failed", this.onOidcAuthFailed.bind(this));
92✔
86
        this.bus.on("mongodb-oidc-plugin:auth-succeeded", this.onOidcAuthSucceeded.bind(this));
92✔
87
    }
92✔
88

89
    async connect(settings: ConnectionSettings): Promise<AnyConnectionState> {
2✔
90
        this.emit("connection-requested", this.state);
415✔
91

92
        if (this.state.tag === "connected" || this.state.tag === "connecting") {
415✔
93
            await this.disconnect();
22✔
94
        }
22✔
95

96
        let serviceProvider: NodeDriverServiceProvider;
415✔
97
        let connectionInfo: ConnectionInfo;
415✔
98

99
        try {
415✔
100
            settings = { ...settings };
415✔
101
            settings.connectionString = setAppNameParamIfMissing({
415✔
102
                connectionString: settings.connectionString,
415✔
103
                defaultAppName: `${packageInfo.mcpServerName} ${packageInfo.version}`,
415✔
104
            });
415✔
105

106
            connectionInfo = generateConnectionInfoFromCliArgs({
415✔
107
                ...this.userConfig,
415✔
108
                ...this.driverOptions,
415✔
109
                connectionSpecifier: settings.connectionString,
415✔
110
            });
415✔
111

112
            if (connectionInfo.driverOptions.oidc) {
415✔
113
                connectionInfo.driverOptions.oidc.allowedFlows ??= ["auth-code"];
14✔
114
                connectionInfo.driverOptions.oidc.notifyDeviceFlow ??= this.onOidcNotifyDeviceFlow.bind(this);
14✔
115
            }
14✔
116

117
            connectionInfo.driverOptions.proxy ??= { useEnvironmentVariableProxies: true };
415✔
118
            connectionInfo.driverOptions.applyProxyToOIDC ??= true;
415✔
119

120
            serviceProvider = await NodeDriverServiceProvider.connect(
415✔
121
                connectionInfo.connectionString,
415✔
122
                {
415✔
123
                    productDocsLink: "https://github.com/mongodb-js/mongodb-mcp-server/",
415✔
124
                    productName: "MongoDB MCP",
415✔
125
                    ...connectionInfo.driverOptions,
415✔
126
                },
415✔
127
                undefined,
415✔
128
                this.bus
415✔
129
            );
415✔
130
        } catch (error: unknown) {
415✔
131
            const errorReason = error instanceof Error ? error.message : `${error as string}`;
10!
132
            this.changeState("connection-errored", {
10✔
133
                tag: "errored",
10✔
134
                errorReason,
10✔
135
                connectedAtlasCluster: settings.atlas,
10✔
136
            });
10✔
137
            throw new MongoDBError(ErrorCodes.MisconfiguredConnectionString, errorReason);
10✔
138
        }
10✔
139

140
        try {
405✔
141
            const connectionType = ConnectionManager.inferConnectionTypeFromSettings(this.userConfig, connectionInfo);
405✔
142
            if (connectionType.startsWith("oidc")) {
415✔
143
                void this.pingAndForget(serviceProvider);
14✔
144

145
                return this.changeState("connection-requested", {
14✔
146
                    tag: "connecting",
14✔
147
                    connectedAtlasCluster: settings.atlas,
14✔
148
                    serviceProvider,
14✔
149
                    connectionStringAuthType: connectionType,
14✔
150
                    oidcConnectionType: connectionType as OIDCConnectionAuthType,
14✔
151
                });
14✔
152
            }
14✔
153

154
            await serviceProvider?.runCommand?.("admin", { hello: 1 });
391✔
155

156
            return this.changeState("connection-succeeded", {
380✔
157
                tag: "connected",
380✔
158
                connectedAtlasCluster: settings.atlas,
380✔
159
                serviceProvider,
380✔
160
                connectionStringAuthType: connectionType,
380✔
161
            });
380✔
162
        } catch (error: unknown) {
401✔
163
            const errorReason = error instanceof Error ? error.message : `${error as string}`;
11!
164
            this.changeState("connection-errored", {
11✔
165
                tag: "errored",
11✔
166
                errorReason,
11✔
167
                connectedAtlasCluster: settings.atlas,
11✔
168
            });
11✔
169
            throw new MongoDBError(ErrorCodes.NotConnectedToMongoDB, errorReason);
11✔
170
        }
11✔
171
    }
415✔
172

173
    async disconnect(): Promise<ConnectionStateDisconnected | ConnectionStateErrored> {
2✔
174
        if (this.state.tag === "disconnected" || this.state.tag === "errored") {
850✔
175
            return this.state;
464✔
176
        }
464✔
177

178
        if (this.state.tag === "connected" || this.state.tag === "connecting") {
850!
179
            try {
386✔
180
                await this.state.serviceProvider?.close(true);
386✔
181
            } finally {
386✔
182
                this.changeState("connection-closed", {
386✔
183
                    tag: "disconnected",
386✔
184
                });
386✔
185
            }
386✔
186
        }
386✔
187

188
        return { tag: "disconnected" };
386✔
189
    }
850✔
190

191
    get currentConnectionState(): AnyConnectionState {
2✔
192
        return this.state;
3,532✔
193
    }
3,532✔
194

195
    changeState<Event extends keyof ConnectionManagerEvents, State extends ConnectionManagerEvents[Event][0]>(
2✔
196
        event: Event,
857✔
197
        newState: State
857✔
198
    ): State {
857✔
199
        this.state = newState;
857✔
200
        // TypeScript doesn't seem to be happy with the spread operator and generics
201
        // eslint-disable-next-line
202
        this.emit(event, ...([newState] as any));
857✔
203
        return newState;
857✔
204
    }
857✔
205

206
    private onOidcAuthFailed(error: unknown): void {
2✔
207
        if (this.state.tag === "connecting" && this.state.connectionStringAuthType?.startsWith("oidc")) {
×
208
            void this.disconnectOnOidcError(error);
×
209
        }
×
210
    }
×
211

212
    private onOidcAuthSucceeded(): void {
2✔
213
        if (this.state.tag === "connecting" && this.state.connectionStringAuthType?.startsWith("oidc")) {
18✔
214
            this.changeState("connection-succeeded", { ...this.state, tag: "connected" });
14✔
215
        }
14✔
216

217
        this.logger.info({
18✔
218
            id: LogId.oidcFlow,
18✔
219
            context: "mongodb-oidc-plugin:auth-succeeded",
18✔
220
            message: "Authenticated successfully.",
18✔
221
        });
18✔
222
    }
18✔
223

224
    private onOidcNotifyDeviceFlow(flowInfo: { verificationUrl: string; userCode: string }): void {
2✔
225
        if (this.state.tag === "connecting" && this.state.connectionStringAuthType?.startsWith("oidc")) {
2✔
226
            this.changeState("connection-requested", {
2✔
227
                ...this.state,
2✔
228
                tag: "connecting",
2✔
229
                connectionStringAuthType: "oidc-device-flow",
2✔
230
                oidcLoginUrl: flowInfo.verificationUrl,
2✔
231
                oidcUserCode: flowInfo.userCode,
2✔
232
            });
2✔
233
        }
2✔
234

235
        this.logger.info({
2✔
236
            id: LogId.oidcFlow,
2✔
237
            context: "mongodb-oidc-plugin:notify-device-flow",
2✔
238
            message: "OIDC Flow changed automatically to device flow.",
2✔
239
        });
2✔
240
    }
2✔
241

242
    static inferConnectionTypeFromSettings(
2✔
243
        config: UserConfig,
423✔
244
        settings: { connectionString: string }
423✔
245
    ): ConnectionStringAuthType {
423✔
246
        const connString = new ConnectionString(settings.connectionString);
423✔
247
        const searchParams = connString.typedSearchParams<MongoClientOptions>();
423✔
248

249
        switch (searchParams.get("authMechanism")) {
423✔
250
            case "MONGODB-OIDC": {
423✔
251
                if (config.transport === "stdio" && config.browser) {
22✔
252
                    return "oidc-auth-flow";
14✔
253
                }
14✔
254

255
                if (config.transport === "http" && config.httpHost === "127.0.0.1" && config.browser) {
22✔
256
                    return "oidc-auth-flow";
2✔
257
                }
2✔
258

259
                return "oidc-device-flow";
6✔
260
            }
6✔
261
            case "MONGODB-X509":
423✔
262
                return "x.509";
2✔
263
            case "GSSAPI":
423✔
264
                return "kerberos";
2✔
265
            case "PLAIN":
423✔
266
                if (searchParams.get("authSource") === "$external") {
4✔
267
                    return "ldap";
2✔
268
                }
2✔
269
                return "scram";
2✔
270
            // default should catch also null, but eslint complains
271
            // about it.
272
            case null:
423✔
273
            default:
423✔
274
                return "scram";
393✔
275
        }
423✔
276
    }
423✔
277

278
    private async pingAndForget(serviceProvider: NodeDriverServiceProvider): Promise<void> {
2✔
279
        try {
14✔
280
            await serviceProvider?.runCommand?.("admin", { hello: 1 });
14✔
281
        } catch (error: unknown) {
14!
282
            this.logger.warning({
×
283
                id: LogId.oidcFlow,
×
284
                context: "pingAndForget",
×
285
                message: String(error),
×
286
            });
×
287
        }
×
288
    }
14✔
289

290
    private async disconnectOnOidcError(error: unknown): Promise<void> {
2✔
291
        try {
×
292
            await this.disconnect();
×
293
        } catch (error: unknown) {
×
294
            this.logger.warning({
×
295
                id: LogId.oidcFlow,
×
296
                context: "disconnectOnOidcError",
×
297
                message: String(error),
×
298
            });
×
299
        } finally {
×
300
            this.changeState("connection-errored", { tag: "errored", errorReason: String(error) });
×
301
        }
×
302
    }
×
303
}
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

© 2026 Coveralls, Inc