Skip to main content
Glama

https://github.com/sammcj/mcp-package-version

by sammcj
utils.go6.51 kB
package handlers import ( "encoding/json" "fmt" "io" "net/http" "regexp" "strconv" "strings" "time" "github.com/mark3labs/mcp-go/mcp" "github.com/sirupsen/logrus" ) // HTTPClient is an interface for making HTTP requests type HTTPClient interface { Do(req *http.Request) (*http.Response, error) } var ( // DefaultHTTPClient is the default HTTP client DefaultHTTPClient HTTPClient = &http.Client{ Timeout: 30 * time.Second, } ) // MakeRequest makes an HTTP request and returns the response body func MakeRequest(client HTTPClient, method, url string, headers map[string]string) ([]byte, error) { return MakeRequestWithLogger(client, nil, method, url, headers) } // MakeRequestWithLogger makes an HTTP request with logging and returns the response body func MakeRequestWithLogger(client HTTPClient, logger *logrus.Logger, method, url string, headers map[string]string) ([]byte, error) { if logger != nil { logger.WithFields(logrus.Fields{ "method": method, "url": url, }).Debug("Making HTTP request") } req, err := http.NewRequest(method, url, nil) if err != nil { if logger != nil { logger.WithFields(logrus.Fields{ "method": method, "url": url, "error": err.Error(), }).Error("Failed to create request") } return nil, fmt.Errorf("failed to create request: %w", err) } // Set headers for key, value := range headers { req.Header.Set(key, value) } // Set default headers if not provided if req.Header.Get("Accept") == "" { req.Header.Set("Accept", "application/json") } if req.Header.Get("User-Agent") == "" { req.Header.Set("User-Agent", "mcp-package-version/1.0.0") } // Send request resp, err := client.Do(req) if err != nil { if logger != nil { logger.WithFields(logrus.Fields{ "method": method, "url": url, "error": err.Error(), }).Error("Failed to send request") } return nil, fmt.Errorf("failed to send request: %w", err) } defer func() { err := resp.Body.Close() if err != nil && logger != nil { logger.WithFields(logrus.Fields{ "method": method, "url": url, "error": err.Error(), }).Error("Failed to close response body") } }() // Read response body body, err := io.ReadAll(resp.Body) if err != nil { if logger != nil { logger.WithFields(logrus.Fields{ "method": method, "url": url, "error": err.Error(), }).Error("Failed to read response body") } return nil, fmt.Errorf("failed to read response body: %w", err) } // Check for errors if resp.StatusCode < 200 || resp.StatusCode >= 300 { if logger != nil { logger.WithFields(logrus.Fields{ "method": method, "url": url, "statusCode": resp.StatusCode, "body": string(body), }).Error("Unexpected status code") } return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode, body) } if logger != nil { logger.WithFields(logrus.Fields{ "method": method, "url": url, "statusCode": resp.StatusCode, }).Debug("HTTP request completed successfully") } return body, nil } // NewToolResultJSON creates a new tool result with JSON content func NewToolResultJSON(data interface{}) (*mcp.CallToolResult, error) { jsonBytes, err := json.MarshalIndent(data, "", " ") if err != nil { return nil, fmt.Errorf("failed to marshal JSON: %w", err) } return mcp.NewToolResultText(string(jsonBytes)), nil } // ParseVersion parses a version string into major, minor, and patch components func ParseVersion(version string) (major, minor, patch int, err error) { // Remove any leading 'v' or other prefixes version = strings.TrimPrefix(version, "v") version = strings.TrimPrefix(version, "V") // Remove any build metadata or pre-release identifiers if idx := strings.IndexAny(version, "-+"); idx != -1 { version = version[:idx] } // Split the version string parts := strings.Split(version, ".") if len(parts) < 1 { return 0, 0, 0, fmt.Errorf("invalid version format: %s", version) } // Parse major version major, err = strconv.Atoi(parts[0]) if err != nil { return 0, 0, 0, fmt.Errorf("invalid major version: %s", parts[0]) } // Parse minor version if available if len(parts) > 1 { minor, err = strconv.Atoi(parts[1]) if err != nil { return major, 0, 0, fmt.Errorf("invalid minor version: %s", parts[1]) } } // Parse patch version if available if len(parts) > 2 { patch, err = strconv.Atoi(parts[2]) if err != nil { return major, minor, 0, fmt.Errorf("invalid patch version: %s", parts[2]) } } return major, minor, patch, nil } // CompareVersions compares two version strings // Returns: // // -1 if v1 < v2 // 0 if v1 == v2 // 1 if v1 > v2 func CompareVersions(v1, v2 string) (int, error) { major1, minor1, patch1, err := ParseVersion(v1) if err != nil { return 0, fmt.Errorf("failed to parse version 1: %w", err) } major2, minor2, patch2, err := ParseVersion(v2) if err != nil { return 0, fmt.Errorf("failed to parse version 2: %w", err) } // Compare major version if major1 < major2 { return -1, nil } if major1 > major2 { return 1, nil } // Compare minor version if minor1 < minor2 { return -1, nil } if minor1 > minor2 { return 1, nil } // Compare patch version if patch1 < patch2 { return -1, nil } if patch1 > patch2 { return 1, nil } // Versions are equal return 0, nil } // CleanVersion removes any leading version prefix (^, ~, >, =, <, etc.) from a version string func CleanVersion(version string) string { re := regexp.MustCompile(`^[\^~>=<]+`) return re.ReplaceAllString(version, "") } // StringPtr returns a pointer to the given string func StringPtr(s string) *string { return &s } // IntPtr returns a pointer to the given int func IntPtr(i int) *int { return &i } // ExtractMajorVersion extracts the major version from a version string func ExtractMajorVersion(version string) (int, error) { major, _, _, err := ParseVersion(version) return major, err } // FuzzyMatch performs a simple fuzzy match between a string and a query func FuzzyMatch(str, query string) bool { if query == "" { return true } if str == "" { return false } // Direct substring match if strings.Contains(str, query) { return true } // Check for character-by-character fuzzy match strIndex := 0 queryIndex := 0 for strIndex < len(str) && queryIndex < len(query) { if str[strIndex] == query[queryIndex] { queryIndex++ } strIndex++ } return queryIndex == len(query) }

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/sammcj/mcp-package-version'

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