diff --git a/src/renderer/src/assets/images/cherry-text-logo.svg b/src/renderer/src/assets/images/cherry-text-logo.svg
new file mode 100644
index 0000000000..4dad25f29f
--- /dev/null
+++ b/src/renderer/src/assets/images/cherry-text-logo.svg
@@ -0,0 +1,55 @@
+
+
\ No newline at end of file
diff --git a/src/renderer/src/assets/images/mcp/npm.svg b/src/renderer/src/assets/images/mcp/npm.svg
deleted file mode 100644
index df5b545d2f..0000000000
--- a/src/renderer/src/assets/images/mcp/npm.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/renderer/src/hooks/useMCPServers.ts b/src/renderer/src/hooks/useMCPServers.ts
index b8d28cb75f..316f89d9dd 100644
--- a/src/renderer/src/hooks/useMCPServers.ts
+++ b/src/renderer/src/hooks/useMCPServers.ts
@@ -29,8 +29,13 @@ export const useMCPServers = () => {
}
export const useMCPServer = (id: string) => {
- const { mcpServers } = useMCPServers()
+ const server = useAppSelector((state) => (state.mcp.servers || []).find((server) => server.id === id))
+ const dispatch = useAppDispatch()
+
return {
- server: mcpServers.find((server) => server.id === id)
+ server,
+ updateMCPServer: (server: MCPServer) => dispatch(updateMCPServer(server)),
+ setMCPServerActive: (server: MCPServer, isActive: boolean) => dispatch(updateMCPServer({ ...server, isActive })),
+ deleteMCPServer: (id: string) => dispatch(deleteMCPServer(id))
}
}
diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json
index fd1f80cc14..8438afb5f7 100644
--- a/src/renderer/src/i18n/locales/en-us.json
+++ b/src/renderer/src/i18n/locales/en-us.json
@@ -1156,7 +1156,13 @@
"registryDefault": "Default",
"not_support": "Model not supported",
"user": "User",
- "system": "System"
+ "system": "System",
+ "types": {
+ "inMemory": "In Memory",
+ "sse": "SSE",
+ "streamableHttp": "Streamable HTTP",
+ "stdio": "STDIO"
+ }
},
"messages.divider": "Show divider between messages",
"messages.grid_columns": "Message grid display columns",
@@ -1442,4 +1448,4 @@
"visualization": "Visualization"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json
index 303574929f..dcafaf1ab8 100644
--- a/src/renderer/src/i18n/locales/ja-jp.json
+++ b/src/renderer/src/i18n/locales/ja-jp.json
@@ -1155,7 +1155,13 @@
"registryDefault": "デフォルト",
"not_support": "モデルはサポートされていません",
"user": "ユーザー",
- "system": "システム"
+ "system": "システム",
+ "types": {
+ "inMemory": "組み込み",
+ "sse": "SSE",
+ "streamableHttp": "ストリーミング",
+ "stdio": "STDIO"
+ }
},
"messages.divider": "メッセージ間に区切り線を表示",
"messages.grid_columns": "メッセージグリッドの表示列数",
@@ -1442,4 +1448,4 @@
"visualization": "可視化"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json
index 6b880a0fa8..c42b99dc26 100644
--- a/src/renderer/src/i18n/locales/ru-ru.json
+++ b/src/renderer/src/i18n/locales/ru-ru.json
@@ -1155,7 +1155,13 @@
"registryDefault": "По умолчанию",
"not_support": "Модель не поддерживается",
"user": "Пользователь",
- "system": "Система"
+ "system": "Система",
+ "types": {
+ "inMemory": "Встроенный",
+ "sse": "SSE",
+ "streamableHttp": "Потоковый HTTP",
+ "stdio": "STDIO"
+ }
},
"messages.divider": "Показывать разделитель между сообщениями",
"messages.grid_columns": "Количество столбцов сетки сообщений",
@@ -1442,4 +1448,4 @@
"visualization": "Визуализация"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json
index 565ad37067..0ce761265e 100644
--- a/src/renderer/src/i18n/locales/zh-cn.json
+++ b/src/renderer/src/i18n/locales/zh-cn.json
@@ -1156,7 +1156,13 @@
"registryDefault": "默认",
"not_support": "模型不支持",
"user": "用户",
- "system": "系统"
+ "system": "系统",
+ "types": {
+ "inMemory": "内置",
+ "sse": "SSE",
+ "streamableHttp": "流式",
+ "stdio": "STDIO"
+ }
},
"messages.divider": "消息分割线",
"messages.grid_columns": "消息网格展示列数",
@@ -1442,4 +1448,4 @@
"visualization": "可视化"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json
index e5093bf728..a6417d212e 100644
--- a/src/renderer/src/i18n/locales/zh-tw.json
+++ b/src/renderer/src/i18n/locales/zh-tw.json
@@ -1155,7 +1155,13 @@
"registryDefault": "預設",
"not_support": "不支援此模型",
"user": "用戶",
- "system": "系統"
+ "system": "系統",
+ "types": {
+ "inMemory": "內置",
+ "sse": "SSE",
+ "streamableHttp": "流式",
+ "stdio": "STDIO"
+ }
},
"messages.divider": "訊息間顯示分隔線",
"messages.grid_columns": "訊息網格展示列數",
@@ -1442,4 +1448,4 @@
"visualization": "視覺化"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx b/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx
index 3de99712b6..0da94f76af 100644
--- a/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx
+++ b/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx
@@ -99,6 +99,7 @@ const InstallNpxUv: FC = ({ mini = false }) => {
@@ -129,6 +130,7 @@ const InstallNpxUv: FC = ({ mini = false }) => {
@@ -170,6 +172,7 @@ const Container = styled.div`
flex-direction: column;
margin-bottom: 20px;
gap: 12px;
+ padding-top: 50px;
`
export default InstallNpxUv
diff --git a/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx b/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx
index 9cc9c837b1..bd1df406e9 100644
--- a/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx
+++ b/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx
@@ -1,26 +1,22 @@
-import { CodeOutlined, PlusOutlined } from '@ant-design/icons'
+import { EditOutlined } from '@ant-design/icons'
import { nanoid } from '@reduxjs/toolkit'
import DragableList from '@renderer/components/DragableList'
-import IndicatorLight from '@renderer/components/IndicatorLight'
-import { HStack, VStack } from '@renderer/components/Layout'
import Scrollbar from '@renderer/components/Scrollbar'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { MCPServer } from '@renderer/types'
+import { Button, Empty, Tag } from 'antd'
+import { MonitorCheck, Plus, Settings2 } from 'lucide-react'
import { FC, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
+import { useNavigate } from 'react-router'
import styled from 'styled-components'
import { SettingTitle } from '..'
-import McpSettings from './McpSettings'
-
-interface Props {
- selectedMcpServer: MCPServer | null
- setSelectedMcpServer: (server: MCPServer | null) => void
-}
-
-const McpServersList: FC = ({ selectedMcpServer, setSelectedMcpServer }) => {
+import EditMcpJsonPopup from './EditMcpJsonPopup'
+const McpServersList: FC = () => {
const { mcpServers, addMCPServer, updateMcpServers } = useMCPServers()
const { t } = useTranslation()
+ const navigate = useNavigate()
const onAddMcpServer = useCallback(async () => {
const newServer = {
@@ -33,78 +29,81 @@ const McpServersList: FC = ({ selectedMcpServer, setSelectedMcpServer })
env: {},
isActive: false
}
- addMCPServer(newServer)
+ await addMCPServer(newServer)
+ navigate(`/settings/mcp/settings`, { state: { server: newServer } })
window.message.success({ content: t('settings.mcp.addSuccess'), key: 'mcp-list' })
- setSelectedMcpServer(newServer)
- }, [addMCPServer, setSelectedMcpServer, t])
+ }, [addMCPServer, navigate, t])
return (
-
-
- {t('settings.mcp.newServer')}
-
-
-
- {t('settings.mcp.addServer')}
-
-
- {(server) => (
- setSelectedMcpServer(server)}
- className={selectedMcpServer?.id === server.id ? 'active' : ''}>
-
+
+
+ {t('settings.mcp.newServer')}
+ } type="text" onClick={() => EditMcpJsonPopup.show()} shape="circle" />
+
+ } type="default" onClick={onAddMcpServer} shape="round">
+ {t('settings.mcp.addServer')}
+
+
+
+ {(server: MCPServer) => (
+ navigate(`/settings/mcp/settings`, { state: { server } })}>
+
+
+ {server.name}
-
+
- {server.name}
-
-
-
-
- {server.description}
-
- )}
-
-
- {selectedMcpServer && }
+
+
+ }
+ type="text"
+ onClick={() => navigate(`/settings/mcp/settings`, { state: { server } })}
+ />
+
+
+ {server.description}
+
+
+ {t(`settings.mcp.types.${server.type || 'stdio'}`)}
+
+
+
+ )}
+
+ {mcpServers.length === 0 && (
+
+ )}
)
}
-const Container = styled(HStack)`
+const Container = styled(Scrollbar)`
+ display: flex;
flex: 1;
- width: 350px;
+ flex-direction: column;
+ width: 100%;
height: calc(100vh - var(--navbar-height));
overflow: hidden;
-`
-
-const ServersList = styled(Scrollbar)`
- gap: 16px;
- display: flex;
- flex-direction: column;
- height: calc(100vh - var(--navbar-height));
- width: 350px;
- padding: 15px;
- border-right: 0.5px solid var(--color-border);
-`
-
-const ServerSettings = styled(VStack)`
- flex: 1;
- height: calc(100vh - var(--navbar-height));
+ padding: 20px;
+ padding-top: 15px;
+ gap: 15px;
+ overflow-y: auto;
`
const ListHeader = styled.div`
width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
h2 {
- font-size: 20px;
+ font-size: 22px;
margin: 0;
}
`
@@ -112,19 +111,17 @@ const ListHeader = styled.div`
const ServerCard = styled.div`
display: flex;
flex-direction: column;
- border: 1px solid var(--color-border);
- border-radius: 8px;
+ border: 0.5px solid var(--color-border);
+ border-radius: var(--list-item-border-radius);
padding: 10px 16px;
- cursor: pointer;
transition: all 0.2s ease;
- height: 120px;
background-color: var(--color-background);
+ margin-bottom: 5px;
+ height: 125px;
+ cursor: pointer;
- &:hover,
- &.active {
+ &:hover {
border-color: var(--color-primary);
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- transform: translateY(-2px);
}
`
@@ -136,16 +133,24 @@ const ServerHeader = styled.div`
const ServerIcon = styled.div`
font-size: 18px;
- color: var(--color-primary);
margin-right: 8px;
+ display: flex;
`
const ServerName = styled.div`
- font-weight: 500;
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+`
+
+const ServerNameText = styled.span`
+ font-size: 15px;
+ font-weight: 500;
+ font-family: Ubuntu;
`
const StatusIndicator = styled.div`
@@ -161,21 +166,14 @@ const ServerDescription = styled.div`
-webkit-box-orient: vertical;
width: 100%;
word-break: break-word;
+ height: 50px;
`
-const AddServerCard = styled(ServerCard)`
+const ServerFooter = styled.div`
display: flex;
- flex-direction: column;
- justify-content: center;
align-items: center;
- border-style: dashed;
- background-color: transparent;
- color: var(--color-text-2);
-`
-
-const AddServerText = styled.div`
- margin-top: 12px;
- font-weight: 500;
+ justify-content: space-between;
+ margin-top: 10px;
`
export default McpServersList
diff --git a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx
index 3800be55cd..174ff5a63c 100644
--- a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx
+++ b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx
@@ -1,11 +1,12 @@
import { DeleteOutlined, SaveOutlined } from '@ant-design/icons'
+import { useTheme } from '@renderer/context/ThemeProvider'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { MCPPrompt, MCPResource, MCPServer, MCPTool } from '@renderer/types'
import { Button, Flex, Form, Input, Radio, Switch, Tabs } from 'antd'
import TextArea from 'antd/es/input/TextArea'
import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { useNavigate } from 'react-router'
+import { useLocation, useNavigate } from 'react-router'
import styled from 'styled-components'
import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '..'
@@ -13,10 +14,6 @@ import MCPPromptsSection from './McpPrompt'
import MCPResourcesSection from './McpResource'
import MCPToolsSection from './McpTool'
-interface Props {
- server: MCPServer
-}
-
interface MCPFormValues {
name: string
description?: string
@@ -61,8 +58,9 @@ const parseKeyValueString = (str: string): Record => {
return result
}
-const McpSettings: React.FC = ({ server }) => {
+const McpSettings: React.FC = () => {
const { t } = useTranslation()
+ const { server } = useLocation().state as { server: MCPServer }
const { deleteMCPServer, updateMCPServer } = useMCPServers()
const [serverType, setServerType] = useState('stdio')
const [form] = Form.useForm()
@@ -77,6 +75,8 @@ const McpSettings: React.FC = ({ server }) => {
const [isShowRegistry, setIsShowRegistry] = useState(false)
const [registry, setRegistry] = useState()
+ const { theme } = useTheme()
+
const navigate = useNavigate()
useEffect(() => {
@@ -538,8 +538,8 @@ const McpSettings: React.FC = ({ server }) => {
}
return (
-
-
+
+
{server?.name}
@@ -557,18 +557,18 @@ const McpSettings: React.FC = ({ server }) => {
icon={}
onClick={onSave}
loading={loading}
+ shape="round"
disabled={!isFormChanged || activeTab !== 'settings'}>
{t('common.save')}
-
setActiveTab(key as TabKey)}
- style={{ marginTop: 8 }}
+ style={{ marginTop: 8, backgroundColor: 'transparent' }}
/>
diff --git a/src/renderer/src/pages/settings/MCPSettings/McpSettingsNavbar.tsx b/src/renderer/src/pages/settings/MCPSettings/McpSettingsNavbar.tsx
index dbd0e8ad69..a40700c3ac 100644
--- a/src/renderer/src/pages/settings/MCPSettings/McpSettingsNavbar.tsx
+++ b/src/renderer/src/pages/settings/MCPSettings/McpSettingsNavbar.tsx
@@ -1,13 +1,11 @@
-import { EditOutlined, ExportOutlined } from '@ant-design/icons'
import { NavbarRight } from '@renderer/components/app/Navbar'
import { HStack } from '@renderer/components/Layout'
import { isWindows } from '@renderer/config/constant'
import { Button } from 'antd'
-import { Search } from 'lucide-react'
+import { Search, SquareArrowOutUpRight } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router'
-import EditMcpJsonPopup from './EditMcpJsonPopup'
import InstallNpxUv from './InstallNpxUv'
export const McpSettingsNavbar = () => {
@@ -27,20 +25,11 @@ export const McpSettingsNavbar = () => {
style={{ fontSize: 13, height: 28, borderRadius: 20 }}>
{t('settings.mcp.searchNpx')}
-
}
+ icon={}
className="nodrag"
style={{ fontSize: 13, height: 28, borderRadius: 20 }}>
{t('settings.mcp.findMore')}
diff --git a/src/renderer/src/pages/settings/MCPSettings/NpxSearch.tsx b/src/renderer/src/pages/settings/MCPSettings/NpxSearch.tsx
index a34c1d89f8..eebd0b926c 100644
--- a/src/renderer/src/pages/settings/MCPSettings/NpxSearch.tsx
+++ b/src/renderer/src/pages/settings/MCPSettings/NpxSearch.tsx
@@ -1,6 +1,6 @@
import { CheckOutlined, PlusOutlined } from '@ant-design/icons'
import { nanoid } from '@reduxjs/toolkit'
-import npmLogo from '@renderer/assets/images/mcp/npm.svg'
+import logo from '@renderer/assets/images/cherry-text-logo.svg'
import { Center, HStack } from '@renderer/components/Layout'
import { useMCPServers } from '@renderer/hooks/useMCPServers'
import { builtinMCPServers } from '@renderer/store/mcp'
@@ -27,9 +27,7 @@ const npmScopes = ['@cherry', '@modelcontextprotocol', '@gongrzhe', '@mcpmarket'
let _searchResults: SearchResult[] = []
-const NpxSearch: FC<{
- setSelectedMcpServer: (server: MCPServer) => void
-}> = ({ setSelectedMcpServer }) => {
+const NpxSearch: FC = () => {
const { t } = useTranslation()
const { Text, Link } = Typography
@@ -126,7 +124,7 @@ const NpxSearch: FC<{
-
+
(
{
setNpmScope(scope)
handleNpmSearch(scope)
@@ -171,6 +168,7 @@ const NpxSearch: FC<{
{record.name}
@@ -178,7 +176,7 @@ const NpxSearch: FC<{
}
extra={
-
+
v{record.version}
@@ -242,6 +238,7 @@ const Container = styled.div`
flex: 1;
flex-direction: column;
gap: 8px;
+ padding-top: 20px;
`
const ResultList = styled.div`
diff --git a/src/renderer/src/pages/settings/MCPSettings/index.tsx b/src/renderer/src/pages/settings/MCPSettings/index.tsx
index 0e7f98a9af..19727e739a 100644
--- a/src/renderer/src/pages/settings/MCPSettings/index.tsx
+++ b/src/renderer/src/pages/settings/MCPSettings/index.tsx
@@ -1,10 +1,7 @@
import { ArrowLeftOutlined } from '@ant-design/icons'
-import { VStack } from '@renderer/components/Layout'
import { useTheme } from '@renderer/context/ThemeProvider'
-import { useMCPServers } from '@renderer/hooks/useMCPServers'
-import { MCPServer } from '@renderer/types'
-import { FC, useEffect, useState } from 'react'
-import { useTranslation } from 'react-i18next'
+import { Button } from 'antd'
+import { FC } from 'react'
import { Route, Routes, useLocation } from 'react-router'
import { Link } from 'react-router-dom'
import styled from 'styled-components'
@@ -12,58 +9,35 @@ import styled from 'styled-components'
import { SettingContainer } from '..'
import InstallNpxUv from './InstallNpxUv'
import McpServersList from './McpServersList'
+import McpSettings from './McpSettings'
import NpxSearch from './NpxSearch'
const MCPSettings: FC = () => {
- const { t } = useTranslation()
- const { mcpServers } = useMCPServers()
- const [selectedMcpServer, setSelectedMcpServer] = useState(mcpServers[0])
const { theme } = useTheme()
const location = useLocation()
const pathname = location.pathname
- useEffect(() => {
- const _selectedMcpServer = mcpServers.find((server) => server.id === selectedMcpServer?.id)
- setSelectedMcpServer(_selectedMcpServer || mcpServers[0])
- }, [mcpServers, selectedMcpServer])
-
- // Check if the selected server still exists in the updated mcpServers list
- useEffect(() => {
- if (selectedMcpServer) {
- const serverExists = mcpServers.some((server) => server.id === selectedMcpServer.id)
- if (!serverExists) {
- setSelectedMcpServer(mcpServers[0])
- }
- }
- }, [mcpServers, selectedMcpServer])
-
const isHome = pathname === '/settings/mcp'
return (
-
+
{!isHome && (
-
- {t('common.back')}
-
+ } shape="circle" />
)}
-
- }
- />
+ } />
+ } />
-
+
}
/>
@@ -77,18 +51,20 @@ const MCPSettings: FC = () => {
/>
-
+
)
}
-const Container = styled(VStack)`
- flex: 1;
-`
-
const BackButtonContainer = styled.div`
- padding: 12px 0 0 12px;
- width: 100%;
- background-color: var(--color-background);
+ display: flex;
+ align-items: center;
+ padding: 10px 20px;
+ background-color: transparent;
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 1000;
`
const MainContainer = styled.div`
@@ -97,21 +73,4 @@ const MainContainer = styled.div`
width: 100%;
`
-const BackButton = styled.div`
- display: inline-flex;
- align-items: center;
- gap: 8px;
- color: var(--color-text-1);
- cursor: pointer;
- padding: 6px 12px;
- border-radius: 4px;
- margin-bottom: 10px;
- background-color: var(--color-bg-1);
-
- &:hover {
- color: var(--color-primary);
- background-color: var(--color-bg-2);
- }
-`
-
export default MCPSettings