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

mongodb-js / mongodb-mcp-server / 17161333946

22 Aug 2025 05:03PM UTC coverage: 81.909% (-0.1%) from 82.022%
17161333946

Pull #471

github

web-flow
Merge 903825dd4 into 49707be70
Pull Request #471: fix: atlas connectCluster defaults to readOnly DB roles - MCP-125

874 of 1092 branches covered (80.04%)

Branch coverage included in aggregate %.

25 of 25 new or added lines in 2 files covered. (100.0%)

49 existing lines in 10 files now uncovered.

4360 of 5298 relevant lines covered (82.3%)

70.64 hits per line

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

79.31
/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
} from "./connectionManager.js";
14
import type { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
15
import { ErrorCodes, MongoDBError } from "./errors.js";
2✔
16
import type { ExportsManager } from "./exportsManager.js";
17

18
export interface SessionOptions {
19
    apiBaseUrl: string;
20
    apiClientId?: string;
21
    apiClientSecret?: string;
22
    logger: CompositeLogger;
23
    exportsManager: ExportsManager;
24
    connectionManager: ConnectionManager;
25
}
26

27
export type SessionEvents = {
28
    connect: [];
29
    close: [];
30
    disconnect: [];
31
    "connection-error": [string];
32
};
33

34
export class Session extends EventEmitter<SessionEvents> {
2✔
35
    readonly sessionId: string = new ObjectId().toString();
2✔
36
    readonly exportsManager: ExportsManager;
37
    readonly connectionManager: ConnectionManager;
38
    readonly apiClient: ApiClient;
39
    mcpClient?: {
40
        name?: string;
41
        version?: string;
42
        title?: string;
43
    };
44

45
    public logger: CompositeLogger;
46

47
    constructor({
2✔
48
        apiBaseUrl,
96✔
49
        apiClientId,
96✔
50
        apiClientSecret,
96✔
51
        logger,
96✔
52
        connectionManager,
96✔
53
        exportsManager,
96✔
54
    }: SessionOptions) {
96✔
55
        super();
96✔
56

57
        this.logger = logger;
96✔
58
        const credentials: ApiClientCredentials | undefined =
96✔
59
            apiClientId && apiClientSecret
96✔
60
                ? {
44✔
61
                      clientId: apiClientId,
44✔
62
                      clientSecret: apiClientSecret,
44✔
63
                  }
44✔
64
                : undefined;
52✔
65

66
        this.apiClient = new ApiClient({ baseUrl: apiBaseUrl, credentials }, logger);
96✔
67
        this.exportsManager = exportsManager;
96✔
68
        this.connectionManager = connectionManager;
96✔
69
        this.connectionManager.on("connection-succeeded", () => this.emit("connect"));
96✔
70
        this.connectionManager.on("connection-timed-out", (error) => this.emit("connection-error", error.errorReason));
96✔
71
        this.connectionManager.on("connection-closed", () => this.emit("disconnect"));
96✔
72
        this.connectionManager.on("connection-errored", (error) => this.emit("connection-error", error.errorReason));
96✔
73
    }
96✔
74

75
    setMcpClient(mcpClient: Implementation | undefined): void {
2✔
76
        if (!mcpClient) {
82!
77
            this.connectionManager.setClientName("unknown");
×
78
            this.logger.debug({
×
79
                id: LogId.serverMcpClientSet,
×
80
                context: "session",
×
81
                message: "MCP client info not found",
×
82
            });
×
83
        }
×
84

85
        this.mcpClient = {
82✔
86
            name: mcpClient?.name || "unknown",
82!
87
            version: mcpClient?.version || "unknown",
82!
88
            title: mcpClient?.title || "unknown",
82✔
89
        };
82✔
90

91
        // Set the client name on the connection manager for appName generation
92
        this.connectionManager.setClientName(this.mcpClient.name || "unknown");
82!
93
    }
82✔
94

95
    async disconnect(): Promise<void> {
2✔
96
        const atlasCluster = this.connectedAtlasCluster;
788✔
97

98
        try {
788✔
99
            await this.connectionManager.disconnect();
788✔
100
        } catch (err: unknown) {
788!
101
            const error = err instanceof Error ? err : new Error(String(err));
×
102
            this.logger.error({
×
103
                id: LogId.mongodbDisconnectFailure,
×
104
                context: "session",
×
105
                message: `Error closing service provider: ${error.message}`,
×
106
            });
×
107
        }
×
108

109
        if (atlasCluster?.username && atlasCluster?.projectId) {
788✔
110
            void this.apiClient
1✔
111
                .deleteDatabaseUser({
1✔
112
                    params: {
1✔
113
                        path: {
1✔
114
                            groupId: atlasCluster.projectId,
1✔
115
                            username: atlasCluster.username,
1✔
116
                            databaseName: "admin",
1✔
117
                        },
1✔
118
                    },
1✔
119
                })
1✔
120
                .catch((err: unknown) => {
1✔
UNCOV
121
                    const error = err instanceof Error ? err : new Error(String(err));
×
UNCOV
122
                    this.logger.error({
×
UNCOV
123
                        id: LogId.atlasDeleteDatabaseUserFailure,
×
UNCOV
124
                        context: "session",
×
UNCOV
125
                        message: `Error deleting previous database user: ${error.message}`,
×
UNCOV
126
                    });
×
127
                });
1✔
128
        }
1✔
129
    }
788✔
130

131
    async close(): Promise<void> {
2✔
132
        await this.disconnect();
80✔
133
        await this.apiClient.close();
80✔
134
        await this.exportsManager.close();
79✔
135
        this.emit("close");
79✔
136
    }
80✔
137

138
    async connectToMongoDB(settings: ConnectionSettings): Promise<void> {
2✔
139
        try {
401✔
140
            await this.connectionManager.connect({ ...settings });
401✔
141
        } catch (error: unknown) {
401✔
142
            const message = error instanceof Error ? error.message : (error as string);
29!
143
            this.emit("connection-error", message);
29✔
144
            throw error;
29✔
145
        }
29✔
146
    }
401✔
147

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

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

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

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