"use client"; import { MCPCard } from "@/components/mcp-card"; import { canCreateMCP } from "lib/auth/client-permissions"; import { Button } from "@/components/ui/button"; import Link from "next/link"; import { MCPOverview, RECOMMENDED_MCPS } from "@/components/mcp-overview"; import { Skeleton } from "ui/skeleton"; import { ScrollArea } from "ui/scroll-area"; import { useTranslations } from "next-intl"; import { MCPIcon } from "ui/mcp-icon"; import { useMcpList } from "@/hooks/queries/use-mcp-list"; import dynamic from "next/dynamic"; import { useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; import { Loader2 } from "lucide-react"; import { cn } from "lib/utils"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "ui/dropdown-menu"; import { useRouter } from "next/navigation"; import { BasicUser } from "app-types/user"; const LightRays = dynamic(() => import("@/components/ui/light-rays"), { ssr: false, }); interface MCPDashboardProps { message?: string; user: BasicUser; } export default function MCPDashboard({ message, user }: MCPDashboardProps) { const t = useTranslations("MCP"); const router = useRouter(); // Check if user can create MCP connections using Better Auth permissions const canCreate = canCreateMCP(user?.role); const { data: mcpList, isLoading, isValidating, } = useMcpList({ refreshInterval: 10000, }); const { myServers, featuredServers } = useMemo(() => { if (!mcpList) return { myServers: [], featuredServers: [] }; const sortFn = (a: any, b: any) => { if (a.status === b.status) return 0; if (a.status === "authorizing") return -1; if (b.status === "authorizing") return 1; return 0; }; const owned = mcpList.filter((s) => s.userId === user?.id).sort(sortFn); const featured = mcpList .filter((s) => s.userId !== user?.id && s.visibility === "public") .sort(sortFn); return { myServers: owned, featuredServers: featured }; }, [mcpList]); const displayIcons = useMemo(() => { const shuffled = [...RECOMMENDED_MCPS].sort(() => 0.5 - Math.random()); return shuffled.slice(0, 5); }, []); // Delay showing validating spinner until validating persists for 500ms const [showValidating, setShowValidating] = useState(false); const handleRecommendedSelect = (mcp: (typeof RECOMMENDED_MCPS)[number]) => { const params = new URLSearchParams(); params.set("name", mcp.name); params.set("config", JSON.stringify(mcp.config)); router.push(`/mcp/create?${params.toString()}`); }; const particle = useMemo(() => { return ( <>
); }, [isLoading, mcpList?.length]); useEffect(() => { if (isValidating) { setShowValidating(false); const timerId = setTimeout(() => setShowValidating(true), 500); return () => clearTimeout(timerId); } setShowValidating(false); }, [isValidating]); useEffect(() => { if (message) { toast(

{message}

, { id: "mcp-list-message", }); } }, []); return ( <> {particle}

{canCreate ? t("mcpServers") : t("availableMcpServers")} {showValidating && isValidating && !isLoading && ( )}

{canCreate && mcpList?.length ? ( {RECOMMENDED_MCPS.map((mcp) => { const Icon = mcp.icon; return ( handleRecommendedSelect(mcp)} className="cursor-pointer" > {mcp.label} ); })} ) : null} {canCreate && ( )} {canCreate && ( )}
{isLoading ? (
) : myServers?.length || featuredServers?.length ? (
{myServers?.length > 0 && (

{t("myMcpServers")}

{myServers.map((mcp) => ( ))}
)} {featuredServers?.length > 0 && (

{t("featuredMcpServers")}

{featuredServers.map((mcp) => ( ))}
)}
) : // When MCP list is empty canCreate ? ( ) : (

{t("noMcpServersAvailable")}

{t("noMcpServersAvailableDescription")}

)}
); }