package handler
import (
"context"
"encoding/base64"
"fmt"
"os"
"github.com/mark3labs/mcp-go/mcp"
)
func (fs *FilesystemHandler) HandleReadFile(
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() {
// For directories, return a resource reference instead
resourceURI := pathToResourceURI(validPath)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("This is a directory. Use the resource URI to browse its contents: %s", resourceURI),
},
mcp.EmbeddedResource{
Type: "resource",
Resource: mcp.TextResourceContents{
URI: resourceURI,
MIMEType: "text/plain",
Text: fmt.Sprintf("Directory: %s", validPath),
},
},
},
}, nil
}
// Determine MIME type
mimeType := detectMimeType(validPath)
// Check file size
if info.Size() > MAX_INLINE_SIZE {
// File is too large to inline, return a resource reference
resourceURI := pathToResourceURI(validPath)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("File is too large to display inline (%d bytes). Access it via resource URI: %s", info.Size(), resourceURI),
},
mcp.EmbeddedResource{
Type: "resource",
Resource: mcp.TextResourceContents{
URI: resourceURI,
MIMEType: "text/plain",
Text: fmt.Sprintf("Large file: %s (%s, %d bytes)", validPath, mimeType, info.Size()),
},
},
},
}, nil
}
// Read file content
content, err := os.ReadFile(validPath)
if err != nil {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Error reading file: %v", err),
},
},
IsError: true,
}, nil
}
// Check if it's a text file
if isTextFile(mimeType) {
// It's a text file, return as text
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: string(content),
},
},
}, nil
} else if isImageFile(mimeType) {
// It's an image file, return as image content
if info.Size() <= MAX_BASE64_SIZE {
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Image file: %s (%s, %d bytes)", validPath, mimeType, info.Size()),
},
mcp.ImageContent{
Type: "image",
Data: base64.StdEncoding.EncodeToString(content),
MIMEType: mimeType,
},
},
}, nil
} else {
// Too large for base64, return a reference
resourceURI := pathToResourceURI(validPath)
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Image file is too large to display inline (%d bytes). Access it via resource URI: %s", info.Size(), resourceURI),
},
mcp.EmbeddedResource{
Type: "resource",
Resource: mcp.TextResourceContents{
URI: resourceURI,
MIMEType: "text/plain",
Text: fmt.Sprintf("Large image: %s (%s, %d bytes)", validPath, mimeType, info.Size()),
},
},
},
}, nil
}
} else {
// It's another type of binary file
resourceURI := pathToResourceURI(validPath)
if info.Size() <= MAX_BASE64_SIZE {
// Small enough for base64 encoding
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Binary file: %s (%s, %d bytes)", validPath, mimeType, info.Size()),
},
mcp.EmbeddedResource{
Type: "resource",
Resource: mcp.BlobResourceContents{
URI: resourceURI,
MIMEType: mimeType,
Blob: base64.StdEncoding.EncodeToString(content),
},
},
},
}, nil
} else {
// Too large for base64, return a reference
return &mcp.CallToolResult{
Content: []mcp.Content{
mcp.TextContent{
Type: "text",
Text: fmt.Sprintf("Binary file: %s (%s, %d bytes). Access it via resource URI: %s", validPath, mimeType, info.Size(), resourceURI),
},
mcp.EmbeddedResource{
Type: "resource",
Resource: mcp.TextResourceContents{
URI: resourceURI,
MIMEType: "text/plain",
Text: fmt.Sprintf("Binary file: %s (%s, %d bytes)", validPath, mimeType, info.Size()),
},
},
},
}, nil
}
}
}