Skip to content
Open
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
60 changes: 27 additions & 33 deletions src/app/catalog/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,19 @@
import type {
GithubComStacklokToolhiveRegistryServerInternalServiceRegistryInfo,
V0ServerJson,
V0ServerResponse,
} from "@/generated/types.gen";
import { getAuthenticatedClient } from "@/lib/api-client";

// TODO: remove once UI pagination is implemented
const SERVER_PAGE_LIMIT = 200;

function extractServers(items: V0ServerResponse[]): V0ServerJson[] {
return items
.map((item) => item?.server)
.filter((server): server is V0ServerJson => server != null);
}

/** Fetches all available MCP server registries */
export async function getRegistries(): Promise<
GithubComStacklokToolhiveRegistryServerInternalServiceRegistryInfo[]
Expand All @@ -27,58 +37,42 @@ export async function getRegistries(): Promise<
return registries.data.registries ?? [];
}

/** Fetches MCP servers across all registries */
export async function getServers(): Promise<V0ServerJson[]> {
const api = await getAuthenticatedClient();
const servers = await api.getRegistryV01Servers({

const response = await api.getRegistryV01Servers({
client: api.client,
query: {
version: "latest",
},
query: { version: "latest", limit: SERVER_PAGE_LIMIT },
});

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

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

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

// Extract the server objects from the response
return items
.map((item) => item?.server)
.filter((server): server is V0ServerJson => server != null);
return extractServers(response.data?.servers ?? []);
Comment on lines +44 to +54
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

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

Because the request is now capped with limit: SERVER_PAGE_LIMIT, this function can silently return a truncated server list when the API provides metadata.nextCursor (i.e., more results exist). Consider at least detecting response.data?.metadata?.nextCursor and surfacing it (return it alongside servers, or log a warning/telemetry) so missing servers aren’t mistaken for an empty/complete catalog while UI pagination is still unimplemented.

Copilot uses AI. Check for mistakes.
}

/**
* Fetches all MCP servers from a specific registry
* Fetches MCP servers from a specific registry
* @param registryName - The unique name of the registry to query
*/
export async function getServersByRegistryName(
registryName: string,
): Promise<V0ServerJson[]> {
const api = await getAuthenticatedClient();
const servers = await api.getRegistryByRegistryNameV01Servers({

const response = await api.getRegistryByRegistryNameV01Servers({
client: api.client,
path: {
registryName,
},
query: {
version: "latest",
},
path: { registryName },
query: { version: "latest", limit: SERVER_PAGE_LIMIT },
});

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

const items = servers.data?.servers ?? [];
return items
.map((item) => item?.server)
.filter((server): server is V0ServerJson => server != null);
return extractServers(response.data?.servers ?? []);
}
Loading