From 7b9f7d5acd181c6c6bfbffac0796ffcfcf97afd5 Mon Sep 17 00:00:00 2001 From: ningyv <1793599591@qq.com> Date: Thu, 10 Apr 2025 16:10:24 +0800 Subject: [PATCH] feature/1.7-MCP --- .../mcpService/IntegrationAIContainer.tsx | 138 ++++++++++-------- .../src/pages/mcpService/McpKeyContainer.tsx | 84 ++++++----- 2 files changed, 123 insertions(+), 99 deletions(-) diff --git a/frontend/packages/core/src/pages/mcpService/IntegrationAIContainer.tsx b/frontend/packages/core/src/pages/mcpService/IntegrationAIContainer.tsx index 248da0ee..d46a0bdb 100644 --- a/frontend/packages/core/src/pages/mcpService/IntegrationAIContainer.tsx +++ b/frontend/packages/core/src/pages/mcpService/IntegrationAIContainer.tsx @@ -1,4 +1,4 @@ -import { App, Card, Select } from 'antd' +import { App, Button, Card, Empty, Select } from 'antd' import { $t } from '@common/locales/index.ts' import { Icon } from '@iconify/react/dist/iconify.js' import { useEffect, useState } from 'react' @@ -8,7 +8,8 @@ import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' import { useFetch } from '@common/hooks/http' import { useConnection } from './hook/useConnection' import { ClientRequest, Tool, ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js' -import { z } from "zod"; +import { z } from 'zod' +import { useNavigate } from 'react-router-dom' type ConfigList = { openApi?: { @@ -30,19 +31,26 @@ type ApiKeyItem = { value: string } -const IntegrationAIContainer = ({ type, handleToolsChange }: { type: 'global' | 'service'; handleToolsChange: (value: Tool[]) => void }) => { +const IntegrationAIContainer = ({ + type, + handleToolsChange +}: { + type: 'global' | 'service' + handleToolsChange: (value: Tool[]) => void +}) => { const [activeTab, setActiveTab] = useState('mcp') const { message } = App.useApp() const [configContent, setConfigContent] = useState('') const [apiKey, setApiKey] = useState('') const [apiKeyList, setApiKeyList] = useState<{ value: string; label: string }[]>([]) const [mcpServerUrl, setMcpServerUrl] = useState('') + const navigator = useNavigate() const [errors, setErrors] = useState>({ resources: null, prompts: null, - tools: null, - }); - + tools: null + }) + const [tabContent, setTabContent] = useState({ mcp: { title: $t('MCP 配置'), @@ -113,6 +121,10 @@ const IntegrationAIContainer = ({ type, handleToolsChange }: { type: 'global' | }) } + const addKey = () => { + navigator('/mcpKey') + } + /** * 获取 API Key 列表 */ @@ -144,43 +156,39 @@ const IntegrationAIContainer = ({ type, handleToolsChange }: { type: 'global' | } const clearError = (tabKey: keyof typeof errors) => { - setErrors((prev) => ({ ...prev, [tabKey]: null })); - }; + setErrors((prev) => ({ ...prev, [tabKey]: null })) + } - const makeRequest = async ( - request: ClientRequest, - schema: T, - tabKey?: keyof typeof errors, - ) => { + const makeRequest = async (request: ClientRequest, schema: T, tabKey?: keyof typeof errors) => { try { - const response = await makeConnectionRequest(request, schema); + const response = await makeConnectionRequest(request, schema) if (tabKey !== undefined) { - clearError(tabKey); + clearError(tabKey) } - return response; + return response } catch (e) { - const errorString = (e as Error).message ?? String(e); + const errorString = (e as Error).message ?? String(e) if (tabKey !== undefined) { setErrors((prev) => ({ ...prev, - [tabKey]: errorString, - })); + [tabKey]: errorString + })) } - throw e; + throw e } - }; + } const listTools = async () => { const response = await makeRequest( { - method: "tools/list" as const, - params: {}, + method: 'tools/list' as const, + params: {} }, ListToolsResultSchema, - "tools", - ); + 'tools' + ) handleToolsChange(response.tools) - }; + } const { connectionStatus, @@ -192,13 +200,13 @@ const IntegrationAIContainer = ({ type, handleToolsChange }: { type: 'global' | handleCompletion, completionsSupported, connect: connectMcpServer, - disconnect: disconnectMcpServer, + disconnect: disconnectMcpServer } = useConnection({ transportType: 'sse', sseUrl: '', proxyServerUrl: mcpServerUrl, - requestTimeout: 1000, - }); + requestTimeout: 1000 + }) useEffect(() => { if (type === 'global') { @@ -209,12 +217,10 @@ const IntegrationAIContainer = ({ type, handleToolsChange }: { type: 'global' | getKeysList() }, []) useEffect(() => { - if (apiKey) { - if (activeTab === 'openApi' && tabContent?.openApi?.configContent) { - setConfigContent(tabContent?.openApi?.configContent?.replace('{your_api_key}', apiKey) || '') - } else if (activeTab === 'mcp' && tabContent?.mcp?.configContent) { - setConfigContent(tabContent.mcp.configContent?.replace('{your_api_key}', apiKey) || '') - } + if (activeTab === 'openApi' && tabContent?.openApi?.configContent) { + setConfigContent(tabContent?.openApi?.configContent?.replace('{your_api_key}', apiKey || '{your_api_key}')) + } else if (activeTab === 'mcp' && tabContent?.mcp?.configContent) { + setConfigContent(tabContent.mcp.configContent?.replace('{your_api_key}', apiKey || '{your_api_key}')) } }, [apiKey, activeTab, tabContent]) @@ -308,34 +314,42 @@ const IntegrationAIContainer = ({ type, handleToolsChange }: { type: 'global' | {activeTab === 'mcp' && ( <>
API Key
- + - - + > +
+ {apiKey} + handleCopy(configContent)} + sx={{ + position: 'absolute', + top: '0px', + right: '5px', + color: '#999', + transition: 'none', + '&.MuiButtonBase-root:hover': { + background: 'transparent', + color: '#3D46F2', + transition: 'none' + } + }} + > +
+ + + ) : ( + + + + )} )} diff --git a/frontend/packages/core/src/pages/mcpService/McpKeyContainer.tsx b/frontend/packages/core/src/pages/mcpService/McpKeyContainer.tsx index a7aaa5a6..31c73d41 100644 --- a/frontend/packages/core/src/pages/mcpService/McpKeyContainer.tsx +++ b/frontend/packages/core/src/pages/mcpService/McpKeyContainer.tsx @@ -1,7 +1,7 @@ import InsidePage from '@common/components/aoplatform/InsidePage' import { IconButton } from '@common/components/postcat/api/IconButton' import { $t } from '@common/locales/index.ts' -import { Button, Card, App } from 'antd' +import { Button, Card, App, Empty } from 'antd' import { useFetch } from '@common/hooks/http' import { BasicResponse, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const' import { useEffect, useRef, useState } from 'react' @@ -139,17 +139,36 @@ const McpKeyContainer = () => { {$t('新增 API Key')}
- {keys.map((key, index) => ( - -
-
-

{key.name}

-
- {key.value} + {keys.length ? ( + keys.map((key, index) => ( + +
+
+

{key.name}

+
+ {key.value} + { + copyCode(key?.value) + }} + sx={{ + color: '#333', + transition: 'none', + '&.MuiButtonBase-root:hover': { + background: 'transparent', + color: '#3D46F2', + transition: 'none' + } + }} + > +
+
+
{ - copyCode(key?.value) + editKey(key) }} sx={{ color: '#333', @@ -161,35 +180,26 @@ const McpKeyContainer = () => { } }} > + { + deleteKey(key.id) + }} + sx={{ + color: '#333', + transition: 'none', + '&.MuiButtonBase-root:hover': { background: 'transparent', color: 'red', transition: 'none' } + }} + >
-
- { - editKey(key) - }} - sx={{ - color: '#333', - transition: 'none', - '&.MuiButtonBase-root:hover': { background: 'transparent', color: '#3D46F2', transition: 'none' } - }} - > - { - deleteKey(key.id) - }} - sx={{ - color: '#333', - transition: 'none', - '&.MuiButtonBase-root:hover': { background: 'transparent', color: 'red', transition: 'none' } - }} - > -
-
- - ))} + + )) + ) : ( + <> + + + )}