chore: add toggle loading sever MCP (#5225)

* chore: add toggle loading sever mcp

* chore: remove duplicate classname

* chore: remove log

* chore: remove log

* fix: save server config

---------

Co-authored-by: Louis <louis@jan.ai>
This commit is contained in:
Faisal Amir 2025-06-09 23:56:49 +07:00 committed by GitHub
parent 891c149f1b
commit 8ba4b0be36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 62 additions and 28 deletions

View File

@ -2,20 +2,27 @@ import * as React from 'react'
import * as SwitchPrimitive from '@radix-ui/react-switch'
import { cn } from '@/lib/utils'
import { IconLoader2 } from '@tabler/icons-react'
function Switch({
className,
...props
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
type SwitchProps = React.ComponentProps<typeof SwitchPrimitive.Root> & {
loading?: boolean
}
function Switch({ loading, className, ...props }: SwitchProps) {
return (
<SwitchPrimitive.Root
data-slot="switch"
className={cn(
'peer cursor-pointer data-[state=checked]:bg-accent data-[state=unchecked]:bg-main-view-fg/20 focus-visible:border-none inline-flex h-[18px] w-8.5 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-0 disabled:cursor-not-allowed disabled:opacity-50',
'relative peer cursor-pointer data-[state=checked]:bg-accent data-[state=unchecked]:bg-main-view-fg/20 focus-visible:border-none inline-flex h-[18px] w-8.5 shrink-0 items-center rounded-full border border-transparent shadow-xs outline-none focus-visible:ring-0 disabled:cursor-not-allowed disabled:opacity-50 transition-all',
loading && 'w-4.5 pointer-events-none',
className
)}
{...props}
>
{loading && (
<div className="absolute inset-0 flex items-center justify-center z-10 size-3.5 top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2">
<IconLoader2 className="animate-spin text-main-view-fg/50" />
</div>
)}
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className={cn(

View File

@ -216,7 +216,6 @@ const DropdownModelProvider = ({
// Add the filtered items to their respective groups
filteredItems.forEach((item) => {
const providerKey = item.provider.provider
console.log(providerKey, 'providerKey')
if (!groups[providerKey]) {
groups[providerKey] = []
}

View File

@ -19,6 +19,7 @@ type MCPServerStoreState = {
mcpServers: MCPServers
loading: boolean
deletedServerKeys: string[]
getServerConfig: (key: string) => MCPServerConfig | undefined
setLeftPanel: (value: boolean) => void
addServer: (key: string, config: MCPServerConfig) => void
editServer: (key: string, config: MCPServerConfig) => void
@ -34,7 +35,11 @@ export const useMCPServers = create<MCPServerStoreState>()((set, get) => ({
loading: false,
deletedServerKeys: [],
setLeftPanel: (value) => set({ open: value }),
getServerConfig: (key) => {
const mcpServers = get().mcpServers
// Return the server configuration if it exists, otherwise return undefined
return mcpServers[key] ? mcpServers[key] : undefined
},
// Add a new MCP server or update if the key already exists
addServer: (key, config) =>
set((state) => {

View File

@ -35,6 +35,7 @@ function MCPServers() {
deleteServer,
syncServers,
syncServersAndRestart,
getServerConfig,
} = useMCPServers()
const { allowAllMCPPermissions, setAllowAllMCPPermissions } =
useToolApproval()
@ -56,6 +57,9 @@ function MCPServers() {
MCPServerConfig | Record<string, MCPServerConfig> | undefined
>(undefined)
const [connectedServers, setConnectedServers] = useState<string[]>([])
const [loadingServers, setLoadingServers] = useState<{
[key: string]: boolean
}>({})
const handleOpenDialog = (serverKey?: string) => {
if (serverKey) {
@ -70,10 +74,13 @@ function MCPServers() {
setOpen(true)
}
const handleSaveServer = (name: string, config: MCPServerConfig) => {
const handleSaveServer = async (name: string, config: MCPServerConfig) => {
try {
await toggleServer(name, false)
} catch (error) {
console.error('Error deactivating server:', error)
}
if (editingKey) {
// Edit existing server
// If server name changed, delete old one and add new one
if (editingKey !== name) {
deleteServer(editingKey)
@ -85,7 +92,9 @@ function MCPServers() {
// Add new server
addServer(name, config)
}
syncServersAndRestart()
syncServers()
await toggleServer(name, true)
}
const handleEdit = (serverKey: string) => {
@ -105,7 +114,7 @@ function MCPServers() {
}
}
const handleOpenJsonEditor = (serverKey?: string) => {
const handleOpenJsonEditor = async (serverKey?: string) => {
if (serverKey) {
// Edit single server JSON
setJsonServerName(serverKey)
@ -118,12 +127,19 @@ function MCPServers() {
setJsonEditorOpen(true)
}
const handleSaveJson = (
const handleSaveJson = async (
data: MCPServerConfig | Record<string, MCPServerConfig>
) => {
if (jsonServerName) {
try {
await toggleServer(jsonServerName, false)
} catch (error) {
console.error('Error deactivating server:', error)
}
// Save single server
editServer(jsonServerName, data as MCPServerConfig)
syncServers()
toggleServer(jsonServerName, true)
} else {
// Save all servers
// Clear existing servers first
@ -138,23 +154,24 @@ function MCPServers() {
}
)
}
syncServersAndRestart()
}
const toggleServer = (serverKey: string, active: boolean) => {
if (serverKey)
if (active)
if (serverKey) {
setLoadingServers((prev) => ({ ...prev, [serverKey]: true }))
const config = getServerConfig(serverKey)
if (active && config) {
invoke('activate_mcp_server', {
name: serverKey,
config: {
...(mcpServers[serverKey] as MCPServerConfig),
...(config ?? (mcpServers[serverKey] as MCPServerConfig)),
active,
},
})
.then(() => {
// Save single server
editServer(serverKey, {
...(mcpServers[serverKey] as MCPServerConfig),
...(config ?? (mcpServers[serverKey] as MCPServerConfig)),
active,
})
syncServers()
@ -164,25 +181,30 @@ function MCPServers() {
getConnectedServers().then(setConnectedServers)
})
.catch((error) => {
editServer(serverKey, {
...(config ?? (mcpServers[serverKey] as MCPServerConfig)),
active: false,
})
toast.error(error, {
description:
'Please check the parameters according to the tutorial.',
})
})
else {
.finally(() => {
setLoadingServers((prev) => ({ ...prev, [serverKey]: false }))
})
} else {
editServer(serverKey, {
...(mcpServers[serverKey] as MCPServerConfig),
...(config ?? (mcpServers[serverKey] as MCPServerConfig)),
active,
})
syncServers()
invoke('deactivate_mcp_server', { name: serverKey })
.catch((error) => {
toast.error(`Failed to deactivate server ${serverKey}: ${error}`)
})
.finally(() => {
getConnectedServers().then(setConnectedServers)
})
invoke('deactivate_mcp_server', { name: serverKey }).finally(() => {
getConnectedServers().then(setConnectedServers)
setLoadingServers((prev) => ({ ...prev, [serverKey]: false }))
})
}
}
}
useEffect(() => {
@ -334,7 +356,8 @@ function MCPServers() {
</div>
<div className="ml-2">
<Switch
checked={config.active === false ? false : true}
checked={config.active}
loading={!!loadingServers[key]}
onCheckedChange={(checked) =>
toggleServer(key, checked)
}