refactor: simplify MCP service integration and cleanup logic

- Replaced singleton pattern with direct instantiation of McpService for cleaner code.
- Updated IPC handlers to use the new mcpService instance directly.
- Removed unnecessary logging in Inputbar and MCPToolsButton components to streamline functionality.
- Cleaned up error handling in McpSettings to improve user experience.
This commit is contained in:
kangfenmao 2025-05-21 13:10:27 +08:00
parent 44685d8dcf
commit be49503bde
6 changed files with 36 additions and 88 deletions

View File

@ -115,7 +115,7 @@ if (!app.requestSingleInstanceLock()) {
app.on('will-quit', async () => {
// event.preventDefault()
try {
await mcpService().cleanup()
await mcpService.cleanup()
} catch (error) {
Logger.error('Error cleaning up MCP service:', error)
}

View File

@ -20,7 +20,7 @@ import FileService from './services/FileService'
import FileStorage from './services/FileStorage'
import { GeminiService } from './services/GeminiService'
import KnowledgeService from './services/KnowledgeService'
import { getMcpInstance } from './services/MCPService'
import mcpService from './services/MCPService'
import NotificationService from './services/NotificationService'
import * as NutstoreService from './services/NutstoreService'
import ObsidianVaultService from './services/ObsidianVaultService'
@ -320,19 +320,17 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
)
// Register MCP handlers
ipcMain.handle(IpcChannel.Mcp_RemoveServer, (event, server) => getMcpInstance().removeServer(event, server))
ipcMain.handle(IpcChannel.Mcp_RestartServer, (event, server) => getMcpInstance().restartServer(event, server))
ipcMain.handle(IpcChannel.Mcp_StopServer, (event, server) => getMcpInstance().stopServer(event, server))
ipcMain.handle(IpcChannel.Mcp_ListTools, (event, server) => getMcpInstance().listTools(event, server))
ipcMain.handle(IpcChannel.Mcp_CallTool, (event, params) => getMcpInstance().callTool(event, params))
ipcMain.handle(IpcChannel.Mcp_ListPrompts, (event, server) => getMcpInstance().listPrompts(event, server))
ipcMain.handle(IpcChannel.Mcp_GetPrompt, (event, params) => getMcpInstance().getPrompt(event, params))
ipcMain.handle(IpcChannel.Mcp_ListResources, (event, server) => getMcpInstance().listResources(event, server))
ipcMain.handle(IpcChannel.Mcp_GetResource, (event, params) => getMcpInstance().getResource(event, params))
ipcMain.handle(IpcChannel.Mcp_GetInstallInfo, () => getMcpInstance().getInstallInfo())
ipcMain.handle(IpcChannel.Mcp_CheckConnectivity, (event, params) =>
getMcpInstance().checkMcpConnectivity(event, params)
)
ipcMain.handle(IpcChannel.Mcp_RemoveServer, mcpService.removeServer)
ipcMain.handle(IpcChannel.Mcp_RestartServer, mcpService.restartServer)
ipcMain.handle(IpcChannel.Mcp_StopServer, mcpService.stopServer)
ipcMain.handle(IpcChannel.Mcp_ListTools, mcpService.listTools)
ipcMain.handle(IpcChannel.Mcp_CallTool, mcpService.callTool)
ipcMain.handle(IpcChannel.Mcp_ListPrompts, mcpService.listPrompts)
ipcMain.handle(IpcChannel.Mcp_GetPrompt, mcpService.getPrompt)
ipcMain.handle(IpcChannel.Mcp_ListResources, mcpService.listResources)
ipcMain.handle(IpcChannel.Mcp_GetResource, mcpService.getResource)
ipcMain.handle(IpcChannel.Mcp_GetInstallInfo, mcpService.getInstallInfo)
ipcMain.handle(IpcChannel.Mcp_CheckConnectivity, mcpService.checkMcpConnectivity)
ipcMain.handle(IpcChannel.App_IsBinaryExist, (_, name: string) => isBinaryExists(name))
ipcMain.handle(IpcChannel.App_GetBinaryPath, (_, name: string) => getBinaryPath(name))

View File

@ -69,18 +69,10 @@ function withCache<T extends unknown[], R>(
}
class McpService {
private static instance: McpService | null = null
private clients: Map<string, Client> = new Map()
private pendingClients: Map<string, Promise<Client>> = new Map()
public static getInstance(): McpService {
if (!McpService.instance) {
McpService.instance = new McpService()
}
return McpService.instance
}
private constructor() {
constructor() {
this.initClient = this.initClient.bind(this)
this.listTools = this.listTools.bind(this)
this.callTool = this.callTool.bind(this)
@ -661,13 +653,4 @@ class McpService {
})
}
let mcpInstance: ReturnType<typeof McpService.getInstance> | null = null
export const getMcpInstance = () => {
if (!mcpInstance) {
mcpInstance = McpService.getInstance()
}
return mcpInstance
}
export default McpService.getInstance
export default new McpService()

View File

@ -226,10 +226,7 @@ const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic, topic }) =
const { message, blocks } = getUserMessage(baseUserMessage)
currentMessageId.current = message.id
Logger.log('[DEBUG] Created message and blocks:', message, blocks)
Logger.log('[DEBUG] Dispatching _sendMessage')
dispatch(_sendMessage(message, blocks, assistant, topic.id))
Logger.log('[DEBUG] _sendMessage dispatched')
// Clear input
setText('')

View File

@ -3,7 +3,6 @@ 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'
@ -110,11 +109,6 @@ 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()
@ -314,11 +308,6 @@ 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)
@ -392,40 +381,33 @@ const MCPToolsButton: FC<Props> = ({ ref, setInputValue, resizeTextArea, Toolbar
const [resourcesList, setResourcesList] = useState<QuickPanelListItem[]>([])
useEffect(() => {
runAsyncFunction(async () => {
let isMounted = true
let isMounted = true
const fetchResources = async () => {
const resources: MCPResource[] = []
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)
}))
)
}
for (const server of activedMcpServers) {
const serverResources = await window.api.mcp.listResources(server)
resources.push(...serverResources)
}
// Avoid mcp following the software startup, affecting the startup speed
if (isFirstResourcesListCall) {
await delay(initMcpDelay)
isFirstResourcesListCall = false
fetchResources()
if (isMounted) {
setResourcesList(
resources.map((resource) => ({
label: resource.name,
description: resource.description,
icon: <SquareTerminal />,
action: () => handleResourceSelect(resource)
}))
)
}
}
return () => {
isMounted = false
}
})
fetchResources()
return () => {
isMounted = false
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activedMcpServers])

View File

@ -168,10 +168,6 @@ const McpSettings: React.FC = () => {
setTools(localTools)
} catch (error) {
setLoadingServer(server.id)
window.message.error({
content: t('settings.mcp.tools.loadError') + ' ' + formatError(error),
key: 'mcp-tools-error'
})
} finally {
setLoadingServer(null)
}
@ -185,10 +181,6 @@ const McpSettings: React.FC = () => {
const localPrompts = await window.api.mcp.listPrompts(server)
setPrompts(localPrompts)
} catch (error) {
window.message.error({
content: t('settings.mcp.prompts.loadError') + ' ' + formatError(error),
key: 'mcp-prompts-error'
})
setPrompts([])
} finally {
setLoadingServer(null)
@ -203,10 +195,6 @@ const McpSettings: React.FC = () => {
const localResources = await window.api.mcp.listResources(server)
setResources(localResources)
} catch (error) {
window.message.error({
content: t('settings.mcp.resources.loadError') + ' ' + formatError(error),
key: 'mcp-resources-error'
})
setResources([])
} finally {
setLoadingServer(null)