From 60c3fd112e72d5551f18f5c36a26fdca2ef56096 Mon Sep 17 00:00:00 2001 From: Manuel Odendahl Date: Mon, 10 Feb 2025 17:30:41 -0500 Subject: [PATCH] :tractor: Move client to mcp-server command --- cmd/mcp-server/cmds/client.go | 22 ++ cmd/mcp-server/cmds/client/helpers/client.go | 85 +++++++ cmd/mcp-server/cmds/client/layers/client.go | 39 ++++ cmd/mcp-server/cmds/client/prompts.go | 203 ++++++++++++++++ cmd/mcp-server/cmds/client/resources.go | 177 ++++++++++++++ cmd/mcp-server/cmds/client/tools.go | 233 +++++++++++++++++++ cmd/mcp-server/cmds/client/version.go | 25 ++ cmd/mcp-server/main.go | 20 +- 8 files changed, 801 insertions(+), 3 deletions(-) create mode 100644 cmd/mcp-server/cmds/client.go create mode 100644 cmd/mcp-server/cmds/client/helpers/client.go create mode 100644 cmd/mcp-server/cmds/client/layers/client.go create mode 100644 cmd/mcp-server/cmds/client/prompts.go create mode 100644 cmd/mcp-server/cmds/client/resources.go create mode 100644 cmd/mcp-server/cmds/client/tools.go create mode 100644 cmd/mcp-server/cmds/client/version.go diff --git a/cmd/mcp-server/cmds/client.go b/cmd/mcp-server/cmds/client.go new file mode 100644 index 0000000..aa01752 --- /dev/null +++ b/cmd/mcp-server/cmds/client.go @@ -0,0 +1,22 @@ +package cmds + +import ( + "github.com/go-go-golems/glazed/pkg/help" + "github.com/go-go-golems/go-go-mcp/cmd/mcp-server/cmds/client" + "github.com/spf13/cobra" +) + +var ClientCmd = &cobra.Command{ + Use: "client", + Short: "MCP client functionality", + Long: `Client commands for interacting with MCP servers`, +} + +func InitClientCommand(helpSystem *help.HelpSystem) error { + // Add client subcommands + ClientCmd.AddCommand(client.ToolsCmd) + ClientCmd.AddCommand(client.ResourcesCmd) + ClientCmd.AddCommand(client.PromptsCmd) + + return nil +} diff --git a/cmd/mcp-server/cmds/client/helpers/client.go b/cmd/mcp-server/cmds/client/helpers/client.go new file mode 100644 index 0000000..36931e8 --- /dev/null +++ b/cmd/mcp-server/cmds/client/helpers/client.go @@ -0,0 +1,85 @@ +package helpers + +import ( + "context" + "fmt" + + glazed_layers "github.com/go-go-golems/glazed/pkg/cmds/layers" + "github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/layers" + "github.com/go-go-golems/go-go-mcp/pkg/client" + "github.com/go-go-golems/go-go-mcp/pkg/protocol" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" +) + +type ClientSettings = layers.ClientSettings + +// CreateClient initializes and returns a new MCP client based on the provided flags. +func CreateClient(cmd *cobra.Command) (*client.Client, error) { + transport, err := cmd.Flags().GetString("transport") + if err != nil { + return nil, fmt.Errorf("failed to get transport flag: %w", err) + } + + serverURL, err := cmd.Flags().GetString("server") + if err != nil { + return nil, fmt.Errorf("failed to get server flag: %w", err) + } + + cmdArgs, err := cmd.Flags().GetStringSlice("command") + if err != nil { + return nil, fmt.Errorf("failed to get command flag: %w", err) + } + + return createClient(&ClientSettings{ + Transport: transport, + Server: serverURL, + Command: cmdArgs, + }) +} + +// CreateClientFromSettings initializes and returns a new MCP client based on the provided settings. +func CreateClientFromSettings(parsedLayers *glazed_layers.ParsedLayers) (*client.Client, error) { + s := &ClientSettings{} + if err := parsedLayers.InitializeStruct(layers.ClientLayerSlug, s); err != nil { + return nil, err + } + + return createClient(s) +} + +func createClient(s *ClientSettings) (*client.Client, error) { + var t client.Transport + var err error + + switch s.Transport { + case "command": + if len(s.Command) == 0 { + return nil, fmt.Errorf("command is required for command transport") + } + log.Debug().Msgf("Creating command transport with args: %v", s.Command) + t, err = client.NewCommandStdioTransport(log.Logger, s.Command[0], s.Command[1:]...) + if err != nil { + return nil, fmt.Errorf("failed to create command transport: %w", err) + } + case "sse": + log.Debug().Msgf("Creating SSE transport with server URL: %s", s.Server) + t = client.NewSSETransport(s.Server, log.Logger) + default: + return nil, fmt.Errorf("invalid transport type: %s", s.Transport) + } + + // Create and initialize client + c := client.NewClient(log.Logger, t) + log.Debug().Msgf("Initializing client") + err = c.Initialize(context.Background(), protocol.ClientCapabilities{ + Sampling: &protocol.SamplingCapability{}, + }) + if err != nil { + return nil, fmt.Errorf("failed to initialize client: %w", err) + } + + log.Debug().Msgf("Client initialized") + + return c, nil +} diff --git a/cmd/mcp-server/cmds/client/layers/client.go b/cmd/mcp-server/cmds/client/layers/client.go new file mode 100644 index 0000000..a22e78f --- /dev/null +++ b/cmd/mcp-server/cmds/client/layers/client.go @@ -0,0 +1,39 @@ +package layers + +import ( + "github.com/go-go-golems/glazed/pkg/cmds/layers" + "github.com/go-go-golems/glazed/pkg/cmds/parameters" +) + +type ClientSettings struct { + Transport string `glazed.parameter:"transport"` + Server string `glazed.parameter:"server"` + Command []string `glazed.parameter:"command"` +} + +const ClientLayerSlug = "mcp-client" + +func NewClientParameterLayer() (layers.ParameterLayer, error) { + return layers.NewParameterLayer(ClientLayerSlug, "MCP Client Settings", + layers.WithParameterDefinitions( + parameters.NewParameterDefinition( + "transport", + parameters.ParameterTypeString, + parameters.WithHelp("Transport type (command or sse)"), + parameters.WithDefault("command"), + ), + parameters.NewParameterDefinition( + "server", + parameters.ParameterTypeString, + parameters.WithHelp("Server URL for SSE transport"), + parameters.WithDefault("http://localhost:3001"), + ), + parameters.NewParameterDefinition( + "command", + parameters.ParameterTypeStringList, + parameters.WithHelp("Command and arguments for command transport"), + parameters.WithDefault([]string{"mcp-server", "start", "--transport", "stdio"}), + ), + ), + ) +} diff --git a/cmd/mcp-server/cmds/client/prompts.go b/cmd/mcp-server/cmds/client/prompts.go new file mode 100644 index 0000000..1e0d314 --- /dev/null +++ b/cmd/mcp-server/cmds/client/prompts.go @@ -0,0 +1,203 @@ +package client + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/go-go-golems/glazed/pkg/cli" + "github.com/go-go-golems/glazed/pkg/cmds" + glazed_layers "github.com/go-go-golems/glazed/pkg/cmds/layers" + "github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/layers" + + "github.com/go-go-golems/glazed/pkg/cmds/parameters" + "github.com/go-go-golems/glazed/pkg/middlewares" + "github.com/go-go-golems/glazed/pkg/settings" + "github.com/go-go-golems/glazed/pkg/types" + "github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/helpers" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + promptArgs string +) + +// PromptsCmd handles the "prompts" command group +var PromptsCmd = &cobra.Command{ + Use: "prompts", + Short: "Interact with prompts", + Long: `List available prompts and execute specific prompts.`, +} + +type ListPromptsCommand struct { + *cmds.CommandDescription +} + +type ExecutePromptCommand struct { + *cmds.CommandDescription +} + +type ExecutePromptSettings struct { + Args string `glazed.parameter:"args"` + PromptName string `glazed.parameter:"prompt-name"` +} + +func NewListPromptsCommand() (*ListPromptsCommand, error) { + glazedParameterLayer, err := settings.NewGlazedParameterLayers() + if err != nil { + return nil, errors.Wrap(err, "could not create Glazed parameter layer") + } + + clientLayer, err := layers.NewClientParameterLayer() + if err != nil { + return nil, errors.Wrap(err, "could not create client parameter layer") + } + + return &ListPromptsCommand{ + CommandDescription: cmds.NewCommandDescription( + "list", + cmds.WithShort("List available prompts"), + cmds.WithLayersList( + glazedParameterLayer, + clientLayer, + ), + ), + }, nil +} + +func NewExecutePromptCommand() (*ExecutePromptCommand, error) { + clientLayer, err := layers.NewClientParameterLayer() + if err != nil { + return nil, errors.Wrap(err, "could not create client parameter layer") + } + + return &ExecutePromptCommand{ + CommandDescription: cmds.NewCommandDescription( + "execute", + cmds.WithShort("Execute a specific prompt"), + cmds.WithFlags( + parameters.NewParameterDefinition( + "args", + parameters.ParameterTypeString, + parameters.WithHelp("Prompt arguments as JSON string"), + parameters.WithDefault(""), + ), + ), + cmds.WithArguments( + parameters.NewParameterDefinition( + "prompt-name", + parameters.ParameterTypeString, + parameters.WithHelp("Name of the prompt to execute"), + parameters.WithRequired(true), + ), + ), + cmds.WithLayersList( + clientLayer, + ), + ), + }, nil +} + +func (c *ListPromptsCommand) RunIntoGlazeProcessor( + ctx context.Context, + parsedLayers *glazed_layers.ParsedLayers, + gp middlewares.Processor, +) error { + client, err := helpers.CreateClientFromSettings(parsedLayers) + if err != nil { + return err + } + defer client.Close(ctx) + + prompts, cursor, err := client.ListPrompts(ctx, "") + if err != nil { + return err + } + + for _, prompt := range prompts { + row := types.NewRow( + types.MRP("name", prompt.Name), + types.MRP("description", prompt.Description), + ) + + // Create a JSON array of arguments + args := make([]map[string]interface{}, len(prompt.Arguments)) + for i, arg := range prompt.Arguments { + args[i] = map[string]interface{}{ + "name": arg.Name, + "required": arg.Required, + "description": arg.Description, + } + } + row.Set("arguments", args) + + if err := gp.AddRow(ctx, row); err != nil { + return err + } + } + + if cursor != "" { + // Add cursor as a final row + cursorRow := types.NewRow( + types.MRP("cursor", cursor), + ) + if err := gp.AddRow(ctx, cursorRow); err != nil { + return err + } + } + + return nil +} + +func (c *ExecutePromptCommand) RunIntoWriter( + ctx context.Context, + parsedLayers *glazed_layers.ParsedLayers, + w io.Writer, +) error { + s := &ExecutePromptSettings{} + if err := parsedLayers.InitializeStruct(glazed_layers.DefaultSlug, s); err != nil { + return err + } + + client, err := helpers.CreateClientFromSettings(parsedLayers) + if err != nil { + return err + } + defer client.Close(ctx) + + // Parse prompt arguments + promptArgMap := make(map[string]string) + if s.Args != "" { + if err := json.Unmarshal([]byte(s.Args), &promptArgMap); err != nil { + return fmt.Errorf("invalid prompt arguments JSON: %w", err) + } + } + + message, err := client.GetPrompt(ctx, s.PromptName, promptArgMap) + if err != nil { + return err + } + + // Write formatted output to writer + _, err = fmt.Fprintf(w, "Role: %s\nContent: %s\n", message.Role, message.Content.Text) + return err +} + +func init() { + listCmd, err := NewListPromptsCommand() + cobra.CheckErr(err) + + executeCmd, err := NewExecutePromptCommand() + cobra.CheckErr(err) + + listCobraCmd, err := cli.BuildCobraCommandFromGlazeCommand(listCmd) + cobra.CheckErr(err) + + executeCobraCmd, err := cli.BuildCobraCommandFromWriterCommand(executeCmd) + cobra.CheckErr(err) + + PromptsCmd.AddCommand(listCobraCmd) + PromptsCmd.AddCommand(executeCobraCmd) +} diff --git a/cmd/mcp-server/cmds/client/resources.go b/cmd/mcp-server/cmds/client/resources.go new file mode 100644 index 0000000..7dc596f --- /dev/null +++ b/cmd/mcp-server/cmds/client/resources.go @@ -0,0 +1,177 @@ +package client + +import ( + "context" + "fmt" + "io" + + "github.com/go-go-golems/glazed/pkg/cli" + "github.com/go-go-golems/glazed/pkg/cmds" + glazed_layers "github.com/go-go-golems/glazed/pkg/cmds/layers" + "github.com/go-go-golems/glazed/pkg/cmds/parameters" + "github.com/go-go-golems/glazed/pkg/middlewares" + "github.com/go-go-golems/glazed/pkg/settings" + "github.com/go-go-golems/glazed/pkg/types" + "github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/helpers" + "github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/layers" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +// ResourcesCmd handles the "resources" command group +var ResourcesCmd = &cobra.Command{ + Use: "resources", + Short: "Interact with resources", + Long: `List available resources and read specific resources.`, +} + +type ListResourcesCommand struct { + *cmds.CommandDescription +} + +type ListResourcesSettings struct { +} + +type ReadResourceCommand struct { + *cmds.CommandDescription +} + +type ReadResourceSettings struct { + URI string `glazed.parameter:"uri"` +} + +func NewListResourcesCommand() (*ListResourcesCommand, error) { + glazedParameterLayer, err := settings.NewGlazedParameterLayers() + if err != nil { + return nil, errors.Wrap(err, "could not create Glazed parameter layer") + } + + clientLayer, err := layers.NewClientParameterLayer() + if err != nil { + return nil, errors.Wrap(err, "could not create client parameter layer") + } + + return &ListResourcesCommand{ + CommandDescription: cmds.NewCommandDescription( + "list", + cmds.WithShort("List available resources"), + cmds.WithLayersList( + glazedParameterLayer, + clientLayer, + ), + ), + }, nil +} + +func NewReadResourceCommand() (*ReadResourceCommand, error) { + clientLayer, err := layers.NewClientParameterLayer() + if err != nil { + return nil, errors.Wrap(err, "could not create client parameter layer") + } + + return &ReadResourceCommand{ + CommandDescription: cmds.NewCommandDescription( + "read", + cmds.WithShort("Read a specific resource"), + cmds.WithArguments( + parameters.NewParameterDefinition( + "uri", + parameters.ParameterTypeString, + parameters.WithHelp("URI of the resource to read"), + parameters.WithRequired(true), + ), + ), + cmds.WithLayersList(clientLayer), + ), + }, nil +} + +func (c *ListResourcesCommand) RunIntoGlazeProcessor( + ctx context.Context, + parsedLayers *glazed_layers.ParsedLayers, + gp middlewares.Processor, +) error { + s := &ListResourcesSettings{} + if err := parsedLayers.InitializeStruct(glazed_layers.DefaultSlug, s); err != nil { + return err + } + + client, err := helpers.CreateClientFromSettings(parsedLayers) + if err != nil { + return err + } + defer client.Close(ctx) + + resources, cursor, err := client.ListResources(ctx, "") + if err != nil { + return err + } + + for _, resource := range resources { + row := types.NewRow( + types.MRP("uri", resource.URI), + types.MRP("name", resource.Name), + types.MRP("description", resource.Description), + types.MRP("mime_type", resource.MimeType), + ) + if err := gp.AddRow(ctx, row); err != nil { + return err + } + } + + if cursor != "" { + // Add cursor as a separate row with a special type + row := types.NewRow( + types.MRP("cursor", cursor), + types.MRP("type", "cursor"), + ) + if err := gp.AddRow(ctx, row); err != nil { + return err + } + } + + return nil +} + +func (c *ReadResourceCommand) RunIntoWriter( + ctx context.Context, + parsedLayers *glazed_layers.ParsedLayers, + w io.Writer, +) error { + s := &ReadResourceSettings{} + if err := parsedLayers.InitializeStruct(glazed_layers.DefaultSlug, s); err != nil { + return err + } + + client, err := helpers.CreateClientFromSettings(parsedLayers) + if err != nil { + return err + } + defer client.Close(ctx) + + content, err := client.ReadResource(ctx, s.URI) + if err != nil { + return err + } + + _, err = fmt.Fprintf(w, "URI: %s\nMimeType: %s\nContent:\n%s\n", + content.URI, content.MimeType, content.Text) + return err +} + +func init() { + listCmd, err := NewListResourcesCommand() + cobra.CheckErr(err) + + cobraListCmd, err := cli.BuildCobraCommandFromGlazeCommand(listCmd) + cobra.CheckErr(err) + + readCmd, err := NewReadResourceCommand() + cobra.CheckErr(err) + + cobraReadCmd, err := cli.BuildCobraCommandFromWriterCommand(readCmd) + cobra.CheckErr(err) + + ResourcesCmd.AddCommand(cobraListCmd) + ResourcesCmd.AddCommand(cobraReadCmd) +} diff --git a/cmd/mcp-server/cmds/client/tools.go b/cmd/mcp-server/cmds/client/tools.go new file mode 100644 index 0000000..eb70b7f --- /dev/null +++ b/cmd/mcp-server/cmds/client/tools.go @@ -0,0 +1,233 @@ +package client + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/go-go-golems/glazed/pkg/cli" + "github.com/go-go-golems/glazed/pkg/cmds" + glazed_layers "github.com/go-go-golems/glazed/pkg/cmds/layers" + "github.com/go-go-golems/glazed/pkg/cmds/parameters" + "github.com/go-go-golems/glazed/pkg/middlewares" + "github.com/go-go-golems/glazed/pkg/settings" + "github.com/go-go-golems/glazed/pkg/types" + "github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/helpers" + "github.com/go-go-golems/go-go-mcp/cmd/mcp-client/cmds/layers" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + toolArgs string +) + +// ToolsCmd handles the "tools" command group +var ToolsCmd = &cobra.Command{ + Use: "tools", + Short: "Interact with tools", + Long: `List available tools and execute specific tools.`, +} + +type ListToolsCommand struct { + *cmds.CommandDescription +} + +type ListToolsSettings struct { +} + +type CallToolCommand struct { + *cmds.CommandDescription +} + +type CallToolSettings struct { + ToolName string `glazed.parameter:"tool-name"` + JSON string `glazed.parameter:"json"` + Args map[string]interface{} `glazed.parameter:"args"` +} + +func NewListToolsCommand() (*ListToolsCommand, error) { + glazedParameterLayer, err := settings.NewGlazedParameterLayers() + if err != nil { + return nil, errors.Wrap(err, "could not create Glazed parameter layer") + } + + clientLayer, err := layers.NewClientParameterLayer() + if err != nil { + return nil, errors.Wrap(err, "could not create client parameter layer") + } + + return &ListToolsCommand{ + CommandDescription: cmds.NewCommandDescription( + "list", + cmds.WithShort("List available tools"), + cmds.WithLayersList( + glazedParameterLayer, + clientLayer, + ), + ), + }, nil +} + +func NewCallToolCommand() (*CallToolCommand, error) { + clientLayer, err := layers.NewClientParameterLayer() + if err != nil { + return nil, errors.Wrap(err, "could not create client parameter layer") + } + + return &CallToolCommand{ + CommandDescription: cmds.NewCommandDescription( + "call", + cmds.WithShort("Call a specific tool"), + cmds.WithArguments( + parameters.NewParameterDefinition( + "tool-name", + parameters.ParameterTypeString, + parameters.WithHelp("Name of the tool to call"), + parameters.WithRequired(true), + ), + ), + cmds.WithFlags( + parameters.NewParameterDefinition( + "json", + parameters.ParameterTypeString, + parameters.WithHelp("Tool arguments as JSON string"), + parameters.WithDefault(""), + ), + parameters.NewParameterDefinition( + "args", + parameters.ParameterTypeKeyValue, + parameters.WithHelp("Tool arguments as key=value pairs"), + parameters.WithDefault(map[string]interface{}{}), + ), + ), + cmds.WithLayersList(clientLayer), + ), + }, nil +} + +func (c *ListToolsCommand) RunIntoGlazeProcessor( + ctx context.Context, + parsedLayers *glazed_layers.ParsedLayers, + gp middlewares.Processor, +) error { + s := &ListToolsSettings{} + if err := parsedLayers.InitializeStruct(glazed_layers.DefaultSlug, s); err != nil { + return err + } + + client, err := helpers.CreateClientFromSettings(parsedLayers) + if err != nil { + return err + } + defer client.Close(ctx) + + tools, cursor, err := client.ListTools(ctx, "") + if err != nil { + return err + } + + for _, tool := range tools { + // First unmarshal the schema into an interface{} to ensure it's valid JSON + var schemaObj interface{} + if err := json.Unmarshal(tool.InputSchema, &schemaObj); err != nil { + return fmt.Errorf("failed to parse schema JSON: %w", err) + } + + row := types.NewRow( + types.MRP("name", tool.Name), + types.MRP("description", tool.Description), + types.MRP("schema", schemaObj), + ) + if err := gp.AddRow(ctx, row); err != nil { + return err + } + } + + if cursor != "" { + row := types.NewRow( + types.MRP("cursor", cursor), + types.MRP("type", "cursor"), + ) + if err := gp.AddRow(ctx, row); err != nil { + return err + } + } + + return nil +} + +func (c *CallToolCommand) RunIntoWriter( + ctx context.Context, + parsedLayers *glazed_layers.ParsedLayers, + w io.Writer, +) error { + s := &CallToolSettings{} + if err := parsedLayers.InitializeStruct(glazed_layers.DefaultSlug, s); err != nil { + return err + } + + client, err := helpers.CreateClientFromSettings(parsedLayers) + if err != nil { + return err + } + defer client.Close(ctx) + + // Parse tool arguments - first try JSON string, then key-value pairs + toolArgMap := make(map[string]interface{}) + + // If JSON args are provided, they take precedence + if s.JSON != "" { + if err := json.Unmarshal([]byte(s.JSON), &toolArgMap); err != nil { + return fmt.Errorf("invalid tool arguments JSON: %w", err) + } + } else if len(s.Args) > 0 { + // Otherwise use key-value pairs if provided + toolArgMap = s.Args + } + + result, err := client.CallTool(ctx, s.ToolName, toolArgMap) + if err != nil { + return err + } + + // Pretty print the result + for _, content := range result.Content { + _, err = fmt.Fprintf(w, "Type: %s\n", content.Type) + if err != nil { + return err + } + + switch content.Type { + case "text": + _, err = fmt.Fprintf(w, "Content:\n%s\n", content.Text) + case "image": + _, err = fmt.Fprintf(w, "Image:\n%s\n", content.Data) + case "resource": + _, err = fmt.Fprintf(w, "URI: %s\nMimeType: %s\n", + content.Resource.URI, content.Resource.MimeType) + } + if err != nil { + return err + } + } + return nil +} + +func init() { + listCmd, err := NewListToolsCommand() + cobra.CheckErr(err) + + cobraListCmd, err := cli.BuildCobraCommandFromGlazeCommand(listCmd) + cobra.CheckErr(err) + + callCmd, err := NewCallToolCommand() + cobra.CheckErr(err) + + cobraCallCmd, err := cli.BuildCobraCommandFromWriterCommand(callCmd) + cobra.CheckErr(err) + + ToolsCmd.AddCommand(cobraListCmd) + ToolsCmd.AddCommand(cobraCallCmd) +} diff --git a/cmd/mcp-server/cmds/client/version.go b/cmd/mcp-server/cmds/client/version.go new file mode 100644 index 0000000..f797d9a --- /dev/null +++ b/cmd/mcp-server/cmds/client/version.go @@ -0,0 +1,25 @@ +package client + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +// Version information +var ( + Version = "dev" + BuildTime = "unknown" + GitCommit = "none" +) + +// VersionCmd handles the "version" command +var VersionCmd = &cobra.Command{ + Use: "version", + Short: "Print version information", + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("mcp-client version %s\n", Version) + fmt.Printf(" Build time: %s\n", BuildTime) + fmt.Printf(" Git commit: %s\n", GitCommit) + }, +} diff --git a/cmd/mcp-server/main.go b/cmd/mcp-server/main.go index 94b1353..b3cf6b8 100644 --- a/cmd/mcp-server/main.go +++ b/cmd/mcp-server/main.go @@ -66,10 +66,24 @@ func initRootCmd() (*help.HelpSystem, error) { helpSystem := help.NewHelpSystem() helpSystem.SetupCobraRootCommand(rootCmd) + rootCmd.PersistentFlags().StringVarP(&transport, "transport", "t", "stdio", "Transport to use (stdio or sse)") + rootCmd.PersistentFlags().IntVarP(&port, "port", "p", 8080, "Port to listen on (for sse transport)") + rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "Enable debug mode") + rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "Log level (debug, info, warn, error)") + rootCmd.PersistentFlags().BoolVar(&withCaller, "with-caller", false, "Log caller information") + err := clay.InitViper("mcp", rootCmd) - cobra.CheckErr(err) - err = clay.InitLogger() - cobra.CheckErr(err) + if err != nil { + return nil, err + } + + // Initialize commands + rootCmd.AddCommand(server_cmds.ClientCmd) + + err = server_cmds.InitClientCommand(helpSystem) + if err != nil { + return nil, errors.Wrap(err, "could not initialize client command") + } rootCmd.AddCommand(runCommandCmd)