Added proper error handling for config file parsing errors
This commit is contained in:
+38
-25
@@ -1,5 +1,5 @@
|
||||
import { VoidSwitch } from '../util/inputs.js';
|
||||
import { MCPServerEventParam, MCPServerObject, MCPServers } from '../../../../common/mcpServiceTypes.js';
|
||||
import { MCPConfigParseError, MCPServerEventParam, MCPServerObject, MCPServers } from '../../../../common/mcpServiceTypes.js';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useAccessor } from '../util/services.js';
|
||||
import { IDisposable } from '../../../../../../../base/common/lifecycle.js';
|
||||
@@ -102,6 +102,7 @@ const MCPServersList = () => {
|
||||
const accessor = useAccessor();
|
||||
const mcpService = accessor.get('IMCPService');
|
||||
const [mcpServers, setMCPServers] = useState<MCPServers>({});
|
||||
const [mcpConfigError, setMCPConfigError] = useState<string | null>(null);
|
||||
|
||||
// Get all servers from MCPConfigService
|
||||
useEffect(() => {
|
||||
@@ -114,35 +115,13 @@ const MCPServersList = () => {
|
||||
setMCPServers(servers);
|
||||
}
|
||||
|
||||
const handleListeners = (e: MCPServerEventParam) => {
|
||||
if (e.response.event === 'add' || e.response.event === 'update' || e.response.event === 'loading') {
|
||||
// Handle the add event
|
||||
const { name, newServer } = e.response;
|
||||
setMCPServers(prevServers => ({
|
||||
...prevServers,
|
||||
[name]: newServer
|
||||
}));
|
||||
return;
|
||||
}
|
||||
if (e.response.event === 'delete') {
|
||||
// Handle the delete event
|
||||
const { name, prevServer } = e.response;
|
||||
setMCPServers(prevServers => {
|
||||
const newServers = { ...prevServers };
|
||||
delete newServers[name];
|
||||
return newServers;
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw new Error('Event not handled');
|
||||
}
|
||||
|
||||
// Set up listeners for server events
|
||||
const disposables: IDisposable[] = []
|
||||
disposables.push(mcpService.onDidAddServer(handleListeners));
|
||||
disposables.push(mcpService.onDidDeleteServer(handleListeners));
|
||||
disposables.push(mcpService.onDidUpdateServer(handleListeners));
|
||||
disposables.push(mcpService.onLoadingServers(handleListeners));
|
||||
disposables.push(mcpService.onConfigParsingError(handleListeners));
|
||||
|
||||
// Clean up subscription when component unmounts
|
||||
return () => {
|
||||
@@ -152,14 +131,48 @@ const MCPServersList = () => {
|
||||
|
||||
}, [mcpService]);
|
||||
|
||||
const handleListeners = (e: MCPServerEventParam | MCPConfigParseError) => {
|
||||
if (e.response.event === 'config-error') {
|
||||
// Handle the config error event
|
||||
const { error } = e.response;
|
||||
setMCPConfigError(error);
|
||||
return;
|
||||
}
|
||||
if (e.response.event === 'add' || e.response.event === 'update' || e.response.event === 'loading') {
|
||||
// Handle the add event
|
||||
const { name, newServer } = e.response;
|
||||
setMCPServers(prevServers => ({
|
||||
...prevServers,
|
||||
[name]: newServer
|
||||
}));
|
||||
return;
|
||||
}
|
||||
if (e.response.event === 'delete') {
|
||||
// Handle the delete event
|
||||
const { name, prevServer } = e.response;
|
||||
setMCPServers(prevServers => {
|
||||
const newServers = { ...prevServers };
|
||||
delete newServers[name];
|
||||
return newServers;
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw new Error('Event not handled');
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-white rounded-md py-4">
|
||||
<div>
|
||||
{Object.entries(mcpServers).map(([name, server]) => (
|
||||
{!mcpConfigError && Object.entries(mcpServers).map(([name, server]) => (
|
||||
<div className="py-2" key={name}>
|
||||
<MCPServer name={name} server={server} />
|
||||
</div>
|
||||
))}
|
||||
{mcpConfigError && (
|
||||
<div className="text-red-500 text-sm font-medium">
|
||||
{mcpConfigError}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ 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 { MCPServers, MCPConfig, MCPServerEventParam, MCPServerEventAddParam, MCPServerEventUpdateParam, MCPServerEventDeleteParam, MCPServerEventLoadingParam } from './mcpServiceTypes.js';
|
||||
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';
|
||||
|
||||
@@ -28,6 +28,7 @@ export interface IMCPService {
|
||||
onDidUpdateServer: Event<MCPServerEventUpdateParam>;
|
||||
onDidDeleteServer: Event<MCPServerEventDeleteParam>;
|
||||
onLoadingServers: Event<MCPServerEventLoadingParam>;
|
||||
onConfigParsingError: Event<MCPConfigParseError>;
|
||||
}
|
||||
|
||||
export const IMCPService = createDecorator<IMCPService>('mcpConfigService');
|
||||
@@ -51,10 +52,12 @@ class MCPService extends Disposable implements IMCPService {
|
||||
private readonly _onDidUpdateServer = new Emitter<MCPServerEventUpdateParam>();
|
||||
private readonly _onDidDeleteServer = new Emitter<MCPServerEventDeleteParam>();
|
||||
private readonly _onLoadingServers = new Emitter<MCPServerEventLoadingParam>();
|
||||
private readonly _onConfigParsingError = new Emitter<MCPConfigParseError>();
|
||||
public readonly onDidAddServer = this._onDidAddServer.event;
|
||||
public readonly onDidUpdateServer = this._onDidUpdateServer.event;
|
||||
public readonly onDidDeleteServer = this._onDidDeleteServer.event;
|
||||
public readonly onLoadingServers = this._onLoadingServers.event;
|
||||
public readonly onConfigParsingError = this._onConfigParsingError.event;
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@@ -110,12 +113,9 @@ class MCPService extends Disposable implements IMCPService {
|
||||
// Parse the MCP config file
|
||||
const mcpConfig = await this._parseMCPConfigFile();
|
||||
|
||||
if (mcpConfig) {
|
||||
// Create the initial server list
|
||||
// await this._createInitialServerList(mcpConfig);
|
||||
if (mcpConfig && mcpConfig.mcpServers) {
|
||||
|
||||
// Setup the server list
|
||||
console.log('MCP Config file parsed:', JSON.stringify(mcpConfig, null, 2));
|
||||
this.channel.call('setupServers', mcpConfig)
|
||||
}
|
||||
|
||||
@@ -170,14 +170,35 @@ class MCPService extends Disposable implements IMCPService {
|
||||
}
|
||||
|
||||
private async _parseMCPConfigFile(): Promise<MCPConfig | null> {
|
||||
// Remove any previous config parsing error
|
||||
// This isn't super intuitive, but it works
|
||||
this._onConfigParsingError.fire({
|
||||
response: {
|
||||
event: 'config-error',
|
||||
error: null
|
||||
}
|
||||
});
|
||||
|
||||
// Process config file
|
||||
const mcpConfigUri = await this._getMCPConfigPath();
|
||||
|
||||
try {
|
||||
const fileContent = await this.fileService.readFile(mcpConfigUri);
|
||||
const contentString = fileContent.value.toString();
|
||||
return JSON.parse(contentString);
|
||||
const configJson = JSON.parse(contentString);
|
||||
if (!configJson.mcpServers) {
|
||||
throw new Error('Invalid MCP config file: missing mcpServers property');
|
||||
}
|
||||
return configJson as MCPConfig;
|
||||
} catch (error) {
|
||||
console.error('Error reading or parsing MCP config file:', error);
|
||||
const fullError = `Error parsing MCP config file: ${error}`;
|
||||
console.error(fullError);
|
||||
this._onConfigParsingError.fire({
|
||||
response: {
|
||||
event: 'config-error',
|
||||
error: fullError
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -194,7 +215,6 @@ class MCPService extends Disposable implements IMCPService {
|
||||
if (e.contains(mcpConfigUri)) {
|
||||
const mcpConfig = await this._parseMCPConfigFile();
|
||||
if (mcpConfig && mcpConfig.mcpServers) {
|
||||
|
||||
// Call the setupServers method in the main process
|
||||
this.channel.call('setupServers', mcpConfig)
|
||||
}
|
||||
|
||||
@@ -134,6 +134,14 @@ export interface MCPConfig {
|
||||
mcpServers: Record<string, MCPServerConfig>;
|
||||
}
|
||||
|
||||
export interface MCPConfigParseError {
|
||||
// Error message
|
||||
response: {
|
||||
event: 'config-error';
|
||||
error: string | null;
|
||||
}
|
||||
}
|
||||
|
||||
// SERVER EVENT TYPES ------------------------------------------
|
||||
|
||||
export interface MCPServerObject {
|
||||
|
||||
Reference in New Issue
Block a user