From d56521260c6334047073052952df0270979c7d48 Mon Sep 17 00:00:00 2001 From: Vaayne Date: Sat, 20 Sep 2025 00:13:26 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20perf:=20add=20caching=20la?= =?UTF-8?q?yer=20for=20MCP=20servers=20and=20providers=20data=20access?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Extract getServersFromRedux to shared utility getMCPServersFromRedux - Implement 5-minute TTL cache for MCP servers and providers - Reduce redundant Redux store queries in API server - Improve response times for frequently accessed data --- src/main/apiServer/services/mcp.ts | 31 +++--------------------------- src/main/apiServer/utils/index.ts | 17 +++++++++++++++- src/main/apiServer/utils/mcp.ts | 30 ++++++++++++++++++++++++----- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/main/apiServer/services/mcp.ts b/src/main/apiServer/services/mcp.ts index 99f173211..f18e662f2 100644 --- a/src/main/apiServer/services/mcp.ts +++ b/src/main/apiServer/services/mcp.ts @@ -13,8 +13,7 @@ import { Request, Response } from 'express' import { IncomingMessage, ServerResponse } from 'http' import { loggerService } from '../../services/LoggerService' -import { reduxService } from '../../services/ReduxService' -import { getMcpServerById } from '../utils/mcp' +import { getMcpServerById, getMCPServersFromRedux } from '../utils/mcp' const logger = loggerService.withContext('MCPApiService') const transports: Record = {} @@ -57,34 +56,10 @@ class MCPApiService extends EventEmitter { this.transport.onmessage = this.onMessage } - /** - * Get servers directly from Redux store - */ - private async getServersFromRedux(): Promise { - try { - logger.silly('Getting servers from Redux store') - - // Try to get from cache first (faster) - const cachedServers = reduxService.selectSync('state.mcp.servers') - if (cachedServers && Array.isArray(cachedServers)) { - logger.silly(`Found ${cachedServers.length} servers in Redux cache`) - return cachedServers - } - - // If cache is not available, get fresh data - const servers = await reduxService.select('state.mcp.servers') - logger.silly(`Fetched ${servers?.length || 0} servers from Redux store`) - return servers || [] - } catch (error: any) { - logger.error('Failed to get servers from Redux:', error) - return [] - } - } - // get all activated servers async getAllServers(req: Request): Promise { try { - const servers = await this.getServersFromRedux() + const servers = await getMCPServersFromRedux() logger.silly(`Returning ${servers.length} servers`) const resp: McpServersResp = { servers: {} @@ -111,7 +86,7 @@ class MCPApiService extends EventEmitter { async getServerById(id: string): Promise { try { logger.silly(`getServerById called with id: ${id}`) - const servers = await this.getServersFromRedux() + const servers = await getMCPServersFromRedux() const server = servers.find((s) => s.id === id) if (!server) { logger.warn(`Server with id ${id} not found`) diff --git a/src/main/apiServer/utils/index.ts b/src/main/apiServer/utils/index.ts index f1e0d6845..bd2a9aa92 100644 --- a/src/main/apiServer/utils/index.ts +++ b/src/main/apiServer/utils/index.ts @@ -1,12 +1,24 @@ +import { CacheService } from '@main/services/CacheService' import { loggerService } from '@main/services/LoggerService' import { reduxService } from '@main/services/ReduxService' import { ApiModel, Model, Provider } from '@types' const logger = loggerService.withContext('ApiServerUtils') +// Cache configuration +const PROVIDERS_CACHE_KEY = 'api-server:providers' +const PROVIDERS_CACHE_TTL = 5 * 60 * 1000 // 5 minutes + export async function getAvailableProviders(): Promise { try { - // Wait for store to be ready before accessing providers + // Try to get from cache first (faster) + const cachedSupportedProviders = CacheService.get(PROVIDERS_CACHE_KEY) + if (cachedSupportedProviders) { + logger.debug(`Found ${cachedSupportedProviders.length} supported providers (from cache)`) + return cachedSupportedProviders + } + + // If cache is not available, get fresh data from Redux const providers = await reduxService.select('state.llm.providers') if (!providers || !Array.isArray(providers)) { logger.warn('No providers found in Redux store, returning empty array') @@ -18,6 +30,9 @@ export async function getAvailableProviders(): Promise { (p: Provider) => p.enabled && (p.type === 'openai' || p.type === 'anthropic') ) + // Cache the filtered results + CacheService.set(PROVIDERS_CACHE_KEY, supportedProviders, PROVIDERS_CACHE_TTL) + logger.info(`Filtered to ${supportedProviders.length} supported providers from ${providers.length} total providers`) return supportedProviders diff --git a/src/main/apiServer/utils/mcp.ts b/src/main/apiServer/utils/mcp.ts index 1ebe06ba6..380b8f1d9 100644 --- a/src/main/apiServer/utils/mcp.ts +++ b/src/main/apiServer/utils/mcp.ts @@ -1,3 +1,4 @@ +import { CacheService } from '@main/services/CacheService' import mcpService from '@main/services/MCPService' import { Server } from '@modelcontextprotocol/sdk/server/index.js' import { CallToolRequestSchema, ListToolsRequestSchema, ListToolsResult } from '@modelcontextprotocol/sdk/types.js' @@ -8,6 +9,10 @@ import { reduxService } from '../../services/ReduxService' const logger = loggerService.withContext('MCPApiService') +// Cache configuration +const MCP_SERVERS_CACHE_KEY = 'api-server:mcp-servers' +const MCP_SERVERS_CACHE_TTL = 5 * 60 * 1000 // 5 minutes + const cachedServers: Record = {} async function handleListToolsRequest(request: any, extra: any): Promise { @@ -33,18 +38,33 @@ async function handleCallToolRequest(request: any, extra: any): Promise { } async function getMcpServerConfigById(id: string): Promise { - const servers = await getServersFromRedux() + const servers = await getMCPServersFromRedux() return servers.find((s) => s.id === id || s.name === id) } /** * Get servers directly from Redux store */ -async function getServersFromRedux(): Promise { +export async function getMCPServersFromRedux(): Promise { try { + logger.silly('Getting servers from Redux store') + + // Try to get from cache first (faster) + const cachedServers = CacheService.get(MCP_SERVERS_CACHE_KEY) + if (cachedServers) { + logger.silly(`Found ${cachedServers.length} servers (from cache)`) + return cachedServers + } + + // If cache is not available, get fresh data from Redux const servers = await reduxService.select('state.mcp.servers') - logger.silly(`Fetched ${servers?.length || 0} servers from Redux store`) - return servers || [] + const serverList = servers || [] + + // Cache the results + CacheService.set(MCP_SERVERS_CACHE_KEY, serverList, MCP_SERVERS_CACHE_TTL) + + logger.silly(`Fetched ${serverList.length} servers from Redux store`) + return serverList } catch (error: any) { logger.error('Failed to get servers from Redux:', error) return [] @@ -54,7 +74,7 @@ async function getServersFromRedux(): Promise { export async function getMcpServerById(id: string): Promise { const server = cachedServers[id] if (!server) { - const servers = await getServersFromRedux() + const servers = await getMCPServersFromRedux() const mcpServer = servers.find((s) => s.id === id || s.name === id) if (!mcpServer) { throw new Error(`Server not found: ${id}`)