@@ -5,10 +5,9 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
5
5
import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js" ;
6
6
import path from "path" ;
7
7
import { handleConnectToServer } from "../connectServer.js" ;
8
- import { SystemCommandManager } from "../syscmd/index.js" ;
9
8
import logger from "../utils/logger.js" ;
10
9
import { convertToOpenAITools , loadConfigAndServers } from "../utils/toolHandler.js" ;
11
- import { iServerConfig , iTool } from "../utils/types.js" ;
10
+ import { iConfig , iServerConfig , iTool } from "../utils/types.js" ;
12
11
import { IMCPServerManager } from "./interface.js" ;
13
12
14
13
export class MCPServerManager implements IMCPServerManager {
@@ -20,6 +19,7 @@ export class MCPServerManager implements IMCPServerManager {
20
19
private toolInfos : iTool [ ] = [ ] ;
21
20
// SSE/Websocket 開起來的Client
22
21
private tempClients : Map < string , Client > = new Map ( ) ;
22
+ private prevConfig : Record < string , iServerConfig > = { } ;
23
23
public configPath : string ;
24
24
25
25
private constructor ( configPath ?: string ) {
@@ -52,9 +52,11 @@ export class MCPServerManager implements IMCPServerManager {
52
52
await this . connectAllServers ( ) ;
53
53
}
54
54
55
+ // 連接所有 MCP 伺服器,不檢查是否已經開啟,執行前請確保 mcpServers 都已關閉
55
56
async connectAllServers ( ) : Promise < { serverName : string ; error : unknown } [ ] > {
56
57
const errorArray : { serverName : string ; error : unknown } [ ] = [ ] ;
57
58
const { config, servers } = await loadConfigAndServers ( this . configPath ) ;
59
+ this . prevConfig = config . mcpServers ;
58
60
// only connect enabled servers
59
61
const enabledServers = Object . keys ( config . mcpServers ) . filter ( ( serverName ) => config . mcpServers [ serverName ] . enabled ) ;
60
62
logger . info ( `Connect to ${ enabledServers . length } enabled servers...` ) ;
@@ -147,6 +149,8 @@ export class MCPServerManager implements IMCPServerManager {
147
149
} ) ;
148
150
}
149
151
152
+ logger . info ( `Add new server completed: ${ serverName } ` ) ;
153
+
150
154
return {
151
155
success : true ,
152
156
serverName,
@@ -171,16 +175,19 @@ export class MCPServerManager implements IMCPServerManager {
171
175
async syncServersWithConfig ( ) : Promise < { serverName : string ; error : unknown } [ ] > {
172
176
logger . info ( "Syncing servers with configuration..." ) ;
173
177
const errorArray : { serverName : string ; error : unknown } [ ] = [ ] ;
178
+ let newConfig : iConfig | undefined ;
174
179
175
180
try {
176
181
// Get configuration differences
177
- const { config : newConfig } = await loadConfigAndServers ( this . configPath ) ;
178
- const currentServers = new Set ( this . servers . keys ( ) ) ;
182
+ const { config : newConfig_ } = await loadConfigAndServers ( this . configPath ) ;
183
+ newConfig = newConfig_ ;
184
+ const currentRunningServers = new Set ( this . servers . keys ( ) ) ;
179
185
const configuredServers = new Set ( Object . keys ( newConfig . mcpServers || { } ) ) ;
180
186
181
187
// Handle servers to be removed
182
- for ( const serverName of currentServers ) {
183
- if ( ! configuredServers . has ( serverName ) ) {
188
+ for ( const serverName of currentRunningServers ) {
189
+ // 設定中沒有此 server 或 有此 server 但設定中 disabled
190
+ if ( ! configuredServers . has ( serverName ) || ! newConfig . mcpServers [ serverName ] . enabled ) {
184
191
logger . info ( `Removing server: ${ serverName } ` ) ;
185
192
await this . disconnectSingleServer ( serverName ) ;
186
193
}
@@ -190,8 +197,13 @@ export class MCPServerManager implements IMCPServerManager {
190
197
for ( const serverName of configuredServers ) {
191
198
try {
192
199
const serverConfig = newConfig . mcpServers [ serverName ] ;
200
+ // 設定中 disabled 則不處理
201
+ if ( ! serverConfig . enabled ) continue ;
202
+
203
+ if ( ! serverConfig . transport ) serverConfig . transport = "command" ;
193
204
194
- if ( ! currentServers . has ( serverName ) ) {
205
+ // 該 Server 還沒有執行
206
+ if ( ! currentRunningServers . has ( serverName ) ) {
195
207
// New server
196
208
logger . info ( `Adding new server: ${ serverName } ` ) ;
197
209
const result = await this . connectSingleServer ( serverName , serverConfig , { } ) ;
@@ -202,11 +214,11 @@ export class MCPServerManager implements IMCPServerManager {
202
214
} ) ;
203
215
}
204
216
} else {
205
- // Existing server, check properties
217
+ // 該 Server 已經執行,確認設定是否變更
206
218
// check command, args(string[]), env(Record<string, string>)
207
219
const isPropertiesChanged = this . checkPropertiesChanged ( serverName , serverConfig ) ;
208
220
if ( isPropertiesChanged ) {
209
- logger . info ( `Properties changed for server: ${ serverName } ` ) ;
221
+ logger . info ( `Properties changed and Restart server: ${ serverName } ` ) ;
210
222
await this . disconnectSingleServer ( serverName ) ;
211
223
const result = await this . connectSingleServer ( serverName , serverConfig , { } ) ;
212
224
if ( ! result . success ) {
@@ -241,6 +253,10 @@ export class MCPServerManager implements IMCPServerManager {
241
253
} catch ( error ) {
242
254
logger . error ( "Error during server configuration sync:" , error ) ;
243
255
throw error ;
256
+ } finally {
257
+ if ( newConfig ) {
258
+ this . prevConfig = newConfig . mcpServers ;
259
+ }
244
260
}
245
261
}
246
262
@@ -279,7 +295,7 @@ export class MCPServerManager implements IMCPServerManager {
279
295
// Remove from toolInfos
280
296
this . toolInfos = this . toolInfos . filter ( ( info ) => info . name !== serverName ) ;
281
297
282
- logger . info ( `Server ${ serverName } disconnected ` ) ;
298
+ logger . info ( `Remove server completed: ${ serverName } ` ) ;
283
299
}
284
300
} catch ( error ) {
285
301
logger . error ( `Error disconnecting server ${ serverName } :` , error ) ;
@@ -314,36 +330,11 @@ export class MCPServerManager implements IMCPServerManager {
314
330
}
315
331
316
332
checkPropertiesChanged ( serverName : string , config : iServerConfig ) {
317
- const client = this . servers . get ( serverName ) ;
318
- if ( ! client ) return true ;
319
- const currentParams = ( client ?. transport as any ) . _serverParams as iServerConfig ;
320
-
321
- // check transport type changed
322
- if ( currentParams . transport !== config . transport ) return true ;
323
-
324
- // if command transport, check command, args and env
325
- if ( config . transport === "command" && currentParams . transport === "command" ) {
326
- const currentCommand = currentParams . command || "" ;
327
- const newCommand = SystemCommandManager . getInstance ( ) . getValue ( config . command || "" ) || config . command || "" ;
328
- const currentArgs = currentParams . args || [ ] ;
329
- const newArgs = config . args || [ ] ;
330
-
331
- return (
332
- currentCommand !== newCommand ||
333
- currentArgs . join ( "," ) !== newArgs . join ( "," ) ||
334
- JSON . stringify ( currentParams . env ) !== JSON . stringify ( config . env )
335
- ) ;
336
- }
337
-
338
- // if sse or websocket transport, check url
339
- if (
340
- ( config . transport === "sse" || config . transport === "websocket" ) &&
341
- ( currentParams . transport === "sse" || currentParams . transport === "websocket" )
342
- ) {
343
- return currentParams . url !== config . url ;
344
- }
333
+ const currentParams = this . prevConfig [ serverName ] as iServerConfig ;
345
334
346
- return false ;
335
+ if ( ! currentParams || ! config ) return true ;
336
+ if ( JSON . stringify ( currentParams ) !== JSON . stringify ( config ) ) return true ;
337
+ else return false ;
347
338
}
348
339
349
340
getAvailableTools ( ) : ToolDefinition [ ] {
@@ -388,4 +379,4 @@ export class MCPServerManager implements IMCPServerManager {
388
379
// logger.info("Reconnect all MCP servers completed");
389
380
// return errorArray;
390
381
// }
391
- }
382
+ }
0 commit comments