Skip to content

Commit 965fd1f

Browse files
authored
refactor: use direct registry API call for server detail page (#490)
Replace the multi-registry Promise.all fan-out with a single direct API call to /registry/{registryName}/v0.1/servers/{serverName}/versions/{version}. The registry name is now threaded from the catalog list page to the detail page via a query parameter, avoiding unnecessary API calls. Falls back to fetching the default registry when the query param is missing (e.g. direct URL navigation). Also removes the unused CATALOG_ALL_REGISTRIES constant and the "All registries" select option that was no longer needed. Made-with: Cursor
1 parent 37cd87b commit 965fd1f

9 files changed

Lines changed: 41 additions & 30 deletions

File tree

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
"use server";
22

3+
import { getRegistries } from "@/app/catalog/actions";
34
import { getAuthenticatedClient } from "@/lib/api-client";
45

5-
export async function getServerDetails(serverName: string, version: string) {
6+
export async function getServerDetails(
7+
registryName: string | undefined,
8+
serverName: string,
9+
version: string,
10+
) {
611
const api = await getAuthenticatedClient();
712

8-
const registriesResult = await api.getV1Registries({ client: api.client });
9-
const registryName = registriesResult.data?.registries?.[0]?.name;
13+
const resolvedRegistry = registryName || (await getRegistries()).at(0)?.name;
1014

11-
if (!registryName) {
15+
if (!resolvedRegistry) {
1216
return {
1317
error: new Error("No registry available"),
1418
data: null,
1519
response: null,
1620
};
1721
}
1822

19-
const { error, data, response } =
20-
await api.getRegistryByRegistryNameV01ServersByServerNameVersionsByVersion({
21-
path: {
22-
registryName,
23-
serverName,
24-
version,
25-
},
26-
client: api.client,
27-
});
28-
29-
return { error, data, response };
23+
return api.getRegistryByRegistryNameV01ServersByServerNameVersionsByVersion({
24+
path: { registryName: resolvedRegistry, serverName, version },
25+
client: api.client,
26+
});
3027
}

src/app/catalog/[repoName]/[serverName]/[version]/not-found.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button";
55

66
export default function NotFound() {
77
return (
8-
<div className="flex flex-col gap-5 px-8 pt-5 pb-8">
8+
<div className="flex flex-col gap-5">
99
<NavigateBackButton
1010
href="/catalog"
1111
variant="outline"

src/app/catalog/[repoName]/[serverName]/[version]/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,22 @@ interface CatalogDetailPageProps {
1111
serverName: string;
1212
version: string;
1313
}>;
14+
searchParams: Promise<{
15+
registryName?: string;
16+
}>;
1417
}
1518

1619
export default async function CatalogDetailPage({
1720
params,
21+
searchParams,
1822
}: CatalogDetailPageProps) {
1923
const { repoName, serverName, version } = await params;
24+
const { registryName } = await searchParams;
25+
26+
const fullServerName = `${repoName}/${serverName}`;
2027
const { data: serverResponse, response } = await getServerDetails(
21-
`${repoName}/${serverName}`,
28+
registryName,
29+
fullServerName,
2230
version,
2331
);
2432

src/app/catalog/components/__tests__/servers.test.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe("Servers", () => {
3333
render(
3434
<Servers
3535
servers={mockServers}
36+
registryName="default-registry"
3637
viewMode="grid"
3738
searchQuery=""
3839
onClearSearch={mockOnClearSearch}
@@ -48,6 +49,7 @@ describe("Servers", () => {
4849
const { container } = render(
4950
<Servers
5051
servers={mockServers}
52+
registryName="default-registry"
5153
viewMode="grid"
5254
searchQuery=""
5355
onClearSearch={mockOnClearSearch}
@@ -64,6 +66,7 @@ describe("Servers", () => {
6466
render(
6567
<Servers
6668
servers={mockServers}
69+
registryName="default-registry"
6770
viewMode="list"
6871
searchQuery=""
6972
onClearSearch={mockOnClearSearch}
@@ -79,6 +82,7 @@ describe("Servers", () => {
7982
render(
8083
<Servers
8184
servers={mockServers}
85+
registryName="default-registry"
8286
viewMode="list"
8387
searchQuery=""
8488
onClearSearch={mockOnClearSearch}
@@ -95,6 +99,7 @@ describe("Servers", () => {
9599
render(
96100
<Servers
97101
servers={[]}
102+
registryName="default-registry"
98103
viewMode="grid"
99104
searchQuery="nonexistent"
100105
onClearSearch={mockOnClearSearch}
@@ -114,6 +119,7 @@ describe("Servers", () => {
114119
render(
115120
<Servers
116121
servers={[]}
122+
registryName="default-registry"
117123
viewMode="grid"
118124
searchQuery="nonexistent"
119125
onClearSearch={onClearSearch}
@@ -135,6 +141,7 @@ describe("Servers", () => {
135141
render(
136142
<Servers
137143
servers={[]}
144+
registryName="default-registry"
138145
viewMode="grid"
139146
searchQuery=""
140147
onClearSearch={mockOnClearSearch}
@@ -151,6 +158,7 @@ describe("Servers", () => {
151158
render(
152159
<Servers
153160
servers={[]}
161+
registryName="default-registry"
154162
viewMode="list"
155163
searchQuery=""
156164
onClearSearch={mockOnClearSearch}
@@ -164,6 +172,7 @@ describe("Servers", () => {
164172
const { container } = render(
165173
<Servers
166174
servers={[]}
175+
registryName="default-registry"
167176
viewMode="grid"
168177
searchQuery=""
169178
onClearSearch={mockOnClearSearch}
@@ -177,6 +186,7 @@ describe("Servers", () => {
177186
render(
178187
<Servers
179188
servers={[]}
189+
registryName="default-registry"
180190
viewMode="grid"
181191
searchQuery=""
182192
onClearSearch={mockOnClearSearch}

src/app/catalog/components/server-filters.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
} from "@/components/ui/select";
1313
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
1414
import type { GithubComStacklokToolhiveRegistryServerInternalServiceRegistryInfo } from "@/generated/types.gen";
15-
import { CATALOG_ALL_REGISTRIES } from "../constants";
1615

1716
interface ServerFiltersProps {
1817
registries: GithubComStacklokToolhiveRegistryServerInternalServiceRegistryInfo[];
@@ -68,10 +67,9 @@ export function ServerFilters({
6867
className="w-38 h-9 bg-white dark:bg-card"
6968
aria-label="Select registry"
7069
>
71-
<SelectValue placeholder="All registries" />
70+
<SelectValue placeholder="Select registry" />
7271
</SelectTrigger>
7372
<SelectContent>
74-
<SelectItem value={CATALOG_ALL_REGISTRIES}>All registries</SelectItem>
7573
{registries
7674
.filter(
7775
(registry): registry is typeof registry & { name: string } =>

src/app/catalog/components/servers-wrapper.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export function ServersWrapper({
4848
<PageHeader title="MCP Server Catalog">
4949
<ServerFilters
5050
registries={registries}
51-
selectedRegistry={selectedRegistry}
51+
selectedRegistry={selectedRegistry || registries[0]?.name || ""}
5252
onRegistryChange={handleRegistryChange}
5353
viewMode={viewMode}
5454
onViewModeChange={handleViewModeChange}
@@ -63,6 +63,7 @@ export function ServersWrapper({
6363
) : (
6464
<Servers
6565
servers={servers}
66+
registryName={selectedRegistry || registries[0]?.name || ""}
6667
viewMode={viewMode}
6768
searchQuery={search}
6869
onClearSearch={handleClearSearch}

src/app/catalog/components/servers.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { ServersTable } from "./servers-table";
99

1010
interface ServersProps {
1111
servers: V0ServerJson[];
12+
registryName: string;
1213
viewMode: "grid" | "list";
1314
searchQuery: string;
1415
onClearSearch: () => void;
@@ -20,6 +21,7 @@ interface ServersProps {
2021
*/
2122
export function Servers({
2223
servers,
24+
registryName,
2325
viewMode,
2426
searchQuery,
2527
onClearSearch,
@@ -29,7 +31,7 @@ export function Servers({
2931
const handleServerClick = (server: V0ServerJson) => {
3032
if (!server.name) return;
3133

32-
const detailUrl = `/catalog/${server.name}/${server.version || "latest"}`;
34+
const detailUrl = `/catalog/${server.name}/${server.version || "latest"}?registryName=${encodeURIComponent(registryName)}`;
3335
router.push(detailUrl);
3436
};
3537

src/app/catalog/constants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export const CATALOG_ALL_REGISTRIES = "all";
21
export const CATALOG_VIEW_MODES = ["grid", "list"] as const;
32
export const CATALOG_PREV_CURSOR_HISTORY_KEY = "catalog:prevCursors";
43
export const CATALOG_PAGE_SIZE = 24;

src/app/catalog/hooks/use-catalog-filters.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ import {
66
useQueryStates,
77
} from "nuqs";
88
import { useTransition } from "react";
9-
import {
10-
CATALOG_ALL_REGISTRIES,
11-
CATALOG_PAGE_SIZE,
12-
CATALOG_VIEW_MODES,
13-
} from "../constants";
9+
import { CATALOG_PAGE_SIZE, CATALOG_VIEW_MODES } from "../constants";
1410
import { useSessionStack } from "./use-session-stack";
1511

1612
/**
@@ -61,7 +57,7 @@ export function useCatalogFilters() {
6157
setFilters(
6258
(prev) => ({
6359
...prev,
64-
registryName: value === CATALOG_ALL_REGISTRIES ? null : value,
60+
registryName: value,
6561
cursor: "",
6662
}),
6763
{ startTransition },
@@ -97,7 +93,7 @@ export function useCatalogFilters() {
9793
return {
9894
viewMode,
9995
search,
100-
selectedRegistry: registryName || CATALOG_ALL_REGISTRIES,
96+
selectedRegistry: registryName,
10197
cursor,
10298
limit,
10399
isFirstPage,

0 commit comments

Comments
 (0)