From 72e18fbcc1937a21a160b8610e21e8766b45415e Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sat, 12 Apr 2025 19:25:20 +0800 Subject: [PATCH] feat(MCPSettings): enhance MCP server management and localization updates - Added a new SVG icon for npm in the MCP settings. - Introduced a custom hook `useMCPServer` for retrieving a specific MCP server by ID. - Updated localization files to include new error messages for tool and prompt loading in English, Japanese, Russian, and Chinese. - Refactored MCP settings components for improved navigation and state management, including the use of React Router for routing. - Enhanced the Npx search functionality and UI for better user experience. --- src/renderer/src/assets/images/mcp/npm.svg | 1 + src/renderer/src/hooks/useMCPServers.ts | 7 + src/renderer/src/i18n/locales/en-us.json | 10 +- src/renderer/src/i18n/locales/ja-jp.json | 8 +- src/renderer/src/i18n/locales/ru-ru.json | 8 +- src/renderer/src/i18n/locales/zh-cn.json | 8 +- src/renderer/src/i18n/locales/zh-tw.json | 8 +- src/renderer/src/i18n/translate/el-gr.json | 2 - src/renderer/src/i18n/translate/es-es.json | 2 - src/renderer/src/i18n/translate/fr-fr.json | 2 - src/renderer/src/i18n/translate/pt-pt.json | 2 - .../settings/MCPSettings/InstallNpxUv.tsx | 5 +- .../settings/MCPSettings/McpSettings.tsx | 9 +- .../MCPSettings/McpSettingsNavbar.tsx | 5 +- .../pages/settings/MCPSettings/NpxSearch.tsx | 196 ++++++++++-------- .../src/pages/settings/MCPSettings/index.tsx | 195 ++++++++--------- .../src/pages/settings/SettingsPage.tsx | 37 ++-- 17 files changed, 248 insertions(+), 257 deletions(-) create mode 100644 src/renderer/src/assets/images/mcp/npm.svg diff --git a/src/renderer/src/assets/images/mcp/npm.svg b/src/renderer/src/assets/images/mcp/npm.svg new file mode 100644 index 00000000..df5b545d --- /dev/null +++ b/src/renderer/src/assets/images/mcp/npm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/renderer/src/hooks/useMCPServers.ts b/src/renderer/src/hooks/useMCPServers.ts index 6cf16a29..e6993a34 100644 --- a/src/renderer/src/hooks/useMCPServers.ts +++ b/src/renderer/src/hooks/useMCPServers.ts @@ -26,3 +26,10 @@ export const useMCPServers = () => { updateMcpServers: (servers: MCPServer[]) => dispatch(setMCPServers(servers)) } } + +export const useMCPServer = (id: string) => { + const { mcpServers } = useMCPServers() + return { + server: mcpServers.find((server) => server.id === id) + } +} diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 916d8014..322d5d26 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1064,7 +1064,6 @@ "newServer": "MCP Server", "npx_list": { "actions": "Actions", - "desc": "Search and add npm packages as MCP servers", "description": "Description", "no_packages": "No packages found", "npm": "NPM", @@ -1073,7 +1072,6 @@ "scope_required": "Please enter npm scope", "search": "Search", "search_error": "Search error", - "title": "NPX Package List", "usage": "Usage", "version": "Version" }, @@ -1099,14 +1097,16 @@ "tools": { "inputSchema": "Input Schema", "availableTools": "Available Tools", - "noToolsAvailable": "No tools available" + "noToolsAvailable": "No tools available", + "loadError": "Get tools Error" }, "prompts": { "availablePrompts": "Available Prompts", "noPromptsAvailable": "No prompts available", "arguments": "Arguments", "requiredField": "Required Field", - "genericError": "Get prompt Error" + "genericError": "Get prompt Error", + "loadError": "Get prompts Error" }, "deleteServer": "Delete Server", "deleteServerConfirm": "Are you sure you want to delete this server?", @@ -1393,4 +1393,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 3fb8583a..d3a51115 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1063,7 +1063,6 @@ "newServer": "MCP サーバー", "npx_list": { "actions": "アクション", - "desc": "npm パッケージを検索して MCP サーバーとして追加", "description": "説明", "no_packages": "パッケージが見つかりません", "npm": "NPM", @@ -1072,7 +1071,6 @@ "scope_required": "npm スコープを入力してください", "search": "検索", "search_error": "パッケージの検索に失敗しました", - "title": "NPX パッケージリスト", "usage": "使用法", "version": "バージョン" }, @@ -1098,14 +1096,16 @@ "tools": { "inputSchema": "入力スキーマ", "availableTools": "利用可能なツール", - "noToolsAvailable": "利用可能なツールなし" + "noToolsAvailable": "利用可能なツールなし", + "loadError": "ツール取得エラー" }, "prompts": { "availablePrompts": "利用可能なプロンプト", "noPromptsAvailable": "利用可能なプロンプトはありません", "arguments": "引数", "requiredField": "必須フィールド", - "genericError": "プロンプト取得エラー" + "genericError": "プロンプト取得エラー", + "loadError": "プロンプト取得エラー" }, "deleteServer": "サーバーを削除", "deleteServerConfirm": "このサーバーを削除してもよろしいですか?", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 20593127..72d24e71 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1063,7 +1063,6 @@ "newServer": "MCP сервер", "npx_list": { "actions": "Действия", - "desc": "Поиск и добавление npm пакетов в качестве MCP серверов", "description": "Описание", "no_packages": "Ничего не найдено", "npm": "NPM", @@ -1072,7 +1071,6 @@ "scope_required": "Пожалуйста, введите область npm", "search": "Поиск", "search_error": "Ошибка поиска", - "title": "Список пакетов NPX", "usage": "Использование", "version": "Версия" }, @@ -1098,14 +1096,16 @@ "tools": { "inputSchema": "Схема ввода", "availableTools": "Доступные инструменты", - "noToolsAvailable": "Нет доступных инструментов" + "noToolsAvailable": "Нет доступных инструментов", + "loadError": "Ошибка получения инструментов" }, "prompts": { "availablePrompts": "Доступные подсказки", "noPromptsAvailable": "Нет доступных подсказок", "arguments": "Аргументы", "requiredField": "Обязательное поле", - "genericError": "Ошибка получения подсказки" + "genericError": "Ошибка получения подсказки", + "loadError": "Ошибка получения подсказок" }, "deleteServer": "Удалить сервер", "deleteServerConfirm": "Вы уверены, что хотите удалить этот сервер?", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index dd2c6368..1b7c54df 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1064,7 +1064,6 @@ "newServer": "MCP 服务器", "npx_list": { "actions": "操作", - "desc": "搜索并添加 npm 包作为 MCP 服务", "description": "描述", "no_packages": "未找到包", "npm": "NPM", @@ -1073,7 +1072,6 @@ "scope_required": "请输入 npm 作用域", "search": "搜索", "search_error": "搜索失败", - "title": "NPX 包列表", "usage": "用法", "version": "版本" }, @@ -1099,14 +1097,16 @@ "tools": { "inputSchema": "输入模式", "availableTools": "可用工具", - "noToolsAvailable": "无可用工具" + "noToolsAvailable": "无可用工具", + "loadError": "获取工具失败" }, "prompts": { "availablePrompts": "可用提示", "noPromptsAvailable": "无可用提示", "arguments": "参数", "requiredField": "必填字段", - "genericError": "获取提示错误" + "genericError": "获取提示错误", + "loadError": "获取提示失败" }, "deleteServer": "删除服务器", "deleteServerConfirm": "确定要删除此服务器吗?", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 9097c226..ab094b8e 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1063,7 +1063,6 @@ "newServer": "MCP 伺服器", "npx_list": { "actions": "操作", - "desc": "搜索並添加 npm 包作為 MCP 服務", "description": "描述", "no_packages": "未找到包", "npm": "NPM", @@ -1072,7 +1071,6 @@ "scope_required": "請輸入 npm 作用域", "search": "搜索", "search_error": "搜索失敗", - "title": "NPX 包列表", "usage": "用法", "version": "版本" }, @@ -1098,14 +1096,16 @@ "tools": { "inputSchema": "輸入模式", "availableTools": "可用工具", - "noToolsAvailable": "無可用工具" + "noToolsAvailable": "無可用工具", + "loadError": "獲取工具失敗" }, "prompts": { "availablePrompts": "可用提示", "noPromptsAvailable": "無可用提示", "arguments": "參數", "requiredField": "必填欄位", - "genericError": "獲取提示錯誤" + "genericError": "獲取提示錯誤", + "loadError": "獲取提示失敗" }, "deleteServer": "刪除伺服器", "deleteServerConfirm": "確定要刪除此伺服器嗎?", diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 22b583a3..db99e7c4 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -912,7 +912,6 @@ "noServers": "Δεν έχουν ρυθμιστεί διακομιστές", "npx_list": { "actions": "Ενέργειες", - "desc": "Αναζητήστε και προσθέστε πακέτα npm ως υπηρεσίες MCP", "description": "Περιγραφή", "no_packages": "Δεν βρέθηκαν πακέτα", "npm": "NPM", @@ -921,7 +920,6 @@ "scope_required": "Παρακαλώ εισαγάγετε το σκοπό του npm", "search": "Αναζήτηση", "search_error": "Η αναζήτηση απέτυχε", - "title": "Λίστα πακέτων NPX", "usage": "Χρήση", "version": "Έκδοση" }, diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index ff493576..cb609f66 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -912,7 +912,6 @@ "noServers": "No se han configurado servidores", "npx_list": { "actions": "Acciones", - "desc": "Buscar y agregar paquetes npm como servicios MCP", "description": "Descripción", "no_packages": "No se encontraron paquetes", "npm": "NPM", @@ -921,7 +920,6 @@ "scope_required": "Por favor ingrese el ámbito npm", "search": "Buscar", "search_error": "Error de búsqueda", - "title": "Lista de paquetes NPX", "usage": "Uso", "version": "Versión" }, diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index a972714a..51d4ee83 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -912,7 +912,6 @@ "noServers": "Aucun serveur configuré", "npx_list": { "actions": "Actions", - "desc": "Rechercher et ajouter un package npm en tant que service MCP", "description": "Description", "no_packages": "Aucun package trouvé", "npm": "NPM", @@ -921,7 +920,6 @@ "scope_required": "Veuillez entrer le scope npm", "search": "Rechercher", "search_error": "La recherche a échoué", - "title": "Liste des packages NPX", "usage": "Utilisation", "version": "Version" }, diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index db3da8b9..d4e9a06d 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -912,7 +912,6 @@ "noServers": "Nenhum servidor configurado", "npx_list": { "actions": "Ações", - "desc": "Pesquise e adicione pacotes npm como serviço MCP", "description": "Descrição", "no_packages": "Nenhum pacote encontrado", "npm": "NPM", @@ -921,7 +920,6 @@ "scope_required": "Insira o escopo npm", "search": "Pesquisar", "search_error": "Falha na pesquisa", - "title": "Lista de Pacotes NPX", "usage": "Uso", "version": "Versão" }, diff --git a/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx b/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx index 86bc7c8b..3de99712 100644 --- a/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/InstallNpxUv.tsx @@ -1,9 +1,9 @@ import { CheckCircleOutlined, QuestionCircleOutlined, WarningOutlined } from '@ant-design/icons' import { Center, VStack } from '@renderer/components/Layout' -import { EventEmitter } from '@renderer/services/EventService' import { Alert, Button } from 'antd' import { FC, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import { useNavigate } from 'react-router' import styled from 'styled-components' import { SettingDescription, SettingRow, SettingSubtitle } from '..' @@ -21,6 +21,7 @@ const InstallNpxUv: FC = ({ mini = false }) => { const [bunPath, setBunPath] = useState(null) const [binariesDir, setBinariesDir] = useState(null) const { t } = useTranslation() + const navigate = useNavigate() const checkBinaries = async () => { const uvExists = await window.api.isBinaryExist('uv') @@ -78,7 +79,7 @@ const InstallNpxUv: FC = ({ mini = false }) => { icon={installed ? : } className="nodrag" color={installed ? 'green' : 'danger'} - onClick={() => EventEmitter.emit('mcp:mcp-install')} + onClick={() => navigate('/settings/mcp/mcp-install')} /> ) } diff --git a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx index 0c9c1f15..499e7484 100644 --- a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx @@ -5,6 +5,7 @@ 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 styled from 'styled-components' import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '..' @@ -58,6 +59,8 @@ const McpSettings: React.FC = ({ server }) => { const [isShowRegistry, setIsShowRegistry] = useState(false) const [registry, setRegistry] = useState() + const navigate = useNavigate() + useEffect(() => { const serverType: MCPServer['type'] = server.type || (server.baseUrl ? 'sse' : 'stdio') setServerType(serverType) @@ -114,10 +117,9 @@ const McpSettings: React.FC = ({ server }) => { setLoadingServer(server.id) const localTools = await window.api.mcp.listTools(server) setTools(localTools) - // window.message.success(t('settings.mcp.toolsLoaded')) } catch (error) { window.message.error({ - content: t('settings.mcp.toolsLoadError') + formatError(error), + content: t('settings.mcp.tools.loadError') + ' ' + formatError(error), key: 'mcp-tools-error' }) } finally { @@ -134,7 +136,7 @@ const McpSettings: React.FC = ({ server }) => { setPrompts(localPrompts) } catch (error) { window.message.error({ - content: t('settings.mcp.promptsLoadError') + formatError(error), + content: t('settings.mcp.prompts.loadError') + ' ' + formatError(error), key: 'mcp-prompts-error' }) setPrompts([]) @@ -258,6 +260,7 @@ const McpSettings: React.FC = ({ server }) => { await window.api.mcp.removeServer(server) deleteMCPServer(server.id) window.message.success({ content: t('settings.mcp.deleteSuccess'), key: 'mcp-list' }) + navigate('/settings/mcp') } }) } catch (error: any) { diff --git a/src/renderer/src/pages/settings/MCPSettings/McpSettingsNavbar.tsx b/src/renderer/src/pages/settings/MCPSettings/McpSettingsNavbar.tsx index 8ff50a65..44c8f70c 100644 --- a/src/renderer/src/pages/settings/MCPSettings/McpSettingsNavbar.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/McpSettingsNavbar.tsx @@ -2,15 +2,16 @@ import { EditOutlined, ExportOutlined, SearchOutlined } from '@ant-design/icons' import { NavbarRight } from '@renderer/components/app/Navbar' import { HStack } from '@renderer/components/Layout' import { isWindows } from '@renderer/config/constant' -import { EventEmitter } from '@renderer/services/EventService' import { Button } from 'antd' import { useTranslation } from 'react-i18next' +import { useNavigate } from 'react-router' import EditMcpJsonPopup from './EditMcpJsonPopup' import InstallNpxUv from './InstallNpxUv' export const McpSettingsNavbar = () => { const { t } = useTranslation() + const navigate = useNavigate() const onClick = () => window.open('https://mcp.so/', '_blank') return ( @@ -19,7 +20,7 @@ export const McpSettingsNavbar = () => { - + {npmScopes.map((scope) => ( { setNpmScope(scope) handleNpmSearch(scope) }} - style={{ cursor: searchLoading ? 'not-allowed' : 'pointer' }}> + style={{ + cursor: searchLoading ? 'not-allowed' : 'pointer', + borderRadius: 100, + backgroundColor: 'var(--color-background-mute)' + }}> {scope} ))} - + + {searchLoading && ( +
+ +
+ )} + {!searchLoading && ( + + {searchResults?.map((record) => { + const isInstalled = mcpServers.some((server) => server.name === record.name) + return ( + + {record.name} + + } + extra={ + + + v{record.version} + +