"use client"; import { Loader2Icon, PlugZapIcon, WrenchIcon } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import { JsonCodeEditor } from "@/components/json-code-editor"; import { JsonViewer } from "@/components/json-viewer"; import { OptionCombobox } from "@/components/option-combobox"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Drawer, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, } from "@/components/ui/drawer"; import { Label } from "@/components/ui/label"; import { callMCPTool, listMCPServers, listMCPTools, testMCPConnection, type MCPConnectionResult, type MCPServerInfo, type MCPToolCallResult, type MCPToolInfo, } from "@/lib/api/admin"; const defaultServerCode = ""; export default function MCPDashboardPage() { const [serverCode, setServerCode] = useState(defaultServerCode); const [servers, setServers] = useState([]); const [connection, setConnection] = useState( null, ); const [tools, setTools] = useState([]); const [loadingServers, setLoadingServers] = useState(true); const [testing, setTesting] = useState(false); const [loadingTools, setLoadingTools] = useState(false); const [callingTool, setCallingTool] = useState(false); const [drawerOpen, setDrawerOpen] = useState(false); const [activeTool, setActiveTool] = useState(null); const [argumentsText, setArgumentsText] = useState("{}"); const [toolResult, setToolResult] = useState(null); const [argumentsError, setArgumentsError] = useState(null); const serverOptions = useMemo( () => servers.map((server) => ({ value: server.code, label: server.enabled ? `${server.code} (${server.endpoint})` : `${server.code} (disabled)`, })), [servers], ); useEffect(() => { async function loadServers() { setLoadingServers(true); try { const result = await listMCPServers(); setServers(result); const firstServer = result[0]; if (firstServer) { setServerCode((current) => current || firstServer.code); } } catch (error) { toast.error( error instanceof Error ? error.message : "加载 MCP 服务失败", ); } finally { setLoadingServers(false); } } void loadServers(); }, []); async function handleTestConnection() { setTesting(true); try { const result = await testMCPConnection(serverCode.trim()); setConnection(result); toast.success("MCP 连接成功"); } catch (error) { toast.error(error instanceof Error ? error.message : "MCP 连接失败"); } finally { setTesting(false); } } async function handleListTools() { setLoadingTools(true); try { const result = await listMCPTools(serverCode.trim()); setTools(result); toast.success(`已加载 ${result.length} 个工具`); } catch (error) { toast.error(error instanceof Error ? error.message : "加载工具失败"); } finally { setLoadingTools(false); } } async function handleCallTool() { if (!activeTool) { toast.error("请先选择一个工具"); return; } if (argumentsError) { toast.error("Arguments JSON 格式不合法"); return; } let parsedArguments: Record = {}; try { parsedArguments = argumentsText.trim() ? (JSON.parse(argumentsText) as Record) : {}; } catch { toast.error("arguments 必须是合法 JSON"); return; } setCallingTool(true); try { const result = await callMCPTool({ serverCode: serverCode.trim(), toolName: activeTool.name, arguments: parsedArguments, }); setToolResult(result); toast.success(result.isError ? "工具返回错误结果" : "工具调用成功"); } catch (error) { toast.error(error instanceof Error ? error.message : "工具调用失败"); } finally { setCallingTool(false); } } function openToolDrawer(tool: MCPToolInfo) { setActiveTool(tool); setToolResult(null); if (tool.name === "lorem") { setArgumentsText('{\n "wordCount": 8\n}'); } else if (tool.name === "ping") { setArgumentsText('{\n "message": "hello from dashboard"\n}'); } else { setArgumentsText("{}"); } setDrawerOpen(true); } return ( <>
{/*
*/}
服务配置
{ setServerCode(value); setConnection(null); setTools([]); setToolResult(null); setActiveTool(null); }} />
{connection ? (
已连接 {connection.serverName || "-"}
serverCode: {connection.serverCode}
protocol: {connection.protocol || "-"}
endpoint: {connection.endpoint}
version: {connection.version || "-"}
) : null}
{tools.length === 0 ? (
暂无工具结果,先点击上方“列出工具”。
) : (
工具名
描述
操作
{tools.map((tool) => (
{tool.name}
{tool.description || "-"}
))}
)}
{activeTool?.name || "工具详情"} 在这里查看工具详细信息,并使用当前选择的 MCP Server 直接做一次真实调用测试。
{activeTool ? ( <>
{activeTool.name} {activeTool.title ? ( {activeTool.title} ) : null}

{activeTool.description || "暂无描述"}

Input Schema

Output Schema

{toolResult ? (
{toolResult.isError ? "返回错误" : "调用成功"} {toolResult.toolName}

Content

Structured Content

) : null}
) : null}
); }