mirror of
https://github.com/hanzoai/code.git
synced 2026-09-30 20:53:35 +00:00
Added persistence between sessions for mcpServerState
This commit is contained in:
@@ -18,6 +18,8 @@ import { IMainProcessService } from '../../../../platform/ipc/common/mainProcess
|
||||
import { MCPServers, MCPConfig, MCPServerEventParam, MCPServerEventAddParam, MCPServerEventUpdateParam, MCPServerEventDeleteParam, MCPServerEventLoadingParam, MCPConfigParseError } from './mcpServiceTypes.js';
|
||||
import { Event, Emitter } from '../../../../base/common/event.js';
|
||||
import { InternalToolInfo } from './prompt/prompts.js';
|
||||
import { IVoidSettingsService } from './voidSettingsService.js';
|
||||
import { MCPServerStates } from './voidSettingsTypes.js';
|
||||
|
||||
export interface IMCPService {
|
||||
readonly _serviceBrand: undefined;
|
||||
@@ -66,6 +68,7 @@ class MCPService extends Disposable implements IMCPService {
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IMainProcessService private readonly mainProcessService: IMainProcessService,
|
||||
@IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService,
|
||||
) {
|
||||
super();
|
||||
// Register the service with the instantiation service
|
||||
@@ -111,13 +114,17 @@ class MCPService extends Disposable implements IMCPService {
|
||||
console.log('MCP Config file created:', mcpConfigUri.toString());
|
||||
}
|
||||
|
||||
// Wait for VoidSettingsService to initialize before proceeding
|
||||
await this.voidSettingsService.waitForInitState;
|
||||
|
||||
// Parse the MCP config file
|
||||
const mcpConfig = await this._parseMCPConfigFile();
|
||||
|
||||
if (mcpConfig && mcpConfig.mcpServers) {
|
||||
const updatedServerStates = await this._handleServerStateChange(mcpConfig);
|
||||
|
||||
// Setup the server list
|
||||
this.channel.call('setupServers', mcpConfig)
|
||||
this.channel.call('setupServers', { mcpConfig, serverStates: updatedServerStates })
|
||||
}
|
||||
|
||||
|
||||
@@ -216,8 +223,10 @@ class MCPService extends Disposable implements IMCPService {
|
||||
if (e.contains(mcpConfigUri)) {
|
||||
const mcpConfig = await this._parseMCPConfigFile();
|
||||
if (mcpConfig && mcpConfig.mcpServers) {
|
||||
const updatedServerStates = await this._handleServerStateChange(mcpConfig);
|
||||
|
||||
// Set up the server list
|
||||
this.channel.call('setupServers', mcpConfig)
|
||||
this.channel.call('setupServers', { mcpConfig, serverStates: updatedServerStates })
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -283,6 +292,8 @@ class MCPService extends Disposable implements IMCPService {
|
||||
|
||||
public async toggleServer(serverName: string, isOn: boolean): Promise<void> {
|
||||
this.channel.call('toggleServer', { serverName, isOn })
|
||||
// Update the server state in the local mcpServers list
|
||||
await this.voidSettingsService.updateMCPServerState(serverName, isOn);
|
||||
}
|
||||
|
||||
// utility functions
|
||||
@@ -306,6 +317,37 @@ class MCPService extends Disposable implements IMCPService {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle server state changes
|
||||
private async _handleServerStateChange(mcpConfig: MCPConfig): Promise<MCPServerStates> {
|
||||
// Get the server states from Void Settings Service
|
||||
const savedServerStates = this.voidSettingsService.state.mcpServerStates;
|
||||
|
||||
// Parse the MCP config file for servers
|
||||
const availableServers = Object.keys(mcpConfig.mcpServers);
|
||||
|
||||
// Handle added servers
|
||||
const addedServers = availableServers.filter(serverName => !savedServerStates[serverName]);
|
||||
const addedServersObject = addedServers.reduce((acc, serverName) => {
|
||||
acc[serverName] = { isOn: true };
|
||||
return acc;
|
||||
}, {} as MCPServerStates);
|
||||
await this.voidSettingsService.addMCPServers(addedServersObject);
|
||||
|
||||
// Handle removed servers
|
||||
const removedServers = Object.keys(savedServerStates).filter(serverName => availableServers.indexOf(serverName) === -1);
|
||||
await this.voidSettingsService.removeMCPServers(removedServers);
|
||||
|
||||
// Compile the updated server list as MCPServerStates
|
||||
const updatedServers = Object.keys(savedServerStates).reduce((acc, serverName) => {
|
||||
if (availableServers.includes(serverName)) {
|
||||
acc[serverName] = savedServerStates[serverName];
|
||||
}
|
||||
return acc;
|
||||
}, {} as MCPServerStates);
|
||||
|
||||
return updatedServers;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IMCPService, MCPService, InstantiationType.Eager);
|
||||
|
||||
@@ -16,6 +16,7 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
||||
import { MCPConfig, MCPServerConfig, MCPServerErrorModel, MCPAddResponse, MCPServerEventAddParam, MCPServerEventUpdateParam, MCPServerEventDeleteParam, MCPUpdateResponse, MCPServerModel, MCPDeleteResponse, MCPServerEventLoadingParam } from '../common/mcpServiceTypes.js';
|
||||
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
||||
import { equals } from '../../../../base/common/objects.js';
|
||||
import { MCPServerStates } from '../common/voidSettingsTypes.js';
|
||||
|
||||
export class MCPChannel implements IServerChannel {
|
||||
|
||||
@@ -93,7 +94,9 @@ export class MCPChannel implements IServerChannel {
|
||||
|
||||
// call functions
|
||||
|
||||
private async _callSetupServers(mcpConfig: MCPConfig) {
|
||||
private async _callSetupServers(params: { mcpConfig: MCPConfig, serverStates: MCPServerStates }) {
|
||||
|
||||
const { mcpConfig, serverStates } = params
|
||||
|
||||
// Get all prevServers
|
||||
const prevServers = { ...this.clients }
|
||||
@@ -117,7 +120,7 @@ export class MCPChannel implements IServerChannel {
|
||||
const { prevMCPConfig, newMCPConfig } = getPrevAndNewServerConfig(serverName)
|
||||
const isAdded = !prevMCPConfig && newMCPConfig
|
||||
if (isAdded) {
|
||||
this.mcpEmitters.serverEvent.loading.fire(this._getLoadingServerObject(serverName))
|
||||
this.mcpEmitters.serverEvent.loading.fire(this._getLoadingServerObject(serverName, serverStates[serverName]?.isOn))
|
||||
}
|
||||
return isAdded
|
||||
})
|
||||
@@ -125,7 +128,7 @@ export class MCPChannel implements IServerChannel {
|
||||
const { prevMCPConfig, newMCPConfig } = getPrevAndNewServerConfig(serverName)
|
||||
const isUpdated = prevMCPConfig && newMCPConfig && !equals(prevMCPConfig, newMCPConfig)
|
||||
if (isUpdated) {
|
||||
this.mcpEmitters.serverEvent.loading.fire(this._getLoadingServerObject(serverName))
|
||||
this.mcpEmitters.serverEvent.loading.fire(this._getLoadingServerObject(serverName, serverStates[serverName]?.isOn))
|
||||
}
|
||||
return isUpdated
|
||||
})
|
||||
@@ -133,7 +136,7 @@ export class MCPChannel implements IServerChannel {
|
||||
const { prevMCPConfig, newMCPConfig } = getPrevAndNewServerConfig(serverName)
|
||||
const isDeleted = prevMCPConfig && !newMCPConfig
|
||||
if (isDeleted) {
|
||||
this.mcpEmitters.serverEvent.loading.fire(this._getLoadingServerObject(serverName))
|
||||
this.mcpEmitters.serverEvent.loading.fire(this._getLoadingServerObject(serverName, serverStates[serverName]?.isOn))
|
||||
}
|
||||
return isDeleted
|
||||
})
|
||||
@@ -147,7 +150,7 @@ export class MCPChannel implements IServerChannel {
|
||||
if (addedServers.length > 0) {
|
||||
// Handle added servers
|
||||
const addPromises: Promise<MCPAddResponse>[] = addedServers.map(async (serverName) => {
|
||||
const addedServer = await this._safeSetupServer(mcpServers[serverName], serverName)
|
||||
const addedServer = await this._safeSetupServer(mcpServers[serverName], serverName, serverStates[serverName]?.isOn)
|
||||
return {
|
||||
event: 'add',
|
||||
newServer: addedServer,
|
||||
@@ -162,7 +165,7 @@ export class MCPChannel implements IServerChannel {
|
||||
// Handle updated servers
|
||||
const updatePromises: Promise<MCPUpdateResponse>[] = updatedServers.map(async (serverName) => {
|
||||
const prevServer = this.clients[serverName]?.formattedServer;
|
||||
const newServer = await this._safeSetupServer(mcpServers[serverName], serverName)
|
||||
const newServer = await this._safeSetupServer(mcpServers[serverName], serverName, serverStates[serverName]?.isOn)
|
||||
return {
|
||||
prevServer,
|
||||
newServer: newServer,
|
||||
@@ -191,7 +194,7 @@ export class MCPChannel implements IServerChannel {
|
||||
}
|
||||
}
|
||||
|
||||
private async _callSetupServer(server: MCPServerConfig, serverName: string) {
|
||||
private async _callSetupServer(server: MCPServerConfig, serverName: string, isOn = true) {
|
||||
|
||||
const clientConfig = this.getClientConfig(serverName)
|
||||
const client = new Client(clientConfig)
|
||||
@@ -206,8 +209,8 @@ export class MCPChannel implements IServerChannel {
|
||||
console.log(`Connected via HTTP to ${serverName}`);
|
||||
const { tools } = await client.listTools()
|
||||
formattedServer = {
|
||||
status: 'success',
|
||||
isOn: true,
|
||||
status: isOn ? 'success' : 'offline',
|
||||
isOn,
|
||||
tools: tools,
|
||||
command: server.url.toString(),
|
||||
}
|
||||
@@ -217,8 +220,8 @@ export class MCPChannel implements IServerChannel {
|
||||
await client.connect(transport);
|
||||
console.log(`Connected via SSE to ${serverName}`);
|
||||
formattedServer = {
|
||||
status: 'success',
|
||||
isOn: true,
|
||||
status: isOn ? 'success' : 'offline',
|
||||
isOn,
|
||||
tools: [],
|
||||
command: server.url.toString(),
|
||||
}
|
||||
@@ -244,8 +247,8 @@ export class MCPChannel implements IServerChannel {
|
||||
|
||||
// Format server object
|
||||
formattedServer = {
|
||||
status: 'success',
|
||||
isOn: true,
|
||||
status: isOn ? 'success' : 'offline',
|
||||
isOn,
|
||||
tools: tools,
|
||||
command: fullCommand,
|
||||
}
|
||||
@@ -260,9 +263,9 @@ export class MCPChannel implements IServerChannel {
|
||||
}
|
||||
|
||||
// Helper function to safely setup a server
|
||||
private async _safeSetupServer(serverConfig: MCPServerConfig, serverName: string) {
|
||||
private async _safeSetupServer(serverConfig: MCPServerConfig, serverName: string, isOn = true) {
|
||||
try {
|
||||
return await this._callSetupServer(serverConfig, serverName)
|
||||
return await this._callSetupServer(serverConfig, serverName, isOn)
|
||||
} catch (err) {
|
||||
const typedErr = err as Error
|
||||
console.error(`❌ Failed to connect to server "${serverName}":`, err)
|
||||
@@ -299,11 +302,14 @@ export class MCPChannel implements IServerChannel {
|
||||
}
|
||||
|
||||
private async _callCloseServer(serverName: string) {
|
||||
if (this.clients[serverName]) {
|
||||
const { client } = this.clients[serverName]
|
||||
const server = this.clients[serverName]
|
||||
if (server) {
|
||||
const { client } = server
|
||||
if (client) {
|
||||
await client.close()
|
||||
}
|
||||
// Remove the client from the clients object
|
||||
delete this.clients[serverName].client
|
||||
console.log(`Closed MCP server ${serverName}`);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user