fix(mcp): builtin mcp objects cannot be serialized (#8903)

* fix(mcp): 修复无法序列化的问题

* refactor(i18n): 重构MCP服务器描述的国际化和错误处理

移除MCPServer接口中的descriptionI18nKey字段,改为使用统一的getLabel函数处理国际化
添加getBuiltInMcpServerDescriptionLabel函数集中管理MCP服务器描述
在标签翻译失败时添加错误日志记录

* feat(i18n): 添加 Poe 供应商的多语言支持并重构标签获取逻辑

* refactor(i18n): 优化标签翻译函数中的变量使用

* refactor(i18n): 将参数key重命名为id以提升可读性
This commit is contained in:
Phantom 2025-08-07 13:36:44 +08:00 committed by GitHub
parent 80b2fabea0
commit 18f52f2717
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 61 additions and 39 deletions

View File

@ -4,10 +4,24 @@
* 2.
*/
import { loggerService } from '@logger'
import i18n from './index'
const t = i18n.t
const logger = loggerService.withContext('i18n:label')
const getLabel = (key: string, keyMap: Record<string, string>, fallback?: string) => {
const result = keyMap[key]
if (result) {
return t(result)
} else {
logger.error(`Missing key ${key}`)
return fallback ?? key
}
}
const providerKeyMap = {
'302ai': 'provider.302ai',
aihubmix: 'provider.aihubmix',
@ -63,20 +77,21 @@ const providerKeyMap = {
xirang: 'provider.xirang',
yi: 'provider.yi',
zhinao: 'provider.zhinao',
zhipu: 'provider.zhipu'
zhipu: 'provider.zhipu',
poe: 'provider.poe'
} as const
/**
*
* @param key - key
* @param id - id
* @returns
* @remarks
* i18n label
*
* 使 getProviderName getFancyProviderName
*/
export const getProviderLabel = (key: string): string => {
return providerKeyMap[key] ? t(providerKeyMap[key]) : key
export const getProviderLabel = (id: string): string => {
return getLabel(id, providerKeyMap)
}
const progressKeyMap = {
@ -89,7 +104,7 @@ const progressKeyMap = {
} as const
export const getProgressLabel = (key: string): string => {
return progressKeyMap[key] ? t(progressKeyMap[key]) : key
return getLabel(key, progressKeyMap)
}
const titleKeyMap = {
@ -107,7 +122,7 @@ const titleKeyMap = {
} as const
export const getTitleLabel = (key: string): string => {
return titleKeyMap[key] ? t(titleKeyMap[key]) : key
return getLabel(key, titleKeyMap)
}
const themeModeKeyMap = {
@ -117,7 +132,7 @@ const themeModeKeyMap = {
} as const
export const getThemeModeLabel = (key: string): string => {
return themeModeKeyMap[key] ? t(themeModeKeyMap[key]) : key
return getLabel(key, themeModeKeyMap)
}
const sidebarIconKeyMap = {
@ -131,7 +146,7 @@ const sidebarIconKeyMap = {
} as const
export const getSidebarIconLabel = (key: string): string => {
return sidebarIconKeyMap[key] ? t(sidebarIconKeyMap[key]) : key
return getLabel(key, sidebarIconKeyMap)
}
const shortcutKeyMap = {
@ -165,7 +180,7 @@ const shortcutKeyMap = {
} as const
export const getShortcutLabel = (key: string): string => {
return shortcutKeyMap[key] ? t(shortcutKeyMap[key]) : key
return getLabel(key, shortcutKeyMap)
}
const selectionDescriptionKeyMap = {
@ -174,7 +189,7 @@ const selectionDescriptionKeyMap = {
} as const
export const getSelectionDescriptionLabel = (key: string): string => {
return selectionDescriptionKeyMap[key] ? t(selectionDescriptionKeyMap[key]) : key
return getLabel(key, selectionDescriptionKeyMap)
}
const paintingsImageSizeOptionsKeyMap = {
@ -182,7 +197,7 @@ const paintingsImageSizeOptionsKeyMap = {
} as const
export const getPaintingsImageSizeOptionsLabel = (key: string): string => {
return paintingsImageSizeOptionsKeyMap[key] ? t(paintingsImageSizeOptionsKeyMap[key]) : key
return getLabel(key, paintingsImageSizeOptionsKeyMap)
}
const paintingsQualityOptionsKeyMap = {
@ -193,7 +208,7 @@ const paintingsQualityOptionsKeyMap = {
} as const
export const getPaintingsQualityOptionsLabel = (key: string): string => {
return paintingsQualityOptionsKeyMap[key] ? t(paintingsQualityOptionsKeyMap[key]) : key
return getLabel(key, paintingsQualityOptionsKeyMap)
}
const paintingsModerationOptionsKeyMap = {
@ -202,7 +217,7 @@ const paintingsModerationOptionsKeyMap = {
} as const
export const getPaintingsModerationOptionsLabel = (key: string): string => {
return paintingsModerationOptionsKeyMap[key] ? t(paintingsModerationOptionsKeyMap[key]) : key
return getLabel(key, paintingsModerationOptionsKeyMap)
}
const paintingsBackgroundOptionsKeyMap = {
@ -212,7 +227,7 @@ const paintingsBackgroundOptionsKeyMap = {
} as const
export const getPaintingsBackgroundOptionsLabel = (key: string): string => {
return paintingsBackgroundOptionsKeyMap[key] ? t(paintingsBackgroundOptionsKeyMap[key]) : key
return getLabel(key, paintingsBackgroundOptionsKeyMap)
}
const mcpTypeKeyMap = {
@ -223,7 +238,7 @@ const mcpTypeKeyMap = {
} as const
export const getMcpTypeLabel = (key: string): string => {
return mcpTypeKeyMap[key] ? t(mcpTypeKeyMap[key]) : key
return getLabel(key, mcpTypeKeyMap)
}
const miniappsStatusKeyMap = {
@ -232,7 +247,7 @@ const miniappsStatusKeyMap = {
} as const
export const getMiniappsStatusLabel = (key: string): string => {
return miniappsStatusKeyMap[key] ? t(miniappsStatusKeyMap[key]) : key
return getLabel(key, miniappsStatusKeyMap)
}
const httpMessageKeyMap = {
@ -248,7 +263,7 @@ const httpMessageKeyMap = {
} as const
export const getHttpMessageLabel = (key: string): string => {
return httpMessageKeyMap[key] ? t(httpMessageKeyMap[key]) : key
return getLabel(key, httpMessageKeyMap)
}
const reasoningEffortOptionsKeyMap = {
@ -261,7 +276,7 @@ const reasoningEffortOptionsKeyMap = {
} as const
export const getReasoningEffortOptionsLabel = (key: string): string => {
return reasoningEffortOptionsKeyMap[key] ? t(reasoningEffortOptionsKeyMap[key]) : key
return getLabel(key, reasoningEffortOptionsKeyMap)
}
const fileFieldKeyMap = {
@ -271,5 +286,20 @@ const fileFieldKeyMap = {
} as const
export const getFileFieldLabel = (key: string): string => {
return fileFieldKeyMap[key] ? t(fileFieldKeyMap[key]) : key
return getLabel(key, fileFieldKeyMap)
}
const builtInMcpDescriptionKeyMap = {
'@cherry/mcp-auto-install': 'settings.mcp.builtinServersDescriptions.mcp_auto_install',
'@cherry/memory': 'settings.mcp.builtinServersDescriptions.mcp_auto_install',
'@cherry/sequentialthinking': 'settings.mcp.builtinServersDescriptions.sequentialthinking',
'@cherry/brave-search': 'settings.mcp.builtinServersDescriptions.brave_search',
'@cherry/fetch': 'settings.mcp.builtinServersDescriptions.fetch',
'@cherry/filesystem': 'settings.mcp.builtinServersDescriptions.filesystem',
'@cherry/dify-knowledge': 'settings.mcp.builtinServersDescriptions.dify_knowledge',
'@cherry/python': 'settings.mcp.builtinServersDescriptions.python'
} as const
export const getBuiltInMcpServerDescriptionLabel = (key: string): string => {
return getLabel(key, builtInMcpDescriptionKeyMap, t('settings.mcp.builtinServersDescriptions.no'))
}

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplexity",
"ph8": "PH8",
"poe": "Poe",
"ppio": "PPIO",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplexity",
"ph8": "PH8",
"poe": "Poe",
"ppio": "PPIO パイオウクラウド",
"qiniu": "七牛云 AI 推理",
"qwenlm": "QwenLM",

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplexity",
"ph8": "PH8",
"poe": "Poe",
"ppio": "PPIO",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplexity",
"ph8": "PH8 大模型开放平台",
"poe": "Poe",
"ppio": "PPIO 派欧云",
"qiniu": "七牛云 AI 推理",
"qwenlm": "QwenLM",

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplexity",
"ph8": "PH8 大模型開放平台",
"poe": "Poe",
"ppio": "PPIO 派歐雲",
"qiniu": "七牛雲 AI 推理",
"qwenlm": "QwenLM",

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplexity",
"ph8": "Πλατφόρμα Ανοιχτής Μεγάλης Μοντέλου PH8",
"poe": "Poe",
"ppio": "PPIO Piao Yun",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplejidad",
"ph8": "Plataforma Abierta de Grandes Modelos PH8",
"poe": "Poe",
"ppio": "PPIO Cloud Piao",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplexité",
"ph8": "Plateforme ouverte de grands modèles PH8",
"poe": "Poe",
"ppio": "PPIO Cloud Piou",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",

View File

@ -1715,6 +1715,7 @@
"openrouter": "OpenRouter",
"perplexity": "Perplexidade",
"ph8": "Plataforma Aberta de Grandes Modelos PH8",
"poe": "Poe",
"ppio": "PPIO Nuvem Piao",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",

View File

@ -1,6 +1,6 @@
import { CheckOutlined, PlusOutlined } from '@ant-design/icons'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { getMcpTypeLabel } from '@renderer/i18n/label'
import { getBuiltInMcpServerDescriptionLabel, getMcpTypeLabel } from '@renderer/i18n/label'
import { builtinMCPServers } from '@renderer/store/mcp'
import { Button, Popover, Tag } from 'antd'
import { FC } from 'react'
@ -46,10 +46,7 @@ const BuiltinMCPServersSection: FC = () => {
<Popover
content={
<PopoverContent>
{server.getBuiltinDescription
? server.getBuiltinDescription()
: t('settings.mcp.builtinServersDescriptions.no')}
{getBuiltInMcpServerDescriptionLabel(server.name)}
{server.reference && <ReferenceLink href={server.reference}>{server.reference}</ReferenceLink>}
</PopoverContent>
}
@ -57,11 +54,7 @@ const BuiltinMCPServersSection: FC = () => {
trigger="hover"
placement="topLeft"
overlayStyle={{ maxWidth: 400 }}>
<ServerDescription>
{server.getBuiltinDescription
? server.getBuiltinDescription()
: t('settings.mcp.builtinServersDescriptions.no')}
</ServerDescription>
<ServerDescription>{getBuiltInMcpServerDescriptionLabel(server.name)}</ServerDescription>
</Popover>
<ServerFooter>
<Tag color="processing" style={{ borderRadius: 20, margin: 0, fontWeight: 500 }}>

View File

@ -1,6 +1,5 @@
import { loggerService } from '@logger'
import { createSlice, nanoid, type PayloadAction } from '@reduxjs/toolkit'
import i18n from '@renderer/i18n'
import type { MCPConfig, MCPServer } from '@renderer/types'
const logger = loggerService.withContext('Store:MCP')
@ -76,7 +75,6 @@ export const builtinMCPServers: MCPServer[] = [
id: nanoid(),
name: '@cherry/mcp-auto-install',
reference: 'https://docs.cherry-ai.com/advanced-basic/mcp/auto-install',
getBuiltinDescription: () => i18n.t('settings.mcp.builtinServersDescriptions.mcp_auto_install'),
type: 'inMemory',
command: 'npx',
args: ['-y', '@mcpmarket/mcp-auto-install', 'connect', '--json'],
@ -87,7 +85,6 @@ export const builtinMCPServers: MCPServer[] = [
id: nanoid(),
name: '@cherry/memory',
reference: 'https://github.com/modelcontextprotocol/servers/tree/main/src/memory',
getBuiltinDescription: () => i18n.t('settings.mcp.builtinServersDescriptions.memory'),
type: 'inMemory',
isActive: true,
env: {
@ -99,7 +96,6 @@ export const builtinMCPServers: MCPServer[] = [
{
id: nanoid(),
name: '@cherry/sequentialthinking',
getBuiltinDescription: () => i18n.t('settings.mcp.builtinServersDescriptions.sequentialthinking'),
type: 'inMemory',
isActive: true,
provider: 'CherryAI'
@ -108,7 +104,6 @@ export const builtinMCPServers: MCPServer[] = [
id: nanoid(),
name: '@cherry/brave-search',
type: 'inMemory',
getBuiltinDescription: () => i18n.t('settings.mcp.builtinServersDescriptions.brave_search'),
isActive: false,
env: {
BRAVE_API_KEY: 'YOUR_API_KEY'
@ -120,7 +115,6 @@ export const builtinMCPServers: MCPServer[] = [
id: nanoid(),
name: '@cherry/fetch',
type: 'inMemory',
getBuiltinDescription: () => i18n.t('settings.mcp.builtinServersDescriptions.fetch'),
isActive: true,
provider: 'CherryAI'
},
@ -128,7 +122,6 @@ export const builtinMCPServers: MCPServer[] = [
id: nanoid(),
name: '@cherry/filesystem',
type: 'inMemory',
getBuiltinDescription: () => i18n.t('settings.mcp.builtinServersDescriptions.filesystem'),
args: ['/Users/username/Desktop', '/path/to/other/allowed/dir'],
shouldConfig: true,
isActive: false,
@ -138,7 +131,6 @@ export const builtinMCPServers: MCPServer[] = [
id: nanoid(),
name: '@cherry/dify-knowledge',
type: 'inMemory',
getBuiltinDescription: () => i18n.t('settings.mcp.builtinServersDescriptions.dify_knowledge'),
isActive: false,
env: {
DIFY_KEY: 'YOUR_DIFY_KEY'
@ -150,7 +142,6 @@ export const builtinMCPServers: MCPServer[] = [
id: nanoid(),
name: '@cherry/python',
type: 'inMemory',
getBuiltinDescription: () => i18n.t('settings.mcp.builtinServersDescriptions.python'),
isActive: false,
provider: 'CherryAI'
}

View File

@ -659,7 +659,6 @@ export interface MCPServer {
args?: string[]
env?: Record<string, string>
shouldConfig?: boolean
getBuiltinDescription?: () => string
isActive: boolean
disabledTools?: string[] // List of tool names that are disabled for this server
disabledAutoApproveTools?: string[] // Whether to auto-approve tools for this server