From 745746afa9c97854492f46349521a03ac66db7b8 Mon Sep 17 00:00:00 2001 From: Evandro Fonseca Junior Date: Tue, 28 Oct 2025 22:17:19 -0300 Subject: [PATCH] feat(mcp): add GitHub repo support and tools display in MCP cards - Support installing MCP servers from GitHub repos by converting 'github:user/repo' to 'git+https://github.com/user/repo.git' to avoid SSH issues and improve npm compatibility - Display available tools in MCPCard with badges, showing up to 6 tools and total count for better visibility of server capabilities - Remove unused onViewDetails prop and make uninstall button full-width for improved UI consistency --- app/api/mcp/install-manual/route.ts | 17 ++- components/mcp/MCPCard.tsx | 49 +++++-- components/mcp/MCPMarketplaceModal.tsx | 189 +++++++++++++++++++------ database/ollahub.db-shm | Bin 32768 -> 32768 bytes database/ollahub.db-wal | Bin 593312 -> 1479112 bytes lib/services/mcp-installer.ts | 3 +- lib/services/mcp-validator.ts | 14 ++ lib/utils/system.ts | 15 +- 8 files changed, 227 insertions(+), 60 deletions(-) diff --git a/app/api/mcp/install-manual/route.ts b/app/api/mcp/install-manual/route.ts index f5d2bff..023560c 100644 --- a/app/api/mcp/install-manual/route.ts +++ b/app/api/mcp/install-manual/route.ts @@ -144,14 +144,25 @@ function detectPackageInfo(serverConfig: any): { if (command === "npx" || command === "npm") { // npx @mcpcentral/mcp-time // npx -y @modelcontextprotocol/server-filesystem + // npx -y github:user/repo const packageArg = args.find( - (arg: string) => arg.startsWith("@") || !arg.startsWith("-") + (arg: string) => + arg.startsWith("@") || arg.startsWith("github:") || !arg.startsWith("-") ); - const packageName = packageArg || firstArg || "unknown-npm-package"; + let packageName = packageArg || firstArg || "unknown-npm-package"; + packageName = packageName.replace(/^-y\s*/, "").trim(); + + // Converter github:user/repo para git+https://github.com/user/repo.git + // Isso evita problemas com SSH e funciona melhor com npm install + if (packageName.startsWith("github:")) { + const repoPath = packageName.replace("github:", ""); + packageName = `git+https://github.com/${repoPath}.git`; + console.log(`Converted github: format to: ${packageName}`); + } return { packageRegistry: "npm", - packageName: packageName.replace(/^-y\s*/, "").trim(), + packageName, }; } diff --git a/components/mcp/MCPCard.tsx b/components/mcp/MCPCard.tsx index b8e2689..17cdac3 100644 --- a/components/mcp/MCPCard.tsx +++ b/components/mcp/MCPCard.tsx @@ -18,7 +18,6 @@ interface MCPCardProps { mcp: MCPProvider; onInstall: () => void; onUninstall: () => void; - onViewDetails: () => void; mode?: "marketplace" | "installed"; onEditConfig?: () => void; onTestServer?: () => void; @@ -35,7 +34,6 @@ export const MCPCard = memo( mcp, onInstall, onUninstall, - onViewDetails, mode = "marketplace", onEditConfig, onTestServer, @@ -145,6 +143,41 @@ export const MCPCard = memo( )} + {/* Tools Badges */} + {mcp.tools && Array.isArray(mcp.tools) && mcp.tools.length > 0 && ( +
+
+
+ + + {mcp.tools.length} {mcp.tools.length === 1 ? "Tool" : "Tools"} + +
+ {mcp.tools.length > 6 && ( + + Showing 6 of {mcp.tools.length} + + )} +
+
+ {mcp.tools.slice(0, 6).map((tool: any, index: number) => ( + + {tool.name} + + ))} + {mcp.tools.length > 6 && ( + + +{mcp.tools.length - 6} + + )} +
+
+ )} + {/* Installation Progress */} {installingStatus && (
@@ -168,14 +201,6 @@ export const MCPCard = memo( {/* Actions */}
- - {mode === "installed" && onEditConfig ? ( <> ) : ( diff --git a/components/mcp/MCPMarketplaceModal.tsx b/components/mcp/MCPMarketplaceModal.tsx index 24a80d9..a297f5b 100644 --- a/components/mcp/MCPMarketplaceModal.tsx +++ b/components/mcp/MCPMarketplaceModal.tsx @@ -38,7 +38,6 @@ export function MCPMarketplaceModal({ "rating" | "name" | "recent" | "total_ratings" | "updated_at" >("rating"); const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc"); - const [selectedMCP, setSelectedMCP] = useState(null); const [configModalOpen, setConfigModalOpen] = useState(false); const [selectedConfigMCP, setSelectedConfigMCP] = useState(null); @@ -170,6 +169,32 @@ export function MCPMarketplaceModal({ if (data.success) { setInstalledMcps(data.mcps); console.log("📦 Installed MCPs loaded:", data.mcps.length); + + // Atualizar MCPs do marketplace para marcar como instalados + setMcps((prevMcps) => + prevMcps.map((mcp) => { + const installedMatch = data.mcps.find( + (installed: any) => + installed.id === mcp.id || + installed.repo === mcp.repo || + installed.name === mcp.name + ); + + if (installedMatch) { + return { + ...mcp, + installed: true, + tools: installedMatch.tools || mcp.tools, // Atualizar tools também + }; + } + + return { ...mcp, installed: false }; + }) + ); + + console.log( + `📊 Marketplace updated: ${data.mcps.length} MCPs marked as installed` + ); } else { console.error("Failed to fetch installed MCPs:", data.error); } @@ -385,6 +410,8 @@ export function MCPMarketplaceModal({ const data = await response.json(); if (data.success) { + console.log(`✅ Installation started for ${finalId}`); + // Adicionar ao tracking de instalação setInstallingMcps((prev) => { const next = new Map(prev); @@ -396,20 +423,24 @@ export function MCPMarketplaceModal({ return next; }); - // Atualizar estado local + // Marcar como "instalando" no marketplace (não como instalado ainda) + // O polling vai atualizar para installed: true quando status === "ready" setMcps((prev) => - prev.map((mcp) => - mcp.id === selectedConfigMCP.id - ? { ...mcp, installed: true } - : mcp - ) + prev.map((mcp) => { + // Verificar se é o mesmo MCP (por ID ou repo) + const isSameMcp = + mcp.id === selectedConfigMCP.id || + mcp.id === finalId || + mcp.repo === selectedConfigMCP.repo; + + return isSameMcp ? { ...mcp, installed: false } : mcp; + }) ); + // Fechar modal setConfigModalOpen(false); setSelectedConfigMCP(null); setMcpConfig(null); - - console.log(`✅ Installation started for ${finalId}`); } else { console.error("Error installing MCP:", data.error); } @@ -429,22 +460,36 @@ export function MCPMarketplaceModal({ const data = await response.json(); if (data.success) { - // Atualizar estado local + console.log(`✅ MCP ${mcpId} uninstalled successfully`); + + // Remover do tracking de instalação (se estiver lá) + setInstallingMcps((prev) => { + const next = new Map(prev); + next.delete(mcpId); + return next; + }); + + // Atualizar marketplace: marcar como não instalado setMcps((prev) => prev.map((mcp) => - mcp.id === mcpId ? { ...mcp, installed: false } : mcp + mcp.id === mcpId || mcp.repo === mcpId + ? { ...mcp, installed: false } + : mcp ) ); + + // Recarregar lista de instalados para sincronizar + await fetchInstalledMCPs(); + + console.log(`📦 Lists updated after uninstalling ${mcpId}`); + } else { + console.error("Failed to uninstall MCP:", data.error); } } catch (error) { console.error("Error uninstalling MCP:", error); } }; - const handleViewDetails = (mcp: MCPProvider) => { - setSelectedMCP(mcp); - }; - // Carregar MCPs e categorias quando o modal abrir useEffect(() => { if (isOpen) { @@ -454,11 +499,51 @@ export function MCPMarketplaceModal({ }, [isOpen]); // Removido fetchMCPs das dependências // Carregar MCPs instalados quando aba "installed" for ativada + // Carregar MCPs instalados sempre que o modal abrir (não apenas na aba instalados) useEffect(() => { - if (isOpen && activeTab === "installed") { + if (isOpen) { fetchInstalledMCPs(); + + // Limpar MCPs órfãos no tracking (que estão instalados mas ainda no Map) + // Isso previne polling infinito de MCPs que já completaram instalação + setTimeout(async () => { + if (installingMcps.size > 0) { + console.log( + `🔍 Checking for orphaned installations: ${installingMcps.size} tracked` + ); + + const installedResponse = await fetch("/api/mcp/installed"); + const installedData = await installedResponse.json(); + + if (installedData.success) { + const installedIds = new Set( + installedData.mcps.map((m: any) => m.id) + ); + + setInstallingMcps((prev) => { + const next = new Map(prev); + let removed = 0; + + for (const mcpId of prev.keys()) { + // Se já está instalado, remover do tracking + if (installedIds.has(mcpId)) { + next.delete(mcpId); + removed++; + console.log(`🧹 Removed orphaned tracking for ${mcpId}`); + } + } + + if (removed > 0) { + console.log(`✅ Cleaned ${removed} orphaned installations`); + } + + return next; + }); + } + } + }, 1000); // Aguardar 1 segundo após abrir modal } - }, [isOpen, activeTab]); + }, [isOpen, fetchInstalledMCPs]); // Debounce para searchTerm useEffect(() => { @@ -503,6 +588,10 @@ export function MCPMarketplaceModal({ const isComplete = status === "ready" || status === "failed"; if (isComplete) { + console.log( + `🎉 Installation ${status} for ${mcpId}, updating lists...` + ); + // Remover do tracking setInstallingMcps((prev) => { const next = new Map(prev); @@ -510,11 +599,30 @@ export function MCPMarketplaceModal({ return next; }); - // Atualizar lista de MCPs - await fetchMCPs(); - if (activeTab === "installed") { - await fetchInstalledMCPs(); + // Se instalação foi bem-sucedida, marcar como instalado imediatamente + if (status === "ready") { + const toolsCount = data.validationResult?.tools?.length || 0; + console.log( + `✨ Marking ${mcpId} as installed with ${toolsCount} tools` + ); + + setMcps((prev) => + prev.map((mcp) => + mcp.id === mcpId || mcp.repo === mcpId || mcp.name === mcpId + ? { + ...mcp, + installed: true, + tools: data.validationResult?.tools || mcp.tools, + } + : mcp + ) + ); } + + // Recarregar lista de instalados sempre (não apenas na aba) + await fetchInstalledMCPs(); + + console.log(`✅ Lists synchronized after ${status} for ${mcpId}`); } else { // Atualizar progresso setInstallingMcps((prev) => { @@ -527,9 +635,26 @@ export function MCPMarketplaceModal({ return next; }); } + } else { + // MCP não encontrado ou erro no servidor + console.warn(`⚠️ MCP ${mcpId} not found or error: ${data.error}`); + + // Remover do tracking após erro (evita polling infinito) + setInstallingMcps((prev) => { + const next = new Map(prev); + next.delete(mcpId); + return next; + }); } } catch (error) { - console.error(`Error polling status for ${mcpId}:`, error); + console.error(`❌ Error polling status for ${mcpId}:`, error); + + // Remover do tracking após erro de rede (evita polling infinito) + setInstallingMcps((prev) => { + const next = new Map(prev); + next.delete(mcpId); + return next; + }); } } }, 2000); // Poll a cada 2 segundos @@ -734,7 +859,6 @@ export function MCPMarketplaceModal({ mcp={mcp} onInstall={() => handleInstall(mcp.id)} onUninstall={() => handleUninstall(mcp.id)} - onViewDetails={() => handleViewDetails(mcp)} installingStatus={installingMcps.get(mcp.id)} /> ))} @@ -787,25 +911,6 @@ export function MCPMarketplaceModal({ onTestServer={() => handleTestServer(mcp.id)} isTesting={testingMCP === mcp.id} onUninstall={() => handleUninstall(mcp.id)} - onViewDetails={() => - handleViewDetails({ - id: mcp.id, - name: mcp.name, - author: mcp.author || mcp.owner || "Unknown", - description: - mcp.description || `MCP instalado: ${mcp.name}`, - version: "1.0.0", - category: mcp.category || "other", - tags: mcp.tags || [], - rating: mcp.rating || 0, - totalRatings: mcp.totalRatings || 0, - installed: true, - repository: mcp.repository || "", - homepage: mcp.homepage || "", - config: mcp.config, - tools: mcp.tools, - }) - } installingStatus={installingMcps.get(mcp.id)} /> ))} diff --git a/database/ollahub.db-shm b/database/ollahub.db-shm index 7fd3ab6bc8aee3428f67d8b6bd57636c42f2e4f1..bb59fad91ae381eaf7ffc8091a2dc6000f8bfd54 100644 GIT binary patch delta 1049 zcmb7DX;4mK7(M4Lq*A%%qD4uh$dX;6T|{Kdu0(x?8OCp;#`wWcu6@nEWQi;LF8f+O zS+XP~ds&jLyjS0tZy3{fX3m}GIp@6RdGDRM85trYL!>t;4uVlW5(He*lZu#ZxNQFf?QKlJ2_=MJ$YrW&I-gr}b>UlyB(ahnBx)^JajWc+F5|Ew@Vpk?p>VjHT zgs-v*TX6%o@fuE4>C1RS%cPbcrKn4=7JHD3hjK;11rSAzkUlMr_R1OyE!s<8Y2(B1du*M{^9vavamB<%vrD z{V+2f@8Sa#HempV7z>(FFG~!pYg&Mf*ostGI^i$io|a zL?yo9E56}7exM%hSfBQEpd%a5nJw6hVGL&kBN@eL#;`AA*^m7h#{nG3LB>jxaB{{B z9Kc(Af*(T-u1sj9DT=xfE3p$NaSu;Wg{BN-CyqDt0-=?pDyoK6*o9Ll#8Xtmoo(2e z6D<1EDT=xr>#+~#@fa^rhd%7co}6sai>E2-3T!|QE}$5%P?yaa%n+v5=z7ugDm(jw xc!vtKWN(8k6HeZki9;yEXS8CU8cr*it*DEz8oO~C_wh{rUmk4BE=)3X!Ji5w{m}pb delta 322 zcmZo@U}|V!s+V}A%K!pWK+MR%ATR+)O9C;A+2Z#C|EuMqV2fd%e8o{{vl5#Gtml zAR?RpxISY9%WeMU0b~8}zQPF9sKvPXn;(qzB@o8?6ar&?2-ji*>b=0k%+FA;S&{8G v)8rS)Aama^O@5XJWjx7*G9KkX7@HsD|Kk8E`o+X7&rk%>cC8L1C&35+y{>T& diff --git a/database/ollahub.db-wal b/database/ollahub.db-wal index 45878fe0da638e3b8507ef20672ec2372dbdaff6..e21631714ca4f846065847f44e3a46d83ec71997 100644 GIT binary patch literal 1479112 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 diff --git a/lib/services/mcp-installer.ts b/lib/services/mcp-installer.ts index 4680a88..3e493c4 100644 --- a/lib/services/mcp-installer.ts +++ b/lib/services/mcp-installer.ts @@ -170,11 +170,12 @@ export class MCPInstallerService { }); // Construir ambiente + // Para NPM, executar npx do diretório onde foi instalado (não usa --prefix que não funciona) const environment: MCPEnvironment = { type: "npm", path: npmDir, executable: "npx", - args: ["--prefix", npmDir, packageName], + args: [packageName], }; return environment; diff --git a/lib/services/mcp-validator.ts b/lib/services/mcp-validator.ts index ac361d6..b8dcd93 100644 --- a/lib/services/mcp-validator.ts +++ b/lib/services/mcp-validator.ts @@ -32,11 +32,18 @@ export class MCPValidatorService { ...(config.env || {}), }; + // Para NPM, adicionar node_modules/.bin ao PATH para encontrar binários locais + if (environment.type === "npm" && environment.path) { + const nodeBinPath = `${environment.path}/node_modules/.bin`; + env.PATH = `${nodeBinPath}:${env.PATH || process.env.PATH}`; + } + console.log(`🧪 Testing MCP server: ${command} ${args.join(" ")}`); try { // Spawnar processo do servidor MCP const serverProcess = spawn(command, args, { + cwd: environment.path, // Executar do diretório de instalação env, stdio: ["pipe", "pipe", "pipe"], }); @@ -232,8 +239,15 @@ export class MCPValidatorService { ...(config.env || {}), }; + // Para NPM, adicionar node_modules/.bin ao PATH para encontrar binários locais + if (environment.type === "npm" && environment.path) { + const nodeBinPath = `${environment.path}/node_modules/.bin`; + env.PATH = `${nodeBinPath}:${env.PATH || process.env.PATH}`; + } + try { const serverProcess = spawn(command, args, { + cwd: environment.path, // Executar do diretório de instalação env, stdio: ["pipe", "pipe", "pipe"], }); diff --git a/lib/utils/system.ts b/lib/utils/system.ts index 1f052cc..93d0005 100644 --- a/lib/utils/system.ts +++ b/lib/utils/system.ts @@ -137,10 +137,21 @@ export async function spawnWithProgress( /** * Sanitiza nome de pacote para evitar command injection + * Suporta formatos: @scope/pkg, github:user/repo, git+https://..., etc. */ export function sanitizePackageName(packageName: string): string { - // Remove caracteres perigosos, mantém apenas alphanumeric, @, /, -, _ - return packageName.replace(/[^a-zA-Z0-9@/\-_.]/g, ""); + // URLs git+https:// devem ser preservadas completamente (são seguras) + if ( + packageName.startsWith("git+https://") || + packageName.startsWith("git+http://") + ) { + // Apenas validar que parece uma URL válida + return packageName; + } + + // Remove caracteres perigosos, mantém alphanumeric, @, :, +, /, -, _, . + // Suporta: @scope/pkg, github:user/repo + return packageName.replace(/[^a-zA-Z0-9@:+/\-_.]/g, ""); } /**