From 696bf2dc7960d2a9513e1e78b3c554ac16568ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AF=87=E4=BD=B3=E9=BE=99?= Date: Thu, 24 Apr 2025 15:32:01 +0800 Subject: [PATCH] feat(mcp): mcp setting add service description page --- package.json | 1 + src/renderer/src/i18n/locales/en-us.json | 1 + src/renderer/src/i18n/locales/ja-jp.json | 1 + src/renderer/src/i18n/locales/ru-ru.json | 1 + src/renderer/src/i18n/locales/zh-cn.json | 1 + src/renderer/src/i18n/locales/zh-tw.json | 1 + .../settings/MCPSettings/McpDescription.tsx | 50 +++++++++++++++++++ .../settings/MCPSettings/McpSettings.tsx | 8 +++ .../pages/settings/MCPSettings/NpxSearch.tsx | 3 +- src/renderer/src/types/index.ts | 1 + src/renderer/src/utils/shiki.ts | 34 +++++++++++++ yarn.lock | 18 ++++++- 12 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/renderer/src/pages/settings/MCPSettings/McpDescription.tsx create mode 100644 src/renderer/src/utils/shiki.ts diff --git a/package.json b/package.json index 8f093a05cf..7be0e091b0 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "@langchain/community": "^0.3.36", "@mozilla/readability": "^0.6.0", "@notionhq/client": "^2.2.15", + "@shikijs/markdown-it": "^3.2.2", "@strongtz/win32-arm64-msvc": "^0.4.7", "@tryfabric/martian": "^1.2.4", "@types/react-infinite-scroll-component": "^5.0.0", diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 8438afb5f7..1076a51f29 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1121,6 +1121,7 @@ "installHelp": "Get Installation Help", "tabs": { "general": "General", + "description": "Description", "tools": "Tools", "prompts": "Prompts", "resources": "Resources" diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index dcafaf1ab8..b575262d9f 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1120,6 +1120,7 @@ "installHelp": "インストールヘルプを取得", "tabs": { "general": "一般", + "description": "説明", "tools": "ツール", "prompts": "プロンプト", "resources": "リソース" diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index c42b99dc26..758ddd9f0a 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1120,6 +1120,7 @@ "installHelp": "Получить помощь по установке", "tabs": { "general": "Общие", + "description": "Описание", "tools": "Инструменты", "prompts": "Подсказки", "resources": "Ресурсы" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 0ce761265e..207675f6a9 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1121,6 +1121,7 @@ "installHelp": "获取安装帮助", "tabs": { "general": "通用", + "description": "描述", "tools": "工具", "prompts": "提示", "resources": "资源" diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index a6417d212e..88ce6e96f8 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1120,6 +1120,7 @@ "installHelp": "獲取安裝幫助", "tabs": { "general": "通用", + "description": "描述", "tools": "工具", "prompts": "提示", "resources": "資源" diff --git a/src/renderer/src/pages/settings/MCPSettings/McpDescription.tsx b/src/renderer/src/pages/settings/MCPSettings/McpDescription.tsx new file mode 100644 index 0000000000..ede1d0cd52 --- /dev/null +++ b/src/renderer/src/pages/settings/MCPSettings/McpDescription.tsx @@ -0,0 +1,50 @@ +import { useTheme } from '@renderer/context/ThemeProvider' +import { getShikiInstance } from '@renderer/utils/shiki' +import { Card } from 'antd' +import MarkdownIt from 'markdown-it' +import { npxFinder } from 'npx-scope-finder' +import { useEffect, useRef, useState } from 'react' +import styled from 'styled-components' + +interface McpDescriptionProps { + searchKey: string +} + +const MCPDescription = ({ searchKey }: McpDescriptionProps) => { + const [renderedMarkdown, setRenderedMarkdown] = useState('') + const [loading, setLoading] = useState(false) + + const md = useRef( + new MarkdownIt({ + linkify: true, // 自动转换 URL 为链接 + typographer: true // 启用印刷格式优化 + }) + ) + const { theme } = useTheme() + + useEffect(() => { + const sk = getShikiInstance(theme) + md.current.use(sk) + getMcpInfo() + }, [theme, searchKey]) + + const getMcpInfo = async () => { + setLoading(true) + const packages = await npxFinder(searchKey).finally(() => setLoading(false)) + const readme = packages[0]?.original?.readme ?? '暂无描述' + setRenderedMarkdown(md.current.render(readme)) + } + + return ( +
+ +
+ +
+ ) +} +const Section = styled.div` + padding-top: 8px; +` + +export default MCPDescription diff --git a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx index 174ff5a63c..8e3c2293ea 100644 --- a/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/McpSettings.tsx @@ -1,6 +1,7 @@ import { DeleteOutlined, SaveOutlined } from '@ant-design/icons' import { useTheme } from '@renderer/context/ThemeProvider' import { useMCPServers } from '@renderer/hooks/useMCPServers' +import MCPDescription from '@renderer/pages/settings/MCPSettings/McpDescription' 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' @@ -516,6 +517,13 @@ const McpSettings: React.FC = () => { ) } ] + if (server.searchKey) { + tabs.push({ + key: 'description', + label: t('settings.mcp.tabs.description'), + children: + }) + } if (server.isActive) { tabs.push( diff --git a/src/renderer/src/pages/settings/MCPSettings/NpxSearch.tsx b/src/renderer/src/pages/settings/MCPSettings/NpxSearch.tsx index eebd0b926c..54fa3201a9 100644 --- a/src/renderer/src/pages/settings/MCPSettings/NpxSearch.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/NpxSearch.tsx @@ -206,7 +206,8 @@ const NpxSearch: FC = () => { args: record.configSample?.args ?? ['-y', record.fullName], env: record.configSample?.env, isActive: false, - type: record.type + type: record.type, + searchKey: record.fullName } addMCPServer(newServer) diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 8974e37bf1..24fbddfca5 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -403,6 +403,7 @@ export interface MCPServer { disabledTools?: string[] // List of tool names that are disabled for this server configSample?: MCPConfigSample headers?: Record // Custom headers to be sent with requests to this server + searchKey?: string } export interface MCPToolInputSchema { diff --git a/src/renderer/src/utils/shiki.ts b/src/renderer/src/utils/shiki.ts new file mode 100644 index 0000000000..f25a1d48b6 --- /dev/null +++ b/src/renderer/src/utils/shiki.ts @@ -0,0 +1,34 @@ +import { ThemeMode } from '@renderer/types' +import { MarkdownItShikiOptions, setupMarkdownIt } from '@shikijs/markdown-it' +import MarkdownIt from 'markdown-it' +import { BuiltinLanguage, BuiltinTheme, bundledLanguages, createHighlighter } from 'shiki' + +const defaultOptions = { + themes: { + light: 'one-light', + dark: 'material-theme-darker' + }, + defaultColor: 'light' +} +const initHighlighter = async (options: MarkdownItShikiOptions) => { + const themeNames = ('themes' in options ? Object.values(options.themes) : [options.theme]).filter( + Boolean + ) as BuiltinTheme[] + return await createHighlighter({ + themes: themeNames, + langs: options.langs || (Object.keys(bundledLanguages) as BuiltinLanguage[]) + }) +} + +const highlighter = await initHighlighter(defaultOptions) + +export function getShikiInstance(theme: ThemeMode) { + const options = { + ...defaultOptions, + defaultColor: theme + } + + return function (markdownit: MarkdownIt) { + setupMarkdownIt(markdownit, highlighter, options) + } +} diff --git a/yarn.lock b/yarn.lock index 4bb74f6d5f..37c28b9aee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3268,6 +3268,21 @@ __metadata: languageName: node linkType: hard +"@shikijs/markdown-it@npm:^3.2.2": + version: 3.2.2 + resolution: "@shikijs/markdown-it@npm:3.2.2" + dependencies: + markdown-it: "npm:^14.1.0" + shiki: "npm:3.2.2" + peerDependencies: + markdown-it-async: ^2.2.0 + peerDependenciesMeta: + markdown-it-async: + optional: true + checksum: 10c0/37c98e45a0905ea58f605c7cd341c83f3289b6a37093862535c59f7cc178fe9bfb13413fea68d4d923341e51b2e5718fc5172147d15e07457a76aceed2ac1f95 + languageName: node + linkType: hard + "@shikijs/themes@npm:3.2.2": version: 3.2.2 resolution: "@shikijs/themes@npm:3.2.2" @@ -4315,6 +4330,7 @@ __metadata: "@mozilla/readability": "npm:^0.6.0" "@notionhq/client": "npm:^2.2.15" "@reduxjs/toolkit": "npm:^2.2.5" + "@shikijs/markdown-it": "npm:^3.2.2" "@strongtz/win32-arm64-msvc": "npm:^0.4.7" "@swc/plugin-styled-components": "npm:^7.1.3" "@tavily/core": "patch:@tavily/core@npm%3A0.3.1#~/.yarn/patches/@tavily-core-npm-0.3.1-fe69bf2bea.patch" @@ -15553,7 +15569,7 @@ __metadata: languageName: node linkType: hard -"shiki@npm:^3.2.1": +"shiki@npm:3.2.2, shiki@npm:^3.2.1": version: 3.2.2 resolution: "shiki@npm:3.2.2" dependencies: