jan/web-app/src/hooks/useMCPServers.ts
Dinh Long Nguyen a30eb7f968
feat: Jan Web (reusing Jan Desktop UI) (#6298)
* add platform guards

* add service management

* fix types

* move to zustand for servicehub

* update App Updater

* update tauri missing move

* update app updater

* refactor: move PlatformFeatures to separate const file

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

* change tauri fetch name

* update implementation

* update extension fetch

* make web version run properly

* disabled unused web settings

* fix all tests

* fix lint

* fix tests

* add mock for extension

* fix build

* update make and mise

* fix tsconfig for web-extensions

* fix loader type

* cleanup

* fix test

* update error handling + mcp should be working

* Update mcp init

* use separate is_web_app build property

* Remove fixed model catalog url

* fix additional tests

* fix download issue (event emitter not implemented correctly)

* Update Title html

* fix app logs

* update root tsx render timing

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-05 01:47:46 +07:00

129 lines
3.7 KiB
TypeScript

import { create } from 'zustand'
import { getServiceHub } from '@/hooks/useServiceHub'
// Define the structure of an MCP server configuration
export type MCPServerConfig = {
command: string
args: string[]
env: Record<string, string>
active?: boolean
type?: 'stdio' | 'http' | 'sse'
url?: string
headers?: Record<string, string>
timeout?: number
}
// Define the structure of all MCP servers
export type MCPServers = {
[key: string]: MCPServerConfig
}
type MCPServerStoreState = {
open: boolean
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
renameServer: (
oldKey: string,
newKey: string,
config: MCPServerConfig
) => void
deleteServer: (key: string) => void
setServers: (servers: MCPServers) => void
syncServers: () => Promise<void>
syncServersAndRestart: () => Promise<void>
}
export const useMCPServers = create<MCPServerStoreState>()((set, get) => ({
open: true,
mcpServers: {}, // Start with empty object
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) => {
// Remove the key first if it exists to maintain insertion order
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { [key]: _, ...restServers } = state.mcpServers
const mcpServers = { [key]: config, ...restServers }
return { mcpServers }
}),
// Edit an existing MCP server configuration
editServer: (key, config) =>
set((state) => {
// Only proceed if the server exists
if (!state.mcpServers[key]) return state
const mcpServers = { ...state.mcpServers, [key]: config }
return { mcpServers }
}),
// Rename a server while preserving its position
renameServer: (oldKey, newKey, config) =>
set((state) => {
// Only proceed if the server exists
if (!state.mcpServers[oldKey]) return state
const entries = Object.entries(state.mcpServers)
const mcpServers: MCPServers = {}
// Rebuild the object with the same order, replacing the old key with the new key
entries.forEach(([key, serverConfig]) => {
if (key === oldKey) {
mcpServers[newKey] = config
} else {
mcpServers[key] = serverConfig
}
})
return { mcpServers }
}),
setServers: (servers) =>
set((state) => {
const mcpServers = { ...state.mcpServers, ...servers }
return { mcpServers }
}),
// Delete an MCP server by key
deleteServer: (key) =>
set((state) => {
// Create a copy of the current state
const updatedServers = { ...state.mcpServers }
// Delete the server if it exists
if (updatedServers[key]) {
delete updatedServers[key]
}
return {
mcpServers: updatedServers,
deletedServerKeys: [...state.deletedServerKeys, key],
}
}),
syncServers: async () => {
const mcpServers = get().mcpServers
await getServiceHub().mcp().updateMCPConfig(
JSON.stringify({
mcpServers,
})
)
},
syncServersAndRestart: async () => {
const mcpServers = get().mcpServers
await getServiceHub().mcp().updateMCPConfig(
JSON.stringify({
mcpServers,
})
).then(() => getServiceHub().mcp().restartMCPServers())
},
}))