From 1e830c06130695443837aa479c14f1d2257bcfc3 Mon Sep 17 00:00:00 2001 From: Chen Tao <70054568+eeee0717@users.noreply.github.com> Date: Mon, 17 Mar 2025 08:40:38 +0800 Subject: [PATCH] feat(mcp): add json import --- src/renderer/src/i18n/locales/en-us.json | 8 +- 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/pages/settings/MCPSettings/index.tsx | 171 +++++++++++++++--- 6 files changed, 178 insertions(+), 33 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index da654d1f..83e62831 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -839,7 +839,13 @@ "scope_required": "Please enter npm scope", "no_packages": "No packages found", "search_error": "Search error" - } + }, + "jsonMode": "JSON Schema", + "normalMode": "Normal mode", + "jsonModeHint": "Edit the JSON representation of the MCP server configuration. Please ensure the format is correct before saving.", + "jsonFormatError": "JSON formatting error", + "jsonSaveSuccess": "JSON configuration has been saved.", + "jsonSaveError": "Failed to save JSON configuration." }, "messages.divider": "Show divider between messages", "messages.grid_columns": "Message grid display columns", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index e310b15f..3f4321d3 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -839,7 +839,13 @@ "scope_required": "npm スコープを入力してください", "no_packages": "パッケージが見つかりません", "search_error": "パッケージの検索に失敗しました" - } + }, + "jsonMode": "JSONスキーマ", + "normalMode": "通常モード", + "jsonModeHint": "MCPサーバー設定のJSON表現を編集します。保存する前に、フォーマットが正しいことを確認してください。", + "jsonFormatError": "JSONフォーマットエラー", + "jsonSaveSuccess": "JSON設定が保存されました。", + "jsonSaveError": "JSON設定の保存に失敗しました" }, "messages.divider": "メッセージ間に区切り線を表示", "messages.grid_columns": "メッセージグリッドの表示列数", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 13a3539b..c26d15db 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -839,7 +839,13 @@ "scope_required": "Пожалуйста, введите область npm", "no_packages": "Ничего не найдено", "search_error": "Ошибка поиска" - } + }, + "jsonMode": "JSON-схема", + "normalMode": "Обычный режим", + "jsonModeHint": "Редактируйте JSON-форматирование конфигурации сервера MCP. Перед сохранением убедитесь, что формат правильный.", + "jsonFormatError": "Ошибка форматирования JSON", + "jsonSaveSuccess": "JSON конфигурация сохранена", + "jsonSaveError": "Не удалось сохранить конфигурацию JSON" }, "messages.divider": "Показывать разделитель между сообщениями", "messages.grid_columns": "Количество столбцов сетки сообщений", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 8592f91f..629da17e 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -839,7 +839,13 @@ "scope_required": "请输入 npm 作用域", "no_packages": "未找到包", "search_error": "搜索失败" - } + }, + "jsonMode": "JSON模式", + "normalMode": "常规模式", + "jsonModeHint": "编辑MCP服务器配置的JSON表示。保存前请确保格式正确。", + "jsonFormatError": "JSON格式化错误", + "jsonSaveSuccess": "JSON配置已保存", + "jsonSaveError": "保存JSON配置失败" }, "messages.divider": "消息分割线", "messages.grid_columns": "消息网格展示列数", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 8bb89750..0deae2e0 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -839,7 +839,13 @@ "scope_required": "請輸入 npm 作用域", "no_packages": "未找到包", "search_error": "搜索失敗" - } + }, + "jsonMode": "JSON模式", + "normalMode": "常規模式", + "jsonModeHint": "編輯MCP伺服器配置的JSON表示。保存前請確保格式正確。", + "jsonFormatError": "JSON格式錯誤", + "jsonSaveSuccess": "JSON配置已儲存", + "jsonSaveError": "保存JSON配置失敗" }, "messages.divider": "訊息間顯示分隔線", "messages.grid_columns": "訊息網格展示列數", diff --git a/src/renderer/src/pages/settings/MCPSettings/index.tsx b/src/renderer/src/pages/settings/MCPSettings/index.tsx index c3202858..61a3d2e8 100644 --- a/src/renderer/src/pages/settings/MCPSettings/index.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/index.tsx @@ -1,9 +1,10 @@ import { DeleteOutlined, EditOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons' import { useTheme } from '@renderer/context/ThemeProvider' -import { useAppSelector } from '@renderer/store' +import { useAppDispatch, useAppSelector } from '@renderer/store' +import { setMCPServers } from '@renderer/store/mcp' import { MCPServer } from '@renderer/types' -import { Button, Card, Space, Switch, Table, Tag, Tooltip, Typography } from 'antd' -import { FC } from 'react' +import { Button, Card, Input, Space, Switch, Table, Tabs, Tag, Tooltip, Typography } from 'antd' +import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '..' @@ -14,6 +15,13 @@ const MCPSettings: FC = () => { const { t } = useTranslation() const { theme } = useTheme() const { Paragraph, Text } = Typography + const { TextArea } = Input + const [activeTab, setActiveTab] = useState('normal') + const [jsonConfig, setJsonConfig] = useState('') + const [jsonSaving, setJsonSaving] = useState(false) + const [jsonError, setJsonError] = useState('') + const dispatch = useAppDispatch() + const ipcRenderer = window.electron.ipcRenderer const mcpServers = useAppSelector((state) => state.mcp.servers) const handleDelete = (serverName: string) => { @@ -43,6 +51,72 @@ const MCPSettings: FC = () => { } } + const handleTabChange = (key: string) => { + setActiveTab(key) + + if (key === 'json') { + try { + const mcpServersObj: Record = {} + + mcpServers.forEach((server) => { + const { name, ...serverData } = server + mcpServersObj[name] = serverData + }) + + const standardFormat = { + mcpServers: mcpServersObj + } + + const formattedJson = JSON.stringify(standardFormat, null, 2) + setJsonConfig(formattedJson) + setJsonError('') + } catch (error) { + console.error('Failed to format JSON:', error) + setJsonError(t('settings.mcp.jsonFormatError')) + } + } + } + + const handleSaveJson = async () => { + setJsonSaving(true) + try { + if (!jsonConfig.trim()) { + dispatch(setMCPServers([])) + window.message.success(t('settings.mcp.jsonSaveSuccess')) + setJsonError('') + setJsonSaving(false) + return + } + const parsedConfig = JSON.parse(jsonConfig) + + if (!parsedConfig.mcpServers || typeof parsedConfig.mcpServers !== 'object') { + throw new Error(t('settings.mcp.invalidMcpFormat')) + } + + const serversArray: MCPServer[] = [] + for (const [name, serverConfig] of Object.entries(parsedConfig.mcpServers)) { + const server: MCPServer = { + name, + isActive: false, + ...(serverConfig as any) + } + serversArray.push(server) + } + + dispatch(setMCPServers(serversArray)) + ipcRenderer.send('mcp:servers-from-renderer', mcpServers) + + window.message.success(t('settings.mcp.jsonSaveSuccess')) + setJsonError('') + } catch (error: any) { + console.error('Failed to save JSON config:', error) + setJsonError(error.message || t('settings.mcp.jsonSaveError')) + window.message.error(t('settings.mcp.jsonSaveError')) + } finally { + setJsonSaving(false) + } + } + const columns = [ { title: t('settings.mcp.name'), @@ -137,32 +211,73 @@ const MCPSettings: FC = () => { {t('settings.mcp.config_description')} -
- - - {mcpServers.length}{' '} - {mcpServers.length === 1 ? t('settings.mcp.serverSingular') : t('settings.mcp.serverPlural')} - -
+ +
+ + + {mcpServers.length}{' '} + {mcpServers.length === 1 ? t('settings.mcp.serverSingular') : t('settings.mcp.serverPlural')} + +
- - (!record.isActive ? 'inactive-row' : '')} - onRow={(record) => ({ - style: !record.isActive ? inactiveRowStyle : {} - })} - /> - + +
(!record.isActive ? 'inactive-row' : '')} + onRow={(record) => ({ + style: !record.isActive ? inactiveRowStyle : {} + })} + /> + + + ) + }, + { + label: t('settings.mcp.jsonMode'), + key: 'json', + children: ( + <> +
+ + {jsonError ? {jsonError} : ''} +
+ +