Command:
@@ -983,11 +986,6 @@ const MCPServer = ({ name, server }: { name: string, server: MCPServerObject })
// Main component that renders the list of servers
const MCPServersList = () => {
const mcpServiceState = useMCPServiceState()
- const accessor = useAccessor();
-
- const userSpecifiedMCPServerNames = mcpServiceState.userSpecifiedMCPServerNames
- // TODO tell the user what servers they've specified (might be different from those found)
-
let content: React.ReactNode
if (mcpServiceState.error) {
diff --git a/src/vs/workbench/contrib/void/common/mcpService.ts b/src/vs/workbench/contrib/void/common/mcpService.ts
index 3b73af87..8868b9fb 100644
--- a/src/vs/workbench/contrib/void/common/mcpService.ts
+++ b/src/vs/workbench/contrib/void/common/mcpService.ts
@@ -14,17 +14,16 @@ import { IProductService } from '../../../../platform/product/common/productServ
import { VSBuffer } from '../../../../base/common/buffer.js';
import { IChannel } from '../../../../base/parts/ipc/common/ipc.js';
import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js';
-import { MCPServerOfName, MCPConfigFileType, MCPAddServerResponse, MCPUpdateServerResponse, MCPDeleteServerResponse, MCPServerEventResponse, MCPServerObject, MCPToolCallParams, MCPGenericToolResponse } from './mcpServiceTypes.js';
+import { MCPServerOfName, MCPConfigFileType, MCPAddServerResponse, MCPUpdateServerResponse, MCPDeleteServerResponse, MCPServerObject, MCPToolCallParams, MCPGenericToolResponse } from './mcpServiceTypes.js';
import { Event, Emitter } from '../../../../base/common/event.js';
import { InternalToolInfo } from './prompt/prompts.js';
import { IVoidSettingsService } from './voidSettingsService.js';
-import { MCPServerStateOfName } from './voidSettingsTypes.js';
+import { MCPUserStateOfName } from './voidSettingsTypes.js';
type MCPState = {
mcpServerOfName: MCPServerOfName,
- error: string | undefined,
- userSpecifiedMCPServerNames: string[],
+ error: string | undefined, // global parsing error
}
export interface IMCPService {
@@ -77,7 +76,6 @@ class MCPService extends Disposable implements IMCPService {
state: MCPState = {
mcpServerOfName: {},
error: undefined,
- userSpecifiedMCPServerNames: [],
}
// Emitters for server events
@@ -96,15 +94,12 @@ class MCPService extends Disposable implements IMCPService {
@IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService,
) {
super();
- // Register the service with the instantiation service
this.channel = this.mainProcessService.getChannel('void-channel-mcp')
- // Register listeners for the channel
- this._register((this.channel.listen('onAdd_server') satisfies Event)(e => this._onGetServerEvent(e)));
- this._register((this.channel.listen('onUpdate_server') satisfies Event)(e => this._onGetServerEvent(e)));
- this._register((this.channel.listen('onDelete_server') satisfies Event)(e => this._onGetServerEvent(e)));
- // this._register((this.channel.listen('onLoading_server') satisfies Event)(e => this._onServerEvent(e)));
- // Initialize the service
+ this._register((this.channel.listen('onAdd_server') satisfies Event)(e => { this._setMCPServerState(e.response.name, e.response.newServer) }));
+ this._register((this.channel.listen('onUpdate_server') satisfies Event)(e => { this._setMCPServerState(e.response.name, e.response.newServer) }));
+ this._register((this.channel.listen('onDelete_server') satisfies Event)(e => { this._setMCPServerState(e.response.name, e.response.newServer) }));
+
this._initialize();
}
@@ -127,12 +122,7 @@ class MCPService extends Disposable implements IMCPService {
}
}
- private async _onGetServerEvent(e: MCPServerEventResponse) {
- this._setMCPServer(e.response.name, e.response.newServer)
- }
-
-
- private readonly _setMCPServer = async (serverName: string, newServer: MCPServerObject | undefined) => {
+ private readonly _setMCPServerState = async (serverName: string, newServer: MCPServerObject | undefined) => {
this.state = {
...this.state,
mcpServerOfName: {
@@ -261,49 +251,37 @@ class MCPService extends Disposable implements IMCPService {
private async _refreshMCPServers(): Promise {
this._setHasError(undefined)
- // TODO!!! set is loading
- const mcpConfigFile = await this._parseMCPConfigFile();
- if (!mcpConfigFile) { console.log(`Not setting state: MCP config file not found`); return }
- if (!mcpConfigFile?.mcpServers) { console.log(`Not setting state: MCP config file did not have an 'mcpServers' field`); return }
+ const newConfigFileJSON = await this._parseMCPConfigFile();
+ if (!newConfigFileJSON) { console.log(`Not setting state: MCP config file not found`); return }
+ if (!newConfigFileJSON?.mcpServers) { console.log(`Not setting state: MCP config file did not have an 'mcpServers' field`); return }
- // set state to loading if it's the first time we're seeing it
- const mcpConfigOfName = mcpConfigFile.mcpServers
+
+ const oldConfigFileNames = Object.keys(this.state.mcpServerOfName)
+ const newConfigFileNames = Object.keys(newConfigFileJSON.mcpServers)
+
+ const addedServerNames = newConfigFileNames.filter(serverName => !oldConfigFileNames.includes(serverName)); // in new and not in old
+ const removedServerNames = oldConfigFileNames.filter(serverName => !newConfigFileNames.includes(serverName)); // in old and not in new
+
+ // set isOn to any new servers in the config
+ const addedUserStateOfName: MCPUserStateOfName = {}
+ for (const name in addedServerNames) { addedUserStateOfName[name] = { isOn: true } }
+ await this.voidSettingsService.addMCPUserStateOfNames(addedUserStateOfName);
+
+ // delete isOn for any servers that no longer show up in the config
+ await this.voidSettingsService.removeMCPUserStateOfNames(removedServerNames);
+
+ // set all servers to loading
+ const mcpConfigOfName = newConfigFileJSON.mcpServers
for (const serverName in mcpConfigOfName) {
if (serverName in this.state.mcpServerOfName) continue
- this._setMCPServer(serverName, {
- isOn: false,
+ this._setMCPServerState(serverName, {
status: 'loading',
- error: undefined,
- command: undefined,
tools: [],
})
}
- const currMCPStateOfName = this.voidSettingsService.state.mcpServerStateOfName;
- const availableServers = Object.keys(mcpConfigFile.mcpServers);
-
- // Handle added servers
- const addedServers = availableServers.filter(serverName => !currMCPStateOfName[serverName]?.isOn);
- const addedServersObject = addedServers.reduce((acc, serverName) => {
- acc[serverName] = { isOn: true };
- return acc;
- }, {} as MCPServerStateOfName);
- await this.voidSettingsService.addMCPServerStateOfName(addedServersObject);
-
- // Handle removed servers
- const removedServers = Object.keys(currMCPStateOfName).filter(serverName => availableServers.indexOf(serverName) === -1);
- await this.voidSettingsService.removeMCPServerStateNames(removedServers);
-
- // Compile the updated server list as MCPServerStates
- const updatedServers = Object.keys(currMCPStateOfName).reduce((acc, serverName) => {
- if (availableServers.includes(serverName)) {
- acc[serverName] = currMCPStateOfName[serverName];
- }
- return acc;
- }, {} as MCPServerStateOfName);
-
- this.channel.call('refreshMCPServers', { mcpConfig: mcpConfigFile, serverStates: updatedServers })
+ this.channel.call('refreshMCPServers', { mcpConfig: newConfigFileJSON, userStateOfName: this.voidSettingsService.state.mcpUserStateOfName })
}
public async callMCPTool(toolData: MCPToolCallParams): Promise {
diff --git a/src/vs/workbench/contrib/void/common/mcpServiceTypes.ts b/src/vs/workbench/contrib/void/common/mcpServiceTypes.ts
index cc568191..61bcf095 100644
--- a/src/vs/workbench/contrib/void/common/mcpServiceTypes.ts
+++ b/src/vs/workbench/contrib/void/common/mcpServiceTypes.ts
@@ -138,7 +138,6 @@ export interface MCPServerObject {
// Command-based server properties
tools: MCPTool[],
status: 'loading' | 'error' | 'success' | 'offline',
- isOn: boolean | undefined,
command?: string,
error?: string,
}
diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/workbench/contrib/void/common/voidSettingsService.ts
index 2458b1cd..b8f0b2dd 100644
--- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts
+++ b/src/vs/workbench/contrib/void/common/voidSettingsService.ts
@@ -13,7 +13,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo
import { IMetricsService } from './metricsService.js';
import { defaultProviderSettings, getModelCapabilities, ModelOverrides } from './modelCapabilities.js';
import { VOID_SETTINGS_STORAGE_KEY } from './storageKeys.js';
-import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidStatefulModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, ModelSelectionOptions, OptionsOfModelSelection, ChatMode, OverridesOfModel, defaultOverridesOfModel, MCPServerStateOfName, MCPServerState } from './voidSettingsTypes.js';
+import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidStatefulModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, ModelSelectionOptions, OptionsOfModelSelection, ChatMode, OverridesOfModel, defaultOverridesOfModel, MCPUserStateOfName as MCPUserStateOfName, MCPUserState } from './voidSettingsTypes.js';
// name is the name in the dropdown
@@ -43,7 +43,7 @@ export type VoidSettingsState = {
readonly optionsOfModelSelection: OptionsOfModelSelection;
readonly overridesOfModel: OverridesOfModel;
readonly globalSettings: GlobalSettings;
- readonly mcpServerStateOfName: MCPServerStateOfName;
+ readonly mcpUserStateOfName: MCPUserStateOfName; // user-controlled state of MCP servers
readonly _modelOptions: ModelOption[] // computed based on the two above items
}
@@ -76,9 +76,9 @@ export interface IVoidSettingsService {
addModel(providerName: ProviderName, modelName: string): void;
deleteModel(providerName: ProviderName, modelName: string): boolean;
- addMCPServerStateOfName(serverStateOfName: MCPServerStateOfName): Promise;
- removeMCPServerStateNames(serverNames: string[]): Promise;
- setMCPServerState(serverName: string, state: MCPServerState): Promise;
+ addMCPUserStateOfNames(userStateOfName: MCPUserStateOfName): Promise;
+ removeMCPUserStateOfNames(serverNames: string[]): Promise;
+ setMCPServerState(serverName: string, state: MCPUserState): Promise;
}
@@ -218,7 +218,7 @@ const defaultState = () => {
optionsOfModelSelection: { 'Chat': {}, 'Ctrl+K': {}, 'Autocomplete': {}, 'Apply': {} },
overridesOfModel: deepClone(defaultOverridesOfModel),
_modelOptions: [], // computed later
- mcpServerStateOfName: {},
+ mcpUserStateOfName: {},
}
return d
}
@@ -368,7 +368,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
const newGlobalSettings = this.state.globalSettings
const newOverridesOfModel = this.state.overridesOfModel
- const newMCPServerStateOfName = this.state.mcpServerStateOfName
+ const newMCPUserStateOfName = this.state.mcpUserStateOfName
const newState = {
modelSelectionOfFeature: newModelSelectionOfFeature,
@@ -376,7 +376,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
settingsOfProvider: newSettingsOfProvider,
globalSettings: newGlobalSettings,
overridesOfModel: newOverridesOfModel,
- mcpServerStateOfName: newMCPServerStateOfName,
+ mcpUserStateOfName: newMCPUserStateOfName,
}
this.state = _validatedModelState(newState)
@@ -541,11 +541,11 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
}
// MCP Server State
- private _setMCPServerStateOfName = async (newStates: MCPServerStateOfName) => {
+ private _setMCPUserStateOfName = async (newStates: MCPUserStateOfName) => {
const newState: VoidSettingsState = {
...this.state,
- mcpServerStateOfName: {
- ...this.state.mcpServerStateOfName,
+ mcpUserStateOfName: {
+ ...this.state.mcpUserStateOfName,
...newStates
}
};
@@ -555,18 +555,18 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
this._metricsService.capture('Set MCP Server States', { newStates });
}
- addMCPServerStateOfName = async (newMCPStates: MCPServerStateOfName) => {
- const { mcpServerStateOfName: mcpServerStates } = this.state
+ addMCPUserStateOfNames = async (newMCPStates: MCPUserStateOfName) => {
+ const { mcpUserStateOfName: mcpServerStates } = this.state
const newMCPServerStates = {
...mcpServerStates,
...newMCPStates,
}
- await this._setMCPServerStateOfName(newMCPServerStates)
- this._metricsService.capture('Add MCP Server', { servers: Object.keys(newMCPStates).join(', ') });
+ await this._setMCPUserStateOfName(newMCPServerStates)
+ this._metricsService.capture('Add MCP Servers', { servers: Object.keys(newMCPStates).join(', ') });
}
- removeMCPServerStateNames = async (serverNames: string[]) => {
- const { mcpServerStateOfName: mcpServerStates } = this.state
+ removeMCPUserStateOfNames = async (serverNames: string[]) => {
+ const { mcpUserStateOfName: mcpServerStates } = this.state
const newMCPServerStates = {
...mcpServerStates,
}
@@ -575,18 +575,18 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
delete newMCPServerStates[serverName]
}
})
- await this._setMCPServerStateOfName(newMCPServerStates)
- this._metricsService.capture('Remove MCP Server', { servers: serverNames.join(', ') });
+ await this._setMCPUserStateOfName(newMCPServerStates)
+ this._metricsService.capture('Remove MCP Servers', { servers: serverNames.join(', ') });
}
- setMCPServerState = async (serverName: string, state: MCPServerState) => {
- const { mcpServerStateOfName: mcpServerStates } = this.state
+ setMCPServerState = async (serverName: string, state: MCPUserState) => {
+ const { mcpUserStateOfName: mcpServerStates } = this.state
if (!(serverName in mcpServerStates)) return // if not in list, do nothing
const newMCPServerStates = {
...mcpServerStates,
[serverName]: state,
}
- await this._setMCPServerStateOfName(newMCPServerStates)
+ await this._setMCPUserStateOfName(newMCPServerStates)
this._metricsService.capture('Update MCP Server State', { serverName, state });
}
diff --git a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts
index e2faf506..bdb34504 100644
--- a/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts
+++ b/src/vs/workbench/contrib/void/common/voidSettingsTypes.ts
@@ -494,10 +494,10 @@ export const defaultOverridesOfModel = overridesOfModel
-export interface MCPServerStateOfName {
- [serverName: string]: MCPServerState | undefined;
+export interface MCPUserStateOfName {
+ [serverName: string]: MCPUserState | undefined;
}
-export interface MCPServerState {
+export interface MCPUserState {
isOn: boolean;
}
diff --git a/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts b/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts
index 977ea841..0dcf98fa 100644
--- a/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts
+++ b/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts
@@ -17,7 +17,7 @@ import { MCPConfigFileType, MCPConfigFileServerType, MCPServerErrorModel, MCPSer
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { equals } from '../../../../base/common/objects.js';
-import { MCPServerStateOfName } from '../common/voidSettingsTypes.js';
+import { MCPUserStateOfName } from '../common/voidSettingsTypes.js';
// const getLoadingServerObject = (serverName: string, isOn: boolean | undefined) => {
@@ -118,9 +118,9 @@ export class MCPChannel implements IServerChannel {
// server functions
- private async _refreshMCPServers(params: { mcpConfig: MCPConfigFileType, serverStates: MCPServerStateOfName }) {
+ private async _refreshMCPServers(params: { mcpConfig: MCPConfigFileType, userStateOfName: MCPUserStateOfName }) {
- const { mcpConfig, serverStates } = params
+ const { mcpConfig, userStateOfName } = params
// Get all prevServers
const prevServers = { ...this.clients }
@@ -174,7 +174,7 @@ export class MCPChannel implements IServerChannel {
if (addedServers.length > 0) {
// emit added servers
const addPromises = addedServers.map(async (serverName) => {
- const addedServer = await this._safeSetupServer(mcpServers[serverName], serverName, serverStates[serverName]?.isOn)
+ const addedServer = await this._safeSetupServer(mcpServers[serverName], serverName, userStateOfName[serverName]?.isOn)
return {
type: 'add',
newServer: addedServer,
@@ -189,7 +189,7 @@ export class MCPChannel implements IServerChannel {
// emit updated servers
const updatePromises = updatedServers.map(async (serverName) => {
const prevServer = this.clients[serverName]?.formattedServer;
- const newServer = await this._safeSetupServer(mcpServers[serverName], serverName, serverStates[serverName]?.isOn)
+ const newServer = await this._safeSetupServer(mcpServers[serverName], serverName, userStateOfName[serverName]?.isOn)
return {
type: 'update',
prevServer,
@@ -234,7 +234,6 @@ export class MCPChannel implements IServerChannel {
const { tools } = await client.listTools()
formattedServer = {
status: isOn ? 'success' : 'offline',
- isOn,
tools: tools,
command: server.url.toString(),
}
@@ -245,7 +244,6 @@ export class MCPChannel implements IServerChannel {
console.log(`Connected via SSE to ${serverName}`);
formattedServer = {
status: isOn ? 'success' : 'offline',
- isOn,
tools: [],
command: server.url.toString(),
}
@@ -272,7 +270,6 @@ export class MCPChannel implements IServerChannel {
// Format server object
formattedServer = {
status: isOn ? 'success' : 'offline',
- isOn,
tools: tools,
command: fullCommand,
}
@@ -301,7 +298,6 @@ export class MCPChannel implements IServerChannel {
const formattedError: MCPServerErrorModel = {
status: 'error',
- isOn: false,
tools: [],
error: typedErr.message,
command: fullCommand,
@@ -369,7 +365,6 @@ export class MCPChannel implements IServerChannel {
name: serverName,
newServer: {
status: 'offline',
- isOn,
tools: [],
command: '',
// Explicitly set error to undefined