Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/app/catalog/[repoName]/[serverName]/[version]/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,21 @@ import { getAuthenticatedClient } from "@/lib/api-client";
export async function getServerDetails(serverName: string, version: string) {
const api = await getAuthenticatedClient();

const registriesResult = await api.getV1Registries({ client: api.client });
const registryName = registriesResult.data?.registries?.[0]?.name;

if (!registryName) {
return {
error: new Error("No registry available"),
data: null,
response: null,
};
Comment on lines +8 to +16
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no registry is available, getServerDetails() returns { error: Error, data: null, response: null }. The calling page only checks response?.status === 404, so this case will render an essentially empty detail page instead of failing fast. Prefer throwing (or returning a response/status that the page can handle) when the registry list is empty or the registries call errors, so this surfaces via error.tsx rather than silently continuing.

Suggested change
const registriesResult = await api.getV1Registries({ client: api.client });
const registryName = registriesResult.data?.registries?.[0]?.name;
if (!registryName) {
return {
error: new Error("No registry available"),
data: null,
response: null,
};
const { error: registriesError, data: registriesData } =
await api.getV1Registries({ client: api.client });
if (registriesError) {
throw registriesError;
}
const registryName = registriesData?.registries?.[0]?.name;
if (!registryName) {
throw new Error("No registry available");

Copilot uses AI. Check for mistakes.
}

const { error, data, response } =
await api.getRegistryV01ServersByServerNameVersionsByVersion({
await api.getRegistryByRegistryNameV01ServersByServerNameVersionsByVersion({
path: {
registryName,
serverName,
version,
},
Expand Down
2 changes: 1 addition & 1 deletion src/app/catalog/actions.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mockedGetRegistryV01Servers } from "@mocks/fixtures/registry_v0_1_servers/get";
import { mockedGetRegistryV01Servers } from "@mocks/fixtures/registry_registryName_v0_1_servers/get";
import { HttpResponse } from "msw";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { getServers } from "./actions";
Expand Down
37 changes: 10 additions & 27 deletions src/app/catalog/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ export async function getRegistries(): Promise<
GithubComStacklokToolhiveRegistryServerInternalServiceRegistryInfo[]
> {
const api = await getAuthenticatedClient();
const registries = await api.getExtensionV0Registries({
const registries = await api.getV1Registries({
client: api.client,
});

if (registries.error) {
console.error("[catalog] Failed to fetch registries:", registries.error);
throw registries.error;
Expand All @@ -23,6 +22,7 @@ export async function getRegistries(): Promise<
if (!registries.data) {
return [];
}
// console.log("registries", JSON.stringify(registries.data, null, 2));
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s a commented-out console.log left in getRegistries(). This adds noise and is easy to forget; please remove it (or gate behind a debug flag/env var if you need occasional inspection).

Suggested change
// console.log("registries", JSON.stringify(registries.data, null, 2));

Copilot uses AI. Check for mistakes.

return registries.data.registries ?? [];
}
Expand All @@ -38,38 +38,21 @@ interface ServerListParams {
search?: string;
}

/**
* Fetches MCP servers using the first available registry as default.
* Used when no specific registry is selected by the user.
*/
export async function getServers(
params?: ServerListParams,
): Promise<ServerListResult> {
const api = await getAuthenticatedClient();
const servers = await api.getRegistryV01Servers({
client: api.client,
query: {
version: "latest",
cursor: params?.cursor || undefined,
limit: params?.limit,
search: params?.search || undefined,
},
});
const registries = await getRegistries();
const defaultRegistry = registries[0]?.name;

if (servers.error) {
console.error("[catalog] Failed to fetch servers:", servers.error);
throw servers.error;
}

if (!servers.data) {
if (!defaultRegistry) {
return { servers: [] };
}

const data = servers.data;
const items = Array.isArray(data?.servers) ? data.servers : [];

return {
servers: items
.map((item) => item?.server)
.filter((server): server is V0ServerJson => server != null),
nextCursor: data.metadata?.nextCursor,
};
return getServersByRegistryName(defaultRegistry, params);
}
Comment on lines 45 to 56
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getServers() now calls getRegistries() and then getServersByRegistryName(), which results in multiple authenticated client creations and at least two network calls for every getServers() invocation. Since getServers() is used in several server routes/components, consider reusing a single authenticated client within getServers() (fetch registries and servers with the same client) and/or memoizing the default registry lookup per request (e.g., React/Next cache) to avoid repeated registry fetches.

Copilot uses AI. Check for mistakes.

/**
Expand Down
14 changes: 7 additions & 7 deletions src/app/catalog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getRegistries, getServers, getServersByRegistryName } from "./actions";
import { getRegistries, getServersByRegistryName } from "./actions";
import { ServersWrapper } from "./components/servers-wrapper";
import { CATALOG_PAGE_SIZE } from "./constants";

Expand All @@ -20,12 +20,12 @@ export default async function CatalogPage({ searchParams }: CatalogPageProps) {
search: search || undefined,
};

const [registries, { servers, nextCursor }] = await Promise.all([
getRegistries(),
registryName
? getServersByRegistryName(registryName, paginationParams)
: getServers(paginationParams),
]);
const registries = await getRegistries();
const selectedRegistry = registryName ?? registries[0]?.name;

Comment on lines +23 to +25
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CatalogPage always awaits getRegistries() before fetching servers. When registryName is present in searchParams, the servers request doesn’t depend on registries and could be started in parallel (Promise.all) to avoid an unnecessary waterfall on the initial render.

Suggested change
const registries = await getRegistries();
const selectedRegistry = registryName ?? registries[0]?.name;
const registriesPromise = getRegistries();
if (registryName) {
const [registries, { servers, nextCursor }] = await Promise.all([
registriesPromise,
getServersByRegistryName(registryName, paginationParams),
]);
return (
<ServersWrapper
servers={servers}
registries={registries}
nextCursor={nextCursor}
/>
);
}
const registries = await registriesPromise;
const selectedRegistry = registries[0]?.name;

Copilot uses AI. Check for mistakes.
const { servers, nextCursor } = selectedRegistry
? await getServersByRegistryName(selectedRegistry, paginationParams)
: { servers: [], nextCursor: undefined };

return (
<ServersWrapper
Expand Down
118 changes: 38 additions & 80 deletions src/generated/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
// This file is auto-generated by @hey-api/openapi-ts

export {
deleteExtensionV0RegistriesByRegistryName,
deleteRegistryByRegistryNameV01ServersByServerNameVersionsByVersion,
deleteRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByNameVersionsByVersion,
deleteV1EntriesByTypeByNameVersionsByVersion,
deleteV1RegistriesByName,
deleteV1SourcesByName,
getExtensionV0Registries,
getExtensionV0RegistriesByRegistryName,
getHealth,
getOpenapiJson,
getReadiness,
Expand All @@ -19,9 +14,6 @@ export {
getRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByName,
getRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByNameVersions,
getRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByNameVersionsByVersion,
getRegistryV01Servers,
getRegistryV01ServersByServerNameVersions,
getRegistryV01ServersByServerNameVersionsByVersion,
getV1Registries,
getV1RegistriesByName,
getV1RegistriesByNameEntries,
Expand All @@ -30,51 +22,28 @@ export {
getV1SourcesByNameEntries,
getVersion,
type Options,
postByRegistryNameV01Publish,
postRegistryByRegistryNameV01xDevToolhiveSkills,
postRegistryV01Publish,
postV1Entries,
putExtensionV0RegistriesByRegistryName,
putV1EntriesByTypeByNameClaims,
putV1RegistriesByName,
putV1SourcesByName,
} from "./sdk.gen";
export type {
ClientOptions,
DeleteExtensionV0RegistriesByRegistryNameData,
DeleteExtensionV0RegistriesByRegistryNameError,
DeleteExtensionV0RegistriesByRegistryNameErrors,
DeleteExtensionV0RegistriesByRegistryNameResponse,
DeleteExtensionV0RegistriesByRegistryNameResponses,
DeleteRegistryByRegistryNameV01ServersByServerNameVersionsByVersionData,
DeleteRegistryByRegistryNameV01ServersByServerNameVersionsByVersionError,
DeleteRegistryByRegistryNameV01ServersByServerNameVersionsByVersionErrors,
DeleteRegistryByRegistryNameV01ServersByServerNameVersionsByVersionResponse,
DeleteRegistryByRegistryNameV01ServersByServerNameVersionsByVersionResponses,
DeleteRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByNameVersionsByVersionData,
DeleteRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByNameVersionsByVersionError,
DeleteRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByNameVersionsByVersionErrors,
DeleteRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByNameVersionsByVersionResponse,
DeleteRegistryByRegistryNameV01xDevToolhiveSkillsByNamespaceByNameVersionsByVersionResponses,
DeleteV1EntriesByTypeByNameVersionsByVersionData,
DeleteV1EntriesByTypeByNameVersionsByVersionError,
DeleteV1EntriesByTypeByNameVersionsByVersionErrors,
DeleteV1EntriesByTypeByNameVersionsByVersionResponse,
DeleteV1EntriesByTypeByNameVersionsByVersionResponses,
DeleteV1RegistriesByNameData,
DeleteV1RegistriesByNameError,
DeleteV1RegistriesByNameErrors,
DeleteV1RegistriesByNameResponse,
DeleteV1RegistriesByNameResponses,
DeleteV1SourcesByNameData,
DeleteV1SourcesByNameError,
DeleteV1SourcesByNameErrors,
GetExtensionV0RegistriesByRegistryNameData,
GetExtensionV0RegistriesByRegistryNameError,
GetExtensionV0RegistriesByRegistryNameErrors,
GetExtensionV0RegistriesByRegistryNameResponse,
GetExtensionV0RegistriesByRegistryNameResponses,
GetExtensionV0RegistriesData,
GetExtensionV0RegistriesError,
GetExtensionV0RegistriesErrors,
GetExtensionV0RegistriesResponse,
GetExtensionV0RegistriesResponses,
DeleteV1SourcesByNameResponse,
DeleteV1SourcesByNameResponses,
GetHealthData,
GetHealthResponse,
GetHealthResponses,
Expand Down Expand Up @@ -123,60 +92,61 @@ export type {
GetRegistryByRegistryNameV01xDevToolhiveSkillsErrors,
GetRegistryByRegistryNameV01xDevToolhiveSkillsResponse,
GetRegistryByRegistryNameV01xDevToolhiveSkillsResponses,
GetRegistryV01ServersByServerNameVersionsByVersionData,
GetRegistryV01ServersByServerNameVersionsByVersionError,
GetRegistryV01ServersByServerNameVersionsByVersionErrors,
GetRegistryV01ServersByServerNameVersionsByVersionResponse,
GetRegistryV01ServersByServerNameVersionsByVersionResponses,
GetRegistryV01ServersByServerNameVersionsData,
GetRegistryV01ServersByServerNameVersionsError,
GetRegistryV01ServersByServerNameVersionsErrors,
GetRegistryV01ServersByServerNameVersionsResponse,
GetRegistryV01ServersByServerNameVersionsResponses,
GetRegistryV01ServersData,
GetRegistryV01ServersError,
GetRegistryV01ServersErrors,
GetRegistryV01ServersResponse,
GetRegistryV01ServersResponses,
GetV1RegistriesByNameData,
GetV1RegistriesByNameEntriesData,
GetV1RegistriesByNameEntriesError,
GetV1RegistriesByNameEntriesErrors,
GetV1RegistriesByNameEntriesResponse,
GetV1RegistriesByNameEntriesResponses,
GetV1RegistriesByNameError,
GetV1RegistriesByNameErrors,
GetV1RegistriesByNameResponse,
GetV1RegistriesByNameResponses,
GetV1RegistriesData,
GetV1RegistriesError,
GetV1RegistriesErrors,
GetV1RegistriesResponse,
GetV1RegistriesResponses,
GetV1SourcesByNameData,
GetV1SourcesByNameEntriesData,
GetV1SourcesByNameEntriesError,
GetV1SourcesByNameEntriesErrors,
GetV1SourcesByNameEntriesResponse,
GetV1SourcesByNameEntriesResponses,
GetV1SourcesByNameError,
GetV1SourcesByNameErrors,
GetV1SourcesByNameResponse,
GetV1SourcesByNameResponses,
GetV1SourcesData,
GetV1SourcesError,
GetV1SourcesErrors,
GetV1SourcesResponse,
GetV1SourcesResponses,
GetVersionData,
GetVersionResponse,
GetVersionResponses,
GithubComStacklokToolhiveRegistryServerInternalConfigApiConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigFileConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigFilterConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigGitAuthConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigGitConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigKubernetesConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigManagedConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigNameFilterConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigSourceType,
GithubComStacklokToolhiveRegistryServerInternalConfigSyncPolicyConfig,
GithubComStacklokToolhiveRegistryServerInternalConfigTagFilterConfig,
GithubComStacklokToolhiveRegistryServerInternalServiceCreationType,
GithubComStacklokToolhiveRegistryServerInternalServiceRegistryCreateRequest,
GithubComStacklokToolhiveRegistryServerInternalServiceEntryVersionInfo,
GithubComStacklokToolhiveRegistryServerInternalServiceRegistryEntriesResponse,
GithubComStacklokToolhiveRegistryServerInternalServiceRegistryEntryInfo,
GithubComStacklokToolhiveRegistryServerInternalServiceRegistryInfo,
GithubComStacklokToolhiveRegistryServerInternalServiceRegistryListResponse,
GithubComStacklokToolhiveRegistryServerInternalServiceRegistrySyncStatus,
GithubComStacklokToolhiveRegistryServerInternalServiceSkill,
GithubComStacklokToolhiveRegistryServerInternalServiceSkillIcon,
GithubComStacklokToolhiveRegistryServerInternalServiceSkillPackage,
GithubComStacklokToolhiveRegistryServerInternalServiceSkillRepository,
GithubComStacklokToolhiveRegistryServerInternalServiceSourceEntriesResponse,
GithubComStacklokToolhiveRegistryServerInternalServiceSourceEntryInfo,
GithubComStacklokToolhiveRegistryServerInternalServiceSourceInfo,
GithubComStacklokToolhiveRegistryServerInternalServiceSourceListResponse,
GithubComStacklokToolhiveRegistryServerInternalServiceSourceSyncStatus,
InternalApiHealthResponse,
InternalApiReadinessResponse,
InternalApiV1PublishEntryRequest,
InternalApiV1RegistryListResponse,
InternalApiVersionResponse,
InternalApiXSkillsSkillListMetadata,
InternalApiXSkillsSkillListResponse,
Expand All @@ -190,36 +160,24 @@ export type {
ModelRepository,
ModelStatus,
ModelTransport,
PostByRegistryNameV01PublishData,
PostByRegistryNameV01PublishError,
PostByRegistryNameV01PublishErrors,
PostByRegistryNameV01PublishResponse,
PostByRegistryNameV01PublishResponses,
PostRegistryByRegistryNameV01xDevToolhiveSkillsData,
PostRegistryByRegistryNameV01xDevToolhiveSkillsError,
PostRegistryByRegistryNameV01xDevToolhiveSkillsErrors,
PostRegistryByRegistryNameV01xDevToolhiveSkillsResponse,
PostRegistryByRegistryNameV01xDevToolhiveSkillsResponses,
PostRegistryV01PublishData,
PostRegistryV01PublishError,
PostRegistryV01PublishErrors,
PostV1EntriesData,
PostV1EntriesError,
PostV1EntriesErrors,
PutExtensionV0RegistriesByRegistryNameData,
PutExtensionV0RegistriesByRegistryNameError,
PutExtensionV0RegistriesByRegistryNameErrors,
PutExtensionV0RegistriesByRegistryNameResponse,
PutExtensionV0RegistriesByRegistryNameResponses,
PostV1EntriesResponse,
PostV1EntriesResponses,
PutV1EntriesByTypeByNameClaimsData,
PutV1EntriesByTypeByNameClaimsError,
PutV1EntriesByTypeByNameClaimsErrors,
PutV1RegistriesByNameData,
PutV1RegistriesByNameError,
PutV1RegistriesByNameErrors,
PutV1RegistriesByNameResponse,
PutV1RegistriesByNameResponses,
PutV1SourcesByNameData,
PutV1SourcesByNameError,
PutV1SourcesByNameErrors,
PutV1SourcesByNameResponse,
PutV1SourcesByNameResponses,
RegistrySkill,
RegistrySkillIcon,
RegistrySkillPackage,
Expand Down
Loading
Loading