Skip to main content
Glama

Filesystem MCP Server

by mark3labs
package handler import ( "context" "encoding/json" "fmt" "os" "path/filepath" "github.com/mark3labs/mcp-go/mcp" ) func (fs *FilesystemHandler) HandleTree( 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 } // Extract depth parameter (optional, default: 3) depth := 3 // Default value if depthParam, err := request.RequireFloat("depth"); err == nil { depth = int(depthParam) } // Extract follow_symlinks parameter (optional, default: false) followSymlinks := false // Default value if followParam, err := request.RequireBool("follow_symlinks"); err == nil { followSymlinks = followParam } // Validate the path is within allowed directories 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: The specified path is not a directory", }, }, IsError: true, }, nil } // Build the tree structure tree, err := fs.buildTree(validPath, depth, 0, followSymlinks) if err != nil { return &mcp.CallToolResult{ Content: []mcp.Content{ mcp.TextContent{ Type: "text", Text: fmt.Sprintf("Error building directory tree: %v", err), }, }, IsError: true, }, nil } // Convert to JSON jsonData, err := json.MarshalIndent(tree, "", " ") if err != nil { return &mcp.CallToolResult{ Content: []mcp.Content{ mcp.TextContent{ Type: "text", Text: fmt.Sprintf("Error generating JSON: %v", err), }, }, IsError: true, }, nil } // Create resource URI for the directory resourceURI := pathToResourceURI(validPath) // Return the result return &mcp.CallToolResult{ Content: []mcp.Content{ mcp.TextContent{ Type: "text", Text: fmt.Sprintf("Directory tree for %s (max depth: %d):\n\n%s", validPath, depth, string(jsonData)), }, mcp.EmbeddedResource{ Type: "resource", Resource: mcp.TextResourceContents{ URI: resourceURI, MIMEType: "application/json", Text: string(jsonData), }, }, }, }, nil } // buildTree builds a tree representation of the filesystem starting at the given path func (fs *FilesystemHandler) buildTree(path string, maxDepth int, currentDepth int, followSymlinks bool) (*FileNode, error) { // Validate the path validPath, err := fs.validatePath(path) if err != nil { return nil, err } // Get file info info, err := os.Stat(validPath) if err != nil { return nil, err } // Create the node node := &FileNode{ Name: filepath.Base(validPath), Path: validPath, Modified: info.ModTime(), } // Set type and size if info.IsDir() { node.Type = "directory" // If we haven't reached the max depth, process children if currentDepth < maxDepth { // Read directory entries entries, err := os.ReadDir(validPath) if err != nil { return nil, err } // Process each entry for _, entry := range entries { entryPath := filepath.Join(validPath, entry.Name()) // Handle symlinks if entry.Type()&os.ModeSymlink != 0 { if !followSymlinks { // Skip symlinks if not following them continue } // Resolve symlink linkDest, err := filepath.EvalSymlinks(entryPath) if err != nil { // Skip invalid symlinks continue } // Validate the symlink destination is within allowed directories if !fs.isPathInAllowedDirs(linkDest) { // Skip symlinks pointing outside allowed directories continue } entryPath = linkDest } // Recursively build child node childNode, err := fs.buildTree(entryPath, maxDepth, currentDepth+1, followSymlinks) if err != nil { // Skip entries with errors continue } // Add child to the current node node.Children = append(node.Children, childNode) } } } else { node.Type = "file" node.Size = info.Size() } return node, nil }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mark3labs/mcp-filesystem-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server