Skip to content

Commit 4532915

Browse files
authored
Update ux catalogs (#1153)
* Update UX filters mcp servers Signed-off-by: Mihai Criveti <[email protected]> * Update partial for mcp registry Signed-off-by: Mihai Criveti <[email protected]> * Update partial for mcp registry Signed-off-by: Mihai Criveti <[email protected]> * Update partial for mcp registry Signed-off-by: Mihai Criveti <[email protected]> --------- Signed-off-by: Mihai Criveti <[email protected]>
1 parent 08665dd commit 4532915

File tree

4 files changed

+747
-169
lines changed

4 files changed

+747
-169
lines changed

mcpgateway/admin.py

Lines changed: 40 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -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)

mcpgateway/static/admin.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4798,7 +4798,7 @@ function showTab(tabName) {
47984798
if (tabName === "mcp-registry") {
47994799
// Load MCP Registry content
48004800
const registryContent = safeGetElement(
4801-
"mcp-registry-servers",
4801+
"mcp-registry-content",
48024802
);
48034803
if (registryContent) {
48044804
// Always load on first visit or if showing loading message
@@ -4820,7 +4820,7 @@ function showTab(tabName) {
48204820
"GET",
48214821
`${rootPath}/admin/mcp-registry/partial`,
48224822
{
4823-
target: "#mcp-registry-servers",
4823+
target: "#mcp-registry-content",
48244824
swap: "innerHTML",
48254825
},
48264826
)
@@ -13764,7 +13764,7 @@ window.submitApiKeyForm = function (event) {
1376413764
"GET",
1376513765
`${rootPath}/admin/mcp-registry/partial`,
1376613766
{
13767-
target: "#mcp-registry-servers",
13767+
target: "#mcp-registry-content",
1376813768
swap: "innerHTML",
1376913769
},
1377013770
);

mcpgateway/templates/admin.html

Lines changed: 1 addition & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,74 +1103,8 @@
11031103
<!-- MCP Registry Panel -->
11041104
<div id="mcp-registry-panel" class="tab-panel hidden">
11051105
<div class="container mx-auto px-4 py-8">
1106-
<div class="mb-6">
1107-
<h1 class="text-3xl font-bold mb-2 dark:text-gray-200">
1108-
MCP Registry
1109-
</h1>
1110-
<p class="text-gray-600 dark:text-gray-400">
1111-
Browse and add public MCP servers to your gateway
1112-
</p>
1113-
</div>
1114-
1115-
<!-- Filters -->
1116-
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm p-4 mb-6">
1117-
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
1118-
<div>
1119-
<label
1120-
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
1121-
>Category</label
1122-
>
1123-
<select
1124-
id="category-filter"
1125-
name="category"
1126-
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
1127-
hx-get="{{ root_path }}/admin/mcp-registry/partial"
1128-
hx-trigger="change"
1129-
hx-target="#mcp-registry-servers"
1130-
hx-include="[name='auth_type'],[name='search']"
1131-
>
1132-
<option value="">All Categories</option>
1133-
</select>
1134-
</div>
1135-
<div>
1136-
<label
1137-
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
1138-
>Auth Type</label
1139-
>
1140-
<select
1141-
id="auth-filter"
1142-
name="auth_type"
1143-
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
1144-
hx-get="{{ root_path }}/admin/mcp-registry/partial"
1145-
hx-trigger="change"
1146-
hx-target="#mcp-registry-servers"
1147-
hx-include="[name='category'],[name='search']"
1148-
>
1149-
<option value="">All Auth Types</option>
1150-
</select>
1151-
</div>
1152-
<div class="md:col-span-2">
1153-
<label
1154-
class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
1155-
>Search</label
1156-
>
1157-
<input
1158-
type="text"
1159-
id="search-input"
1160-
name="search"
1161-
placeholder="Search servers..."
1162-
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
1163-
hx-get="{{ root_path }}/admin/mcp-registry/partial"
1164-
hx-trigger="keyup changed delay:500ms"
1165-
hx-target="#mcp-registry-servers"
1166-
hx-include="[name='category'],[name='auth_type']"
1167-
/>
1168-
</div>
1169-
</div>
1170-
</div>
1171-
11721106
<!-- Server Grid -->
1173-
<div id="mcp-registry-servers">
1107+
<div id="mcp-registry-content">
11741108
<div class="text-center py-8">
11751109
<div
11761110
class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 dark:border-gray-100"

0 commit comments

Comments
 (0)