From 48dab27e24faace4132c3a2fd3a56e18dd6ff525 Mon Sep 17 00:00:00 2001 From: Soumyadas15 Date: Fri, 6 Jun 2025 01:46:37 +0530 Subject: [PATCH] polish(ui): cyhange mcp copywrite + get all mcp servers from smithery with pagination --- backend/mcp_local/api.py | 30 +++++++--- .../agents/_components/mcp-configuration.tsx | 48 ++++++++++++++- .../agents/_components/mcp/browse-dialog.tsx | 59 ++++++++++++++++--- .../_components/mcp/category-sidebar.tsx | 5 +- .../_components/mcp/mcp-server-card.tsx | 9 +-- .../(dashboard)/agents/new/[agentId]/page.tsx | 2 +- .../hooks/react-query/mcp/use-mcp-servers.ts | 17 +++++- 7 files changed, 141 insertions(+), 29 deletions(-) diff --git a/backend/mcp_local/api.py b/backend/mcp_local/api.py index b6deaaab..c946d6ee 100644 --- a/backend/mcp_local/api.py +++ b/backend/mcp_local/api.py @@ -69,6 +69,7 @@ class PopularServersV2Response(BaseModel): categorized: Dict[str, List[Dict[str, Any]]] total: int categoryCount: int + pagination: Dict[str, int] class CustomMCPConnectionRequest(BaseModel): """Request model for connecting to a custom MCP server""" @@ -293,6 +294,8 @@ async def get_popular_mcp_servers( @router.get("/mcp/popular-servers/v2", response_model=PopularServersV2Response) async def get_popular_mcp_servers_v2( + page: int = Query(1, ge=1, description="Page number"), + pageSize: int = Query(200, ge=1, le=500, description="Items per page (max 500 for comprehensive categorization)"), user_id: str = Depends(get_current_user_id_from_jwt) ): """ @@ -300,6 +303,10 @@ async def get_popular_mcp_servers_v2( Returns servers grouped by category with proper metadata and usage statistics. This endpoint fetches real data from the Smithery registry API and categorizes it. + + Query parameters: + - page: Page number (default: 1) + - pageSize: Number of items per page (default: 200, max: 500) """ logger.info(f"Fetching v2 popular MCP servers for user {user_id}") @@ -317,10 +324,10 @@ async def get_popular_mcp_servers_v2( else: logger.warning("No Smithery API key found in environment variables") - # Fetch more servers for better coverage + # Use provided pagination parameters params = { - "page": 1, - "pageSize": 100 # Get more servers + "page": page, + "pageSize": pageSize } response = await client.get( @@ -337,11 +344,13 @@ async def get_popular_mcp_servers_v2( servers=[], categorized={}, total=0, - categoryCount=0 + categoryCount=0, + pagination={"currentPage": page, "pageSize": pageSize, "totalPages": 0, "totalCount": 0} ) data = response.json() servers = data.get("servers", []) + pagination_data = data.get("pagination", {}) # Category mappings based on server types and names category_mappings = { @@ -523,8 +532,14 @@ async def get_popular_mcp_servers_v2( success=True, servers=servers, categorized=sorted_categories, - total=len(servers), - categoryCount=len(sorted_categories) + total=pagination_data.get("totalCount", len(servers)), + categoryCount=len(sorted_categories), + pagination={ + "currentPage": pagination_data.get("currentPage", page), + "pageSize": pagination_data.get("pageSize", pageSize), + "totalPages": pagination_data.get("totalPages", 1), + "totalCount": pagination_data.get("totalCount", len(servers)) + } ) except Exception as e: @@ -534,6 +549,7 @@ async def get_popular_mcp_servers_v2( servers=[], categorized={}, total=0, - categoryCount=0 + categoryCount=0, + pagination={"currentPage": page, "pageSize": pageSize, "totalPages": 0, "totalCount": 0} ) diff --git a/frontend/src/app/(dashboard)/agents/_components/mcp-configuration.tsx b/frontend/src/app/(dashboard)/agents/_components/mcp-configuration.tsx index f78604d0..df77ca66 100644 --- a/frontend/src/app/(dashboard)/agents/_components/mcp-configuration.tsx +++ b/frontend/src/app/(dashboard)/agents/_components/mcp-configuration.tsx @@ -6,7 +6,7 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, D import { Card } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { ScrollArea } from '@/components/ui/scroll-area'; -import { Loader2, Search, Plus, Settings, ExternalLink, Shield, X, Sparkles, ChevronRight } from 'lucide-react'; +import { Loader2, Search, Plus, Settings, ExternalLink, Shield, X, Sparkles, ChevronRight, ChevronLeft } from 'lucide-react'; import { usePopularMCPServers, usePopularMCPServersV2, useMCPServers, useMCPServerDetails } from '@/hooks/react-query/mcp/use-mcp-servers'; import { cn } from '@/lib/utils'; @@ -194,9 +194,11 @@ export const MCPConfiguration: React.FC = ({ const [configuringServer, setConfiguringServer] = useState(null); const [editingIndex, setEditingIndex] = useState(null); const [selectedCategory, setSelectedCategory] = useState(null); + const [currentPage, setCurrentPage] = useState(1); + const [pageSize] = useState(200); const { data: popularServers } = usePopularMCPServers(); - const { data: popularServersV2, isLoading: isLoadingV2 } = usePopularMCPServersV2(); + const { data: popularServersV2, isLoading: isLoadingV2 } = usePopularMCPServersV2(currentPage, pageSize); const { data: searchResults, isLoading: isSearching } = useMCPServers( searchQuery.length > 2 ? searchQuery : undefined ); @@ -206,6 +208,17 @@ export const MCPConfiguration: React.FC = ({ setEditingIndex(null); }; + React.useEffect(() => { + setCurrentPage(1); + }, [searchQuery]); + + React.useEffect(() => { + if (showBrowseDialog) { + setCurrentPage(1); + setSelectedCategory(null); + } + }, [showBrowseDialog]); + const handleEditMCP = (index: number) => { const mcp = configuredMCPs[index]; setConfiguringServer({ @@ -584,6 +597,37 @@ export const MCPConfiguration: React.FC = ({ + + {!searchQuery && popularServersV2?.success && popularServersV2.pagination && ( +
+
+ Showing {((currentPage - 1) * pageSize) + 1} to {Math.min(currentPage * pageSize, popularServersV2.pagination.totalCount)} of {popularServersV2.pagination.totalCount} servers +
+
+ + + Page {currentPage} of {popularServersV2.pagination.totalPages} + + +
+
+ )} diff --git a/frontend/src/app/(dashboard)/agents/_components/mcp/browse-dialog.tsx b/frontend/src/app/(dashboard)/agents/_components/mcp/browse-dialog.tsx index a9ddfa30..0812003b 100644 --- a/frontend/src/app/(dashboard)/agents/_components/mcp/browse-dialog.tsx +++ b/frontend/src/app/(dashboard)/agents/_components/mcp/browse-dialog.tsx @@ -1,8 +1,9 @@ -import React, { useState } from 'react'; -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import React, { useState, useEffect } from 'react'; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { ScrollArea } from '@/components/ui/scroll-area'; -import { Search } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Search, ChevronLeft, ChevronRight } from 'lucide-react'; import { usePopularMCPServers, usePopularMCPServersV2, useMCPServers } from '@/hooks/react-query/mcp/use-mcp-servers'; import { McpServerCard } from './mcp-server-card'; import { CategorySidebar } from './category-sidebar'; @@ -23,15 +24,28 @@ export const BrowseDialog: React.FC = ({ }) => { const [searchQuery, setSearchQuery] = useState(''); const [selectedCategory, setSelectedCategory] = useState(null); + const [currentPage, setCurrentPage] = useState(1); + const [pageSize] = useState(200); const { data: popularServers } = usePopularMCPServers(); - const { data: popularServersV2, isLoading: isLoadingV2 } = usePopularMCPServersV2(); + const { data: popularServersV2, isLoading: isLoadingV2 } = usePopularMCPServersV2(currentPage, pageSize); const { data: searchResults, isLoading: isSearching } = useMCPServers( searchQuery.length > 2 ? searchQuery : undefined ); const categories = popularServersV2?.success ? Object.keys(popularServersV2.categorized) : []; + useEffect(() => { + setCurrentPage(1); + }, [searchQuery]); + + useEffect(() => { + if (open) { + setCurrentPage(1); + setSelectedCategory(null); + } + }, [open]); + return ( @@ -41,8 +55,6 @@ export const BrowseDialog: React.FC = ({ Discover and add Model Context Protocol servers from Smithery - - {/* Search */}
= ({ className="pl-10" />
-
{!searchQuery && categories.length > 0 && ( = ({
+ + {!searchQuery && popularServersV2?.success && popularServersV2.pagination && ( +
+
+ Showing {((currentPage - 1) * pageSize) + 1} to {Math.min(currentPage * pageSize, popularServersV2.pagination.totalCount)} of {popularServersV2.pagination.totalCount} servers +
+
+ + + Page {currentPage} of {popularServersV2.pagination.totalPages} + + +
+
+ )} +
); -}; \ No newline at end of file +}; diff --git a/frontend/src/app/(dashboard)/agents/_components/mcp/category-sidebar.tsx b/frontend/src/app/(dashboard)/agents/_components/mcp/category-sidebar.tsx index 2f36416b..58ae2d73 100644 --- a/frontend/src/app/(dashboard)/agents/_components/mcp/category-sidebar.tsx +++ b/frontend/src/app/(dashboard)/agents/_components/mcp/category-sidebar.tsx @@ -19,7 +19,7 @@ export const CategorySidebar: React.FC = ({ categorizedServers, }) => { return ( -
+

Categories

@@ -48,9 +48,6 @@ export const CategorySidebar: React.FC = ({ > {categoryIcons[category] || "🧩"} {category} - - {count} - ); })} diff --git a/frontend/src/app/(dashboard)/agents/_components/mcp/mcp-server-card.tsx b/frontend/src/app/(dashboard)/agents/_components/mcp/mcp-server-card.tsx index 9939e2ef..49943029 100644 --- a/frontend/src/app/(dashboard)/agents/_components/mcp/mcp-server-card.tsx +++ b/frontend/src/app/(dashboard)/agents/_components/mcp/mcp-server-card.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Card } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Shield, ExternalLink, ChevronRight, Sparkles } from 'lucide-react'; +import { truncateString } from '@/lib/utils'; interface McpServerCardProps { server: any; @@ -14,7 +15,7 @@ export const McpServerCard: React.FC = ({ server, onClick }) className="p-4 cursor-pointer hover:bg-muted/50 transition-colors" onClick={() => onClick(server)} > -
+
{server.iconUrl ? ( {server.displayName ) : ( @@ -22,7 +23,7 @@ export const McpServerCard: React.FC = ({ server, onClick })
)} -
+

{server.displayName || server.name}

{server.security?.scanPassed && ( @@ -34,8 +35,8 @@ export const McpServerCard: React.FC = ({ server, onClick }) )}
-

- {server.description} +

+ {truncateString(server.description, 100)}

Used {server.useCount} times diff --git a/frontend/src/app/(dashboard)/agents/new/[agentId]/page.tsx b/frontend/src/app/(dashboard)/agents/new/[agentId]/page.tsx index 039bcb14..a0aa2d88 100644 --- a/frontend/src/app/(dashboard)/agents/new/[agentId]/page.tsx +++ b/frontend/src/app/(dashboard)/agents/new/[agentId]/page.tsx @@ -374,7 +374,7 @@ export default function AgentConfigurationPage() {
- MCP Servers + Integrations (via MCP) New
diff --git a/frontend/src/hooks/react-query/mcp/use-mcp-servers.ts b/frontend/src/hooks/react-query/mcp/use-mcp-servers.ts index c3aea920..f59c31f6 100644 --- a/frontend/src/hooks/react-query/mcp/use-mcp-servers.ts +++ b/frontend/src/hooks/react-query/mcp/use-mcp-servers.ts @@ -71,6 +71,12 @@ interface PopularServersV2Response { }>>; total: number; categoryCount: number; + pagination: { + currentPage: number; + pageSize: number; + totalPages: number; + totalCount: number; + }; } export const useMCPServers = (query?: string, page: number = 1, pageSize: number = 20) => { @@ -164,17 +170,22 @@ export const usePopularMCPServers = () => { }); }; -export const usePopularMCPServersV2 = () => { +export const usePopularMCPServersV2 = (page: number = 1, pageSize: number = 200) => { const supabase = createClient(); return useQuery({ - queryKey: ['mcp-servers-popular-v2'], + queryKey: ['mcp-servers-popular-v2', page, pageSize], queryFn: async (): Promise => { const { data: { session } } = await supabase.auth.getSession(); if (!session) throw new Error('No session'); + const params = new URLSearchParams({ + page: page.toString(), + pageSize: pageSize.toString(), + }); + const response = await fetch( - `${API_URL}/mcp/popular-servers/v2`, + `${API_URL}/mcp/popular-servers/v2?${params}`, { headers: { 'Authorization': `Bearer ${session.access_token}`,