fix: remove button for mcp server install
This commit is contained in:
@@ -1601,6 +1601,12 @@ export function registerIpcHandlers(bundle: EngineBundle): void {
|
|||||||
return engine.addToConfig(agentId as import('../engine/MCPAgentConfigEngine').MCPAgentId);
|
return engine.addToConfig(agentId as import('../engine/MCPAgentConfigEngine').MCPAgentId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
safeHandle('mcp:removeFromAgentConfig', async (_event: unknown, agentId: string) => {
|
||||||
|
const { MCPAgentConfigEngine } = await import('../engine/MCPAgentConfigEngine');
|
||||||
|
const engine = new MCPAgentConfigEngine(buildMcpAgentConfigOptions(bundle));
|
||||||
|
return engine.removeFromConfig(agentId as import('../engine/MCPAgentConfigEngine').MCPAgentId);
|
||||||
|
});
|
||||||
|
|
||||||
safeHandle('mcp:isConfigured', async (_event: unknown, agentId: string) => {
|
safeHandle('mcp:isConfigured', async (_event: unknown, agentId: string) => {
|
||||||
const { MCPAgentConfigEngine } = await import('../engine/MCPAgentConfigEngine');
|
const { MCPAgentConfigEngine } = await import('../engine/MCPAgentConfigEngine');
|
||||||
const engine = new MCPAgentConfigEngine(buildMcpAgentConfigOptions(bundle));
|
const engine = new MCPAgentConfigEngine(buildMcpAgentConfigOptions(bundle));
|
||||||
|
|||||||
@@ -392,6 +392,7 @@ export const electronAPI: ElectronAPI = {
|
|||||||
mcp: {
|
mcp: {
|
||||||
getAgents: () => ipcRenderer.invoke('mcp:getAgents'),
|
getAgents: () => ipcRenderer.invoke('mcp:getAgents'),
|
||||||
addToAgentConfig: (agentId: string) => ipcRenderer.invoke('mcp:addToAgentConfig', agentId),
|
addToAgentConfig: (agentId: string) => ipcRenderer.invoke('mcp:addToAgentConfig', agentId),
|
||||||
|
removeFromAgentConfig: (agentId: string) => ipcRenderer.invoke('mcp:removeFromAgentConfig', agentId),
|
||||||
isConfigured: (agentId: string) => ipcRenderer.invoke('mcp:isConfigured', agentId),
|
isConfigured: (agentId: string) => ipcRenderer.invoke('mcp:isConfigured', agentId),
|
||||||
getPort: () => ipcRenderer.invoke('mcp:getPort'),
|
getPort: () => ipcRenderer.invoke('mcp:getPort'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -848,6 +848,7 @@ export interface ElectronAPI {
|
|||||||
mcp: {
|
mcp: {
|
||||||
getAgents: () => Promise<Array<{ id: string; label: string }>>;
|
getAgents: () => Promise<Array<{ id: string; label: string }>>;
|
||||||
addToAgentConfig: (agentId: string) => Promise<{ success: boolean; configPath: string; error?: string }>;
|
addToAgentConfig: (agentId: string) => Promise<{ success: boolean; configPath: string; error?: string }>;
|
||||||
|
removeFromAgentConfig: (agentId: string) => Promise<{ success: boolean; configPath: string; error?: string }>;
|
||||||
isConfigured: (agentId: string) => Promise<boolean>;
|
isConfigured: (agentId: string) => Promise<boolean>;
|
||||||
getPort: () => Promise<number | null>;
|
getPort: () => Promise<number | null>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ const MCPStatusBadge: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Button to add bDS MCP server to an agent's config. Shows "Configured" if already present. */
|
/** Button to add/remove bDS MCP server to/from an agent's config. */
|
||||||
const MCPAgentButton: React.FC<{ agentId: string; agentLabel: string }> = ({ agentId, agentLabel }) => {
|
const MCPAgentButton: React.FC<{ agentId: string; agentLabel: string }> = ({ agentId, agentLabel }) => {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const [configured, setConfigured] = React.useState(false);
|
const [configured, setConfigured] = React.useState(false);
|
||||||
@@ -149,7 +149,30 @@ const MCPAgentButton: React.FC<{ agentId: string; agentLabel: string }> = ({ age
|
|||||||
}, [agentId]);
|
}, [agentId]);
|
||||||
|
|
||||||
if (configured) {
|
if (configured) {
|
||||||
return <span className="badge badge-success">{t('settings.mcp.alreadyConfigured')}</span>;
|
return (
|
||||||
|
<button
|
||||||
|
className="secondary danger"
|
||||||
|
disabled={loading}
|
||||||
|
onClick={async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const result = await window.electronAPI?.mcp?.removeFromAgentConfig(agentId);
|
||||||
|
if (result?.success) {
|
||||||
|
showToast.success(t('settings.toast.mcpConfigRemoveSuccess', { agent: agentLabel }));
|
||||||
|
setConfigured(false);
|
||||||
|
} else {
|
||||||
|
showToast.error(t('settings.toast.mcpConfigRemoveFailed', { agent: agentLabel, error: result?.error ?? 'Unknown error' }));
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
showToast.error(t('settings.toast.mcpConfigRemoveFailed', { agent: agentLabel, error: 'Unexpected error' }));
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('settings.mcp.removeFromAgent', { agent: agentLabel })}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -988,8 +988,11 @@
|
|||||||
"settings.mcp.agentsTitle": "Agenten-Konfiguration",
|
"settings.mcp.agentsTitle": "Agenten-Konfiguration",
|
||||||
"settings.mcp.agentsDescription": "Fügen Sie den bDS MCP-Server zur Konfiguration Ihres Programmieragenten hinzu. Vorhandene Einstellungen bleiben erhalten.",
|
"settings.mcp.agentsDescription": "Fügen Sie den bDS MCP-Server zur Konfiguration Ihres Programmieragenten hinzu. Vorhandene Einstellungen bleiben erhalten.",
|
||||||
"settings.mcp.addToAgent": "Zu {agent} hinzufügen",
|
"settings.mcp.addToAgent": "Zu {agent} hinzufügen",
|
||||||
|
"settings.mcp.removeFromAgent": "Aus {agent} entfernen",
|
||||||
"settings.mcp.alreadyConfigured": "Konfiguriert",
|
"settings.mcp.alreadyConfigured": "Konfiguriert",
|
||||||
"settings.toast.mcpConfigSuccess": "bDS MCP-Server zur {agent}-Konfiguration hinzugefügt",
|
"settings.toast.mcpConfigSuccess": "bDS MCP-Server zur {agent}-Konfiguration hinzugefügt",
|
||||||
|
"settings.toast.mcpConfigRemoveSuccess": "bDS MCP-Server aus der {agent}-Konfiguration entfernt",
|
||||||
"settings.toast.mcpConfigFailed": "Konfiguration von {agent} fehlgeschlagen: {error}",
|
"settings.toast.mcpConfigFailed": "Konfiguration von {agent} fehlgeschlagen: {error}",
|
||||||
|
"settings.toast.mcpConfigRemoveFailed": "Entfernen aus {agent} fehlgeschlagen: {error}",
|
||||||
"settings.toast.mcpConfigPath": "Konfiguration geschrieben nach {path}"
|
"settings.toast.mcpConfigPath": "Konfiguration geschrieben nach {path}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -988,8 +988,11 @@
|
|||||||
"settings.mcp.agentsTitle": "Agent Configuration",
|
"settings.mcp.agentsTitle": "Agent Configuration",
|
||||||
"settings.mcp.agentsDescription": "Add the bDS MCP server to your coding agent's configuration. Existing settings are preserved.",
|
"settings.mcp.agentsDescription": "Add the bDS MCP server to your coding agent's configuration. Existing settings are preserved.",
|
||||||
"settings.mcp.addToAgent": "Add to {agent}",
|
"settings.mcp.addToAgent": "Add to {agent}",
|
||||||
|
"settings.mcp.removeFromAgent": "Remove from {agent}",
|
||||||
"settings.mcp.alreadyConfigured": "Configured",
|
"settings.mcp.alreadyConfigured": "Configured",
|
||||||
"settings.toast.mcpConfigSuccess": "bDS MCP server added to {agent} configuration",
|
"settings.toast.mcpConfigSuccess": "bDS MCP server added to {agent} configuration",
|
||||||
|
"settings.toast.mcpConfigRemoveSuccess": "bDS MCP server removed from {agent} configuration",
|
||||||
"settings.toast.mcpConfigFailed": "Failed to configure {agent}: {error}",
|
"settings.toast.mcpConfigFailed": "Failed to configure {agent}: {error}",
|
||||||
|
"settings.toast.mcpConfigRemoveFailed": "Failed to remove from {agent}: {error}",
|
||||||
"settings.toast.mcpConfigPath": "Config written to {path}"
|
"settings.toast.mcpConfigPath": "Config written to {path}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -988,8 +988,11 @@
|
|||||||
"settings.mcp.agentsTitle": "Configuración de agentes",
|
"settings.mcp.agentsTitle": "Configuración de agentes",
|
||||||
"settings.mcp.agentsDescription": "Añada el servidor MCP de bDS a la configuración de su agente de programación. Las configuraciones existentes se conservan.",
|
"settings.mcp.agentsDescription": "Añada el servidor MCP de bDS a la configuración de su agente de programación. Las configuraciones existentes se conservan.",
|
||||||
"settings.mcp.addToAgent": "Añadir a {agent}",
|
"settings.mcp.addToAgent": "Añadir a {agent}",
|
||||||
|
"settings.mcp.removeFromAgent": "Eliminar de {agent}",
|
||||||
"settings.mcp.alreadyConfigured": "Configurado",
|
"settings.mcp.alreadyConfigured": "Configurado",
|
||||||
"settings.toast.mcpConfigSuccess": "Servidor MCP de bDS añadido a la configuración de {agent}",
|
"settings.toast.mcpConfigSuccess": "Servidor MCP de bDS añadido a la configuración de {agent}",
|
||||||
|
"settings.toast.mcpConfigRemoveSuccess": "Servidor MCP de bDS eliminado de la configuración de {agent}",
|
||||||
"settings.toast.mcpConfigFailed": "Error al configurar {agent}: {error}",
|
"settings.toast.mcpConfigFailed": "Error al configurar {agent}: {error}",
|
||||||
|
"settings.toast.mcpConfigRemoveFailed": "Error al eliminar de {agent}: {error}",
|
||||||
"settings.toast.mcpConfigPath": "Configuración escrita en {path}"
|
"settings.toast.mcpConfigPath": "Configuración escrita en {path}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -986,8 +986,11 @@
|
|||||||
"settings.mcp.agentsTitle": "Configuration des agents",
|
"settings.mcp.agentsTitle": "Configuration des agents",
|
||||||
"settings.mcp.agentsDescription": "Ajoutez le serveur MCP bDS à la configuration de votre agent de programmation. Les paramètres existants sont préservés.",
|
"settings.mcp.agentsDescription": "Ajoutez le serveur MCP bDS à la configuration de votre agent de programmation. Les paramètres existants sont préservés.",
|
||||||
"settings.mcp.addToAgent": "Ajouter à {agent}",
|
"settings.mcp.addToAgent": "Ajouter à {agent}",
|
||||||
|
"settings.mcp.removeFromAgent": "Retirer de {agent}",
|
||||||
"settings.mcp.alreadyConfigured": "Configuré",
|
"settings.mcp.alreadyConfigured": "Configuré",
|
||||||
"settings.toast.mcpConfigSuccess": "Serveur MCP bDS ajouté à la configuration de {agent}",
|
"settings.toast.mcpConfigSuccess": "Serveur MCP bDS ajouté à la configuration de {agent}",
|
||||||
|
"settings.toast.mcpConfigRemoveSuccess": "Serveur MCP bDS retiré de la configuration de {agent}",
|
||||||
"settings.toast.mcpConfigFailed": "Échec de la configuration de {agent}: {error}",
|
"settings.toast.mcpConfigFailed": "Échec de la configuration de {agent}: {error}",
|
||||||
|
"settings.toast.mcpConfigRemoveFailed": "Échec du retrait de {agent}: {error}",
|
||||||
"settings.toast.mcpConfigPath": "Configuration écrite dans {path}"
|
"settings.toast.mcpConfigPath": "Configuration écrite dans {path}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -986,8 +986,11 @@
|
|||||||
"settings.mcp.agentsTitle": "Configurazione agenti",
|
"settings.mcp.agentsTitle": "Configurazione agenti",
|
||||||
"settings.mcp.agentsDescription": "Aggiungi il server MCP bDS alla configurazione del tuo agente di programmazione. Le impostazioni esistenti vengono preservate.",
|
"settings.mcp.agentsDescription": "Aggiungi il server MCP bDS alla configurazione del tuo agente di programmazione. Le impostazioni esistenti vengono preservate.",
|
||||||
"settings.mcp.addToAgent": "Aggiungi a {agent}",
|
"settings.mcp.addToAgent": "Aggiungi a {agent}",
|
||||||
|
"settings.mcp.removeFromAgent": "Rimuovi da {agent}",
|
||||||
"settings.mcp.alreadyConfigured": "Configurato",
|
"settings.mcp.alreadyConfigured": "Configurato",
|
||||||
"settings.toast.mcpConfigSuccess": "Server MCP bDS aggiunto alla configurazione di {agent}",
|
"settings.toast.mcpConfigSuccess": "Server MCP bDS aggiunto alla configurazione di {agent}",
|
||||||
|
"settings.toast.mcpConfigRemoveSuccess": "Server MCP bDS rimosso dalla configurazione di {agent}",
|
||||||
"settings.toast.mcpConfigFailed": "Configurazione di {agent} non riuscita: {error}",
|
"settings.toast.mcpConfigFailed": "Configurazione di {agent} non riuscita: {error}",
|
||||||
|
"settings.toast.mcpConfigRemoveFailed": "Rimozione da {agent} non riuscita: {error}",
|
||||||
"settings.toast.mcpConfigPath": "Configurazione scritta in {path}"
|
"settings.toast.mcpConfigPath": "Configurazione scritta in {path}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,76 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||||
import { render, screen, fireEvent } from '@testing-library/react';
|
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
||||||
import { SettingsView } from '../../../src/renderer/components/SettingsView/SettingsView';
|
import { SettingsView } from '../../../src/renderer/components/SettingsView/SettingsView';
|
||||||
import { useAppStore } from '../../../src/renderer/store';
|
import { useAppStore } from '../../../src/renderer/store';
|
||||||
|
|
||||||
|
describe('MCPAgentButton uninstall', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
useAppStore.setState({
|
||||||
|
activeProject: {
|
||||||
|
id: 'p1',
|
||||||
|
name: 'Test',
|
||||||
|
slug: 'test',
|
||||||
|
isActive: true,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
gitDiffPreferences: { wordWrap: true, viewStyle: 'inline', hideUnchangedRegions: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
(window as any).electronAPI = {
|
||||||
|
...(window as any).electronAPI,
|
||||||
|
app: { getDefaultProjectPath: vi.fn().mockResolvedValue('/repo') },
|
||||||
|
meta: {
|
||||||
|
getCategories: vi.fn().mockResolvedValue(['article']),
|
||||||
|
getProjectMetadata: vi.fn().mockResolvedValue({
|
||||||
|
maxPostsPerPage: 75,
|
||||||
|
publicUrl: 'https://example.com',
|
||||||
|
categorySettings: { article: { renderInLists: true, showTitle: true } },
|
||||||
|
}),
|
||||||
|
updateProjectMetadata: vi.fn().mockResolvedValue({}),
|
||||||
|
},
|
||||||
|
chat: {
|
||||||
|
getSystemPrompt: vi.fn().mockResolvedValue({ success: true, prompt: '' }),
|
||||||
|
getApiKey: vi.fn().mockResolvedValue({ hasKey: false, maskedKey: '' }),
|
||||||
|
getAvailableModels: vi.fn().mockResolvedValue({ success: true, models: [], selectedModel: '' }),
|
||||||
|
},
|
||||||
|
templates: { getEnabledByKind: vi.fn().mockResolvedValue([]) },
|
||||||
|
projects: { update: vi.fn().mockResolvedValue({}) },
|
||||||
|
mcp: {
|
||||||
|
getAgents: vi.fn().mockResolvedValue([
|
||||||
|
{ id: 'claude-code', label: 'Claude Code' },
|
||||||
|
]),
|
||||||
|
addToAgentConfig: vi.fn().mockResolvedValue({ success: true, configPath: '/p' }),
|
||||||
|
removeFromAgentConfig: vi.fn().mockResolvedValue({ success: true, configPath: '/p' }),
|
||||||
|
isConfigured: vi.fn().mockResolvedValue(true),
|
||||||
|
getPort: vi.fn().mockResolvedValue(4124),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows an uninstall button when agent is already configured', async () => {
|
||||||
|
render(<SettingsView />);
|
||||||
|
const btn = await screen.findByRole('button', { name: /remove from claude code/i });
|
||||||
|
expect(btn).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls removeFromAgentConfig and shows add button after removal', async () => {
|
||||||
|
render(<SettingsView />);
|
||||||
|
const btn = await screen.findByRole('button', { name: /remove from claude code/i });
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
fireEvent.click(btn);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect((window as any).electronAPI.mcp.removeFromAgentConfig).toHaveBeenCalledWith('claude-code');
|
||||||
|
|
||||||
|
const addBtn = await screen.findByRole('button', { name: /add to claude code/i });
|
||||||
|
expect(addBtn).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('SettingsView Diff Preferences', () => {
|
describe('SettingsView Diff Preferences', () => {
|
||||||
let updateProjectMock: ReturnType<typeof vi.fn>;
|
let updateProjectMock: ReturnType<typeof vi.fn>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user