@@ -9504,6 +9504,7 @@ async def catalog_partial(
95049504 category : Optional [str ] = None ,
95059505 auth_type : Optional [str ] = None ,
95069506 search : Optional [str ] = None ,
9507+ page : int = 1 ,
95079508 db : Session = Depends (get_db ),
95089509 _user = Depends (get_current_user_with_permissions ),
95099510) -> HTMLResponse :
@@ -9514,6 +9515,7 @@ async def catalog_partial(
95149515 category: Filter by category
95159516 auth_type: Filter by authentication type
95169517 search: Search term
9518+ page: Page number (1-indexed)
95179519 db: Database session
95189520 _user: Authenticated user
95199521
@@ -9528,109 +9530,48 @@ async def catalog_partial(
95289530
95299531 root_path = request .scope .get ("root_path" , "" )
95309532
9531- catalog_request = CatalogListRequest (category = category , auth_type = auth_type , search = search , show_available_only = True , limit = 50 , offset = 0 )
9533+ # Calculate pagination
9534+ page_size = 100
9535+ offset = (page - 1 ) * page_size
95329536
9533- response = await catalog_service .get_catalog_servers (catalog_request , db )
9534-
9535- # Build HTML for server cards
9536- servers_html = '<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">'
9537+ catalog_request = CatalogListRequest (category = category , auth_type = auth_type , search = search , show_available_only = False , limit = page_size , offset = offset )
95379538
9538- for server in response .servers :
9539- # Badge colors based on auth type
9540- auth_badge_class = "bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200"
9541- if server .auth_type == "OAuth2.1" :
9542- auth_badge_class = "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"
9543- elif server .auth_type == "API Key" :
9544- auth_badge_class = "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200"
9545- elif server .auth_type == "Open" :
9546- auth_badge_class = "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
9547-
9548- # Registration status
9549- if server .is_registered :
9550- register_button = '<button class="w-full px-4 py-2 bg-gray-300 text-gray-600 rounded-md cursor-not-allowed" disabled>Already Registered</button>'
9551- else :
9552- if server .requires_api_key or server .auth_type in ["API Key" , "OAuth2.1 & API Key" ]:
9553- # Show modal for API key input
9554- register_button = f"""
9555- <button class="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
9556- onclick="showApiKeyModal('{ server .id } ', '{ server .name } ', '{ server .url } ')">
9557- Add Server
9558- </button>
9559- """
9560- else :
9561- # Direct registration for servers that don't require API key
9562- register_button = f"""
9563- <button class="w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
9564- hx-post="{ root_path } /admin/mcp-registry/{ server .id } /register"
9565- hx-swap="outerHTML"
9566- hx-indicator="#{ server .id } -spinner">
9567- <span id="{ server .id } -spinner" class="htmx-indicator">
9568- <span class="inline-block animate-spin rounded-full h-4 w-4 border-b-2 border-white"></span>
9569- </span>
9570- Add Server
9571- </button>
9572- """
9539+ response = await catalog_service .get_catalog_servers (catalog_request , db )
95739540
9574- servers_html += f"""
9575- <div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm p-4 hover:shadow-md transition-shadow">
9576- <div class="mb-3">
9577- <h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-1">{ server .name } </h3>
9578- <div class="flex gap-2 mb-2">
9579- <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium { auth_badge_class } ">
9580- { server .auth_type }
9581- </span>
9582- <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
9583- { server .category }
9584- </span>
9585- </div>
9586- <p class="text-sm text-gray-600 dark:text-gray-400">{ server .description } </p>
9587- </div>
9541+ # Calculate statistics and pagination info
9542+ total_servers = response .total
9543+ registered_count = sum (1 for s in response .servers if s .is_registered )
9544+ total_pages = (total_servers + page_size - 1 ) // page_size # Ceiling division
95889545
9589- <div class="mb-3">
9590- <p class="text-xs text-gray-500 dark:text-gray-400 truncate">
9591- <span class="font-semibold">Provider:</span> { server .provider }
9592- </p>
9593- <p class="text-xs text-gray-500 dark:text-gray-400 truncate">
9594- <span class="font-semibold">URL:</span> { server .url }
9595- </p>
9596- </div>
9546+ # Count servers by category, auth type, and provider
9547+ servers_by_category = {}
9548+ servers_by_auth_type = {}
9549+ servers_by_provider = {}
95979550
9598- <div>
9599- { register_button }
9600- </div>
9601- </div>
9602- """
9551+ for server in response .servers :
9552+ servers_by_category [server .category ] = servers_by_category .get (server .category , 0 ) + 1
9553+ servers_by_auth_type [server .auth_type ] = servers_by_auth_type .get (server .auth_type , 0 ) + 1
9554+ servers_by_provider [server .provider ] = servers_by_provider .get (server .provider , 0 ) + 1
9555+
9556+ stats = {
9557+ "total_servers" : total_servers ,
9558+ "registered_servers" : registered_count ,
9559+ "categories" : response .categories ,
9560+ "auth_types" : response .auth_types ,
9561+ "providers" : response .providers ,
9562+ "servers_by_category" : servers_by_category ,
9563+ "servers_by_auth_type" : servers_by_auth_type ,
9564+ "servers_by_provider" : servers_by_provider ,
9565+ }
96039566
9604- servers_html += "</div>"
9605-
9606- # Update filter dropdowns with available options
9607- servers_html += f"""
9608- <script>
9609- (function() {{
9610- // Update category filter options
9611- const categoryFilter = document.getElementById('category-filter');
9612- if (categoryFilter && categoryFilter.options.length <= 1) {{
9613- { "; " .join ([f'categoryFilter.options.add(new Option("{ cat } ", "{ cat } "))' for cat in response .categories ])}
9614- }}
9615-
9616- // Update auth type filter options
9617- const authFilter = document.getElementById('auth-filter');
9618- if (authFilter && authFilter.options.length <= 1) {{
9619- { "; " .join ([f'authFilter.options.add(new Option("{ auth } ", "{ auth } "))' for auth in response .auth_types ])}
9620- }}
9621-
9622- // Set selected values
9623- if (categoryFilter) categoryFilter.value = "{ category or "" } ";
9624- if (authFilter) authFilter.value = "{ auth_type or "" } ";
9625- }})();
9626- </script>
9627- """ # nosec B608
9628-
9629- if not response .servers :
9630- servers_html = """
9631- <div class="text-center py-8">
9632- <p class="text-gray-500">No servers found matching your filters</p>
9633- </div>
9634- """
9567+ context = {
9568+ "request" : request ,
9569+ "servers" : response .servers ,
9570+ "stats" : stats ,
9571+ "root_path" : root_path ,
9572+ "page" : page ,
9573+ "total_pages" : total_pages ,
9574+ "page_size" : page_size ,
9575+ }
96359576
9636- return HTMLResponse ( content = servers_html )
9577+ return request . app . state . templates . TemplateResponse ( "mcp_registry_partial.html" , context )
0 commit comments