Skip to main content
Glama

MCPJungle mcp gateway

by mcpjungle
Mozilla Public License 2.0
638
  • Apple
middleware.go•6.13 kB
package api import ( "context" "fmt" "net/http" "strings" "github.com/gin-gonic/gin" "github.com/mcpjungle/mcpjungle/internal/model" "github.com/mcpjungle/mcpjungle/pkg/types" ) // requireInitialized is middleware to reject requests to certain routes if the server is not initialized func (s *Server) requireInitialized() gin.HandlerFunc { return func(c *gin.Context) { cfg, err := s.configService.GetConfig() if err != nil || !cfg.Initialized { c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "server is not initialized"}) return } // propagate the server mode in context for other middleware/handlers to use c.Set("mode", cfg.Mode) c.Next() } } // verifyUserAuthForAPIAccess is middleware that checks for a valid user token if the server is in enterprise mode. // this middleware doesn't care about the role of the user, it just verifies that they're authenticated. func (s *Server) verifyUserAuthForAPIAccess() gin.HandlerFunc { return func(c *gin.Context) { mode, exists := c.Get("mode") if !exists { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "server mode not found in context"}) return } m, ok := mode.(model.ServerMode) if !ok { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "invalid server mode in context"}) return } if m == model.ModeDev { // no auth is required in case of dev mode c.Next() return } authHeader := c.GetHeader("Authorization") token := strings.TrimPrefix(authHeader, "Bearer ") if token == "" { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing access token"}) return } // Verify that the token is valid and corresponds to a user authenticatedUser, err := s.userService.GetUserByAccessToken(token) if err != nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid access token: " + err.Error()}) return } // Store user in context for potential role checks in subsequent handlers c.Set("user", authenticatedUser) c.Next() } } // requireAdminUser is middleware that ensures the authenticated user has an admin role when in enterprise mode. // It assumes that verifyUserAuthForAPIAccess middleware has already run and set the user in context. func (s *Server) requireAdminUser() gin.HandlerFunc { return func(c *gin.Context) { mode, exists := c.Get("mode") if !exists { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "server mode not found in context"}) return } m, ok := mode.(model.ServerMode) if !ok { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "invalid server mode in context"}) return } if m == model.ModeDev { // no admin check is required in dev mode c.Next() return } authenticatedUser, exists := c.Get("user") if !exists { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "user is not authenticated"}) return } u, ok := authenticatedUser.(*model.User) if ok && u.Role == types.UserRoleAdmin { c.Next() return } c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "user is not authorized to perform this action"}) } } // requireServerMode is middleware that checks if the server is in a specific mode. // If not, the request is rejected with a 403 Forbidden status. // This is useful for routes that should only be accessible in certain modes (e.g., enterprise-only features). // NOTE: ModeProd is supported for backwards compatibility, it is equivalent to ModeEnterprise. func (s *Server) requireServerMode(m model.ServerMode) gin.HandlerFunc { return func(c *gin.Context) { mode, exists := c.Get("mode") if !exists { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "server mode not found in context"}) return } currentMode, ok := mode.(model.ServerMode) if !ok { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "invalid server mode in context"}) return } if currentMode == m { // current mode matches the required mode, allow access c.Next() return } if model.IsEnterpriseMode(currentMode) && model.IsEnterpriseMode(m) { // both current and required modes are enterprise modes, allow access c.Next() return } // current mode does not match the required mode, reject the request c.AbortWithStatusJSON( http.StatusForbidden, gin.H{"error": fmt.Sprintf("this request is only allowed in %s mode", m)}, ) } } // checkAuthForMcpProxyAccess is middleware for MCP proxy that checks for a valid MCP client token // if the server is in enterprise mode. // In development mode, mcp clients do not require auth to access the MCP proxy. func (s *Server) checkAuthForMcpProxyAccess() gin.HandlerFunc { return func(c *gin.Context) { mode, exists := c.Get("mode") if !exists { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "server mode not found in context"}) return } m, ok := mode.(model.ServerMode) if !ok { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "invalid server mode in context"}) return } // the gin context doesn't get passed down to the MCP proxy server, so we need to // set values in the underlying request's context to be able to access them from proxy. ctx := context.WithValue(c.Request.Context(), "mode", m) c.Request = c.Request.WithContext(ctx) if m == model.ModeDev { // no auth is required in case of dev mode c.Next() return } authHeader := c.GetHeader("Authorization") token := strings.TrimPrefix(authHeader, "Bearer ") if token == "" { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing MCP client access token"}) return } client, err := s.mcpClientService.GetClientByToken(token) if err != nil { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid MCP client token"}) return } // inject the authenticated MCP client in context for the proxy to use ctx = context.WithValue(c.Request.Context(), "client", client) c.Request = c.Request.WithContext(ctx) c.Next() } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mcpjungle/MCPJungle'

If you have feedback or need assistance with the MCP directory API, please join our Discord server