Add comprehensive CSS styles for the MCP Marketplace interface, including card hover effects, responsive grid layout, modal backdrop blur, category badges, rating stars, install buttons, and loading animations. Also include custom scrollbar styling for the model selector to enhance user experience and visual consistency.
159 lines
4.6 KiB
TypeScript
159 lines
4.6 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import {
|
|
MCPProvider,
|
|
MCPSearchParams,
|
|
DeepNLPSearchParams,
|
|
} from "@/lib/types/mcp";
|
|
import { DeepNLPService } from "@/lib/services/deepnlp";
|
|
import { MCPRepository } from "@/database/repositories/mcp";
|
|
import { MCPCacheRepository } from "@/database/repositories/mcp-cache";
|
|
import { MCPSyncService } from "@/lib/services/mcp-sync";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export async function GET(request: Request) {
|
|
try {
|
|
const { searchParams } = new URL(request.url);
|
|
const category = searchParams.get("category") || undefined;
|
|
const search = searchParams.get("search") || undefined;
|
|
const sort =
|
|
(searchParams.get("sort") as MCPSearchParams["sort"]) || "rating";
|
|
const order = (searchParams.get("order") as "asc" | "desc") || "desc";
|
|
const page = parseInt(searchParams.get("page") || "0");
|
|
const limit = parseInt(searchParams.get("limit") || "2000"); // Aumentado para 2000
|
|
|
|
console.log("MCP List API called with params:", {
|
|
category,
|
|
search,
|
|
sort,
|
|
order,
|
|
page,
|
|
limit,
|
|
});
|
|
|
|
// 1. Verificar se cache do DB é válido
|
|
const cacheValid = MCPCacheRepository.isCacheValid();
|
|
|
|
if (!cacheValid) {
|
|
// Iniciar sincronização em background se necessário
|
|
if (!MCPSyncService.isSyncing()) {
|
|
console.log("Cache invalid, starting background sync...");
|
|
MCPSyncService.backgroundSync().catch(console.error);
|
|
}
|
|
}
|
|
|
|
// 2. Tentar buscar do banco de dados primeiro
|
|
const cachedMcps = MCPCacheRepository.search({
|
|
query: search,
|
|
category: category !== "all" ? category : undefined,
|
|
limit,
|
|
offset: page * limit,
|
|
sortBy: sort,
|
|
order: order,
|
|
});
|
|
|
|
if (cachedMcps.length > 0 || cacheValid) {
|
|
console.log(`Found ${cachedMcps.length} MCPs in cache`);
|
|
|
|
// Transformar para formato interno
|
|
const mcps = cachedMcps.map((item) =>
|
|
DeepNLPService.transformToMCPProvider(item)
|
|
);
|
|
|
|
// Adicionar status de instalação
|
|
const installedMCPs = MCPRepository.listInstalled();
|
|
const mcpsWithStatus = mcps.map((mcp) => ({
|
|
...mcp,
|
|
installed: installedMCPs.includes(mcp.id),
|
|
}));
|
|
|
|
const total = MCPCacheRepository.count({
|
|
category: category !== "all" ? category : undefined,
|
|
});
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
mcps: mcpsWithStatus,
|
|
total,
|
|
hasMore: (page + 1) * limit < total,
|
|
cached: true,
|
|
lastSync: MCPCacheRepository.getLastSync(),
|
|
});
|
|
}
|
|
|
|
// 3. Fallback: buscar da API se cache vazio
|
|
console.log("Cache empty, falling back to DeepNLP API");
|
|
|
|
const deepnlpCategory =
|
|
category && category !== "all"
|
|
? DeepNLPService.getCategoryMapping(category)
|
|
: undefined;
|
|
|
|
const deepnlpParams: DeepNLPSearchParams = {
|
|
query: search,
|
|
category: deepnlpCategory,
|
|
page_id: page,
|
|
count_per_page: limit,
|
|
mode: "list",
|
|
};
|
|
|
|
console.log("Calling DeepNLP API with params:", deepnlpParams);
|
|
|
|
const response = await DeepNLPService.searchMCPs(deepnlpParams);
|
|
const mcps = response.items.map((item) =>
|
|
DeepNLPService.transformToMCPProvider(item)
|
|
);
|
|
|
|
// Adicionar status de instalação
|
|
const installedMCPs = MCPRepository.listInstalled();
|
|
const mcpsWithStatus = mcps.map((mcp) => ({
|
|
...mcp,
|
|
installed: installedMCPs.includes(mcp.id),
|
|
}));
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
mcps: mcpsWithStatus,
|
|
total: response.total_hits,
|
|
hasMore: response.total_hits > (page + 1) * limit,
|
|
cached: false,
|
|
});
|
|
} catch (error: any) {
|
|
console.error("Error fetching MCPs from DeepNLP:", error);
|
|
|
|
// Fallback para dados mock em caso de erro
|
|
console.log("Falling back to mock data due to API error");
|
|
|
|
const mockMCPs: MCPProvider[] = [
|
|
{
|
|
id: "google-maps/google-maps",
|
|
name: "Google Maps",
|
|
author: "Google",
|
|
description:
|
|
"Google Maps Location services, directions, and place details",
|
|
version: "1.0.0",
|
|
category: "map",
|
|
tags: ["maps", "location", "directions"],
|
|
rating: 4.5,
|
|
totalRatings: 2,
|
|
downloads: 20,
|
|
capabilities: ["geocoding", "directions", "places"],
|
|
repository:
|
|
"https://github.com/modelcontextprotocol/servers/tree/main/src/google-maps",
|
|
installed: false,
|
|
subfield: "MAP",
|
|
field: "MCP SERVER",
|
|
},
|
|
];
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
mcps: mockMCPs,
|
|
total: mockMCPs.length,
|
|
hasMore: false,
|
|
fallback: true,
|
|
error: error.message,
|
|
});
|
|
}
|
|
}
|