@@ -68,7 +68,7 @@ func NewFilesystemServer(allowedDirs []string) (*FilesystemServer, error) {
68
68
allowedDirs : normalized ,
69
69
server : server .NewMCPServer (
70
70
"secure-filesystem-server" ,
71
- "0.3 .0" ,
71
+ "0.4 .0" ,
72
72
server .WithResourceCapabilities (true , true ),
73
73
),
74
74
}
@@ -171,7 +171,7 @@ func (s *FilesystemServer) isPathInAllowedDirs(path string) bool {
171
171
if err != nil {
172
172
return false
173
173
}
174
-
174
+
175
175
// Add trailing separator to ensure we're checking a directory or a file within a directory
176
176
// and not a prefix match (e.g., /tmp/foo should not match /tmp/foobar)
177
177
if ! strings .HasSuffix (absPath , string (filepath .Separator )) {
@@ -217,7 +217,7 @@ func (s *FilesystemServer) validatePath(requestedPath string) (string, error) {
217
217
if err != nil {
218
218
return "" , fmt .Errorf ("parent directory does not exist: %s" , parent )
219
219
}
220
-
220
+
221
221
if ! s .isPathInAllowedDirs (realParent ) {
222
222
return "" , fmt .Errorf (
223
223
"access denied - parent directory outside allowed directories" ,
@@ -232,7 +232,7 @@ func (s *FilesystemServer) validatePath(requestedPath string) (string, error) {
232
232
"access denied - symlink target outside allowed directories" ,
233
233
)
234
234
}
235
-
235
+
236
236
return realPath , nil
237
237
}
238
238
@@ -334,54 +334,54 @@ func (s *FilesystemServer) handleReadResource(
334
334
request mcp.ReadResourceRequest ,
335
335
) ([]mcp.ResourceContents , error ) {
336
336
uri := request .Params .URI
337
-
337
+
338
338
// Check if it's a file:// URI
339
339
if ! strings .HasPrefix (uri , "file://" ) {
340
340
return nil , fmt .Errorf ("unsupported URI scheme: %s" , uri )
341
341
}
342
-
342
+
343
343
// Extract the path from the URI
344
344
path := strings .TrimPrefix (uri , "file://" )
345
-
345
+
346
346
// Validate the path
347
347
validPath , err := s .validatePath (path )
348
348
if err != nil {
349
349
return nil , err
350
350
}
351
-
351
+
352
352
// Get file info
353
353
fileInfo , err := os .Stat (validPath )
354
354
if err != nil {
355
355
return nil , err
356
356
}
357
-
357
+
358
358
// If it's a directory, return a listing
359
359
if fileInfo .IsDir () {
360
360
entries , err := os .ReadDir (validPath )
361
361
if err != nil {
362
362
return nil , err
363
363
}
364
-
364
+
365
365
var result strings.Builder
366
366
result .WriteString (fmt .Sprintf ("Directory listing for: %s\n \n " , validPath ))
367
-
367
+
368
368
for _ , entry := range entries {
369
369
entryPath := filepath .Join (validPath , entry .Name ())
370
370
entryURI := pathToResourceURI (entryPath )
371
-
371
+
372
372
if entry .IsDir () {
373
373
result .WriteString (fmt .Sprintf ("[DIR] %s (%s)\n " , entry .Name (), entryURI ))
374
374
} else {
375
375
info , err := entry .Info ()
376
376
if err == nil {
377
- result .WriteString (fmt .Sprintf ("[FILE] %s (%s) - %d bytes\n " ,
377
+ result .WriteString (fmt .Sprintf ("[FILE] %s (%s) - %d bytes\n " ,
378
378
entry .Name (), entryURI , info .Size ()))
379
379
} else {
380
380
result .WriteString (fmt .Sprintf ("[FILE] %s (%s)\n " , entry .Name (), entryURI ))
381
381
}
382
382
}
383
383
}
384
-
384
+
385
385
return []mcp.ResourceContents {
386
386
mcp.TextResourceContents {
387
387
URI : uri ,
@@ -390,10 +390,10 @@ func (s *FilesystemServer) handleReadResource(
390
390
},
391
391
}, nil
392
392
}
393
-
393
+
394
394
// It's a file, determine how to handle it
395
395
mimeType := detectMimeType (validPath )
396
-
396
+
397
397
// Check file size
398
398
if fileInfo .Size () > MAX_INLINE_SIZE {
399
399
// File is too large to inline, return a reference instead
@@ -405,13 +405,13 @@ func (s *FilesystemServer) handleReadResource(
405
405
},
406
406
}, nil
407
407
}
408
-
408
+
409
409
// Read the file content
410
410
content , err := os .ReadFile (validPath )
411
411
if err != nil {
412
412
return nil , err
413
413
}
414
-
414
+
415
415
// Handle based on content type
416
416
if isTextFile (mimeType ) {
417
417
// It's a text file, return as text
@@ -483,7 +483,7 @@ func (s *FilesystemServer) handleReadFile(
483
483
IsError : true ,
484
484
}, nil
485
485
}
486
-
486
+
487
487
if info .IsDir () {
488
488
// For directories, return a resource reference instead
489
489
resourceURI := pathToResourceURI (validPath )
@@ -507,7 +507,7 @@ func (s *FilesystemServer) handleReadFile(
507
507
508
508
// Determine MIME type
509
509
mimeType := detectMimeType (validPath )
510
-
510
+
511
511
// Check file size
512
512
if info .Size () > MAX_INLINE_SIZE {
513
513
// File is too large to inline, return a resource reference
@@ -558,7 +558,7 @@ func (s *FilesystemServer) handleReadFile(
558
558
} else {
559
559
// It's a binary file
560
560
resourceURI := pathToResourceURI (validPath )
561
-
561
+
562
562
if info .Size () <= MAX_BASE64_SIZE {
563
563
// Small enough for base64 encoding
564
564
return & mcp.CallToolResult {
@@ -732,7 +732,7 @@ func (s *FilesystemServer) handleListDirectory(
732
732
IsError : true ,
733
733
}, nil
734
734
}
735
-
735
+
736
736
if ! info .IsDir () {
737
737
return & mcp.CallToolResult {
738
738
Content : []mcp.Content {
@@ -760,17 +760,17 @@ func (s *FilesystemServer) handleListDirectory(
760
760
761
761
var result strings.Builder
762
762
result .WriteString (fmt .Sprintf ("Directory listing for: %s\n \n " , validPath ))
763
-
763
+
764
764
for _ , entry := range entries {
765
765
entryPath := filepath .Join (validPath , entry .Name ())
766
766
resourceURI := pathToResourceURI (entryPath )
767
-
767
+
768
768
if entry .IsDir () {
769
769
result .WriteString (fmt .Sprintf ("[DIR] %s (%s)\n " , entry .Name (), resourceURI ))
770
770
} else {
771
771
info , err := entry .Info ()
772
772
if err == nil {
773
- result .WriteString (fmt .Sprintf ("[FILE] %s (%s) - %d bytes\n " ,
773
+ result .WriteString (fmt .Sprintf ("[FILE] %s (%s) - %d bytes\n " ,
774
774
entry .Name (), resourceURI , info .Size ()))
775
775
} else {
776
776
result .WriteString (fmt .Sprintf ("[FILE] %s (%s)\n " , entry .Name (), resourceURI ))
@@ -908,7 +908,7 @@ func (s *FilesystemServer) handleMoveFile(
908
908
IsError : true ,
909
909
}, nil
910
910
}
911
-
911
+
912
912
// Check if source exists
913
913
if _ , err := os .Stat (validSource ); os .IsNotExist (err ) {
914
914
return & mcp.CallToolResult {
@@ -921,7 +921,7 @@ func (s *FilesystemServer) handleMoveFile(
921
921
IsError : true ,
922
922
}, nil
923
923
}
924
-
924
+
925
925
validDest , err := s .validatePath (destination )
926
926
if err != nil {
927
927
return & mcp.CallToolResult {
@@ -934,7 +934,7 @@ func (s *FilesystemServer) handleMoveFile(
934
934
IsError : true ,
935
935
}, nil
936
936
}
937
-
937
+
938
938
// Create parent directory for destination if it doesn't exist
939
939
destDir := filepath .Dir (validDest )
940
940
if err := os .MkdirAll (destDir , 0755 ); err != nil {
@@ -1009,7 +1009,7 @@ func (s *FilesystemServer) handleSearchFiles(
1009
1009
IsError : true ,
1010
1010
}, nil
1011
1011
}
1012
-
1012
+
1013
1013
// Check if it's a directory
1014
1014
info , err := os .Stat (validPath )
1015
1015
if err != nil {
@@ -1023,7 +1023,7 @@ func (s *FilesystemServer) handleSearchFiles(
1023
1023
IsError : true ,
1024
1024
}, nil
1025
1025
}
1026
-
1026
+
1027
1027
if ! info .IsDir () {
1028
1028
return & mcp.CallToolResult {
1029
1029
Content : []mcp.Content {
@@ -1064,15 +1064,15 @@ func (s *FilesystemServer) handleSearchFiles(
1064
1064
// Format results with resource URIs
1065
1065
var formattedResults strings.Builder
1066
1066
formattedResults .WriteString (fmt .Sprintf ("Found %d results:\n \n " , len (results )))
1067
-
1067
+
1068
1068
for _ , result := range results {
1069
1069
resourceURI := pathToResourceURI (result )
1070
1070
info , err := os .Stat (result )
1071
1071
if err == nil {
1072
1072
if info .IsDir () {
1073
1073
formattedResults .WriteString (fmt .Sprintf ("[DIR] %s (%s)\n " , result , resourceURI ))
1074
1074
} else {
1075
- formattedResults .WriteString (fmt .Sprintf ("[FILE] %s (%s) - %d bytes\n " ,
1075
+ formattedResults .WriteString (fmt .Sprintf ("[FILE] %s (%s) - %d bytes\n " ,
1076
1076
result , resourceURI , info .Size ()))
1077
1077
}
1078
1078
} else {
@@ -1132,15 +1132,15 @@ func (s *FilesystemServer) handleGetFileInfo(
1132
1132
}
1133
1133
1134
1134
resourceURI := pathToResourceURI (validPath )
1135
-
1135
+
1136
1136
// Determine file type text
1137
1137
var fileTypeText string
1138
1138
if info .IsDirectory {
1139
1139
fileTypeText = "Directory"
1140
1140
} else {
1141
1141
fileTypeText = "File"
1142
1142
}
1143
-
1143
+
1144
1144
return & mcp.CallToolResult {
1145
1145
Content : []mcp.Content {
1146
1146
mcp.TextContent {
@@ -1164,10 +1164,10 @@ func (s *FilesystemServer) handleGetFileInfo(
1164
1164
Resource : mcp.TextResourceContents {
1165
1165
URI : resourceURI ,
1166
1166
MIMEType : "text/plain" ,
1167
- Text : fmt .Sprintf ("%s: %s (%s, %d bytes)" ,
1168
- fileTypeText ,
1169
- validPath ,
1170
- mimeType ,
1167
+ Text : fmt .Sprintf ("%s: %s (%s, %d bytes)" ,
1168
+ fileTypeText ,
1169
+ validPath ,
1170
+ mimeType ,
1171
1171
info .Size ),
1172
1172
},
1173
1173
},
@@ -1187,7 +1187,7 @@ func (s *FilesystemServer) handleListAllowedDirectories(
1187
1187
1188
1188
var result strings.Builder
1189
1189
result .WriteString ("Allowed directories:\n \n " )
1190
-
1190
+
1191
1191
for _ , dir := range displayDirs {
1192
1192
resourceURI := pathToResourceURI (dir )
1193
1193
result .WriteString (fmt .Sprintf ("%s (%s)\n " , dir , resourceURI ))
0 commit comments