From e0e1d285e407ee2ef0a34b6e35b45b711c7c0687 Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat <43230886+MyPrototypeWhat@users.noreply.github.com> Date: Sun, 16 Mar 2025 23:09:40 +0800 Subject: [PATCH] feat: mcp npx list (#3409) * feat: add npm scope search functionality in MCPSettings - Integrated npx-scope-finder to enable searching for npm packages by scope. - Added UI elements for inputting npm scope and displaying search results in a table format. - Enhanced user feedback with loading indicators and messages for search results. * feat: add key property to package formatting in MCPSettings - Added a key property to the package formatting logic to ensure unique identification of each package in the results. * feat: enhance MCPSettings with NPX package list localization - Added localization support for NPX package list in English, Japanese, Russian, Simplified Chinese, and Traditional Chinese. - Updated UI elements in MCPSettings to utilize localized strings for package list features, including title, description, and various labels. - Improved user experience by integrating translations for package-related actions and placeholders. --- package.json | 1 + src/renderer/src/i18n/locales/en-us.json | 14 +- src/renderer/src/i18n/locales/ja-jp.json | 14 +- src/renderer/src/i18n/locales/ru-ru.json | 14 +- src/renderer/src/i18n/locales/zh-cn.json | 14 +- src/renderer/src/i18n/locales/zh-tw.json | 14 +- .../src/pages/settings/MCPSettings.tsx | 280 +++++++++++++----- yarn.lock | 8 + 8 files changed, 282 insertions(+), 77 deletions(-) diff --git a/package.json b/package.json index 838298b2..49e724bd 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "fetch-socks": "^1.3.2", "fs-extra": "^11.2.0", "markdown-it": "^14.1.0", + "npx-scope-finder": "^1.2.0", "officeparser": "^4.1.1", "p-queue": "^8.1.0", "tokenx": "^0.4.1", diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index bcdebb02..41d294af 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -824,7 +824,19 @@ "updateSuccess": "Server updated successfully", "updateError": "Failed to update server", "url": "URL", - "toggleError": "Toggle failed" + "toggleError": "Toggle failed", + "npx_list": { + "title": "NPX Package List", + "desc": "Search and add npm packages as MCP servers", + "scope_placeholder": "Enter npm scope (e.g. @your-org)", + "search": "Search", + "package_name": "Package Name", + "description": "Description", + "usage": "Usage", + "npm": "NPM", + "version": "Version", + "actions": "Actions" + } }, "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 115aeb11..5d25c9cd 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -824,7 +824,19 @@ "updateSuccess": "サーバーが正常に更新されました", "updateError": "サーバーの更新に失敗しました", "url": "URL", - "toggleError": "切り替えに失敗しました" + "toggleError": "切り替えに失敗しました", + "npx_list": { + "title": "NPX パッケージリスト", + "desc": "npm パッケージを検索して MCP サーバーとして追加", + "scope_placeholder": "npm スコープを入力 (例: @your-org)", + "search": "検索", + "package_name": "パッケージ名", + "description": "説明", + "usage": "使用法", + "npm": "NPM", + "version": "バージョン", + "actions": "アクション" + } }, "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 179540cb..4c00a130 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -824,7 +824,19 @@ "updateSuccess": "Сервер успешно обновлен", "updateError": "Ошибка обновления сервера", "url": "URL", - "toggleError": "Переключение не удалось" + "toggleError": "Переключение не удалось", + "npx_list": { + "title": "Список пакетов NPX", + "desc": "Поиск и добавление npm пакетов в качестве MCP серверов", + "scope_placeholder": "Введите область npm (например, @your-org)", + "search": "Поиск", + "package_name": "Имя пакета", + "description": "Описание", + "usage": "Использование", + "npm": "NPM", + "version": "Версия", + "actions": "Действия" + } }, "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 90bff49e..a5d5703f 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -824,7 +824,19 @@ "updateSuccess": "服务器更新成功", "updateError": "更新服务器失败", "url": "URL", - "toggleError": "切换失败" + "toggleError": "切换失败", + "npx_list": { + "title": "NPX 包列表", + "desc": "搜索并添加 npm 包作为 MCP 服务", + "scope_placeholder": "输入 npm 作用域 (例如 @your-org)", + "search": "搜索", + "package_name": "包名称", + "description": "描述", + "usage": "用法", + "npm": "NPM", + "version": "版本", + "actions": "操作" + } }, "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 ca021830..d5bf7df8 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -824,7 +824,19 @@ "updateSuccess": "伺服器更新成功", "updateError": "更新伺服器失敗", "url": "URL", - "toggleError": "切換失敗" + "toggleError": "切換失敗", + "npx_list": { + "title": "NPX 包列表", + "desc": "搜索並添加 npm 包作為 MCP 服務", + "scope_placeholder": "輸入 npm 作用域 (例如 @your-org)", + "search": "搜索", + "package_name": "包名稱", + "description": "描述", + "usage": "用法", + "npm": "NPM", + "version": "版本", + "actions": "操作" + } }, "messages.divider": "訊息間顯示分隔線", "messages.grid_columns": "訊息網格展示列數", diff --git a/src/renderer/src/pages/settings/MCPSettings.tsx b/src/renderer/src/pages/settings/MCPSettings.tsx index 059d64c8..ed94d931 100644 --- a/src/renderer/src/pages/settings/MCPSettings.tsx +++ b/src/renderer/src/pages/settings/MCPSettings.tsx @@ -1,9 +1,10 @@ -import { DeleteOutlined, EditOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons' +import { DeleteOutlined, EditOutlined, PlusOutlined, QuestionCircleOutlined, SearchOutlined } from '@ant-design/icons' import { useTheme } from '@renderer/context/ThemeProvider' import { useAppSelector } from '@renderer/store' import { MCPServer } from '@renderer/types' -import { Button, Card, Form, Input, Modal, Radio, Space, Switch, Table, Tag, Tooltip, Typography } from 'antd' +import { Button, Card, Form, Input, Modal, Radio, Space, Spin, Switch, Table, Tag, Tooltip, Typography } from 'antd' import TextArea from 'antd/es/input/TextArea' +import { npxFinder } from 'npx-scope-finder' import { FC, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -20,6 +21,15 @@ interface MCPFormValues { isActive: boolean } +interface SearchResult { + name: string + description: string + version: string + usage: string + npmLink: string + fullName: string +} + const MCPSettings: FC = () => { const { t } = useTranslation() const { theme } = useTheme() @@ -32,6 +42,48 @@ const MCPSettings: FC = () => { const [form] = Form.useForm() const [serverType, setServerType] = useState<'sse' | 'stdio'>('stdio') + // Add new state variables for npm scope search + const [npmScope, setNpmScope] = useState('') + const [searchLoading, setSearchLoading] = useState(false) + const [searchResults, setSearchResults] = useState([]) + + // Add new function to handle npm scope search + const handleNpmSearch = async () => { + if (!npmScope.trim()) { + window.message.warning('Please enter an npm scope') + return + } + + setSearchLoading(true) + try { + // Call npxFinder to search for packages + const packages = await npxFinder(npmScope) + + // Map the packages to our desired format + const formattedResults = packages.map((pkg) => { + return { + key: pkg.name, + name: pkg.name || '', + description: pkg.description || 'No description available', + version: pkg.version || 'Latest', + usage: `npx ${pkg.name}`, + npmLink: pkg.links?.npm || `https://www.npmjs.com/package/${pkg.name}`, + fullName: pkg.name || '' + } + }) + + setSearchResults(formattedResults) + + if (formattedResults.length === 0) { + window.message.info('No packages found for this scope') + } + } catch (error: any) { + window.message.error(`Failed to search npm packages: ${error.message}`) + } finally { + setSearchLoading(false) + } + } + // Watch the serverType field to update the form layout dynamically useEffect(() => { const type = form.getFieldValue('serverType') @@ -44,7 +96,6 @@ const MCPSettings: FC = () => { form.resetFields() form.setFieldsValue({ serverType: 'stdio', isActive: true }) setServerType('stdio') - setEditingServer(null) setIsModalVisible(true) } @@ -284,76 +335,161 @@ const MCPSettings: FC = () => { })} /> - - -
- - - - - -