From 6e757573b9e94d897e00f16b8bc6f140019c1282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Kadir=20Ak=C4=B1n?= Date: Tue, 25 Mar 2025 22:15:04 +0300 Subject: [PATCH 1/3] idiomatic --- .github/pull_request_template.md | 29 + .gitmessage | 27 + .golangci.yml | 90 ++ cmd/mcptools/main.go | 935 ++++++++++-------- cmd/mcptools/main_test.go | 104 +- pkg/client/client.go | 72 +- .../formatter.go => jsonutils/formatting.go} | 199 ++-- .../formatting_test.go} | 51 +- pkg/transport/http.go | 100 +- pkg/transport/stdio.go | 137 ++- pkg/transport/transport.go | 45 +- 11 files changed, 1073 insertions(+), 716 deletions(-) create mode 100644 .github/pull_request_template.md create mode 100644 .gitmessage create mode 100644 .golangci.yml rename pkg/{formatter/formatter.go => jsonutils/formatting.go} (57%) rename pkg/{formatter/formatter_test.go => jsonutils/formatting_test.go} (60%) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..9d51559 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,29 @@ +# Description + +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. + +Fixes # (issue) + +## Type of change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## How Has This Been Tested? + +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. + +## Checklist: + +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules \ No newline at end of file diff --git a/.gitmessage b/.gitmessage new file mode 100644 index 0000000..98e35ea --- /dev/null +++ b/.gitmessage @@ -0,0 +1,27 @@ +# : +# |<---- Using a maximum of 50 characters ---->| + +# Explain why this change is being made +# |<---- Try to limit each line to a maximum of 72 characters ---->| + +# Provide links to any relevant tickets, issues, or other resources +# Example: Resolves: #123 +# See: #456, #789 + +# --- COMMIT END --- +# Type can be +# feat (new feature) +# fix (bug fix) +# refactor (refactoring code) +# style (formatting, missing semicolons, etc; no code change) +# docs (changes to documentation) +# test (adding or refactoring tests; no production code change) +# chore (updating grunt tasks etc; no production code change) +# -------------------- +# Remember to +# Use the imperative mood in the subject line +# Do not end the subject line with a period +# Separate subject from body with a blank line +# Use the body to explain what and why vs. how +# Can use multiple lines with "-" for bullet points in body +# -------------------- \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..40630a4 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,90 @@ +run: + timeout: 5m + modules-download-mode: readonly + allow-parallel-runners: true + go: '1.24' + +output: + format: colored-line-number + sort-results: true + +linters-settings: + revive: + rules: + - name: exported + severity: warning + disabled: false + arguments: + - checkPrivateReceivers + - sayRepetitiveInsteadOfStutters + - name: unexported-return + severity: warning + disabled: false + - name: unused-parameter + severity: warning + disabled: false + govet: + check-shadowing: true + gocyclo: + min-complexity: 15 + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 3 + misspell: + locale: US + goimports: + local-prefixes: github.com/f/mcptools + gofumpt: + extra-rules: true + nolintlint: + allow-unused: false + require-explanation: true + require-specific: true + +linters: + disable-all: true + enable: + - bodyclose + - dogsled + - dupl + - errcheck + - exportloopref + - goconst + - gocritic + - gocyclo + - gofmt + - gofumpt + - goimports + - gosec + - gosimple + - govet + - ineffassign + - misspell + - nakedret + - noctx + - nolintlint + - revive + - staticcheck + - stylecheck + - typecheck + - unconvert + - unused + +issues: + exclude-rules: + - path: _test\.go + linters: + - dupl + - gosec + - gomnd + - path: pkg/jsonutils + linters: + - dupl + - path: cmd/mcptools/main.go + linters: + - gocyclo + max-issues-per-linter: 0 + max-same-issues: 0 + fix: false \ No newline at end of file diff --git a/cmd/mcptools/main.go b/cmd/mcptools/main.go index 552a005..ee8fb67 100644 --- a/cmd/mcptools/main.go +++ b/cmd/mcptools/main.go @@ -8,29 +8,70 @@ import ( "strings" "github.com/f/mcptools/pkg/client" - "github.com/f/mcptools/pkg/formatter" + "github.com/f/mcptools/pkg/jsonutils" "github.com/peterh/liner" "github.com/spf13/cobra" ) +// Version information set during build var ( - // Version is set during build - Version = "dev" - // BuildTime is set during build + Version = "dev" BuildTime = "unknown" ) +// Flag constants +const ( + flagFormat = "--format" + flagFormatShort = "-f" + flagHTTP = "--http" + flagHTTPShort = "-H" + flagServer = "--server" + flagServerShort = "-s" + flagParams = "--params" + flagParamsShort = "-p" + flagHelp = "--help" + flagHelpShort = "-h" + entityTypeTool = "tool" + entityTypePrompt = "prompt" + entityTypeRes = "resource" +) + +// Global flags var ( - serverURL string - format string - httpMode bool + serverURL string + formatOption string + httpMode bool paramsString string ) +// Common errors +var ( + errCommandRequired = fmt.Errorf("command to execute is required when using stdio transport") +) + func main() { cobra.EnableCommandSorting = false - - var rootCmd = &cobra.Command{ + + rootCmd := newRootCmd() + rootCmd.AddCommand( + newVersionCmd(), + newToolsCmd(), + newResourcesCmd(), + newPromptsCmd(), + newCallCmd(), + newGetPromptCmd(), + newReadResourceCmd(), + newShellCmd(), + ) + + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} + +// newRootCmd creates the root command for the MCP CLI +func newRootCmd() *cobra.Command { + cmd := &cobra.Command{ Use: "mcp", Short: "MCP is a command line interface for interacting with MCP servers", Long: `MCP is a command line interface for interacting with Model Context Protocol (MCP) servers. @@ -38,447 +79,498 @@ It allows you to discover and call tools, list resources, and interact with MCP- } // Global flags - rootCmd.PersistentFlags().StringVarP(&serverURL, "server", "s", "http://localhost:8080", "MCP server URL (when using HTTP transport)") - rootCmd.PersistentFlags().StringVarP(&format, "format", "f", "table", "Output format (table, json, pretty)") - rootCmd.PersistentFlags().BoolVarP(&httpMode, "http", "H", false, "Use HTTP transport instead of stdio") - rootCmd.PersistentFlags().StringVarP(¶msString, "params", "p", "{}", "JSON string of parameters to pass to the tool (for call command)") + cmd.PersistentFlags().StringVarP(&serverURL, "server", "s", "http://localhost:8080", "MCP server URL (when using HTTP transport)") + cmd.PersistentFlags().StringVarP(&formatOption, "format", "f", "table", "Output format (table, json, pretty)") + cmd.PersistentFlags().BoolVarP(&httpMode, "http", "H", false, "Use HTTP transport instead of stdio") + cmd.PersistentFlags().StringVarP(¶msString, "params", "p", "{}", "JSON string of parameters to pass to the tool (for call command)") + + return cmd +} - // Version command - var versionCmd = &cobra.Command{ +// newVersionCmd creates the version command +func newVersionCmd() *cobra.Command { + return &cobra.Command{ Use: "version", Short: "Print the version information", - Run: func(cmd *cobra.Command, args []string) { + Run: func(_ *cobra.Command, _ []string) { fmt.Printf("MCP version %s (built at %s)\n", Version, BuildTime) }, } +} + +// createClient creates a client based on the global flags and arguments +func createClient(args []string) (*client.Client, error) { + if !httpMode && len(args) == 0 { + return nil, errCommandRequired + } - // List tools command - var toolsCmd = &cobra.Command{ - Use: "tools [command args...]", - Short: "List available tools on the MCP server", - DisableFlagParsing: true, // Important: Don't parse flags for this command - SilenceUsage: true, - Run: func(cmd *cobra.Command, args []string) { - // Special handling for --help flag - if len(args) == 1 && (args[0] == "--help" || args[0] == "-h") { - cmd.Help() + if httpMode { + return client.New(serverURL), nil + } + + return client.NewStdio(args), nil +} + +// processFlags extracts global flags from the given arguments +// Returns the remaining non-flag arguments +func processFlags(args []string) []string { + parsedArgs := []string{} + + i := 0 + for i < len(args) { + switch { + case (args[i] == flagFormat || args[i] == flagFormatShort) && i+1 < len(args): + formatOption = args[i+1] + i += 2 + case args[i] == flagHTTP || args[i] == flagHTTPShort: + httpMode = true + i++ + case (args[i] == flagServer || args[i] == flagServerShort) && i+1 < len(args): + serverURL = args[i+1] + i += 2 + default: + parsedArgs = append(parsedArgs, args[i]) + i++ + } + } + + return parsedArgs +} + +// formatAndPrintResponse formats and prints the response from an MCP method +func formatAndPrintResponse(resp map[string]any, err error) error { + if err != nil { + return fmt.Errorf("error: %w", err) + } + + output, err := jsonutils.Format(resp, formatOption) + if err != nil { + return fmt.Errorf("error formatting output: %w", err) + } + + fmt.Println(output) + return nil +} + +// newToolsCmd creates the tools command +func newToolsCmd() *cobra.Command { + return &cobra.Command{ + Use: "tools [command args...]", + Short: "List available tools on the MCP server", + DisableFlagParsing: true, + SilenceUsage: true, + Run: func(thisCmd *cobra.Command, args []string) { + if len(args) == 1 && (args[0] == flagHelp || args[0] == flagHelpShort) { + _ = thisCmd.Help() return } - - // For other flags like --format, --http, etc, we need to handle them manually - // since DisableFlagParsing is true - cmdArgs := args - parsedArgs := []string{} - - // Process global flags and remove them from args - i := 0 - for i < len(cmdArgs) { - if cmdArgs[i] == "--format" || cmdArgs[i] == "-f" { - if i+1 < len(cmdArgs) { - format = cmdArgs[i+1] - i += 2 - continue - } - } else if cmdArgs[i] == "--http" || cmdArgs[i] == "-H" { - httpMode = true - i++ - continue - } else if cmdArgs[i] == "--server" || cmdArgs[i] == "-s" { - if i+1 < len(cmdArgs) { - serverURL = cmdArgs[i+1] - i += 2 - continue - } - } - - parsedArgs = append(parsedArgs, cmdArgs[i]) - i++ + + parsedArgs := processFlags(args) + + mcpClient, err := createClient(parsedArgs) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + fmt.Fprintf(os.Stderr, "Example: mcp tools npx -y @modelcontextprotocol/server-filesystem ~/Code\n") + os.Exit(1) } - - // Now parsedArgs contains only the command to execute - if !httpMode && len(parsedArgs) == 0 { - fmt.Fprintln(os.Stderr, "Error: command to execute is required when using stdio transport") - fmt.Fprintln(os.Stderr, "Example: mcp tools npx -y @modelcontextprotocol/server-filesystem ~/Code") + + resp, listErr := mcpClient.ListTools() + if formatErr := formatAndPrintResponse(resp, listErr); formatErr != nil { + fmt.Fprintf(os.Stderr, "%v\n", formatErr) os.Exit(1) } + }, + } +} - var mcpClient *client.Client - if httpMode { - mcpClient = client.New(serverURL) - } else { - mcpClient = client.NewStdio(parsedArgs) +// newResourcesCmd creates the resources command +func newResourcesCmd() *cobra.Command { + return &cobra.Command{ + Use: "resources [command args...]", + Short: "List available resources on the MCP server", + DisableFlagParsing: true, + SilenceUsage: true, + Run: func(thisCmd *cobra.Command, args []string) { + if len(args) == 1 && (args[0] == flagHelp || args[0] == flagHelpShort) { + _ = thisCmd.Help() + return } - resp, err := mcpClient.ListTools() + parsedArgs := processFlags(args) + + mcpClient, err := createClient(parsedArgs) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) + fmt.Fprintf(os.Stderr, "Example: mcp resources npx -y @modelcontextprotocol/server-filesystem ~/Code\n") + os.Exit(1) + } + + resp, listErr := mcpClient.ListResources() + if formatErr := formatAndPrintResponse(resp, listErr); formatErr != nil { + fmt.Fprintf(os.Stderr, "%v\n", formatErr) os.Exit(1) } + }, + } +} + +// newPromptsCmd creates the prompts command +func newPromptsCmd() *cobra.Command { + return &cobra.Command{ + Use: "prompts [command args...]", + Short: "List available prompts on the MCP server", + DisableFlagParsing: true, + SilenceUsage: true, + Run: func(thisCmd *cobra.Command, args []string) { + if len(args) == 1 && (args[0] == flagHelp || args[0] == flagHelpShort) { + _ = thisCmd.Help() + return + } + + parsedArgs := processFlags(args) - output, err := formatter.Format(resp, format) + mcpClient, err := createClient(parsedArgs) if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + fmt.Fprintf(os.Stderr, "Example: mcp prompts npx -y @modelcontextprotocol/server-filesystem ~/Code\n") os.Exit(1) } - fmt.Println(output) + resp, listErr := mcpClient.ListPrompts() + if formatErr := formatAndPrintResponse(resp, listErr); formatErr != nil { + fmt.Fprintf(os.Stderr, "%v\n", formatErr) + os.Exit(1) + } }, } +} - // List resources command - var resourcesCmd = &cobra.Command{ - Use: "resources [command args...]", - Short: "List available resources on the MCP server", - DisableFlagParsing: true, // Important: Don't parse flags for this command - SilenceUsage: true, - Run: func(cmd *cobra.Command, args []string) { - // Special handling for --help flag - if len(args) == 1 && (args[0] == "--help" || args[0] == "-h") { - cmd.Help() +// newCallCmd creates the call command +func newCallCmd() *cobra.Command { + return &cobra.Command{ + Use: "call entity [command args...]", + Short: "Call a tool, resource, or prompt on the MCP server", + DisableFlagParsing: true, + SilenceUsage: true, + Run: func(thisCmd *cobra.Command, args []string) { + if len(args) == 1 && (args[0] == flagHelp || args[0] == flagHelpShort) { + _ = thisCmd.Help() return } - - // For other flags like --format, --http, etc, we need to handle them manually - // since DisableFlagParsing is true + + if len(args) == 0 { + fmt.Fprintln(os.Stderr, "Error: entity name is required") + fmt.Fprintln(os.Stderr, "Example: mcp call read_file npx -y @modelcontextprotocol/server-filesystem ~/Code") + os.Exit(1) + } + cmdArgs := args parsedArgs := []string{} - - // Process global flags and remove them from args + entityName := "" + i := 0 + entityExtracted := false + for i < len(cmdArgs) { - if cmdArgs[i] == "--format" || cmdArgs[i] == "-f" { - if i+1 < len(cmdArgs) { - format = cmdArgs[i+1] - i += 2 - continue - } - } else if cmdArgs[i] == "--http" || cmdArgs[i] == "-H" { + switch { + case (cmdArgs[i] == flagFormat || cmdArgs[i] == flagFormatShort) && i+1 < len(cmdArgs): + formatOption = cmdArgs[i+1] + i += 2 + case cmdArgs[i] == flagHTTP || cmdArgs[i] == flagHTTPShort: httpMode = true i++ - continue - } else if cmdArgs[i] == "--server" || cmdArgs[i] == "-s" { - if i+1 < len(cmdArgs) { - serverURL = cmdArgs[i+1] - i += 2 - continue - } + case (cmdArgs[i] == flagServer || cmdArgs[i] == flagServerShort) && i+1 < len(cmdArgs): + serverURL = cmdArgs[i+1] + i += 2 + case (cmdArgs[i] == flagParams || cmdArgs[i] == flagParamsShort) && i+1 < len(cmdArgs): + paramsString = cmdArgs[i+1] + i += 2 + case !entityExtracted: + entityName = cmdArgs[i] + entityExtracted = true + i++ + default: + parsedArgs = append(parsedArgs, cmdArgs[i]) + i++ } - - parsedArgs = append(parsedArgs, cmdArgs[i]) - i++ } - + + if entityName == "" { + fmt.Fprintln(os.Stderr, "Error: entity name is required") + fmt.Fprintln(os.Stderr, "Example: mcp call read_file npx -y @modelcontextprotocol/server-filesystem ~/Code") + os.Exit(1) + } + + entityType := entityTypeTool + + parts := strings.SplitN(entityName, ":", 2) + if len(parts) == 2 { + entityType = parts[0] + entityName = parts[1] + } + if !httpMode && len(parsedArgs) == 0 { fmt.Fprintln(os.Stderr, "Error: command to execute is required when using stdio transport") - fmt.Fprintln(os.Stderr, "Example: mcp resources npx -y @modelcontextprotocol/server-filesystem ~/Code") + fmt.Fprintln(os.Stderr, "Example: mcp call read_file npx -y @modelcontextprotocol/server-filesystem ~/Code") os.Exit(1) } - var mcpClient *client.Client - if httpMode { - mcpClient = client.New(serverURL) - } else { - mcpClient = client.NewStdio(parsedArgs) + var params map[string]any + if paramsString != "" { + if jsonErr := json.Unmarshal([]byte(paramsString), ¶ms); jsonErr != nil { + fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", jsonErr) + os.Exit(1) + } } - resp, err := mcpClient.ListResources() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + mcpClient, clientErr := createClient(parsedArgs) + if clientErr != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", clientErr) os.Exit(1) } - output, err := formatter.Format(resp, format) - if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + var resp map[string]any + var execErr error + + switch entityType { + case entityTypeTool: + resp, execErr = mcpClient.CallTool(entityName, params) + case entityTypeRes: + resp, execErr = mcpClient.ReadResource(entityName) + case entityTypePrompt: + resp, execErr = mcpClient.GetPrompt(entityName) + default: + fmt.Fprintf(os.Stderr, "Error: unsupported entity type: %s\n", entityType) os.Exit(1) } - fmt.Println(output) + if formatErr := formatAndPrintResponse(resp, execErr); formatErr != nil { + fmt.Fprintf(os.Stderr, "%v\n", formatErr) + os.Exit(1) + } }, } +} - // List prompts command - var promptsCmd = &cobra.Command{ - Use: "prompts [command args...]", - Short: "List available prompts on the MCP server", - DisableFlagParsing: true, // Important: Don't parse flags for this command - SilenceUsage: true, - Run: func(cmd *cobra.Command, args []string) { - // Special handling for --help flag - if len(args) == 1 && (args[0] == "--help" || args[0] == "-h") { - cmd.Help() +// newGetPromptCmd creates the get prompt command +func newGetPromptCmd() *cobra.Command { + return &cobra.Command{ + Use: "get-prompt prompt [command args...]", + Short: "Get a prompt on the MCP server", + DisableFlagParsing: true, + SilenceUsage: true, + Run: func(thisCmd *cobra.Command, args []string) { + if len(args) == 1 && (args[0] == flagHelp || args[0] == flagHelpShort) { + _ = thisCmd.Help() return } - - // For other flags like --format, --http, etc, we need to handle them manually - // since DisableFlagParsing is true + + if len(args) == 0 { + fmt.Fprintln(os.Stderr, "Error: prompt name is required") + fmt.Fprintln(os.Stderr, "Example: mcp get-prompt read_file npx -y @modelcontextprotocol/server-filesystem ~/Code") + os.Exit(1) + } + cmdArgs := args parsedArgs := []string{} - - // Process global flags and remove them from args + promptName := "" + i := 0 + promptExtracted := false + for i < len(cmdArgs) { - if cmdArgs[i] == "--format" || cmdArgs[i] == "-f" { - if i+1 < len(cmdArgs) { - format = cmdArgs[i+1] - i += 2 - continue - } - } else if cmdArgs[i] == "--http" || cmdArgs[i] == "-H" { + switch { + case (cmdArgs[i] == flagFormat || cmdArgs[i] == flagFormatShort) && i+1 < len(cmdArgs): + formatOption = cmdArgs[i+1] + i += 2 + case cmdArgs[i] == flagHTTP || cmdArgs[i] == flagHTTPShort: httpMode = true i++ - continue - } else if cmdArgs[i] == "--server" || cmdArgs[i] == "-s" { - if i+1 < len(cmdArgs) { - serverURL = cmdArgs[i+1] - i += 2 - continue - } + case (cmdArgs[i] == flagServer || cmdArgs[i] == flagServerShort) && i+1 < len(cmdArgs): + serverURL = cmdArgs[i+1] + i += 2 + case (cmdArgs[i] == flagParams || cmdArgs[i] == flagParamsShort) && i+1 < len(cmdArgs): + paramsString = cmdArgs[i+1] + i += 2 + case !promptExtracted: + promptName = cmdArgs[i] + promptExtracted = true + i++ + default: + parsedArgs = append(parsedArgs, cmdArgs[i]) + i++ } - - parsedArgs = append(parsedArgs, cmdArgs[i]) - i++ } - - if !httpMode && len(parsedArgs) == 0 { - fmt.Fprintln(os.Stderr, "Error: command to execute is required when using stdio transport") - fmt.Fprintln(os.Stderr, "Example: mcp prompts npx -y @modelcontextprotocol/server-filesystem ~/Code") + + if promptName == "" { + fmt.Fprintln(os.Stderr, "Error: prompt name is required") + fmt.Fprintln(os.Stderr, "Example: mcp get-prompt read_file npx -y @modelcontextprotocol/server-filesystem ~/Code") os.Exit(1) } - var mcpClient *client.Client - if httpMode { - mcpClient = client.New(serverURL) - } else { - mcpClient = client.NewStdio(parsedArgs) + var params map[string]any + if paramsString != "" { + if jsonErr := json.Unmarshal([]byte(paramsString), ¶ms); jsonErr != nil { + fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", jsonErr) + os.Exit(1) + } } - resp, err := mcpClient.ListPrompts() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + mcpClient, clientErr := createClient(parsedArgs) + if clientErr != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", clientErr) os.Exit(1) } - output, err := formatter.Format(resp, format) - if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + resp, execErr := mcpClient.GetPrompt(promptName) + if formatErr := formatAndPrintResponse(resp, execErr); formatErr != nil { + fmt.Fprintf(os.Stderr, "%v\n", formatErr) os.Exit(1) } - - fmt.Println(output) }, } +} - // Call command - var callCmd = &cobra.Command{ - Use: "call entity [command args...]", - Short: "Call a tool, resource, or prompt on the MCP server", - DisableFlagParsing: true, // Important: Don't parse flags for this command - SilenceUsage: true, - Run: func(cmd *cobra.Command, args []string) { - // Special handling for --help flag - if len(args) == 1 && (args[0] == "--help" || args[0] == "-h") { - cmd.Help() +// newReadResourceCmd creates the read resource command +func newReadResourceCmd() *cobra.Command { + return &cobra.Command{ + Use: "read-resource resource [command args...]", + Short: "Read a resource on the MCP server", + DisableFlagParsing: true, + SilenceUsage: true, + Run: func(thisCmd *cobra.Command, args []string) { + if len(args) == 1 && (args[0] == flagHelp || args[0] == flagHelpShort) { + _ = thisCmd.Help() return } - + if len(args) == 0 { - fmt.Fprintln(os.Stderr, "Error: entity name is required") - fmt.Fprintln(os.Stderr, "Example: mcp call read_file npx -y @modelcontextprotocol/server-filesystem ~/Code") + fmt.Fprintln(os.Stderr, "Error: resource name is required") + fmt.Fprintln(os.Stderr, "Example: mcp read-resource npx -y @modelcontextprotocol/server-filesystem ~/Code") os.Exit(1) } - - // Process our flags manually since DisableFlagParsing is true + cmdArgs := args parsedArgs := []string{} - entityName := "" - - // Process global flags and remove them from args + resourceName := "" + i := 0 - entityExtracted := false - + resourceExtracted := false + for i < len(cmdArgs) { - if cmdArgs[i] == "--format" || cmdArgs[i] == "-f" { - if i+1 < len(cmdArgs) { - format = cmdArgs[i+1] - i += 2 - continue - } - } else if cmdArgs[i] == "--http" || cmdArgs[i] == "-H" { + switch { + case (cmdArgs[i] == flagFormat || cmdArgs[i] == flagFormatShort) && i+1 < len(cmdArgs): + formatOption = cmdArgs[i+1] + i += 2 + case cmdArgs[i] == flagHTTP || cmdArgs[i] == flagHTTPShort: httpMode = true i++ - continue - } else if cmdArgs[i] == "--server" || cmdArgs[i] == "-s" { - if i+1 < len(cmdArgs) { - serverURL = cmdArgs[i+1] - i += 2 - continue - } - } else if cmdArgs[i] == "--params" || cmdArgs[i] == "-p" { - if i+1 < len(cmdArgs) { - paramsString = cmdArgs[i+1] - i += 2 - continue - } - } else if !entityExtracted { - // The first non-flag argument is the entity name - entityName = cmdArgs[i] - entityExtracted = true + case (cmdArgs[i] == flagServer || cmdArgs[i] == flagServerShort) && i+1 < len(cmdArgs): + serverURL = cmdArgs[i+1] + i += 2 + case !resourceExtracted: + resourceName = cmdArgs[i] + resourceExtracted = true + i++ + default: + parsedArgs = append(parsedArgs, cmdArgs[i]) i++ - continue } - - // Any other arguments get passed to the command - parsedArgs = append(parsedArgs, cmdArgs[i]) - i++ - } - - if entityName == "" { - fmt.Fprintln(os.Stderr, "Error: entity name is required") - fmt.Fprintln(os.Stderr, "Example: mcp call read_file npx -y @modelcontextprotocol/server-filesystem ~/Code") - os.Exit(1) } - entityType := "tool" // Default to tool - - // Check if entityName contains a type prefix - parts := strings.SplitN(entityName, ":", 2) - if len(parts) == 2 { - entityType = parts[0] - entityName = parts[1] - } - - if !httpMode && len(parsedArgs) == 0 { - fmt.Fprintln(os.Stderr, "Error: command to execute is required when using stdio transport") - fmt.Fprintln(os.Stderr, "Example: mcp call read_file npx -y @modelcontextprotocol/server-filesystem ~/Code") + if resourceName == "" { + fmt.Fprintln(os.Stderr, "Error: resource name is required") + fmt.Fprintln(os.Stderr, "Example: mcp read-resource npx -y @modelcontextprotocol/server-filesystem ~/Code") os.Exit(1) } - // Parse parameters - var params map[string]interface{} - if paramsString != "" { - if err := json.Unmarshal([]byte(paramsString), ¶ms); err != nil { - fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", err) + var params map[string]any + if len(parsedArgs) > 0 { + if jsonErr := json.Unmarshal([]byte(strings.Join(parsedArgs, " ")), ¶ms); jsonErr != nil { + fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", jsonErr) os.Exit(1) } } - - var mcpClient *client.Client - - if httpMode { - mcpClient = client.New(serverURL) - } else { - mcpClient = client.NewStdio(parsedArgs) - } - - var resp map[string]interface{} - var err error - - switch entityType { - case "tool": - resp, err = mcpClient.CallTool(entityName, params) - case "resource": - resp, err = mcpClient.ReadResource(entityName) - case "prompt": - resp, err = mcpClient.GetPrompt(entityName) - default: - fmt.Fprintf(os.Stderr, "Error: unsupported entity type: %s\n", entityType) - os.Exit(1) - } - - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + + mcpClient, clientErr := createClient(parsedArgs) + if clientErr != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", clientErr) os.Exit(1) } - output, err := formatter.Format(resp, format) - if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + resp, execErr := mcpClient.ReadResource(resourceName) + if formatErr := formatAndPrintResponse(resp, execErr); formatErr != nil { + fmt.Fprintf(os.Stderr, "%v\n", formatErr) os.Exit(1) } - - fmt.Println(output) }, } +} - // Shell command - var shellCmd = &cobra.Command{ +// newShellCmd creates the shell command +func newShellCmd() *cobra.Command { + return &cobra.Command{ Use: "shell [command args...]", Short: "Start an interactive shell for MCP commands", DisableFlagParsing: true, SilenceUsage: true, - Run: func(cmd *cobra.Command, args []string) { - // Special handling for --help flag - if len(args) == 1 && (args[0] == "--help" || args[0] == "-h") { - cmd.Help() + Run: func(thisCmd *cobra.Command, args []string) { + if len(args) == 1 && (args[0] == flagHelp || args[0] == flagHelpShort) { + _ = thisCmd.Help() return } - - // For other flags like --format, we need to handle them manually + cmdArgs := args parsedArgs := []string{} - - // Process global flags and remove them from args + i := 0 for i < len(cmdArgs) { - if cmdArgs[i] == "--format" || cmdArgs[i] == "-f" { - if i+1 < len(cmdArgs) { - format = cmdArgs[i+1] - i += 2 - continue - } - } else if cmdArgs[i] == "--server" || cmdArgs[i] == "-s" { - if i+1 < len(cmdArgs) { - serverURL = cmdArgs[i+1] - i += 2 - continue - } + switch { + case (cmdArgs[i] == flagFormat || cmdArgs[i] == flagFormatShort) && i+1 < len(cmdArgs): + formatOption = cmdArgs[i+1] + i += 2 + case (cmdArgs[i] == flagServer || cmdArgs[i] == flagServerShort) && i+1 < len(cmdArgs): + serverURL = cmdArgs[i+1] + i += 2 + default: + parsedArgs = append(parsedArgs, cmdArgs[i]) + i++ } - - parsedArgs = append(parsedArgs, cmdArgs[i]) - i++ } - + if len(parsedArgs) == 0 { fmt.Fprintln(os.Stderr, "Error: command to execute is required when using the shell") fmt.Fprintln(os.Stderr, "Example: mcp shell npx -y @modelcontextprotocol/server-filesystem ~/Code") os.Exit(1) } - // Create the stdio client that will be reused for all commands mcpClient := client.NewStdio(parsedArgs) - - // Try to connect and get server info - _, err := mcpClient.ListTools() - if err != nil { - fmt.Fprintf(os.Stderr, "Error connecting to MCP server: %v\n", err) + + _, listErr := mcpClient.ListTools() + if listErr != nil { + fmt.Fprintf(os.Stderr, "Error connecting to MCP server: %v\n", listErr) os.Exit(1) } - - // Start the interactive shell + fmt.Println("mcp > connected to MCP server over stdio") fmt.Println("mcp > Type '/h' for help or '/q' to quit") - - // Create a new line state with history capability + line := liner.NewLiner() defer line.Close() - - // Load command history from ~/.mcp_history if it exists + historyFile := filepath.Join(os.Getenv("HOME"), ".mcp_history") if f, err := os.Open(historyFile); err == nil { - line.ReadHistory(f) + _, _ = line.ReadHistory(f) f.Close() } - - // Save history on exit + defer func() { if f, err := os.Create(historyFile); err == nil { - line.WriteHistory(f) + _, _ = line.WriteHistory(f) f.Close() } }() - - // Set completion handler for commands + line.SetCompleter(func(line string) (c []string) { commands := []string{"tools", "resources", "prompts", "call", "format", "help", "exit", "/h", "/q", "/help", "/quit"} for _, cmd := range commands { @@ -488,7 +580,7 @@ It allows you to discover and call tools, list resources, and interact with MCP- } return }) - + for { input, err := line.Prompt("mcp > ") if err != nil { @@ -499,185 +591,174 @@ It allows you to discover and call tools, list resources, and interact with MCP- fmt.Fprintf(os.Stderr, "Error reading input: %v\n", err) break } - + if input == "" { continue } - - // Add the command to history + line.AppendHistory(input) - - // Handle special commands + if input == "/q" || input == "/quit" || input == "exit" { fmt.Println("Exiting MCP shell") break } - + if input == "/h" || input == "/help" || input == "help" { printShellHelp() continue } - - // Parse the input as a command + parts := strings.Fields(input) if len(parts) == 0 { continue } - + command := parts[0] commandArgs := parts[1:] - - // Process the command + switch command { case "tools": - resp, err := mcpClient.ListTools() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + resp, listErr := mcpClient.ListTools() + if listErr != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", listErr) continue } - - output, err := formatter.Format(resp, format) - if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + + output, formatErr := jsonutils.Format(resp, formatOption) + if formatErr != nil { + fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", formatErr) continue } - + fmt.Println(output) - + case "resources": - resp, err := mcpClient.ListResources() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + resp, listErr := mcpClient.ListResources() + if listErr != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", listErr) continue } - - output, err := formatter.Format(resp, format) - if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + + output, formatErr := jsonutils.Format(resp, formatOption) + if formatErr != nil { + fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", formatErr) continue } - + fmt.Println(output) - + case "prompts": - resp, err := mcpClient.ListPrompts() - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + resp, listErr := mcpClient.ListPrompts() + if listErr != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", listErr) continue } - - output, err := formatter.Format(resp, format) - if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + + output, formatErr := jsonutils.Format(resp, formatOption) + if formatErr != nil { + fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", formatErr) continue } - + fmt.Println(output) - + case "call": if len(commandArgs) < 1 { fmt.Println("Usage: call [--params '{...}']") continue } - + entityName := commandArgs[0] - entityType := "tool" // Default to tool - - // Check if entityName contains a type prefix + entityType := entityTypeTool + parts := strings.SplitN(entityName, ":", 2) if len(parts) == 2 { entityType = parts[0] entityName = parts[1] } - - // Parse parameters if provided - params := map[string]interface{}{} + + params := map[string]any{} for i := 1; i < len(commandArgs); i++ { - if commandArgs[i] == "--params" || commandArgs[i] == "-p" { + if commandArgs[i] == flagParams || commandArgs[i] == flagParamsShort { if i+1 < len(commandArgs) { - if err := json.Unmarshal([]byte(commandArgs[i+1]), ¶ms); err != nil { - fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", err) + if jsonErr := json.Unmarshal([]byte(commandArgs[i+1]), ¶ms); jsonErr != nil { + fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", jsonErr) continue } break } } } - - var resp map[string]interface{} - var err error - + + var resp map[string]any + var execErr error + switch entityType { - case "tool": - resp, err = mcpClient.CallTool(entityName, params) - case "resource": - resp, err = mcpClient.ReadResource(entityName) - case "prompt": - resp, err = mcpClient.GetPrompt(entityName) + case entityTypeTool: + resp, execErr = mcpClient.CallTool(entityName, params) + case entityTypeRes: + resp, execErr = mcpClient.ReadResource(entityName) + case entityTypePrompt: + resp, execErr = mcpClient.GetPrompt(entityName) default: fmt.Fprintf(os.Stderr, "Error: unsupported entity type: %s\n", entityType) continue } - - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + + if execErr != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", execErr) continue } - - output, err := formatter.Format(resp, format) - if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + + output, formatErr := jsonutils.Format(resp, formatOption) + if formatErr != nil { + fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", formatErr) continue } - + fmt.Println(output) - + case "format": if len(commandArgs) < 1 { - fmt.Printf("Current format: %s\n", format) + fmt.Printf("Current format: %s\n", formatOption) continue } - + newFormat := commandArgs[0] if newFormat == "json" || newFormat == "j" || - newFormat == "pretty" || newFormat == "p" || - newFormat == "table" || newFormat == "t" { - format = newFormat - fmt.Printf("Format set to: %s\n", format) + newFormat == "pretty" || newFormat == "p" || + newFormat == "table" || newFormat == "t" { + formatOption = newFormat + fmt.Printf("Format set to: %s\n", formatOption) } else { fmt.Println("Invalid format. Use: table, json, or pretty") } - + default: - // Try to interpret the command as a tool call entityName := command - entityType := "tool" // Default to tool - - // Check if entityName contains a type prefix + entityType := entityTypeTool + parts := strings.SplitN(entityName, ":", 2) if len(parts) == 2 { entityType = parts[0] entityName = parts[1] } - - // Parse parameters if provided - params := map[string]interface{}{} - - // Check if the first argument is a JSON object + + params := map[string]any{} + if len(commandArgs) > 0 { firstArg := commandArgs[0] if strings.HasPrefix(firstArg, "{") && strings.HasSuffix(firstArg, "}") { - if err := json.Unmarshal([]byte(firstArg), ¶ms); err != nil { - fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", err) + if jsonErr := json.Unmarshal([]byte(firstArg), ¶ms); jsonErr != nil { + fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", jsonErr) continue } } else { - // Process parameters with --params or -p flag for i := 0; i < len(commandArgs); i++ { - if commandArgs[i] == "--params" || commandArgs[i] == "-p" { + if commandArgs[i] == flagParams || commandArgs[i] == flagParamsShort { if i+1 < len(commandArgs) { - if err := json.Unmarshal([]byte(commandArgs[i+1]), ¶ms); err != nil { - fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", err) + if jsonErr := json.Unmarshal([]byte(commandArgs[i+1]), ¶ms); jsonErr != nil { + fmt.Fprintf(os.Stderr, "Error: invalid JSON for params: %v\n", jsonErr) continue } break @@ -686,54 +767,40 @@ It allows you to discover and call tools, list resources, and interact with MCP- } } } - - var resp map[string]interface{} - var err error - + + var resp map[string]any + var execErr error + switch entityType { - case "tool": - resp, err = mcpClient.CallTool(entityName, params) - case "resource": - resp, err = mcpClient.ReadResource(entityName) - case "prompt": - resp, err = mcpClient.GetPrompt(entityName) + case entityTypeTool: + resp, execErr = mcpClient.CallTool(entityName, params) + case entityTypeRes: + resp, execErr = mcpClient.ReadResource(entityName) + case entityTypePrompt: + resp, execErr = mcpClient.GetPrompt(entityName) default: fmt.Printf("Unknown command: %s\nType '/h' for help\n", command) continue } - - if err != nil { - fmt.Fprintf(os.Stderr, "Error: %v\n", err) + + if execErr != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", execErr) continue } - - output, err := formatter.Format(resp, format) - if err != nil { - fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", err) + + output, formatErr := jsonutils.Format(resp, formatOption) + if formatErr != nil { + fmt.Fprintf(os.Stderr, "Error formatting output: %v\n", formatErr) continue } - + fmt.Println(output) } } }, } - - // Add commands to root - rootCmd.AddCommand(versionCmd) - rootCmd.AddCommand(toolsCmd) - rootCmd.AddCommand(resourcesCmd) - rootCmd.AddCommand(promptsCmd) - rootCmd.AddCommand(callCmd) - rootCmd.AddCommand(shellCmd) - - if err := rootCmd.Execute(); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } } -// Helper function to print shell help func printShellHelp() { fmt.Println("MCP Shell Commands:") fmt.Println(" tools List available tools") @@ -748,4 +815,4 @@ func printShellHelp() { fmt.Println("Special Commands:") fmt.Println(" /h, /help Show this help") fmt.Println(" /q, /quit, exit Exit the shell") -} \ No newline at end of file +} diff --git a/cmd/mcptools/main_test.go b/cmd/mcptools/main_test.go index 9bb6d00..20004e0 100644 --- a/cmd/mcptools/main_test.go +++ b/cmd/mcptools/main_test.go @@ -28,7 +28,7 @@ func (t *MockTransport) Execute(method string, params interface{}) (map[string]i if resp, ok := t.Responses[method]; ok { return resp, nil } - + if method == "tools/list" { return map[string]interface{}{ "tools": []map[string]interface{}{ @@ -43,7 +43,7 @@ func (t *MockTransport) Execute(method string, params interface{}) (map[string]i }, }, nil } - + if method == "tools/call" { paramsMap := params.(map[string]interface{}) toolName := paramsMap["name"].(string) @@ -51,7 +51,7 @@ func (t *MockTransport) Execute(method string, params interface{}) (map[string]i "result": fmt.Sprintf("Called tool: %s", toolName), }, nil } - + if method == "resources/list" { return map[string]interface{}{ "resources": []map[string]interface{}{ @@ -62,7 +62,7 @@ func (t *MockTransport) Execute(method string, params interface{}) (map[string]i }, }, nil } - + if method == "resources/read" { paramsMap := params.(map[string]interface{}) uri := paramsMap["uri"].(string) @@ -70,7 +70,7 @@ func (t *MockTransport) Execute(method string, params interface{}) (map[string]i "content": fmt.Sprintf("Content of resource: %s", uri), }, nil } - + if method == "prompts/list" { return map[string]interface{}{ "prompts": []map[string]interface{}{ @@ -81,7 +81,7 @@ func (t *MockTransport) Execute(method string, params interface{}) (map[string]i }, }, nil } - + if method == "prompts/get" { paramsMap := params.(map[string]interface{}) promptName := paramsMap["name"].(string) @@ -89,7 +89,7 @@ func (t *MockTransport) Execute(method string, params interface{}) (map[string]i "content": fmt.Sprintf("Content of prompt: %s", promptName), }, nil } - + return map[string]interface{}{}, fmt.Errorf("unknown method: %s", method) } @@ -97,13 +97,13 @@ func (t *MockTransport) Execute(method string, params interface{}) (map[string]i func setupTestCommand() (*cobra.Command, *bytes.Buffer) { // Setup output buffer outBuf := &bytes.Buffer{} - + // Create root command rootCmd := &cobra.Command{ Use: "mcp", Short: "MCP CLI", } - + // Create shell command shellCmd := &cobra.Command{ Use: "shell", @@ -111,7 +111,7 @@ func setupTestCommand() (*cobra.Command, *bytes.Buffer) { Run: func(cmd *cobra.Command, args []string) { // Create mock transport mockTransport := NewMockTransport() - + // Create shell instance shell := &Shell{ Transport: mockTransport, @@ -119,14 +119,14 @@ func setupTestCommand() (*cobra.Command, *bytes.Buffer) { Reader: strings.NewReader("tools\ncall test_tool --params '{\"foo\":\"bar\"}'\ntest_tool {\"foo\":\"bar\"}\nresource:test_resource\nprompt:test_prompt\n/q\n"), Writer: outBuf, } - + // Run shell shell.Run() }, } - + rootCmd.AddCommand(shellCmd) - + // Return command and output buffer return rootCmd, outBuf } @@ -142,53 +142,53 @@ type Shell struct { // Run executes the shell with predefined inputs func (s *Shell) Run() { scanner := bufio.NewScanner(s.Reader) - + for scanner.Scan() { input := scanner.Text() - + if input == "/q" || input == "/quit" || input == "exit" { fmt.Fprintln(s.Writer, "Exiting MCP shell") break } - + parts := strings.Fields(input) if len(parts) == 0 { continue } - + command := parts[0] args := parts[1:] - + switch command { case "tools": resp, _ := s.Transport.Execute("tools/list", nil) fmt.Fprintln(s.Writer, "Tools:", resp) - + case "resources": resp, _ := s.Transport.Execute("resources/list", nil) fmt.Fprintln(s.Writer, "Resources:", resp) - + case "prompts": resp, _ := s.Transport.Execute("prompts/list", nil) fmt.Fprintln(s.Writer, "Prompts:", resp) - + case "call": if len(args) < 1 { fmt.Fprintln(s.Writer, "Usage: call [--params '{...}']") continue } - + entityName := args[0] entityType := "tool" - + parts := strings.SplitN(entityName, ":", 2) if len(parts) == 2 { entityType = parts[0] entityName = parts[1] } - + params := map[string]interface{}{} - + for i := 1; i < len(args); i++ { if args[i] == "--params" || args[i] == "-p" { if i+1 < len(args) { @@ -197,9 +197,9 @@ func (s *Shell) Run() { } } } - + var resp map[string]interface{} - + switch entityType { case "tool": resp, _ = s.Transport.Execute("tools/call", map[string]interface{}{ @@ -215,22 +215,22 @@ func (s *Shell) Run() { "name": entityName, }) } - + fmt.Fprintln(s.Writer, "Call result:", resp) - + default: // Try to interpret as a direct tool call entityName := command entityType := "tool" - + parts := strings.SplitN(entityName, ":", 2) if len(parts) == 2 { entityType = parts[0] entityName = parts[1] } - + params := map[string]interface{}{} - + if len(args) > 0 { firstArg := args[0] if strings.HasPrefix(firstArg, "{") && strings.HasSuffix(firstArg, "}") { @@ -246,9 +246,9 @@ func (s *Shell) Run() { } } } - + var resp map[string]interface{} - + switch entityType { case "tool": resp, _ = s.Transport.Execute("tools/call", map[string]interface{}{ @@ -292,15 +292,15 @@ func TestDirectToolCalling(t *testing.T) { expectedOutput: "Content of prompt: test_prompt", }, } - + // Create mock transport mockTransport := NewMockTransport() - + for _, tc := range testCases { t.Run(tc.input, func(t *testing.T) { // Setup output capture outBuf := &bytes.Buffer{} - + // Create shell with input shell := &Shell{ Transport: mockTransport, @@ -308,10 +308,10 @@ func TestDirectToolCalling(t *testing.T) { Reader: strings.NewReader(tc.input + "\n/q\n"), Writer: outBuf, } - + // Run shell shell.Run() - + // Check output if !strings.Contains(outBuf.String(), tc.expectedOutput) { t.Errorf("Expected output to contain %q, got: %s", tc.expectedOutput, outBuf.String()) @@ -324,7 +324,7 @@ func TestDirectToolCalling(t *testing.T) { func TestExecuteShell(t *testing.T) { // Create mock transport mockTransport := NewMockTransport() - + // Test inputs inputs := []string{ "tools", @@ -336,22 +336,22 @@ func TestExecuteShell(t *testing.T) { "prompt:test_prompt", "/q", } - + // Expected outputs for each input expectedOutputs := []string{ - "A test tool", // tools command - "A test resource", // resources command - "A test prompt", // prompts command - "Called tool: test_tool", // call command - "Called tool: test_tool", // direct tool call + "A test tool", // tools command + "A test resource", // resources command + "A test prompt", // prompts command + "Called tool: test_tool", // call command + "Called tool: test_tool", // direct tool call "Content of resource: test_resource", // direct resource read "Content of prompt: test_prompt", // direct prompt get - "Exiting MCP shell", // quit command + "Exiting MCP shell", // quit command } - + // Setup output capture outBuf := &bytes.Buffer{} - + // Create shell with all inputs shell := &Shell{ Transport: mockTransport, @@ -359,10 +359,10 @@ func TestExecuteShell(t *testing.T) { Reader: strings.NewReader(strings.Join(inputs, "\n") + "\n"), Writer: outBuf, } - + // Run shell shell.Run() - + // Check all expected outputs output := outBuf.String() for _, expected := range expectedOutputs { @@ -370,4 +370,4 @@ func TestExecuteShell(t *testing.T) { t.Errorf("Expected output to contain %q, but it doesn't.\nFull output: %s", expected, output) } } -} \ No newline at end of file +} diff --git a/pkg/client/client.go b/pkg/client/client.go index c6bb3f9..9178d48 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -6,78 +6,84 @@ import ( "github.com/f/mcptools/pkg/transport" ) -// Client represents an MCP client +// Client provides an interface to interact with MCP servers. +// It abstracts away the transport mechanism so callers don't need +// to worry about the details of HTTP, stdio, etc. type Client struct { - Transport transport.Transport + transport transport.Transport } -// New creates a new MCP client with HTTP transport +// New creates a new MCP client that communicates with the server +// at the given baseURL via HTTP. func New(baseURL string) *Client { return &Client{ - Transport: transport.NewHTTP(baseURL), + transport: transport.NewHTTP(baseURL), } } -// NewWithTransport creates a new MCP client with the given transport +// NewWithTransport creates a new MCP client using the provided transport. +// This allows callers to provide a custom transport implementation. func NewWithTransport(t transport.Transport) *Client { return &Client{ - Transport: t, + transport: t, } } -// NewStdio creates a new MCP client with Stdio transport +// NewStdio creates a new MCP client that communicates with a command +// via stdin/stdout using JSON-RPC. func NewStdio(command []string) *Client { return &Client{ - Transport: transport.NewStdio(command), + transport: transport.NewStdio(command), } } -// ListTools lists all available tools on the MCP server -func (c *Client) ListTools() (map[string]interface{}, error) { - return c.Transport.Execute("tools/list", nil) +// ListTools retrieves the list of available tools from the MCP server. +func (c *Client) ListTools() (map[string]any, error) { + return c.transport.Execute("tools/list", nil) } -// ListResources lists all available resources on the MCP server -func (c *Client) ListResources() (map[string]interface{}, error) { - return c.Transport.Execute("resources/list", nil) +// ListResources retrieves the list of available resources from the MCP server. +func (c *Client) ListResources() (map[string]any, error) { + return c.transport.Execute("resources/list", nil) } -// ListPrompts lists all available prompts on the MCP server -func (c *Client) ListPrompts() (map[string]interface{}, error) { - return c.Transport.Execute("prompts/list", nil) +// ListPrompts retrieves the list of available prompts from the MCP server. +func (c *Client) ListPrompts() (map[string]any, error) { + return c.transport.Execute("prompts/list", nil) } -// CallTool calls a specific tool on the MCP server -func (c *Client) CallTool(toolName string, args map[string]interface{}) (map[string]interface{}, error) { - params := map[string]interface{}{ +// CallTool calls a specific tool on the MCP server with the given arguments. +func (c *Client) CallTool(toolName string, args map[string]any) (map[string]any, error) { + params := map[string]any{ "name": toolName, "arguments": args, } - return c.Transport.Execute("tools/call", params) + return c.transport.Execute("tools/call", params) } -// GetPrompt gets a specific prompt from the MCP server -func (c *Client) GetPrompt(promptName string) (map[string]interface{}, error) { - params := map[string]interface{}{ +// GetPrompt retrieves a specific prompt from the MCP server. +func (c *Client) GetPrompt(promptName string) (map[string]any, error) { + params := map[string]any{ "name": promptName, } - return c.Transport.Execute("prompts/get", params) + return c.transport.Execute("prompts/get", params) } -// ReadResource reads a specific resource from the MCP server -func (c *Client) ReadResource(uri string) (map[string]interface{}, error) { - params := map[string]interface{}{ +// ReadResource reads the content of a specific resource from the MCP server. +func (c *Client) ReadResource(uri string) (map[string]any, error) { + params := map[string]any{ "uri": uri, } - return c.Transport.Execute("resources/read", params) + return c.transport.Execute("resources/read", params) } -// ParseCommandString parses a command string into a slice of command arguments +// ParseCommandString splits a command string into separate arguments, +// respecting spaces as argument separators. +// Note: This is a simple implementation that doesn't handle quotes or escapes. func ParseCommandString(cmdStr string) []string { if cmdStr == "" { return nil } - - // Simple split by space - in a real implementation, you'd handle quotes and escapes better + return strings.Fields(cmdStr) -} \ No newline at end of file +} diff --git a/pkg/formatter/formatter.go b/pkg/jsonutils/formatting.go similarity index 57% rename from pkg/formatter/formatter.go rename to pkg/jsonutils/formatting.go index edaca96..bdf71f1 100644 --- a/pkg/formatter/formatter.go +++ b/pkg/jsonutils/formatting.go @@ -1,31 +1,59 @@ -package formatter +package jsonutils import ( + "bytes" "encoding/json" "fmt" - "strings" "reflect" "sort" + "strings" "text/tabwriter" - "bytes" ) -// Format formats the given data based on the output format -func Format(data interface{}, format string) (string, error) { +// OutputFormat represents the available output format options +type OutputFormat string + +const ( + // FormatJSON represents compact JSON output + FormatJSON OutputFormat = "json" + // FormatPretty represents pretty-printed JSON output + FormatPretty OutputFormat = "pretty" + // FormatTable represents tabular output + FormatTable OutputFormat = "table" +) + +// ParseFormat converts a string to an OutputFormat +func ParseFormat(format string) OutputFormat { switch strings.ToLower(format) { case "json", "j": - return formatJSON(data, false) + return FormatJSON case "pretty", "p": - return formatJSON(data, true) + return FormatPretty case "table", "t": + return FormatTable + default: + return FormatTable + } +} + +// Format formats the given data according to the specified output format. +func Format(data any, format string) (string, error) { + outputFormat := ParseFormat(format) + + switch outputFormat { + case FormatJSON: + return formatJSON(data, false) + case FormatPretty: + return formatJSON(data, true) + case FormatTable: return formatTable(data) default: return formatTable(data) } } -// formatJSON formats the data as JSON with optional pretty printing -func formatJSON(data interface{}, pretty bool) (string, error) { +// formatJSON converts data to JSON with optional pretty printing. +func formatJSON(data any, pretty bool) (string, error) { var output []byte var err error @@ -42,132 +70,180 @@ func formatJSON(data interface{}, pretty bool) (string, error) { return string(output), nil } -// formatTable formats the data as a table-like view -func formatTable(data interface{}) (string, error) { +// formatTable formats the data as a tabular view based on its structure. +// It tries to detect common MCP response structures and format them appropriately. +func formatTable(data any) (string, error) { // Handle special cases based on common MCP server responses val := reflect.ValueOf(data) - + // For nil values if !val.IsValid() { return "No data available", nil } - + // If it's not a map, just return the JSON representation if val.Kind() != reflect.Map { return formatJSON(data, true) } - + // Try to detect common MCP response structures - mapVal := val.Interface().(map[string]interface{}) - + mapVal, ok := val.Interface().(map[string]any) + if !ok { + return formatJSON(data, true) + } + // Handle tool list if tools, ok := mapVal["tools"]; ok { return formatToolsList(tools) } - + // Handle resource list if resources, ok := mapVal["resources"]; ok { return formatResourcesList(resources) } - + + // Handle prompt list + if prompts, ok := mapVal["prompts"]; ok { + return formatPromptsList(prompts) + } + // Handle tool call with content if content, ok := mapVal["content"]; ok { return formatContent(content) } - + // Generic table for other map structures return formatGenericMap(mapVal) } -// formatToolsList formats a list of tools as a table -func formatToolsList(tools interface{}) (string, error) { - toolsSlice, ok := tools.([]interface{}) +// formatToolsList formats a list of tools as a table with name and description columns. +func formatToolsList(tools any) (string, error) { + toolsSlice, ok := tools.([]any) if !ok { return "", fmt.Errorf("tools is not a slice") } - + if len(toolsSlice) == 0 { return "No tools available", nil } - + var buf bytes.Buffer w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0) - + fmt.Fprintln(w, "NAME\tDESCRIPTION") fmt.Fprintln(w, "----\t-----------") - + for _, t := range toolsSlice { - tool, ok := t.(map[string]interface{}) + tool, ok := t.(map[string]any) if !ok { continue } - + name, _ := tool["name"].(string) desc, _ := tool["description"].(string) - + // Truncate long descriptions if len(desc) > 70 { desc = desc[:67] + "..." } - + fmt.Fprintf(w, "%s\t%s\n", name, desc) } - + w.Flush() return buf.String(), nil } -// formatResourcesList formats a list of resources as a table -func formatResourcesList(resources interface{}) (string, error) { - resourcesSlice, ok := resources.([]interface{}) +// formatResourcesList formats a list of resources as a table with name, type, and URI columns. +func formatResourcesList(resources any) (string, error) { + resourcesSlice, ok := resources.([]any) if !ok { return "", fmt.Errorf("resources is not a slice") } - + if len(resourcesSlice) == 0 { return "No resources available", nil } - + var buf bytes.Buffer w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0) - + fmt.Fprintln(w, "NAME\tTYPE\tURI") fmt.Fprintln(w, "----\t----\t---") - + for _, r := range resourcesSlice { - resource, ok := r.(map[string]interface{}) + resource, ok := r.(map[string]any) if !ok { continue } - + name, _ := resource["name"].(string) resType, _ := resource["type"].(string) uri, _ := resource["uri"].(string) - + fmt.Fprintf(w, "%s\t%s\t%s\n", name, resType, uri) } - + + w.Flush() + return buf.String(), nil +} + +// formatPromptsList formats a list of prompts as a table with name and description columns. +func formatPromptsList(prompts any) (string, error) { + promptsSlice, ok := prompts.([]any) + if !ok { + return "", fmt.Errorf("prompts is not a slice") + } + + if len(promptsSlice) == 0 { + return "No prompts available", nil + } + + var buf bytes.Buffer + w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0) + + fmt.Fprintln(w, "NAME\tDESCRIPTION") + fmt.Fprintln(w, "----\t-----------") + + for _, p := range promptsSlice { + prompt, ok := p.(map[string]any) + if !ok { + continue + } + + name, _ := prompt["name"].(string) + desc, _ := prompt["description"].(string) + + // Truncate long descriptions + if len(desc) > 70 { + desc = desc[:67] + "..." + } + + fmt.Fprintf(w, "%s\t%s\n", name, desc) + } + w.Flush() return buf.String(), nil } -// formatContent formats content (usually tool call results) in a readable way -func formatContent(content interface{}) (string, error) { - contentSlice, ok := content.([]interface{}) +// formatContent formats content (usually tool call results) in a readable way. +// It handles different content types like text and images. +func formatContent(content any) (string, error) { + contentSlice, ok := content.([]any) if !ok { return "", fmt.Errorf("content is not a slice") } - + var buf strings.Builder - + for _, c := range contentSlice { - contentItem, ok := c.(map[string]interface{}) + contentItem, ok := c.(map[string]any) if !ok { continue } - + contentType, _ := contentItem["type"].(string) - + switch contentType { case "text": text, _ := contentItem["text"].(string) @@ -178,33 +254,34 @@ func formatContent(content interface{}) (string, error) { buf.WriteString(fmt.Sprintf("[%s CONTENT]\n", strings.ToUpper(contentType))) } } - + return buf.String(), nil } -// formatGenericMap formats a generic map as a table with keys and values -func formatGenericMap(data map[string]interface{}) (string, error) { +// formatGenericMap formats a generic map as a table with keys and values columns. +// Keys are sorted alphabetically for consistent output. +func formatGenericMap(data map[string]any) (string, error) { if len(data) == 0 { return "No data available", nil } - + var buf bytes.Buffer w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0) - + fmt.Fprintln(w, "KEY\tVALUE") fmt.Fprintln(w, "---\t-----") - + // Sort keys for consistent output keys := make([]string, 0, len(data)) for k := range data { keys = append(keys, k) } sort.Strings(keys) - + for _, k := range keys { v := data[k] var valueStr string - + switch val := v.(type) { case string: valueStr = val @@ -223,10 +300,10 @@ func formatGenericMap(data map[string]interface{}) (string, error) { } } } - + fmt.Fprintf(w, "%s\t%s\n", k, valueStr) } - + w.Flush() return buf.String(), nil -} \ No newline at end of file +} diff --git a/pkg/formatter/formatter_test.go b/pkg/jsonutils/formatting_test.go similarity index 60% rename from pkg/formatter/formatter_test.go rename to pkg/jsonutils/formatting_test.go index 15fe832..d2f266a 100644 --- a/pkg/formatter/formatter_test.go +++ b/pkg/jsonutils/formatting_test.go @@ -1,4 +1,4 @@ -package formatter +package jsonutils import ( "strings" @@ -8,9 +8,10 @@ import ( func TestFormat(t *testing.T) { testCases := []struct { name string - data interface{} + data any format string expectPretty bool + expectError bool }{ { name: "format json", @@ -36,6 +37,18 @@ func TestFormat(t *testing.T) { format: "p", expectPretty: true, }, + { + name: "format table", + data: map[string]string{"key": "value"}, + format: "table", + expectPretty: true, + }, + { + name: "format t", + data: map[string]string{"key": "value"}, + format: "t", + expectPretty: true, + }, { name: "format default", data: map[string]string{"key": "value"}, @@ -47,6 +60,14 @@ func TestFormat(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { output, err := Format(tc.data, tc.format) + + if tc.expectError { + if err == nil { + t.Fatalf("expected error but got none") + } + return + } + if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -66,4 +87,28 @@ func TestFormat(t *testing.T) { } }) } -} \ No newline at end of file +} + +func TestParseFormat(t *testing.T) { + testCases := []struct { + input string + expected OutputFormat + }{ + {"json", FormatJSON}, + {"J", FormatJSON}, + {"pretty", FormatPretty}, + {"P", FormatPretty}, + {"table", FormatTable}, + {"T", FormatTable}, + {"unknown", FormatTable}, // Default is table + } + + for _, tc := range testCases { + t.Run(tc.input, func(t *testing.T) { + result := ParseFormat(tc.input) + if result != tc.expected { + t.Errorf("ParseFormat(%q) = %q, want %q", tc.input, result, tc.expected) + } + }) + } +} diff --git a/pkg/transport/http.go b/pkg/transport/http.go index d1487fc..10e55d4 100644 --- a/pkg/transport/http.go +++ b/pkg/transport/http.go @@ -2,6 +2,7 @@ package transport import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -9,54 +10,77 @@ import ( "net/url" ) -// HTTPTransport provides HTTP transport for MCP servers -type HTTPTransport struct { - BaseURL string - HTTPClient *http.Client +// HTTP implements the Transport interface using HTTP calls. +type HTTP struct { + baseURL string + httpClient *http.Client } -// NewHTTP creates a new HTTP transport -func NewHTTP(baseURL string) *HTTPTransport { - return &HTTPTransport{ - BaseURL: baseURL, - HTTPClient: &http.Client{}, +// NewHTTP creates a new HTTP transport with the given base URL. +func NewHTTP(baseURL string) *HTTP { + return &HTTP{ + baseURL: baseURL, + httpClient: &http.Client{}, } } -// Execute sends a request to the MCP server and returns the response -func (t *HTTPTransport) Execute(method string, params interface{}) (map[string]interface{}, error) { - // For HTTP, we translate the method to a URL pattern +// Execute implements the Transport interface by sending HTTP requests +// to the MCP server and parsing the responses. +func (t *HTTP) Execute(method string, params any) (map[string]any, error) { var endpoint string var httpMethod string var reqBody io.Reader - if method == "tools/list" { - endpoint = fmt.Sprintf("%s/v1/tools", t.BaseURL) - httpMethod = "GET" - } else if method == "resources/list" { - endpoint = fmt.Sprintf("%s/v1/resources", t.BaseURL) - httpMethod = "GET" - } else if method == "tools/call" { - if toolParams, ok := params.(map[string]interface{}); ok { - toolName, _ := toolParams["name"].(string) - endpoint = fmt.Sprintf("%s/v1/tools/%s", t.BaseURL, url.PathEscape(toolName)) - httpMethod = "POST" - - if arguments, ok := toolParams["arguments"].(map[string]interface{}); ok && len(arguments) > 0 { - jsonBody, err := json.Marshal(arguments) - if err != nil { - return nil, fmt.Errorf("error marshaling JSON: %w", err) - } - reqBody = bytes.NewBuffer(jsonBody) + switch method { + case "tools/list": + endpoint = fmt.Sprintf("%s/v1/tools", t.baseURL) + httpMethod = http.MethodGet + case "resources/list": + endpoint = fmt.Sprintf("%s/v1/resources", t.baseURL) + httpMethod = http.MethodGet + case "prompts/list": + endpoint = fmt.Sprintf("%s/v1/prompts", t.baseURL) + httpMethod = http.MethodGet + case "tools/call": + toolParams, ok := params.(map[string]any) + if !ok { + return nil, fmt.Errorf("invalid params for tools/call: expected map[string]any") + } + + toolName, ok := toolParams["name"].(string) + if !ok { + return nil, fmt.Errorf("tool name is required for tools/call") + } + + endpoint = fmt.Sprintf("%s/v1/tools/%s", t.baseURL, url.PathEscape(toolName)) + httpMethod = http.MethodPost + + if arguments, ok := toolParams["arguments"].(map[string]any); ok && len(arguments) > 0 { + jsonBody, err := json.Marshal(arguments) + if err != nil { + return nil, fmt.Errorf("error marshaling tool arguments: %w", err) } - } else { - return nil, fmt.Errorf("invalid params for tools/call") + reqBody = bytes.NewBuffer(jsonBody) } - } else { + case "resources/read": + resParams, ok := params.(map[string]any) + if !ok { + return nil, fmt.Errorf("invalid params for resources/read: expected map[string]any") + } + + uri, ok := resParams["uri"].(string) + if !ok { + return nil, fmt.Errorf("uri is required for resources/read") + } + + endpoint = fmt.Sprintf("%s/v1/resources/%s", t.baseURL, url.PathEscape(uri)) + httpMethod = http.MethodGet + default: return nil, fmt.Errorf("unsupported method: %s", method) } - req, err := http.NewRequest(httpMethod, endpoint, reqBody) + ctx := context.Background() + req, err := http.NewRequestWithContext(ctx, httpMethod, endpoint, reqBody) if err != nil { return nil, fmt.Errorf("error creating request: %w", err) } @@ -64,7 +88,7 @@ func (t *HTTPTransport) Execute(method string, params interface{}) (map[string]i req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - resp, err := t.HTTPClient.Do(req) + resp, err := t.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("error making request: %w", err) } @@ -72,13 +96,13 @@ func (t *HTTPTransport) Execute(method string, params interface{}) (map[string]i if resp.StatusCode < 200 || resp.StatusCode >= 300 { respBody, _ := io.ReadAll(resp.Body) - return nil, fmt.Errorf("server returned non-success status: %d - %s", resp.StatusCode, string(respBody)) + return nil, fmt.Errorf("HTTP error: %d - %s", resp.StatusCode, string(respBody)) } - var result map[string]interface{} + var result map[string]any if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("error decoding JSON response: %w", err) } return result, nil -} \ No newline at end of file +} diff --git a/pkg/transport/stdio.go b/pkg/transport/stdio.go index b0aa3b2..d3f5767 100644 --- a/pkg/transport/stdio.go +++ b/pkg/transport/stdio.go @@ -1,143 +1,132 @@ package transport import ( + "bytes" "encoding/json" "fmt" - "os/exec" - "bytes" "io" "os" + "os/exec" ) -// StdioTransport provides Stdio transport for MCP servers -type StdioTransport struct { - Command []string - NextID int - Debug bool +// Stdio implements the Transport interface by executing a command +// and communicating with it via stdin/stdout using JSON-RPC. +type Stdio struct { + command []string + nextID int + debug bool } -// NewStdio creates a new Stdio transport -func NewStdio(command []string) *StdioTransport { +// NewStdio creates a new Stdio transport that will execute the given command. +// It communicates with the command using JSON-RPC over stdin/stdout. +func NewStdio(command []string) *Stdio { debug := os.Getenv("MCP_DEBUG") == "1" - return &StdioTransport{ - Command: command, - NextID: 1, - Debug: debug, + return &Stdio{ + command: command, + nextID: 1, + debug: debug, } } -// Execute sends a request to the MCP server and returns the response -func (t *StdioTransport) Execute(method string, params interface{}) (map[string]interface{}, error) { - if len(t.Command) == 0 { +// Execute implements the Transport interface by spawning a subprocess +// and communicating with it via JSON-RPC over stdin/stdout. +func (t *Stdio) Execute(method string, params any) (map[string]any, error) { + if len(t.command) == 0 { return nil, fmt.Errorf("no command specified for stdio transport") } - if t.Debug { - fmt.Fprintf(os.Stderr, "DEBUG: Executing command: %v\n", t.Command) + if t.debug { + fmt.Fprintf(os.Stderr, "DEBUG: Executing command: %v\n", t.command) } - // Create the JSON-RPC request - request := JSONRPCRequest{ + request := Request{ JSONRPC: "2.0", Method: method, - ID: t.NextID, + ID: t.nextID, Params: params, } - t.NextID++ + t.nextID++ - // Marshal the request to JSON requestJSON, err := json.Marshal(request) if err != nil { return nil, fmt.Errorf("error marshaling request: %w", err) } - - // Add newline to the request + requestJSON = append(requestJSON, '\n') - if t.Debug { + if t.debug { fmt.Fprintf(os.Stderr, "DEBUG: Sending request: %s\n", string(requestJSON)) } - // Create the command - cmd := exec.Command(t.Command[0], t.Command[1:]...) - - // Create stdin and stdout pipes - stdin, err := cmd.StdinPipe() - if err != nil { - return nil, fmt.Errorf("error getting stdin pipe: %w", err) + cmd := exec.Command(t.command[0], t.command[1:]...) // #nosec G204 + + stdin, stdinErr := cmd.StdinPipe() + if stdinErr != nil { + return nil, fmt.Errorf("error getting stdin pipe: %w", stdinErr) } - - stdout, err := cmd.StdoutPipe() - if err != nil { - return nil, fmt.Errorf("error getting stdout pipe: %w", err) + + stdout, stdoutErr := cmd.StdoutPipe() + if stdoutErr != nil { + return nil, fmt.Errorf("error getting stdout pipe: %w", stdoutErr) } - - // Capture stderr for debugging + var stderrBuf bytes.Buffer cmd.Stderr = &stderrBuf - - // Start the command - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("error starting command: %w", err) + + if startErr := cmd.Start(); startErr != nil { + return nil, fmt.Errorf("error starting command: %w", startErr) } - // Write request to stdin and close it - if _, err := stdin.Write(requestJSON); err != nil { - return nil, fmt.Errorf("error writing to stdin: %w", err) + if _, writeErr := stdin.Write(requestJSON); writeErr != nil { + return nil, fmt.Errorf("error writing to stdin: %w", writeErr) } stdin.Close() - - if t.Debug { + + if t.debug { fmt.Fprintf(os.Stderr, "DEBUG: Wrote request to stdin\n") } - - // Read response from stdout + var respBytes bytes.Buffer - if _, err := io.Copy(&respBytes, stdout); err != nil { - return nil, fmt.Errorf("error reading from stdout: %w", err) + if _, copyErr := io.Copy(&respBytes, stdout); copyErr != nil { + return nil, fmt.Errorf("error reading from stdout: %w", copyErr) } - - if t.Debug { + + if t.debug { fmt.Fprintf(os.Stderr, "DEBUG: Read from stdout: %s\n", respBytes.String()) } - - // Wait for the command to complete - err = cmd.Wait() - - if t.Debug { - fmt.Fprintf(os.Stderr, "DEBUG: Command completed with err: %v\n", err) + + waitErr := cmd.Wait() + + if t.debug { + fmt.Fprintf(os.Stderr, "DEBUG: Command completed with err: %v\n", waitErr) if stderrBuf.Len() > 0 { fmt.Fprintf(os.Stderr, "DEBUG: stderr output: %s\n", stderrBuf.String()) } } - - // If we have stderr output and an error, include it in the error message - if err != nil && stderrBuf.Len() > 0 { - return nil, fmt.Errorf("command error: %w, stderr: %s", err, stderrBuf.String()) + + if waitErr != nil && stderrBuf.Len() > 0 { + return nil, fmt.Errorf("command error: %w, stderr: %s", waitErr, stderrBuf.String()) } - - // If we didn't get any response, this might be a command error + if respBytes.Len() == 0 { if stderrBuf.Len() > 0 { return nil, fmt.Errorf("no response from command, stderr: %s", stderrBuf.String()) } return nil, fmt.Errorf("no response from command") } - - // Parse the response - var response JSONRPCResponse - if err := json.Unmarshal(respBytes.Bytes(), &response); err != nil { - return nil, fmt.Errorf("error unmarshaling response: %w, response: %s", err, respBytes.String()) + + var response Response + if unmarshalErr := json.Unmarshal(respBytes.Bytes(), &response); unmarshalErr != nil { + return nil, fmt.Errorf("error unmarshaling response: %w, response: %s", unmarshalErr, respBytes.String()) } - // Check for error in response if response.Error != nil { return nil, fmt.Errorf("RPC error %d: %s", response.Error.Code, response.Error.Message) } - if t.Debug { + if t.debug { fmt.Fprintf(os.Stderr, "DEBUG: Successfully parsed response\n") } return response.Result, nil -} \ No newline at end of file +} diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index afdb994..1e87288 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -5,39 +5,42 @@ import ( "io" ) -// Transport defines the interface for communicating with MCP servers +// Transport defines the interface for communicating with MCP servers. +// Implementations should handle the specifics of communication protocols. type Transport interface { - // Execute sends a request to the MCP server and returns the response - Execute(method string, params interface{}) (map[string]interface{}, error) + // Execute sends a request to the MCP server and returns the response. + // The method parameter specifies the RPC method to call, and params contains + // the parameters to pass to that method. + Execute(method string, params any) (map[string]any, error) } -// JSONRPCRequest represents a JSON-RPC 2.0 request -type JSONRPCRequest struct { - JSONRPC string `json:"jsonrpc"` - Method string `json:"method"` - ID int `json:"id"` - Params interface{} `json:"params,omitempty"` +// Request represents a JSON-RPC 2.0 request. +type Request struct { + JSONRPC string `json:"jsonrpc"` + Method string `json:"method"` + ID int `json:"id"` + Params any `json:"params,omitempty"` } -// JSONRPCResponse represents a JSON-RPC 2.0 response -type JSONRPCResponse struct { - JSONRPC string `json:"jsonrpc"` - ID int `json:"id"` - Result map[string]interface{} `json:"result,omitempty"` - Error *JSONRPCError `json:"error,omitempty"` +// Response represents a JSON-RPC 2.0 response. +type Response struct { + JSONRPC string `json:"jsonrpc"` + ID int `json:"id"` + Result map[string]any `json:"result,omitempty"` + Error *Error `json:"error,omitempty"` } -// JSONRPCError represents a JSON-RPC 2.0 error -type JSONRPCError struct { +// Error represents a JSON-RPC 2.0 error. +type Error struct { Code int `json:"code"` Message string `json:"message"` } -// ReadJSONRPCResponse reads and parses a JSON-RPC response from a reader -func ReadJSONRPCResponse(r io.Reader) (*JSONRPCResponse, error) { - var response JSONRPCResponse +// ParseResponse reads and parses a JSON-RPC response from a reader. +func ParseResponse(r io.Reader) (*Response, error) { + var response Response if err := json.NewDecoder(r).Decode(&response); err != nil { return nil, err } return &response, nil -} \ No newline at end of file +} From 62cf2110acb204627be295f9778af5ea0b51c1f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fatih=20Kadir=20Ak=C4=B1n?= Date: Wed, 26 Mar 2025 09:08:30 +0300 Subject: [PATCH 2/3] update --- .github/resources/screenshot.png | Bin 0 -> 131008 bytes README.md | 2 ++ pkg/jsonutils/{formatting.go => jsonutils.go} | 0 .../{formatting_test.go => jsonutils_test.go} | 0 4 files changed, 2 insertions(+) create mode 100644 .github/resources/screenshot.png rename pkg/jsonutils/{formatting.go => jsonutils.go} (100%) rename pkg/jsonutils/{formatting_test.go => jsonutils_test.go} (100%) diff --git a/.github/resources/screenshot.png b/.github/resources/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..99afc5a21b7f7f939f9e5d51e80c8afd50ef02f2 GIT binary patch literal 131008 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 Ks=aF{!pe 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
InB3VnQl9q_I0R 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 Date: Wed, 26 Mar 2025 09:11:05 +0300 Subject: [PATCH 3/3] update --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 9b1f339..554c550 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ A command-line interface for interacting with MCP (Model Context Protocol) servers using both stdio and HTTP transport. +## Overview + +```bash +mcp shell npx -y @modelcontextprotocol/server-filesystem ~/Code +``` + +This will open a shell as following: + ![MCP Tools Screenshot](.github/resources/screenshot.png) ## Installation