package handler
import (
"context"
"encoding/base64"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/mark3labs/mcp-go/mcp"
)
// HandleReadResource handles the MCP resource reading functionality
func (fs *FilesystemHandler) HandleReadResource(
ctx context.Context,
request mcp.ReadResourceRequest,
) ([]mcp.ResourceContents, error) {
uri := request.Params.URI
// Check if it's a file:// URI
if !strings.HasPrefix(uri, "file://") {
return nil, fmt.Errorf("unsupported URI scheme: %s", uri)
}
// Extract the path from the URI
path := strings.TrimPrefix(uri, "file://")
// Validate the path
validPath, err := fs.validatePath(path)
if err != nil {
return nil, err
}
// Get file info
fileInfo, err := os.Stat(validPath)
if err != nil {
return nil, err
}
// If it's a directory, return a listing
if fileInfo.IsDir() {
entries, err := os.ReadDir(validPath)
if err != nil {
return nil, err
}
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())
entryURI := pathToResourceURI(entryPath)
if entry.IsDir() {
result.WriteString(fmt.Sprintf("[DIR] %s (%s)\n", entry.Name(), entryURI))
} else {
info, err := entry.Info()
if err == nil {
result.WriteString(fmt.Sprintf("[FILE] %s (%s) - %d bytes\n",
entry.Name(), entryURI, info.Size()))
} else {
result.WriteString(fmt.Sprintf("[FILE] %s (%s)\n", entry.Name(), entryURI))
}
}
}
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: uri,
MIMEType: "text/plain",
Text: result.String(),
},
}, nil
}
// It's a file, determine how to handle it
mimeType := detectMimeType(validPath)
// Check file size
if fileInfo.Size() > MAX_INLINE_SIZE {
// File is too large to inline, return a reference instead
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: uri,
MIMEType: "text/plain",
Text: fmt.Sprintf("File is too large to display inline (%d bytes). Use the read_file tool to access specific portions.", fileInfo.Size()),
},
}, nil
}
// Read the file content
content, err := os.ReadFile(validPath)
if err != nil {
return nil, err
}
// Handle based on content type
if isTextFile(mimeType) {
// It's a text file, return as text
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: uri,
MIMEType: mimeType,
Text: string(content),
},
}, nil
} else {
// It's a binary file
if fileInfo.Size() <= MAX_BASE64_SIZE {
// Small enough for base64 encoding
return []mcp.ResourceContents{
mcp.BlobResourceContents{
URI: uri,
MIMEType: mimeType,
Blob: base64.StdEncoding.EncodeToString(content),
},
}, nil
} else {
// Too large for base64, return a reference
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: uri,
MIMEType: "text/plain",
Text: fmt.Sprintf("Binary file (%s, %d bytes). Use the read_file tool to access specific portions.", mimeType, fileInfo.Size()),
},
}, nil
}
}
}