Skip to main content
Glama

MCPJungle mcp gateway

by mcpjungle
Mozilla Public License 2.0
638
  • Apple
root.go•9.5 kB
/* Package cmd implements the CLI command structure for mcpjungle. Command Organization: - Commands are grouped into "basic" and "advanced" categories using annotations - Within each group, commands are ordered using a numeric "order" annotation - To add a new command: 1. Create the command file in the cmd package 2. Add annotations to specify group and order: cmd.Annotations = map[string]string{ "group": string(subCommandGroupBasic), // or subCommandGroupAdvanced "order": "5", // numeric order within the group } 3. Register the command with rootCmd.AddCommand() Missing annotations will cause groupCommands() to return an error. */ package cmd import ( "errors" "fmt" "net/http" "sort" "strconv" "github.com/mcpjungle/mcpjungle/client" "github.com/mcpjungle/mcpjungle/cmd/config" "github.com/mcpjungle/mcpjungle/pkg/version" "github.com/spf13/cobra" ) // subCommandGroup defines a type for categorizing subcommands into groups type subCommandGroup string const ( // subCommandGroupBasic represents basic commands that are commonly used and essential for beginners subCommandGroupBasic subCommandGroup = "basic" // subCommandGroupAdvanced represents advanced commands that are for advanced or enterprise use cases subCommandGroupAdvanced subCommandGroup = "advanced" ) // unorderedCommand is a special value used to indicate that a command does not have any order specified. const unorderedCommand = -1 // asciiArt contains the MCPJungle ASCII art banner const asciiArt = ` ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā•— ā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā•ā•ā–ˆā–ˆā•”ā•ā•ā–ˆā–ˆā•— ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā•ā•ā•ā•ā• ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā•ā•ā• ā–ˆā–ˆā•”ā–ˆā–ˆā–ˆā–ˆā•”ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā• ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•”ā–ˆā–ˆā•— ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā•ā• ā–ˆā–ˆ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā•—ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•‘ā–ˆā–ˆā•‘ ā–ˆā–ˆā•”ā•ā•ā• ā–ˆā–ˆā•‘ ā•šā•ā• ā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā•‘ ā•šā–ˆā–ˆā–ˆā–ˆā•‘ā•šā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•”ā•ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•— ā•šā•ā• ā•šā•ā• ā•šā•ā•ā•ā•ā•ā•ā•šā•ā• ā•šā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā• ā•šā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā• ā•šā•ā•ā•ā•ā•ā•ā•ā•šā•ā•ā•ā•ā•ā•ā• ` // ErrSilent is a sentinel error used to indicate that the command should not print an error message // This is useful when we handle error printing internally but want main to exit with a non-zero status. // See https://github.com/spf13/cobra/issues/914#issuecomment-548411337 var ErrSilent = errors.New("SilentErr") var registryServerURL string // apiClient is the global API client used by command handlers to interact with the MCPJungle registry server. // It is not the best choice to rely on a global variable, but cobra doesn't seem to provide any neat way to // pass an object down the command tree. var apiClient *client.Client var rootCmd = &cobra.Command{ Use: "mcpjungle", Short: "MCP Gateway for AI Agents", SilenceErrors: true, SilenceUsage: true, CompletionOptions: cobra.CompletionOptions{ DisableDefaultCmd: true, }, Run: func(cmd *cobra.Command, args []string) { // show custom help message when no subcommand is provided displayRootCmdHelpMsg(cmd) }, } func Execute() error { // Store the default help function before setting our custom one defaultHelpFunc := rootCmd.HelpFunc() // Set custom help function that handles both root and subcommands rootCmd.SetHelpFunc(customHelpFunc(defaultHelpFunc)) // Enable built-in --version behavior rootCmd.Version = version.GetVersion() rootCmd.SetVersionTemplate(asciiArt + "\nMCPJungle {{.Version}}\n") // only print usage and error messages if the command usage is incorrect rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error { cmd.Println(err) cmd.Println(cmd.UsageString()) return ErrSilent }) rootCmd.PersistentFlags().StringVar( &registryServerURL, "registry", "http://127.0.0.1:"+BindPortDefault, "Base URL of the MCPJungle registry server", ) // Initialize the API client with the registry server URL & client configuration (if any) rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { cfg := config.Load() // determine the registry server URL to use // precedence: command line flag explicitly set by user > config file > flag default value var u string if cmd.Flags().Changed("registry") { u = registryServerURL // if the user explicitly set the --registry flag, but the config file doesn't have // a registry_url entry, print a tip to let them know they can set it in the config file if cfg.RegistryURL == "" { if cfgFilePath, err := config.AbsPath(); err == nil { cmd.Printf( "TIP: You can set `registry_url: %s` in %s to avoid setting the --registry flag every time.\n\n", registryServerURL, cfgFilePath, ) } } } else if cfg.RegistryURL != "" { u = cfg.RegistryURL } else { u = registryServerURL } apiClient = client.NewClient(u, cfg.AccessToken, http.DefaultClient) } return rootCmd.Execute() } // displayRootCmdHelpMsg displays custom help message for the root command, ie, // when the mcpjungle CLI is run without any subcommands. func displayRootCmdHelpMsg(cmd *cobra.Command) { cmd.Println(cmd.Short) cmd.Println() cmd.Printf("Usage:\n %s\n\n", cmd.UseLine()) // group commands by category commandGroups, err := groupCommands(cmd.Commands()) if err != nil { cmd.Println("Error grouping commands:", err) return } // Display each group displayCommandGroup(cmd, "Basic Commands:", commandGroups[string(subCommandGroupBasic)]) displayCommandGroup(cmd, "Advanced Commands:", commandGroups[string(subCommandGroupAdvanced)]) cmd.Println("Flags:") cmd.Print(cmd.LocalFlags().FlagUsages()) cmd.Printf("Use \"%s [command] --help\" for more information about a command.\n", cmd.CommandPath()) } // customHelpFunc returns a help function that shows appropriate help content. func customHelpFunc(defaultHelpFunc func(*cobra.Command, []string)) func(*cobra.Command, []string) { return func(cmd *cobra.Command, args []string) { if cmd.Parent() == nil { // this is the root command, display custom help message displayRootCmdHelpMsg(cmd) return } // this is a subcommand, use default help defaultHelpFunc(cmd, args) } } // groupCommands organizes sub-commands by their group annotation func groupCommands(commands []*cobra.Command) (map[string][]*cobra.Command, error) { groups := map[string][]*cobra.Command{ string(subCommandGroupBasic): {}, string(subCommandGroupAdvanced): {}, } for _, subCmd := range commands { // skip non-functional commands if !subCmd.IsAvailableCommand() || subCmd.IsAdditionalHelpTopicCommand() { continue } if subCmd.Annotations == nil { return nil, fmt.Errorf("subcommand '%s' has no annotations, cannot determine group", subCmd.Name()) } group := subCmd.Annotations["group"] if group != string(subCommandGroupBasic) && group != string(subCommandGroupAdvanced) { return nil, fmt.Errorf("unknown group '%s' for subcommand '%s'", subCmd.Annotations["group"], subCmd.Name()) } groups[group] = append(groups[group], subCmd) } // sort each group by order annotation for groupName := range groups { sortCommandsByOrder(groups[groupName]) } return groups, nil } // displayCommandGroup shows a group of commands with an optional header func displayCommandGroup(cmd *cobra.Command, header string, commands []*cobra.Command) { if len(commands) == 0 { return } if header != "" { cmd.Println(header) } for _, subCmd := range commands { cmd.Printf(" %-11s %s\n", subCmd.Name(), subCmd.Short) } cmd.Println() } // sortCommandsByOrder sorts sub-commands by their order. // Two subcommands CAN have the same order. // If they belong to the same group, they will be displayed one after the other. // If they belong to different groups, their order only applies within their own group. func sortCommandsByOrder(commands []*cobra.Command) { sort.Slice(commands, func(i, j int) bool { orderI := getOrderValue(commands[i]) orderJ := getOrderValue(commands[j]) // Handle unordered commands (-1) - they go to the end if orderI == unorderedCommand && orderJ == unorderedCommand { // if both commands are unordered, sort by name return commands[i].Name() < commands[j].Name() } if orderI == unorderedCommand { return false // i goes after j } if orderJ == unorderedCommand { return true // i goes before j } return orderI < orderJ }) } // getOrderValue returns the order specified for the given command within its group. // If the command has no specific order, it returns -1 (unordered). func getOrderValue(cmd *cobra.Command) int { if cmd.Annotations == nil { return unorderedCommand } orderStr, exists := cmd.Annotations["order"] if !exists { return unorderedCommand } order, err := strconv.Atoi(orderStr) if err != nil { return unorderedCommand } return order }

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