1
- import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver" ;
2
1
import { ApiClient , ApiClientCredentials } from "./atlas/apiClient.js" ;
3
2
import { Implementation } from "@modelcontextprotocol/sdk/types.js" ;
4
3
import logger , { LogId } from "./logger.js" ;
5
4
import EventEmitter from "events" ;
6
- import { ConnectOptions } from "./config.js" ;
7
- import { setAppNameParamIfMissing } from "../helpers/connectionOptions.js" ;
8
- import { packageInfo } from "./packageInfo.js" ;
5
+ import {
6
+ AtlasClusterConnectionInfo ,
7
+ ConnectionManager ,
8
+ ConnectionSettings ,
9
+ ConnectionStateConnected ,
10
+ } from "./connectionManager.js" ;
11
+ import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver" ;
12
+ import { ErrorCodes , MongoDBError } from "./errors.js" ;
9
13
10
14
export interface SessionOptions {
11
15
apiBaseUrl : string ;
12
16
apiClientId ?: string ;
13
17
apiClientSecret ?: string ;
18
+ connectionManager ?: ConnectionManager ;
14
19
}
15
20
16
21
export type SessionEvents = {
@@ -22,20 +27,14 @@ export type SessionEvents = {
22
27
23
28
export class Session extends EventEmitter < SessionEvents > {
24
29
sessionId ?: string ;
25
- serviceProvider ?: NodeDriverServiceProvider ;
30
+ connectionManager : ConnectionManager ;
26
31
apiClient : ApiClient ;
27
32
agentRunner ?: {
28
33
name : string ;
29
34
version : string ;
30
35
} ;
31
- connectedAtlasCluster ?: {
32
- username : string ;
33
- projectId : string ;
34
- clusterName : string ;
35
- expiryDate : Date ;
36
- } ;
37
36
38
- constructor ( { apiBaseUrl, apiClientId, apiClientSecret } : SessionOptions ) {
37
+ constructor ( { apiBaseUrl, apiClientId, apiClientSecret, connectionManager } : SessionOptions ) {
39
38
super ( ) ;
40
39
41
40
const credentials : ApiClientCredentials | undefined =
@@ -46,10 +45,13 @@ export class Session extends EventEmitter<SessionEvents> {
46
45
}
47
46
: undefined ;
48
47
49
- this . apiClient = new ApiClient ( {
50
- baseUrl : apiBaseUrl ,
51
- credentials,
52
- } ) ;
48
+ this . apiClient = new ApiClient ( { baseUrl : apiBaseUrl , credentials } ) ;
49
+
50
+ this . connectionManager = connectionManager ?? new ConnectionManager ( ) ;
51
+ this . connectionManager . on ( "connection-succeeded" , ( ) => this . emit ( "connect" ) ) ;
52
+ this . connectionManager . on ( "connection-timed-out" , ( error ) => this . emit ( "connection-error" , error . errorReason ) ) ;
53
+ this . connectionManager . on ( "connection-closed" , ( ) => this . emit ( "disconnect" ) ) ;
54
+ this . connectionManager . on ( "connection-errored" , ( error ) => this . emit ( "connection-error" , error . errorReason ) ) ;
53
55
}
54
56
55
57
setAgentRunner ( agentRunner : Implementation | undefined ) {
@@ -62,22 +64,22 @@ export class Session extends EventEmitter<SessionEvents> {
62
64
}
63
65
64
66
async disconnect ( ) : Promise < void > {
65
- if ( this . serviceProvider ) {
66
- try {
67
- await this . serviceProvider . close ( true ) ;
68
- } catch ( err : unknown ) {
69
- const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
70
- logger . error ( LogId . mongodbDisconnectFailure , "Error closing service provider:" , error . message ) ;
71
- }
72
- this . serviceProvider = undefined ;
67
+ const atlasCluster = this . connectedAtlasCluster ;
68
+
69
+ try {
70
+ await this . connectionManager . disconnect ( ) ;
71
+ } catch ( err : unknown ) {
72
+ const error = err instanceof Error ? err : new Error ( String ( err ) ) ;
73
+ logger . error ( LogId . mongodbDisconnectFailure , "Error closing service provider:" , error . message ) ;
73
74
}
74
- if ( this . connectedAtlasCluster ?. username && this . connectedAtlasCluster ?. projectId ) {
75
+
76
+ if ( atlasCluster ?. username && atlasCluster ?. projectId ) {
75
77
void this . apiClient
76
78
. deleteDatabaseUser ( {
77
79
params : {
78
80
path : {
79
- groupId : this . connectedAtlasCluster . projectId ,
80
- username : this . connectedAtlasCluster . username ,
81
+ groupId : atlasCluster . projectId ,
82
+ username : atlasCluster . username ,
81
83
databaseName : "admin" ,
82
84
} ,
83
85
} ,
@@ -90,9 +92,7 @@ export class Session extends EventEmitter<SessionEvents> {
90
92
`Error deleting previous database user: ${ error . message } `
91
93
) ;
92
94
} ) ;
93
- this . connectedAtlasCluster = undefined ;
94
95
}
95
- this . emit ( "disconnect" ) ;
96
96
}
97
97
98
98
async close ( ) : Promise < void > {
@@ -101,35 +101,30 @@ export class Session extends EventEmitter<SessionEvents> {
101
101
this . emit ( "close" ) ;
102
102
}
103
103
104
- async connectToMongoDB ( connectionString : string , connectOptions : ConnectOptions ) : Promise < void > {
105
- connectionString = setAppNameParamIfMissing ( {
106
- connectionString,
107
- defaultAppName : `${ packageInfo . mcpServerName } ${ packageInfo . version } ` ,
108
- } ) ;
109
-
104
+ async connectToMongoDB ( settings : ConnectionSettings ) : Promise < void > {
110
105
try {
111
- this . serviceProvider = await NodeDriverServiceProvider . connect ( connectionString , {
112
- productDocsLink : "https://github.com/mongodb-js/mongodb-mcp-server/" ,
113
- productName : "MongoDB MCP" ,
114
- readConcern : {
115
- level : connectOptions . readConcern ,
116
- } ,
117
- readPreference : connectOptions . readPreference ,
118
- writeConcern : {
119
- w : connectOptions . writeConcern ,
120
- } ,
121
- timeoutMS : connectOptions . timeoutMS ,
122
- proxy : { useEnvironmentVariableProxies : true } ,
123
- applyProxyToOIDC : true ,
124
- } ) ;
125
-
126
- await this . serviceProvider ?. runCommand ?.( "admin" , { hello : 1 } ) ;
106
+ await this . connectionManager . connect ( { ...settings } ) ;
127
107
} catch ( error : unknown ) {
128
- const message = error instanceof Error ? error . message : ` ${ error as string } ` ;
108
+ const message = error instanceof Error ? error . message : ( error as string ) ;
129
109
this . emit ( "connection-error" , message ) ;
130
110
throw error ;
131
111
}
112
+ }
113
+
114
+ get isConnectedToMongoDB ( ) : boolean {
115
+ return this . connectionManager . currentConnectionState . tag === "connected" ;
116
+ }
117
+
118
+ get serviceProvider ( ) : NodeDriverServiceProvider {
119
+ if ( this . isConnectedToMongoDB ) {
120
+ const state = this . connectionManager . currentConnectionState as ConnectionStateConnected ;
121
+ return state . serviceProvider ;
122
+ }
123
+
124
+ throw new MongoDBError ( ErrorCodes . NotConnectedToMongoDB , "Not connected to MongoDB" ) ;
125
+ }
132
126
133
- this . emit ( "connect" ) ;
127
+ get connectedAtlasCluster ( ) : AtlasClusterConnectionInfo | undefined {
128
+ return this . connectionManager . currentConnectionState . connectedAtlasCluster ;
134
129
}
135
130
}
0 commit comments