From 243656a7b6aff582936a0ee6282f3b904f0cbc65 Mon Sep 17 00:00:00 2001 From: viettranx Date: Sun, 29 Mar 2026 07:15:03 +0700 Subject: [PATCH] feat(mcp): add reconnect button to evict stale MCP connections When MCP servers update their tools externally, the gateway keeps stale cached connections. Add POST /v1/mcp/servers/{id}/reconnect endpoint that evicts the pooled connection, forcing re-discovery on next agent use. Exposed via reconnect button on both web and desktop MCP management pages. --- internal/http/mcp.go | 28 +++++++++++++++++++ .../components/settings/mcp/McpServerList.tsx | 16 ++++++++++- .../frontend/src/hooks/use-mcp-servers.ts | 12 +++++++- .../frontend/src/i18n/locales/en/mcp.json | 1 + .../frontend/src/i18n/locales/vi/mcp.json | 1 + .../frontend/src/i18n/locales/zh/mcp.json | 1 + ui/web/src/i18n/locales/en/mcp.json | 5 +++- ui/web/src/i18n/locales/vi/mcp.json | 5 +++- ui/web/src/i18n/locales/zh/mcp.json | 5 +++- ui/web/src/pages/mcp/hooks/use-mcp.ts | 14 ++++++++++ ui/web/src/pages/mcp/mcp-page.tsx | 17 +++++++++-- 11 files changed, 98 insertions(+), 7 deletions(-) diff --git a/internal/http/mcp.go b/internal/http/mcp.go index 1faa95dd..a6eaa0b1 100644 --- a/internal/http/mcp.go +++ b/internal/http/mcp.go @@ -61,6 +61,9 @@ func (h *MCPHandler) RegisterRoutes(mux *http.ServeMux) { // Test connection (no save) mux.HandleFunc("POST /v1/mcp/servers/test", h.auth(h.handleTestConnection)) + // Reconnect (evict pooled connection so next use re-discovers tools) + mux.HandleFunc("POST /v1/mcp/servers/{id}/reconnect", h.auth(h.handleReconnectServer)) + // Server tools (runtime-discovered) mux.HandleFunc("GET /v1/mcp/servers/{id}/tools", h.auth(h.handleListServerTools)) @@ -231,3 +234,28 @@ func (h *MCPHandler) handleDeleteServer(w http.ResponseWriter, r *http.Request) emitAudit(h.msgBus, r, "mcp_server.deleted", "mcp_server", id.String()) writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"}) } + +func (h *MCPHandler) handleReconnectServer(w http.ResponseWriter, r *http.Request) { + locale := store.LocaleFromContext(r.Context()) + id, err := uuid.Parse(r.PathValue("id")) + if err != nil { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": i18n.T(locale, i18n.MsgInvalidID, "server")}) + return + } + + srv, err := h.store.GetServer(r.Context(), id) + if err != nil { + writeJSON(w, http.StatusNotFound, map[string]string{"error": i18n.T(locale, i18n.MsgNotFound, "server", id.String())}) + return + } + + if h.poolEvictor != nil { + tid := store.TenantIDFromContext(r.Context()) + h.poolEvictor.Evict(tid, srv.Name) + } + + h.emitCacheInvalidate() + emitAudit(h.msgBus, r, "mcp_server.reconnected", "mcp_server", id.String()) + slog.Info("mcp.server.reconnect_requested", "server", srv.Name, "id", id) + writeJSON(w, http.StatusOK, map[string]string{"status": "reconnected"}) +} diff --git a/ui/desktop/frontend/src/components/settings/mcp/McpServerList.tsx b/ui/desktop/frontend/src/components/settings/mcp/McpServerList.tsx index 22d5f2be..5682ddd5 100644 --- a/ui/desktop/frontend/src/components/settings/mcp/McpServerList.tsx +++ b/ui/desktop/frontend/src/components/settings/mcp/McpServerList.tsx @@ -14,7 +14,7 @@ export function McpServerList() { const { servers, loading, atLimit, fetchServers, createServer, updateServer, deleteServer, - testConnection, listServerTools, + testConnection, reconnectServer, listServerTools, listGrants, grantAgent, revokeAgent, } = useMcpServers() const { agents } = useAgentCrud() @@ -24,6 +24,7 @@ export function McpServerList() { const [deleteTarget, setDeleteTarget] = useState(null) const [grantsServer, setGrantsServer] = useState(null) const [toolsServer, setToolsServer] = useState(null) + const [reconnectingId, setReconnectingId] = useState(null) function openCreate() { setEditServer(null) @@ -163,6 +164,19 @@ export function McpServerList() { {/* Actions */}
+