fix(mcp): stabilise file existence checks
Use Stat() for file_exists and sort directory listings for deterministic output. Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
599d0b6298
commit
b96b05ab0b
2 changed files with 49 additions and 14 deletions
|
|
@ -11,6 +11,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
core "dappco.re/go/core"
|
||||
|
|
@ -503,6 +504,9 @@ func (s *Service) listDirectory(ctx context.Context, req *mcp.CallToolRequest, i
|
|||
if err != nil {
|
||||
return nil, ListDirectoryOutput{}, log.E("mcp.listDirectory", "failed to list directory", err)
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return entries[i].Name() < entries[j].Name()
|
||||
})
|
||||
result := make([]DirectoryEntry, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
info, _ := e.Info()
|
||||
|
|
@ -545,21 +549,15 @@ func (s *Service) renameFile(ctx context.Context, req *mcp.CallToolRequest, inpu
|
|||
}
|
||||
|
||||
func (s *Service) fileExists(ctx context.Context, req *mcp.CallToolRequest, input FileExistsInput) (*mcp.CallToolResult, FileExistsOutput, error) {
|
||||
exists := s.medium.IsFile(input.Path)
|
||||
if exists {
|
||||
return nil, FileExistsOutput{Exists: true, IsDir: false, Path: input.Path}, nil
|
||||
info, err := s.medium.Stat(input.Path)
|
||||
if err != nil {
|
||||
return nil, FileExistsOutput{Exists: false, IsDir: false, Path: input.Path}, nil
|
||||
}
|
||||
// Check if it's a directory by attempting to list it
|
||||
// List might fail if it's a file too (but we checked IsFile) or if doesn't exist.
|
||||
_, err := s.medium.List(input.Path)
|
||||
isDir := err == nil
|
||||
|
||||
// If List failed, it might mean it doesn't exist OR it's a special file or permissions.
|
||||
// Assuming if List works, it's a directory.
|
||||
|
||||
// Refinement: If it doesn't exist, List returns error.
|
||||
|
||||
return nil, FileExistsOutput{Exists: isDir, IsDir: isDir, Path: input.Path}, nil
|
||||
return nil, FileExistsOutput{
|
||||
Exists: true,
|
||||
IsDir: info.IsDir(),
|
||||
Path: input.Path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) detectLanguage(ctx context.Context, req *mcp.CallToolRequest, input DetectLanguageInput) (*mcp.CallToolResult, DetectLanguageOutput, error) {
|
||||
|
|
|
|||
|
|
@ -216,6 +216,43 @@ func TestMedium_Good_EnsureDir(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestFileExists_Good_FileAndDirectory(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
s, err := New(Options{WorkspaceRoot: tmpDir})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create service: %v", err)
|
||||
}
|
||||
|
||||
if err := s.medium.EnsureDir("nested"); err != nil {
|
||||
t.Fatalf("Failed to create directory: %v", err)
|
||||
}
|
||||
if err := s.medium.Write("nested/file.txt", "content"); err != nil {
|
||||
t.Fatalf("Failed to write file: %v", err)
|
||||
}
|
||||
|
||||
_, fileOut, err := s.fileExists(nil, nil, FileExistsInput{Path: "nested/file.txt"})
|
||||
if err != nil {
|
||||
t.Fatalf("fileExists(file) failed: %v", err)
|
||||
}
|
||||
if !fileOut.Exists {
|
||||
t.Fatal("expected file to exist")
|
||||
}
|
||||
if fileOut.IsDir {
|
||||
t.Fatal("expected file to not be reported as a directory")
|
||||
}
|
||||
|
||||
_, dirOut, err := s.fileExists(nil, nil, FileExistsInput{Path: "nested"})
|
||||
if err != nil {
|
||||
t.Fatalf("fileExists(dir) failed: %v", err)
|
||||
}
|
||||
if !dirOut.Exists {
|
||||
t.Fatal("expected directory to exist")
|
||||
}
|
||||
if !dirOut.IsDir {
|
||||
t.Fatal("expected directory to be reported as a directory")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMedium_Good_IsFile(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
s, err := New(Options{WorkspaceRoot: tmpDir})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue