refactor: streamline MCP service handling and improve IPC registration

* Refactored MCPService to implement a singleton pattern for better instance management.
* Updated IPC registration to utilize the new getMcpInstance method for handling MCP-related requests.
* Removed redundant IPC handlers from the main index file and centralized them in the ipc module.
* Added background throttling option in WindowService configuration to enhance performance.
* Introduced delays in MCPToolsButton to optimize resource and prompt fetching after initial load.
This commit is contained in:
kangfenmao
2025-05-18 18:47:01 +08:00
committed by 亢奋猫
parent 4b2417ce37
commit 8bfbbd497c
7 changed files with 320 additions and 252 deletions
+8 -3
View File
@@ -1,8 +1,8 @@
import { createSelector } from '@reduxjs/toolkit'
import store, { useAppDispatch, useAppSelector } from '@renderer/store'
import { addMCPServer, deleteMCPServer, setMCPServers, updateMCPServer } from '@renderer/store/mcp'
import { MCPServer } from '@renderer/types'
import { IpcChannel } from '@shared/IpcChannel'
import { useMemo } from 'react'
const ipcRenderer = window.electron.ipcRenderer
@@ -14,9 +14,14 @@ ipcRenderer.on(IpcChannel.Mcp_AddServer, (_event, server: MCPServer) => {
store.dispatch(addMCPServer(server))
})
const selectMcpServers = (state) => state.mcp.servers
const selectActiveMcpServers = createSelector([selectMcpServers], (servers) =>
servers.filter((server) => server.isActive)
)
export const useMCPServers = () => {
const mcpServers = useAppSelector((state) => state.mcp.servers)
const activedMcpServers = useMemo(() => mcpServers.filter((server) => server.isActive), [mcpServers])
const mcpServers = useAppSelector(selectMcpServers)
const activedMcpServers = useAppSelector(selectActiveMcpServers)
const dispatch = useAppDispatch()
return {
@@ -3,6 +3,7 @@ import { useAssistant } from '@renderer/hooks/useAssistant'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { EventEmitter } from '@renderer/services/EventService'
import { Assistant, MCPPrompt, MCPResource, MCPServer } from '@renderer/types'
import { delay, runAsyncFunction } from '@renderer/utils'
import { Form, Input, Tooltip } from 'antd'
import { Plus, SquareTerminal } from 'lucide-react'
import { FC, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'
@@ -109,6 +110,11 @@ const extractPromptContent = (response: any): string | null => {
return null
}
// Add static variable before component definition
let isFirstResourcesListCall = true
let isFirstPromptListCall = true
const initMcpDelay = 3
const MCPToolsButton: FC<Props> = ({ ref, setInputValue, resizeTextArea, ToolbarButton, ...props }) => {
const { activedMcpServers } = useMCPServers()
const { t } = useTranslation()
@@ -308,6 +314,11 @@ const MCPToolsButton: FC<Props> = ({ ref, setInputValue, resizeTextArea, Toolbar
const promptList = useMemo(async () => {
const prompts: MCPPrompt[] = []
if (isFirstPromptListCall) {
await delay(initMcpDelay)
isFirstPromptListCall = false
}
for (const server of activedMcpServers) {
const serverPrompts = await window.api.mcp.listPrompts(server)
prompts.push(...serverPrompts)
@@ -319,7 +330,8 @@ const MCPToolsButton: FC<Props> = ({ ref, setInputValue, resizeTextArea, Toolbar
icon: <SquareTerminal />,
action: () => handlePromptSelect(prompt as MCPPromptWithArgs)
}))
}, [handlePromptSelect, activedMcpServers])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activedMcpServers])
const openPromptList = useCallback(async () => {
const prompts = await promptList
@@ -380,33 +392,42 @@ const MCPToolsButton: FC<Props> = ({ ref, setInputValue, resizeTextArea, Toolbar
const [resourcesList, setResourcesList] = useState<QuickPanelListItem[]>([])
useEffect(() => {
let isMounted = true
runAsyncFunction(async () => {
let isMounted = true
const fetchResources = async () => {
const resources: MCPResource[] = []
for (const server of activedMcpServers) {
const serverResources = await window.api.mcp.listResources(server)
resources.push(...serverResources)
const fetchResources = async () => {
const resources: MCPResource[] = []
for (const server of activedMcpServers) {
const serverResources = await window.api.mcp.listResources(server)
resources.push(...serverResources)
}
if (isMounted) {
setResourcesList(
resources.map((resource) => ({
label: resource.name,
description: resource.description,
icon: <SquareTerminal />,
action: () => handleResourceSelect(resource)
}))
)
}
}
if (isMounted) {
setResourcesList(
resources.map((resource) => ({
label: resource.name,
description: resource.description,
icon: <SquareTerminal />,
action: () => handleResourceSelect(resource)
}))
)
// Avoid mcp following the software startup, affecting the startup speed
if (isFirstResourcesListCall) {
await delay(initMcpDelay)
isFirstResourcesListCall = false
fetchResources()
}
}
fetchResources()
return () => {
isMounted = false
}
}, [activedMcpServers, handleResourceSelect])
return () => {
isMounted = false
}
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activedMcpServers])
const openResourcesList = useCallback(async () => {
const resources = resourcesList