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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user