package handler
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/mark3labs/mcp-go/mcp"
)
func (fs *FilesystemHandler) HandleListDirectory(
ctx context.Context,
request mcp.CallToolRequest,
) (*mcp.CallToolResult, error) {
path, err := request.RequireString("path")
if err != nil {
return nil, err
}
// Handle empty or relative paths like "." or "./" by converting to absolute path
if path == "." || path == "./" {
// Get current working directory
cwd, err := os.Getwd()
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Error resolving current directory: %v", err),
},
},
IsError: true,
}, nil
}
path = cwd
}
validPath, err := fs.validatePath(path)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Error: %v", err),
},
},
IsError: true,
}, nil
}
// Check if it's a directory
info, err := os.Stat(validPath)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Error: %v", err),
},
},
IsError: true,
}, nil
}
if !info.IsDir() {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: "Error: Path is not a directory",
},
},
IsError: true,
}, nil
}
entries, err := os.ReadDir(validPath)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Error reading directory: %v", err),
},
},
IsError: true,
}, nil
}
var result strings.Builder
result.WriteString(fmt.Sprintf("Directory listing for: %s\n\n", validPath))
for _, entry := range entries {
entryPath := filepath.Join(validPath, entry.Name())
resourceURI := pathToResourceURI(entryPath)
if entry.IsDir() {
result.WriteString(fmt.Sprintf("[DIR] %s (%s)\n", entry.Name(), resourceURI))
} else {
info, err := entry.Info()
if err == nil {
result.WriteString(fmt.Sprintf("[FILE] %s (%s) - %d bytes\n",
entry.Name(), resourceURI, info.Size()))
} else {
result.WriteString(fmt.Sprintf("[FILE] %s (%s)\n", entry.Name(), resourceURI))
}
}
}
// Return both text content and embedded resource
resourceURI := pathToResourceURI(validPath)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: result.String(),
},
mcp.EmbeddedResource{
Type: "resource",
Resource: mcp.TextResourceContents{
URI: resourceURI,
MIMEType: "text/plain",
Text: fmt.Sprintf("Directory: %s", validPath),
},
},
},
}, nil
}