What? Just by speaking, you can let a large model help you organize your computer desktop?!

Written by
Audrey Miles
Updated on:June-25th-2025
Recommendation

Use AI technology to simplify desktop management and free your hands from tedious file organization.
Core content:
1. Combining MCP protocol with Golang to realize the desktop intelligent cleaning system
2. MCP Server and Client architecture design and communication process
3. Code example: Implement desktop path acquisition and file management tool definition

Yang Fangxian
Founder of 53A/Most Valuable Expert of Tencent Cloud (TVP)


Many people's desktops are often filled with shortcuts, temporary files, test logs,
Debugging screenshots and so on take up space, and manual cleaning is time-consuming and laborious.

This article will build an intelligent
Cleaning the system :
MCP Server provides file management tools .
MCP Client triggers automated operations through natural language commands.

Living in the AI ​​age, we must learn to use AI tools.

1. MCP Architecture Design

1.1 Technical Solution

We use Golang to implement MCP Client and Server. MCP is the Model Context Protocol, which is used to connect large language models and external tools. It adopts a client-server architecture.
MCP Server provides tools, and Client calls these tools. So we need to design an MCP Server to provide desktop cleaning tools, such as scanning temporary files, deleting files, organizing files, etc., and then the Client needs to call these tools and combine them with LLM (qwen2.5:3b) to make intelligent cleaning decisions.
We use stdio (or HTTP SSE) for the transport layer, which is suitable for local inter-process communication. In this demonstration, we compile it directly into binary and run it (or run it through Docker).
Dual-process architecture:
  • MCP Server : Resident in the background, providing three tools: scan_temp_files (scan), delete_files (delete), and organize_desktop (organize).

  • MCP Client: Parses natural language instructions (such as " search the desktop for files ending with .log") and calls the Server tool chain.

1.2 Communication process

2. MCP Server Implementation

2.1 Tool Definition

Let's illustrate this with code:
package  main
import  (  "context"  "fmt"  "github.com/mark3labs/mcp-go/mcp"  "github.com/mark3labs/mcp-go/server"  "os"  "path/filepath"  "strings"  "time")
// Get the desktop path (cross-platform compatibility)func getDesktopPath () string  {   home, _ := os.UserHomeDir()// Create the working directory if it does not exist path := filepath.Join(home,  "Desktop" )        if  _, err := os.Stat(path); os.IsNotExist(err) { os.MkdirAll(path,  0755 ) }        return  path}
func main ()  {  s := server.NewMCPServer( "DesktopCleaner""1.0" , server.WithLogging(), server.WithRecovery(), )// Tool 1: Scan temporary files scanTool := mcp.NewTool( "scan_temp_files" , mcp.WithDescription( "Scan temporary files with specified suffix on the desktop" ), mcp.WithString( "suffix" , mcp.Required(), mcp.Description( "File suffix such as .log/.tmp" ), mcp.Pattern( `^\.[a-zA-Z0-9]+$` )), mcp.WithNumber( "days" , mcp.Description( "Search for files within the last N days" )), ) s.AddTool(scanTool, scanHandler)// Tool 2: Batch Delete delTool := mcp.NewTool( "delete_files" , mcp.WithDescription( "Delete the file in the specified path" ), mcp.WithArray( "paths" , mcp.Required(), mcp.Description( "File path array" )), ) s.AddTool(delTool, deleteHandler)// Tool 3: Desktop Organization orgTool := mcp.NewTool( "organize_desktop" , mcp.WithDescription( "Organize files by type" ), mcp.WithString( "strategy" , mcp.Enum( "type""date" ),  // Classify by type/date mcp.Description( "Organization strategy" )), ) s.AddTool(orgTool, organizeHandler)       if  err := server.ServeStdio(s); err !=  nil  { fmt.Printf( "Server error: %v\n" , err) }}

2.1 Core Processor Implementation

// Scan processorfunc scanHandler (ctx context.Context, req mcp.CallToolRequest)  (*mcp.CallToolResult,  error ) { 	suffix := req.Params.Arguments[ "suffix" ].( string )       // Get the days parameter. If it does not exist, the default value is 0 and no date filtering is performed.       var  days  int       if  daysVal, ok := req.Params.Arguments[ "days" ]; ok {		days, _ = daysVal.( int )	}	desktopPath := getDesktopPath()	fmt.Printf( "Scanning directory: %s, suffix: %s, days: %d\n" , desktopPath, suffix, days)
       var  files [] string err := filepath.Walk(desktopPath,  func (path  string , info os.FileInfo, err  error )  error  {          // Handle error conditions to prevent null pointers          if  err !=  nil  { fmt.Printf( "Error accessing path: %s, Error: %v\n" , path, err)               return  nil  // Continue scanning other files   }          if  info ==  nil  { fmt.Printf( "File information is empty: %s\n" , path)               return  nil   }
         // Check the file extension, ignoring case         if  info.IsDir() {            return  nil         }
         ext := filepath.Ext(path)         if  strings.ToLower(ext) != strings.ToLower(suffix) {            return  nil   }
         // If the number of days is specified, check the file modification time         if  days >  0  {            if  time.Since(info.ModTime()) > time.Duration(days)* 24 *time.Hour {                return  nil  // The file is too old, skip it     }         }
         // The file meets the conditions and is added to the results     files =  append (files, path)  fmt.Printf( "File found: %s\n" , path)          return  nil })
        if  err !=  nil  {    fmt.Printf( "Scanning error: %v\n" , err)            return  mcp.NewToolResultErrorFromErr( "Scan failed" , err),  nil }
result :=  "Found"  + fmt.Sprintf( "%d"len (files)) +  "Files: \n"        for  _, f :=  range  files { result += f +  "\n" }
        return  mcp.NewToolResultText(result),  nil}// Delete the handlerfunc deleteHandler (ctx context.Context, req mcp.CallToolRequest)  (*mcp.CallToolResult,  error ) {  paths := req.Params.Arguments[ "paths" ].([] interface {}) results :=  make ( map [ string ] string )       for  _, p :=  range  paths { path := p.( string )            if  !isUnderDesktop(path) {  // Security check              return  mcp.NewToolResultError( "Illegal path: "  + path),  nil      }      err := os.Remove(path)            if  err !=  nil  { results[path] =  "Delete failed: "  + err.Error()      }  else  { results[path] =  "Deleted successfully"      } }
result :=  "Delete result:\n"        for  path, status :=  range  results { result += path +  ": "  + status +  "\n" }
        return  mcp.NewToolResultText(result),  nil}// Arrange desktop processorfunc organizeHandler (ctx context.Context, req mcp.CallToolRequest)  (*mcp.CallToolResult,  error ) {  strategy, _ := req.Params.Arguments[ "strategy" ].( string )        // The actual sorting logic can be implemented here
        return  mcp.NewToolResultText( "Desktop files have been pressed"  + strategy +  "Organization completed" ),  nil}// Path validity checkfunc isUnderDesktop (path  string ) bool  {   rel, err := filepath.Rel(getDesktopPath(), path)        return  err ==  nil  && !strings.HasPrefix(rel,  ".." )}

3. MCP Client Implementation

3.1 Client Initialization

package  mainimport  (  "context"  "encoding/json"  "fmt"  "github.com/mark3labs/mcp-go/client"  "github.com/mark3labs/mcp-go/mcp"  "strings"  "time")
func main ()  { // Connect to the local Server      mcpClient, err := client.NewStdioMCPClient( "./desktop_cleaner"nil )      if  err !=  nil  {        panic (err) }      defer  mcpClient.Close()// Create context with timeout      ctx, cancel := context.WithTimeout(context.Background(),  30 *time.Second)      defer  cancel()// Initialize handshake fmt.Println( "Initializing client..." ) initRequest := mcp.InitializeRequest{} initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION initRequest.Params.ClientInfo = mcp.Implementation{ Name:     "CleanBot" , Version:  "1.0" , }      if  _, err := mcpClient.Initialize(ctx, initRequest); err !=  nil  {          panic (err) }// Execute toolchain       if  err := cleanTempFiles(ctx, mcpClient); err !=  nil  { fmt.Printf( "Cleanup failed: %v\n" , err) }}

3.2 Tool Call

func cleanTempFiles(ctx context.Context, client *client.Client) error { // Step 1: Scan .log files scanReq := mcp.CallToolRequest{ Request: mcp.Request{ Method: "tools/call", }, } scanReq.Params.Name = "scan_temp_files" scanReq.Params.Arguments = map[string]interface{}{ "suffix": ".log", "days": 7, } scanRes, err := client.CallTool(ctx, scanReq) if err != nil { return err } printToolResult(scanRes) fmt.Println() // 解析扫描结果 files := parseFiles(scanRes) // Step 2: Delete files delReq := mcp.CallToolRequest{ Request: mcp.Request{ Method: "tools/call", }, }    delReq.Params.Name = "delete_files" delReq.Params.Arguments = map[string]interface{}{ "paths": files, } if _, err := client.CallTool(ctx, delReq); err != nil { return err } fmt.Printf("Cleaned %d files successfully\n", len(files)) return nil}// Helper function to print tool resultsfunc printToolResult(result *mcp.CallToolResult) { for _, content := range result.Content { if textContent, ok := content.(mcp.TextContent); ok { fmt.Println(textContent.Text) } else { jsonBytes, _ := json.MarshalIndent(content, "", " ") fmt.Println(string(jsonBytes)) }    }}//Parse calltoolresultfunc parseFiles(result *mcp.CallToolResult) (files []string) { for _, content := range result.Content { if textContent, ok := content.(mcp.TextContent); ok { for _, line := range strings.Split(textContent.Text, "\n") { if strings.HasPrefix(line, "/") { files = append(files, line) } } } else { jsonBytes, _ := json.MarshalIndent(content, "", " ") // Parsed into []string var result []string if err := json.Unmarshal(jsonBytes, &result); err != nil { fmt.Println("Parse failed:", err) } files = append(files, result...) } } return}

4. Scene Demonstration

Combine LLM to achieve intelligent decision-making:

First we need to install the mcphost tool and Ollama

# Install the mcphost tool go install github.com/mark3labs/mcphost@latest
# Start AI coordination mode mcphost --config mcp-systemfile/clean_config.json --model ollama:qwen2.5:3b

Ollama installs qwen2.5:3b large model, size 1.9G

Final result

Thinking...

Assistant: