From 469ae408943ab4c9f5bc59e7ba3571ebaba6eda9 Mon Sep 17 00:00:00 2001 From: dolphin Date: Thu, 10 Apr 2025 10:59:46 +0800 Subject: [PATCH 001/186] =?UTF-8?q?feat:=20mcp=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/bs-comp/sheets/ToolsSheet.tsx | 15 +- .../platform/src/controllers/API/assistant.ts | 6 + .../src/pages/BuildPage/flow/Sidebar.tsx | 1 + .../src/pages/BuildPage/tools/EditMcp.tsx | 350 ++++++++++++++++++ .../src/pages/BuildPage/tools/EditTool.tsx | 38 +- .../src/pages/BuildPage/tools/ToolItem.tsx | 2 +- .../src/pages/BuildPage/tools/index.tsx | 111 +++++- .../pages/SystemPage/components/EditRole.tsx | 3 +- 8 files changed, 494 insertions(+), 32 deletions(-) create mode 100644 src/frontend/platform/src/pages/BuildPage/tools/EditMcp.tsx diff --git a/src/frontend/platform/src/components/bs-comp/sheets/ToolsSheet.tsx b/src/frontend/platform/src/components/bs-comp/sheets/ToolsSheet.tsx index 930bedbab..6c7a36b18 100644 --- a/src/frontend/platform/src/components/bs-comp/sheets/ToolsSheet.tsx +++ b/src/frontend/platform/src/components/bs-comp/sheets/ToolsSheet.tsx @@ -2,9 +2,9 @@ import { Accordion } from "@/components/bs-ui/accordion"; import { Button } from "@/components/bs-ui/button"; import { SearchInput } from "@/components/bs-ui/input"; import { Sheet, SheetContent, SheetTitle, SheetTrigger } from "@/components/bs-ui/sheet"; -import { getAssistantToolsApi } from "@/controllers/API/assistant"; +import { getAssistantMcpApi, getAssistantToolsApi } from "@/controllers/API/assistant"; import ToolItem from "@/pages/BuildPage/tools/ToolItem"; -import { Star, User } from "lucide-react"; +import { CpuIcon, Star, User } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -16,7 +16,8 @@ export default function ToolsSheet({ select, onSelect, children }) { const [allData, setAllData] = useState([]) useEffect(() => { - getAssistantToolsApi(type).then(res => { + const p = type === 'mcp' ? getAssistantMcpApi() : getAssistantToolsApi(type) + p.then(res => { setAllData(res) setKeyword('') }) @@ -61,6 +62,14 @@ export default function ToolsSheet({ select, onSelect, children }) { {t('tools.customTools')} +
setType("mcp")} + > + + MCP工具 +
diff --git a/src/frontend/platform/src/controllers/API/assistant.ts b/src/frontend/platform/src/controllers/API/assistant.ts index 74e908cbd..cb03b8eec 100644 --- a/src/frontend/platform/src/controllers/API/assistant.ts +++ b/src/frontend/platform/src/controllers/API/assistant.ts @@ -89,6 +89,12 @@ export const getAssistantToolsApi = async (type: 'all' | 'default' | 'custom'): return await axios.get(`/api/v1/assistant/tool_list${queryStr[type]}`) }; +// 获取mcp服务集合 +export const getAssistantMcpApi = async (): Promise => { + // TODO 转换数据结构与getAssistantToolsApi一致,方便ui处理 + return await axios.get(`/api/v1/assistant/tool_list?is_preset=false`) +} + // 修改内置工具配置 export const updateAssistantToolApi = async (tool_id, extra) => { return await axios.post(`/api/v1/assistant/tool/config`, { tool_id, extra }) diff --git a/src/frontend/platform/src/pages/BuildPage/flow/Sidebar.tsx b/src/frontend/platform/src/pages/BuildPage/flow/Sidebar.tsx index 2df0d3e9e..5976e8c27 100644 --- a/src/frontend/platform/src/pages/BuildPage/flow/Sidebar.tsx +++ b/src/frontend/platform/src/pages/BuildPage/flow/Sidebar.tsx @@ -31,6 +31,7 @@ export default function Sidebar({ dropdown = false, disabledNodes = [], onInitSt const nodeTemps = useMemo(() => { if (!tempData) return [] + // TODO 追加MCP工具 return tempData.reduce((list, temp) => { if (disabledNodes.includes(temp.type)) return list const newNode = getNodeDataByTemp(temp) diff --git a/src/frontend/platform/src/pages/BuildPage/tools/EditMcp.tsx b/src/frontend/platform/src/pages/BuildPage/tools/EditMcp.tsx new file mode 100644 index 000000000..3757ac74b --- /dev/null +++ b/src/frontend/platform/src/pages/BuildPage/tools/EditMcp.tsx @@ -0,0 +1,350 @@ +import { bsConfirm } from "@/components/bs-ui/alertDialog/useConfirm"; +import { Button } from "@/components/bs-ui/button"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/bs-ui/dialog"; +import { Input, Textarea } from "@/components/bs-ui/input"; +import { Label } from "@/components/bs-ui/label"; +import { Popover, PopoverClose, PopoverContent, PopoverTrigger } from "@/components/bs-ui/popover"; +import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger } from "@/components/bs-ui/select"; +import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle } from "@/components/bs-ui/sheet"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/bs-ui/table"; +import { useToast } from "@/components/bs-ui/toast/use-toast"; +// import { createMcpServer, deleteMcpServer, getMcpTools, testMcpTool, updateMcpServer } from "@/controllers/API/mcp"; +import { captureAndAlertRequestErrorHoc } from "@/controllers/request"; +import { Plus } from "lucide-react"; +import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react"; + +// 测试对话框组件 +const TestDialog = forwardRef((props, ref) => { + const [testShow, setTestShow] = useState(false); + const [toolData, setToolData] = useState(null); + const [params, setParams] = useState({}); + const [result, setResult] = useState(""); + const [loading, setLoading] = useState(false); + const { message } = useToast(); + + useImperativeHandle(ref, () => ({ + open: (tool, config) => { + setToolData({ ...tool, config }); + setParams({}); + setResult(""); + setTestShow(true); + } + })); + + const handleTest = async () => { + // 校验必填参数 + const requiredParams = Object.entries(toolData.inputSchema.properties) + .filter(([_, schema]) => schema.required) + .map(([name]) => name); + + const errors = requiredParams.filter(name => !params[name]); + if (errors.length > 0) { + return message({ description: `以下参数必填:${errors.join(", ")}`, variant: "warning" }); + } + + setLoading(true); + try { + // const res = await captureAndAlertRequestErrorHoc( + // testMcpTool({ + // config: toolData.config, + // toolName: toolData.name, + // params + // }) + // ); + // setResult(JSON.stringify(res, null, 2)); + } finally { + setLoading(false); + } + }; + + return ( + + + + {toolData?.name} + +
+
+ + + + 参数 + 值 + + + + {Object.entries(toolData?.inputSchema?.properties || {}).map(([name, schema]) => ( + + + {name} + {schema.required && *} + + + setParams(prev => ({ + ...prev, + [name]: e.target.value + }))} + /> + + + ))} + +
+
+ +