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

mongodb-js / mongodb-mcp-server / 17461171989

04 Sep 2025 10:36AM UTC coverage: 81.164% (-0.06%) from 81.221%
17461171989

Pull #514

github

web-flow
Merge f2b301a32 into 526fa3b2b
Pull Request #514: fix(connection): support for multiple hosts urls #327

920 of 1221 branches covered (75.35%)

Branch coverage included in aggregate %.

7 of 7 new or added lines in 1 file covered. (100.0%)

6 existing lines in 1 file now uncovered.

4660 of 5654 relevant lines covered (82.42%)

44.23 hits per line

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

76.35
/src/common/session.ts
1
import { ObjectId } from "bson";
2✔
2
import type { ApiClientCredentials } from "./atlas/apiClient.js";
3
import { ApiClient } from "./atlas/apiClient.js";
2✔
4
import type { Implementation } from "@modelcontextprotocol/sdk/types.js";
5
import type { CompositeLogger } from "./logger.js";
6
import { LogId } from "./logger.js";
2✔
7
import EventEmitter from "events";
2✔
8
import type {
9
    AtlasClusterConnectionInfo,
10
    ConnectionManager,
11
    ConnectionSettings,
12
    ConnectionStateConnected,
13
    ConnectionStateErrored,
14
} from "./connectionManager.js";
15
import type { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
16
import { ErrorCodes, MongoDBError } from "./errors.js";
2✔
17
import type { ExportsManager } from "./exportsManager.js";
18
import type { Keychain } from "./keychain.js";
19

20
export interface SessionOptions {
21
    apiBaseUrl: string;
22
    apiClientId?: string;
23
    apiClientSecret?: string;
24
    logger: CompositeLogger;
25
    exportsManager: ExportsManager;
26
    connectionManager: ConnectionManager;
27
    keychain: Keychain;
28
}
29

30
export type SessionEvents = {
31
    connect: [];
32
    close: [];
33
    disconnect: [];
34
    "connection-error": [ConnectionStateErrored];
35
};
36

37
export class Session extends EventEmitter<SessionEvents> {
2✔
38
    readonly sessionId: string = new ObjectId().toString();
2✔
39
    readonly exportsManager: ExportsManager;
40
    readonly connectionManager: ConnectionManager;
41
    readonly apiClient: ApiClient;
42
    readonly keychain: Keychain;
43

44
    mcpClient?: {
45
        name?: string;
46
        version?: string;
47
        title?: string;
48
    };
49

50
    public logger: CompositeLogger;
51

52
    constructor({
2✔
53
        apiBaseUrl,
69✔
54
        apiClientId,
69✔
55
        apiClientSecret,
69✔
56
        logger,
69✔
57
        connectionManager,
69✔
58
        exportsManager,
69✔
59
        keychain,
69✔
60
    }: SessionOptions) {
69✔
61
        super();
69✔
62

63
        this.keychain = keychain;
69✔
64
        this.logger = logger;
69✔
65
        const credentials: ApiClientCredentials | undefined =
69✔
66
            apiClientId && apiClientSecret
69✔
67
                ? {
8✔
68
                      clientId: apiClientId,
8✔
69
                      clientSecret: apiClientSecret,
8✔
70
                  }
8!
71
                : undefined;
61✔
72

73
        this.apiClient = new ApiClient({ baseUrl: apiBaseUrl, credentials }, logger);
69✔
74
        this.exportsManager = exportsManager;
69✔
75
        this.connectionManager = connectionManager;
69✔
76
        this.connectionManager.events.on("connection-success", () => this.emit("connect"));
69✔
77
        this.connectionManager.events.on("connection-time-out", (error) => this.emit("connection-error", error));
69✔
78
        this.connectionManager.events.on("connection-close", () => this.emit("disconnect"));
69✔
79
        this.connectionManager.events.on("connection-error", (error) => this.emit("connection-error", error));
69✔
80
    }
69✔
81

82
    setMcpClient(mcpClient: Implementation | undefined): void {
2✔
83
        if (!mcpClient) {
62!
84
            this.connectionManager.setClientName("unknown");
×
85
            this.logger.debug({
×
86
                id: LogId.serverMcpClientSet,
×
87
                context: "session",
×
88
                message: "MCP client info not found",
×
89
            });
×
90
        }
×
91

92
        this.mcpClient = {
62✔
93
            name: mcpClient?.name || "unknown",
62!
94
            version: mcpClient?.version || "unknown",
62!
95
            title: mcpClient?.title || "unknown",
62✔
96
        };
62✔
97

98
        // Set the client name on the connection manager for appName generation
99
        this.connectionManager.setClientName(this.mcpClient.name || "unknown");
62!
100
    }
62✔
101

102
    async disconnect(): Promise<void> {
2✔
103
        const atlasCluster = this.connectedAtlasCluster;
452✔
104

105
        try {
452✔
106
            await this.connectionManager.disconnect();
452✔
107
        } catch (err: unknown) {
452!
108
            const error = err instanceof Error ? err : new Error(String(err));
×
109
            this.logger.error({
×
110
                id: LogId.mongodbDisconnectFailure,
×
111
                context: "session",
×
112
                message: `Error closing service provider: ${error.message}`,
×
113
            });
×
114
        }
×
115

116
        if (atlasCluster?.username && atlasCluster?.projectId) {
452!
117
            void this.apiClient
1✔
118
                .deleteDatabaseUser({
1✔
119
                    params: {
1✔
120
                        path: {
1✔
121
                            groupId: atlasCluster.projectId,
1✔
122
                            username: atlasCluster.username,
1✔
123
                            databaseName: "admin",
1✔
124
                        },
1✔
125
                    },
1✔
126
                })
1✔
127
                .catch((err: unknown) => {
1✔
UNCOV
128
                    const error = err instanceof Error ? err : new Error(String(err));
×
UNCOV
129
                    this.logger.error({
×
UNCOV
130
                        id: LogId.atlasDeleteDatabaseUserFailure,
×
UNCOV
131
                        context: "session",
×
UNCOV
132
                        message: `Error deleting previous database user: ${error.message}`,
×
UNCOV
133
                    });
×
134
                });
1✔
135
        }
1✔
136
    }
452✔
137

138
    async close(): Promise<void> {
2✔
139
        await this.disconnect();
61✔
140
        await this.apiClient.close();
61✔
141
        await this.exportsManager.close();
61✔
142
        this.emit("close");
61✔
143
    }
61✔
144

145
    async connectToMongoDB(settings: ConnectionSettings): Promise<void> {
2✔
146
        await this.connectionManager.connect({ ...settings });
214✔
147
    }
214✔
148

149
    get isConnectedToMongoDB(): boolean {
2✔
150
        return this.connectionManager.currentConnectionState.tag === "connected";
1,142✔
151
    }
1,142✔
152

153
    get serviceProvider(): NodeDriverServiceProvider {
2✔
154
        if (this.isConnectedToMongoDB) {
155✔
155
            const state = this.connectionManager.currentConnectionState as ConnectionStateConnected;
155✔
156
            return state.serviceProvider;
155✔
157
        }
155!
158

159
        throw new MongoDBError(ErrorCodes.NotConnectedToMongoDB, "Not connected to MongoDB");
×
160
    }
155✔
161

162
    get connectedAtlasCluster(): AtlasClusterConnectionInfo | undefined {
2✔
163
        return this.connectionManager.currentConnectionState.connectedAtlasCluster;
610✔
164
    }
610✔
165
}
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