mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-27 12:51:26 +08:00
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:
parent
80b2fabea0
commit
18f52f2717
@ -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'))
|
||||
}
|
||||
|
||||
@ -1715,6 +1715,7 @@
|
||||
"openrouter": "OpenRouter",
|
||||
"perplexity": "Perplexity",
|
||||
"ph8": "PH8",
|
||||
"poe": "Poe",
|
||||
"ppio": "PPIO",
|
||||
"qiniu": "Qiniu AI",
|
||||
"qwenlm": "QwenLM",
|
||||
|
||||
@ -1715,6 +1715,7 @@
|
||||
"openrouter": "OpenRouter",
|
||||
"perplexity": "Perplexity",
|
||||
"ph8": "PH8",
|
||||
"poe": "Poe",
|
||||
"ppio": "PPIO パイオウクラウド",
|
||||
"qiniu": "七牛云 AI 推理",
|
||||
"qwenlm": "QwenLM",
|
||||
|
||||
@ -1715,6 +1715,7 @@
|
||||
"openrouter": "OpenRouter",
|
||||
"perplexity": "Perplexity",
|
||||
"ph8": "PH8",
|
||||
"poe": "Poe",
|
||||
"ppio": "PPIO",
|
||||
"qiniu": "Qiniu AI",
|
||||
"qwenlm": "QwenLM",
|
||||
|
||||
@ -1715,6 +1715,7 @@
|
||||
"openrouter": "OpenRouter",
|
||||
"perplexity": "Perplexity",
|
||||
"ph8": "PH8 大模型开放平台",
|
||||
"poe": "Poe",
|
||||
"ppio": "PPIO 派欧云",
|
||||
"qiniu": "七牛云 AI 推理",
|
||||
"qwenlm": "QwenLM",
|
||||
|
||||
@ -1715,6 +1715,7 @@
|
||||
"openrouter": "OpenRouter",
|
||||
"perplexity": "Perplexity",
|
||||
"ph8": "PH8 大模型開放平台",
|
||||
"poe": "Poe",
|
||||
"ppio": "PPIO 派歐雲",
|
||||
"qiniu": "七牛雲 AI 推理",
|
||||
"qwenlm": "QwenLM",
|
||||
|
||||
@ -1715,6 +1715,7 @@
|
||||
"openrouter": "OpenRouter",
|
||||
"perplexity": "Perplexity",
|
||||
"ph8": "Πλατφόρμα Ανοιχτής Μεγάλης Μοντέλου PH8",
|
||||
"poe": "Poe",
|
||||
"ppio": "PPIO Piao Yun",
|
||||
"qiniu": "Qiniu AI",
|
||||
"qwenlm": "QwenLM",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 }}>
|
||||
|
||||
@ -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'
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user