diff --git a/cmd/cmd.go b/cmd/cmd.go index 01c9348c3..48ec95aa9 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -829,6 +829,62 @@ func ListRunningHandler(cmd *cobra.Command, args []string) error { return nil } +func ListToolsHandler(cmd *cobra.Command, args []string) { + servers, err := server.ListMCPServers() + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading MCP servers: %v\n", err) + return + } + + if len(servers) == 0 { + fmt.Println("No MCP servers configured.") + fmt.Println("\nTo add MCP servers, create ~/.ollama/mcp-servers.json") + fmt.Println("See https://ollama.com/docs/mcp for configuration details.") + return + } + + fmt.Println("Available MCP Servers:") + fmt.Println() + + var data [][]string + for _, s := range servers { + autoEnable := string(s.AutoEnable) + if autoEnable == "" { + autoEnable = "never" + } + + capabilities := "-" + if len(s.Capabilities) > 0 { + capabilities = strings.Join(s.Capabilities, ", ") + } + + requiresPath := "no" + if s.RequiresPath { + requiresPath = "yes" + } + + data = append(data, []string{s.Name, s.Description, autoEnable, requiresPath, capabilities}) + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"NAME", "DESCRIPTION", "AUTO-ENABLE", "REQUIRES PATH", "CAPABILITIES"}) + table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetHeaderLine(false) + table.SetBorder(false) + table.SetNoWhiteSpace(true) + table.SetTablePadding(" ") + table.AppendBulk(data) + table.Render() + + fmt.Println() + fmt.Println("Auto-enable modes:") + fmt.Println(" never - Must be explicitly configured via API") + fmt.Println(" always - Enables whenever --tools is used") + fmt.Println(" with_path - Enables when --tools has a path argument") + fmt.Println(" if_match - Enables when conditions match (e.g., file exists)") +} + func DeleteHandler(cmd *cobra.Command, args []string) error { client, err := api.ClientFromEnvironment() if err != nil { @@ -2077,11 +2133,17 @@ func NewCLI() *cobra.Command { return } + if listTools, _ := cmd.Flags().GetBool("list-tools"); listTools { + ListToolsHandler(cmd, args) + return + } + cmd.Print(cmd.UsageString()) }, } rootCmd.Flags().BoolP("version", "v", false, "Show version information") + rootCmd.Flags().Bool("list-tools", false, "List available MCP servers and their tools") createCmd := &cobra.Command{ Use: "create MODEL", diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go index 7dc3d0093..42ff92118 100644 --- a/cmd/cmd_test.go +++ b/cmd/cmd_test.go @@ -425,6 +425,7 @@ func TestRunEmbeddingModel(t *testing.T) { cmd.Flags().String("format", "", "") cmd.Flags().String("think", "", "") cmd.Flags().Bool("hidethinking", false, "") + cmd.Flags().String("tools", "", "") oldStdout := os.Stdout r, w, _ := os.Pipe() @@ -517,6 +518,7 @@ func TestRunEmbeddingModelWithFlags(t *testing.T) { cmd.Flags().String("format", "", "") cmd.Flags().String("think", "", "") cmd.Flags().Bool("hidethinking", false, "") + cmd.Flags().String("tools", "", "") if err := cmd.Flags().Set("truncate", "true"); err != nil { t.Fatalf("failed to set truncate flag: %v", err) @@ -618,6 +620,7 @@ func TestRunEmbeddingModelPipedInput(t *testing.T) { cmd.Flags().String("format", "", "") cmd.Flags().String("think", "", "") cmd.Flags().Bool("hidethinking", false, "") + cmd.Flags().String("tools", "", "") // Capture stdin oldStdin := os.Stdin @@ -693,6 +696,7 @@ func TestRunEmbeddingModelNoInput(t *testing.T) { cmd.Flags().String("format", "", "") cmd.Flags().String("think", "", "") cmd.Flags().Bool("hidethinking", false, "") + cmd.Flags().String("tools", "", "") cmd.SetOut(io.Discard) cmd.SetErr(io.Discard)