diff --git a/web-app/src/containers/ChatInput.tsx b/web-app/src/containers/ChatInput.tsx index 7a2ed912cb..a2f4bb013c 100644 --- a/web-app/src/containers/ChatInput.tsx +++ b/web-app/src/containers/ChatInput.tsx @@ -51,7 +51,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { const { prompt, setPrompt } = usePrompt() const { currentThreadId } = useThreads() const { t } = useTranslation() - const { spellCheckChatInput } = useGeneralSetting() + const { spellCheckChatInput, experimentalFeatures } = useGeneralSetting() const maxRows = 10 @@ -459,7 +459,8 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => { )} - {selectedModel?.capabilities?.includes('tools') && + {experimentalFeatures && + selectedModel?.capabilities?.includes('tools') && hasActiveMCPServers && ( { vi.mocked(useGeneralSetting).mockReturnValue({ spellCheckChatInput: true, allowSendWhenUnloaded: false, + experimentalFeatures: true, }) vi.mocked(useModelProvider).mockReturnValue({ diff --git a/web-app/src/hooks/useChat.ts b/web-app/src/hooks/useChat.ts index 02c4252032..e52129145a 100644 --- a/web-app/src/hooks/useChat.ts +++ b/web-app/src/hooks/useChat.ts @@ -31,9 +31,11 @@ import { OUT_OF_CONTEXT_SIZE } from '@/utils/error' import { updateSettings } from '@/services/providers' import { useContextSizeApproval } from './useModelContextApproval' import { useModelLoad } from './useModelLoad' +import { useGeneralSetting } from './useGeneralSetting' export const useChat = () => { const { prompt, setPrompt } = usePrompt() + const { experimentalFeatures } = useGeneralSetting() const { tools, updateTokenSpeed, @@ -247,12 +249,13 @@ export const useChat = () => { let isCompleted = false // Filter tools based on model capabilities and available tools for this thread - let availableTools = selectedModel?.capabilities?.includes('tools') - ? tools.filter((tool) => { - const disabledTools = getDisabledToolsForThread(activeThread.id) - return !disabledTools.includes(tool.name) - }) - : [] + let availableTools = + experimentalFeatures && selectedModel?.capabilities?.includes('tools') + ? tools.filter((tool) => { + const disabledTools = getDisabledToolsForThread(activeThread.id) + return !disabledTools.includes(tool.name) + }) + : [] // TODO: Later replaced by Agent setup? const followUpWithToolUse = true diff --git a/web-app/src/routes/settings/general.tsx b/web-app/src/routes/settings/general.tsx index eeb564ba93..d9e7c6e576 100644 --- a/web-app/src/routes/settings/general.tsx +++ b/web-app/src/routes/settings/general.tsx @@ -45,6 +45,9 @@ import { emit } from '@tauri-apps/api/event' import { stopAllModels } from '@/services/models' import { SystemEvent } from '@/types/events' import { Input } from '@/components/ui/input' +import { getConnectedServers } from '@/services/mcp' +import { invoke } from '@tauri-apps/api/core' +import { useMCPServers } from '@/hooks/useMCPServers' // eslint-disable-next-line @typescript-eslint/no-explicit-any export const Route = createFileRoute(route.settings.general as any)({ @@ -202,6 +205,38 @@ function General() { } }, [t, checkForUpdate]) + const handleStopAllMCPServers = async () => { + try { + const connectedServers = await getConnectedServers() + + // Stop each connected server + const stopPromises = connectedServers.map((serverName) => + invoke('deactivate_mcp_server', { name: serverName }).catch((error) => { + console.error(`Error stopping MCP server ${serverName}:`, error) + return Promise.resolve() // Continue with other servers even if one fails + }) + ) + + await Promise.all(stopPromises) + + // Update server configs to set active: false for stopped servers + const { mcpServers, editServer } = useMCPServers.getState() + connectedServers.forEach((serverName) => { + const serverConfig = mcpServers[serverName] + if (serverConfig) { + editServer(serverName, { ...serverConfig, active: false }) + } + }) + + if (connectedServers.length > 0) { + toast.success(`Stopped ${connectedServers.length} MCP server(s)`) + } + } catch (error) { + console.error('Error stopping MCP servers:', error) + toast.error('Failed to stop MCP servers') + } + } + return (
@@ -395,7 +430,10 @@ function General() { actions={ setExperimentalFeatures(e)} + onCheckedChange={async (e) => { + await handleStopAllMCPServers() + setExperimentalFeatures(e) + }} /> } /> diff --git a/web-app/src/routes/settings/mcp-servers.tsx b/web-app/src/routes/settings/mcp-servers.tsx index b1a3f77ff3..0bc085e14e 100644 --- a/web-app/src/routes/settings/mcp-servers.tsx +++ b/web-app/src/routes/settings/mcp-servers.tsx @@ -114,8 +114,15 @@ function MCPServers() { setDeleteDialogOpen(true) } - const handleConfirmDelete = () => { + const handleConfirmDelete = async () => { if (serverToDelete) { + // Stop the server before deletion + try { + await invoke('deactivate_mcp_server', { name: serverToDelete }) + } catch (error) { + console.error('Error stopping server before deletion:', error) + } + deleteServer(serverToDelete) setServerToDelete(null) syncServersAndRestart() @@ -226,6 +233,7 @@ function MCPServers() { return () => clearInterval(intervalId) }, [setConnectedServers]) + return (