1- import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver" ;
21import { ApiClient , ApiClientCredentials } from "./atlas/apiClient.js" ;
32import { Implementation } from "@modelcontextprotocol/sdk/types.js" ;
43import logger , { LogId } from "./logger.js" ;
54import 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" ;
913
1014export interface SessionOptions {
1115 apiBaseUrl : string ;
1216 apiClientId ?: string ;
1317 apiClientSecret ?: string ;
18+ connectionManager ?: ConnectionManager ;
1419}
1520
1621export type SessionEvents = {
@@ -22,20 +27,14 @@ export type SessionEvents = {
2227
2328export class Session extends EventEmitter < SessionEvents > {
2429 sessionId ?: string ;
25- serviceProvider ?: NodeDriverServiceProvider ;
30+ connectionManager : ConnectionManager ;
2631 apiClient : ApiClient ;
2732 agentRunner ?: {
2833 name : string ;
2934 version : string ;
3035 } ;
31- connectedAtlasCluster ?: {
32- username : string ;
33- projectId : string ;
34- clusterName : string ;
35- expiryDate : Date ;
36- } ;
3736
38- constructor ( { apiBaseUrl, apiClientId, apiClientSecret } : SessionOptions ) {
37+ constructor ( { apiBaseUrl, apiClientId, apiClientSecret, connectionManager } : SessionOptions ) {
3938 super ( ) ;
4039
4140 const credentials : ApiClientCredentials | undefined =
@@ -46,10 +45,13 @@ export class Session extends EventEmitter<SessionEvents> {
4645 }
4746 : undefined ;
4847
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 ) ) ;
5355 }
5456
5557 setAgentRunner ( agentRunner : Implementation | undefined ) {
@@ -62,22 +64,22 @@ export class Session extends EventEmitter<SessionEvents> {
6264 }
6365
6466 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 ) ;
7374 }
74- if ( this . connectedAtlasCluster ?. username && this . connectedAtlasCluster ?. projectId ) {
75+
76+ if ( atlasCluster ?. username && atlasCluster ?. projectId ) {
7577 void this . apiClient
7678 . deleteDatabaseUser ( {
7779 params : {
7880 path : {
79- groupId : this . connectedAtlasCluster . projectId ,
80- username : this . connectedAtlasCluster . username ,
81+ groupId : atlasCluster . projectId ,
82+ username : atlasCluster . username ,
8183 databaseName : "admin" ,
8284 } ,
8385 } ,
@@ -90,9 +92,7 @@ export class Session extends EventEmitter<SessionEvents> {
9092 `Error deleting previous database user: ${ error . message } `
9193 ) ;
9294 } ) ;
93- this . connectedAtlasCluster = undefined ;
9495 }
95- this . emit ( "disconnect" ) ;
9696 }
9797
9898 async close ( ) : Promise < void > {
@@ -101,35 +101,30 @@ export class Session extends EventEmitter<SessionEvents> {
101101 this . emit ( "close" ) ;
102102 }
103103
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 > {
110105 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 } ) ;
127107 } 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 ) ;
129109 this . emit ( "connection-error" , message ) ;
130110 throw error ;
131111 }
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+ }
132126
133- this . emit ( "connect" ) ;
127+ get connectedAtlasCluster ( ) : AtlasClusterConnectionInfo | undefined {
128+ return this . connectionManager . currentConnectionState . connectedAtlasCluster ;
134129 }
135130}
0 commit comments