Skip to main content
Glama

MCPJungle mcp gateway

by mcpjungle
Mozilla Public License 2.0
638
  • Apple
invoke.go•9.5 kB
package cmd import ( "encoding/base64" "encoding/json" "fmt" "os" "path/filepath" "time" "github.com/spf13/afero" "github.com/spf13/cobra" ) var ( invokeCmdInput string invokeCmdGroupName string ) var invokeToolCmd = &cobra.Command{ Use: "invoke <name>", Short: "Invoke a tool", Long: "Invokes a tool supplied by a registered MCP server", Args: cobra.ExactArgs(1), RunE: runInvokeTool, Annotations: map[string]string{ "group": string(subCommandGroupBasic), "order": "5", }, } func init() { invokeToolCmd.Flags().StringVar(&invokeCmdInput, "input", "{}", "valid JSON payload") invokeToolCmd.Flags().StringVar(&invokeCmdGroupName, "group", "", "invoke the tool within a tool group's context") rootCmd.AddCommand(invokeToolCmd) } func getTextContent(c map[string]any) (string, error) { textContent, ok := c["text"].(string) if !ok { return "", fmt.Errorf("text content item does not have a 'text' field: %v", c) } return textContent, nil } func getImageContent(c map[string]any) ([]byte, string, error) { dataStr, ok := c["data"].(string) if !ok { return nil, "", fmt.Errorf("image content item does not have a valid 'data' field: %v", c) } mimeType, ok := c["mimeType"].(string) if !ok { return nil, "", fmt.Errorf("image content item does not have a valid 'mimeType' field: %v", c) } // Decode base64 image data imgData, err := base64.StdEncoding.DecodeString(dataStr) if err != nil { return nil, "", fmt.Errorf("failed to decode base64 image data: %w", err) } // Determine file extension from MIME type ext := ".img" switch mimeType { case "image/png": ext = ".png" case "image/jpeg": ext = ".jpg" case "image/gif": ext = ".gif" } return imgData, ext, nil } func getAudioContent(c map[string]any) ([]byte, string, error) { dataStr, ok := c["data"].(string) if !ok { return nil, "", fmt.Errorf("audio content item does not have a valid 'data' field: %v", c) } mimeType, ok := c["mimeType"].(string) if !ok { return nil, "", fmt.Errorf("audio content item does not have a valid 'mimeType' field: %v", c) } // Decode base64 audio data audioData, err := base64.StdEncoding.DecodeString(dataStr) if err != nil { return nil, "", fmt.Errorf("failed to decode base64 audio data: %w", err) } // Determine file extension from MIME type ext := ".audio" switch mimeType { case "audio/mpeg": ext = ".mp3" case "audio/wav": ext = ".wav" case "audio/ogg": ext = ".ogg" } return audioData, ext, nil } // getFileExtensionFromMimeType returns the appropriate file extension for a given MIME type func getFileExtensionFromMimeType(mimeType string) string { // Common MIME type to extension mapping mimeToExt := map[string]string{ // Documents "application/pdf": ".pdf", "application/json": ".json", "text/plain": ".txt", "text/html": ".html", "text/css": ".css", "text/javascript": ".js", "application/xml": ".xml", "text/xml": ".xml", "text/csv": ".csv", "text/markdown": ".md", // Images "image/png": ".png", "image/jpeg": ".jpg", "image/gif": ".gif", "image/webp": ".webp", // Audio "audio/mpeg": ".mp3", "audio/wav": ".wav", "audio/ogg": ".ogg", // Video "video/mp4": ".mp4", "video/avi": ".avi", // Archives "application/zip": ".zip", "application/gzip": ".gz", // Other "application/octet-stream": ".bin", } if ext, exists := mimeToExt[mimeType]; exists { return ext } // Default to .bin for unknown MIME types return ".bin" } // unpackResourceContent is the core implementation for processing resource content // It handles embedded resource content from MCP tool responses. func unpackResourceContent(cmd *cobra.Command, c map[string]any, tmpDir string, fs afero.Fs) error { resource, ok := c["resource"].(map[string]any) if !ok { return fmt.Errorf("resource content item does not have a valid 'resource' field: %v", c) } uri, _ := resource["uri"].(string) mimeType, _ := resource["mimeType"].(string) // Display resource metadata cmd.Printf("Resource URI: %s\n", uri) if mimeType != "" { cmd.Printf("MIME Type: %s\n", mimeType) } // Handle text resource content if text, ok := resource["text"].(string); ok { cmd.Printf("Text Content:\n%s\n", text) return nil } // Handle blob resource content if blob, ok := resource["blob"].(string); ok { return handleBlobResource(cmd, blob, mimeType, tmpDir, fs) } return fmt.Errorf("resource content does not contain 'text' or 'blob' field: %v", resource) } // handleBlobResource processes blob resource content by decoding base64 data and saving to file func handleBlobResource(cmd *cobra.Command, blobData, mimeType, tmpDir string, fs afero.Fs) error { // Decode base64 blob data data, err := base64.StdEncoding.DecodeString(blobData) if err != nil { return fmt.Errorf("failed to decode base64 blob data: %w", err) } // Determine file extension from MIME type ext := getFileExtensionFromMimeType(mimeType) // Generate unique filename filename := fmt.Sprintf("resource_%d%s", time.Now().UnixNano(), ext) fullPath := filepath.Join(tmpDir, filename) // Write file to disk if err := afero.WriteFile(fs, fullPath, data, 0o644); err != nil { return fmt.Errorf("failed to write resource to disk: %w", err) } cmd.Printf("[Resource saved as %s]\n", filename) return nil } // unpackResourceLinkContent handles resource link content from MCP tool responses func unpackResourceLinkContent(cmd *cobra.Command, c map[string]any) error { // Extract the resource link content from the MCP tool response uri, _ := c["uri"].(string) name, _ := c["name"].(string) description, _ := c["description"].(string) mimeType, _ := c["mimeType"].(string) cmd.Printf("Resource Link URI: %s\n", uri) if name != "" { cmd.Printf("Name: %s\n", name) } if description != "" { cmd.Printf("Description: %s\n", description) } if mimeType != "" { cmd.Printf("MIME Type: %s\n", mimeType) } cmd.Println("Resource link content handled correctly") return nil } func runInvokeTool(cmd *cobra.Command, args []string) error { var input map[string]any if err := json.Unmarshal([]byte(invokeCmdInput), &input); err != nil { return fmt.Errorf("invalid input: %w", err) } toolName := args[0] // If group is specified, validate that the tool is in the group if invokeCmdGroupName != "" { group, err := apiClient.GetToolGroup(invokeCmdGroupName) if err != nil { return fmt.Errorf("failed to get tool group '%s': %w", invokeCmdGroupName, err) } // Check if the tool is included in the group toolInGroup := false for _, includedTool := range group.IncludedTools { if includedTool == toolName { toolInGroup = true break } } if !toolInGroup { return fmt.Errorf("tool '%s' is not available in group '%s'", toolName, invokeCmdGroupName) } cmd.Printf("Invoking tool '%s' from group '%s'\n", toolName, invokeCmdGroupName) if group.Description != "" { cmd.Printf("Group description: %s\n", group.Description) } cmd.Println() } result, err := apiClient.InvokeTool(toolName, input) if err != nil { return fmt.Errorf("failed to invoke tool: %w", err) } if result.IsError { cmd.Println("The tool returned an error:") for k, v := range result.Meta { cmd.Printf("%s: %v\n", k, v) } } else { cmd.Println("Response from tool:") } // result Content needs to be printed regardless of whether the tool returned an error or not // because it may contain useful information cmd.Println() for _, c := range result.Content { cType, ok := c["type"] if !ok { return fmt.Errorf("content item does not have a 'type' field: %v", c) } cmd.Printf("** Content [%s] **\n", cType) switch cType { case "text": textContent, err := getTextContent(c) if err != nil { return err } cmd.Println(textContent) case "image": imgData, ext, err := getImageContent(c) if err != nil { return err } filename := fmt.Sprintf("image_%d%s", time.Now().UnixNano(), ext) if err := os.WriteFile(filename, imgData, 0o644); err != nil { return fmt.Errorf("failed to write image to disk: %w", err) } cmd.Printf("[Image saved as %s]\n", filename) case "audio": audioData, ext, err := getAudioContent(c) if err != nil { return err } filename := fmt.Sprintf("audio_%d%s", time.Now().UnixNano(), ext) if err := os.WriteFile(filename, audioData, 0o644); err != nil { return fmt.Errorf("failed to write audio to disk: %w", err) } cmd.Printf("[Audio saved as %s]\n", filename) case "resource": err := unpackResourceContent(cmd, c, ".", afero.NewOsFs()) if err != nil { return err } case "resource_link": err := unpackResourceLinkContent(cmd, c) if err != nil { return err } default: // Handle unknown content types by displaying the raw content cmd.Printf("[Unknown content type: %s]\n", cType) contentJSON, err := json.MarshalIndent(c, "", " ") if err != nil { cmd.Printf("Raw content: %v\n", c) } else { cmd.Printf("Raw content:\n%s\n", string(contentJSON)) } } cmd.Println() } if result.StructuredContent != nil { cmd.Println() cmd.Println("** Structured Content **") structuredJSON, err := json.MarshalIndent(result.StructuredContent, "", " ") if err != nil { return fmt.Errorf("failed to marshal structured content: %w", err) } cmd.Println(string(structuredJSON)) cmd.Println() } return nil }

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