Skip to main content
Glama

Filesystem MCP Server

by mark3labs
package handler import ( "context" "encoding/json" "os" "path/filepath" "strings" "testing" "github.com/mark3labs/mcp-go/mcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestHandleTree(t *testing.T) { // Setup a temporary directory for the test tmpDir := t.TempDir() // Create a handler with the temp dir as an allowed path allowedDirs := resolveAllowedDirs(t, tmpDir) fsHandler, err := NewFilesystemHandler(allowedDirs) require.NoError(t, err) ctx := context.Background() // Create test directory structure // /tmpDir/ // ├── file1.txt // ├── subdir1/ // │ ├── file2.txt // │ └── subdir2/ // │ └── file3.txt // └── emptydir/ file1Path := filepath.Join(tmpDir, "file1.txt") err = os.WriteFile(file1Path, []byte("content1"), 0644) require.NoError(t, err) subdir1Path := filepath.Join(tmpDir, "subdir1") err = os.Mkdir(subdir1Path, 0755) require.NoError(t, err) file2Path := filepath.Join(subdir1Path, "file2.txt") err = os.WriteFile(file2Path, []byte("content2"), 0644) require.NoError(t, err) subdir2Path := filepath.Join(subdir1Path, "subdir2") err = os.Mkdir(subdir2Path, 0755) require.NoError(t, err) file3Path := filepath.Join(subdir2Path, "file3.txt") err = os.WriteFile(file3Path, []byte("content3"), 0644) require.NoError(t, err) emptydirPath := filepath.Join(tmpDir, "emptydir") err = os.Mkdir(emptydirPath, 0755) require.NoError(t, err) t.Run("tree with default depth", func(t *testing.T) { req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ Arguments: map[string]interface{}{ "path": tmpDir, }, }, } res, err := fsHandler.HandleTree(ctx, req) require.NoError(t, err) require.False(t, res.IsError) // Verify the response contains tree structure require.Len(t, res.Content, 2) textContent := res.Content[0].(mcp.TextContent) assert.Contains(t, textContent.Text, "Directory tree for") assert.Contains(t, textContent.Text, "max depth: 3") // Parse the JSON to verify structure lines := textContent.Text assert.Contains(t, lines, "file1.txt") assert.Contains(t, lines, "subdir1") assert.Contains(t, lines, "file2.txt") assert.Contains(t, lines, "subdir2") assert.Contains(t, lines, "file3.txt") assert.Contains(t, lines, "emptydir") // Verify embedded resource embeddedResource := res.Content[1].(mcp.EmbeddedResource) assert.Equal(t, "resource", embeddedResource.Type) assert.Equal(t, "application/json", embeddedResource.Resource.(mcp.TextResourceContents).MIMEType) }) t.Run("tree with custom depth", func(t *testing.T) { req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ Arguments: map[string]interface{}{ "path": tmpDir, "depth": 2.0, // Only go 2 levels deep }, }, } res, err := fsHandler.HandleTree(ctx, req) require.NoError(t, err) require.False(t, res.IsError) textContent := res.Content[0].(mcp.TextContent) assert.Contains(t, textContent.Text, "max depth: 2") // Should include file1.txt, subdir1, file2.txt, subdir2, emptydir // but NOT file3.txt (which is at depth 3) assert.Contains(t, textContent.Text, "file1.txt") assert.Contains(t, textContent.Text, "subdir1") assert.Contains(t, textContent.Text, "file2.txt") assert.Contains(t, textContent.Text, "subdir2") assert.Contains(t, textContent.Text, "emptydir") // file3.txt should not be included at depth 2 assert.NotContains(t, textContent.Text, "file3.txt") }) t.Run("tree with depth 1", func(t *testing.T) { req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ Arguments: map[string]interface{}{ "path": tmpDir, "depth": 1.0, // Only show immediate children }, }, } res, err := fsHandler.HandleTree(ctx, req) require.NoError(t, err) require.False(t, res.IsError) textContent := res.Content[0].(mcp.TextContent) assert.Contains(t, textContent.Text, "max depth: 1") // Should only include immediate children assert.Contains(t, textContent.Text, "file1.txt") assert.Contains(t, textContent.Text, "subdir1") assert.Contains(t, textContent.Text, "emptydir") // Should not include nested files assert.NotContains(t, textContent.Text, "file2.txt") assert.NotContains(t, textContent.Text, "subdir2") assert.NotContains(t, textContent.Text, "file3.txt") }) t.Run("tree of empty directory", func(t *testing.T) { req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ Arguments: map[string]interface{}{ "path": emptydirPath, }, }, } res, err := fsHandler.HandleTree(ctx, req) require.NoError(t, err) require.False(t, res.IsError) textContent := res.Content[0].(mcp.TextContent) assert.Contains(t, textContent.Text, "Directory tree for") // Parse JSON to verify it's a directory with no children jsonStart := textContent.Text[strings.Index(textContent.Text, "{"):] var tree FileNode err = json.Unmarshal([]byte(jsonStart), &tree) require.NoError(t, err) assert.Equal(t, "directory", tree.Type) assert.Equal(t, "emptydir", tree.Name) assert.Nil(t, tree.Children) }) t.Run("try to tree a file instead of directory", func(t *testing.T) { req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ Arguments: map[string]interface{}{ "path": file1Path, }, }, } res, err := fsHandler.HandleTree(ctx, req) require.NoError(t, err) require.True(t, res.IsError) require.Len(t, res.Content, 1) textContent := res.Content[0].(mcp.TextContent) assert.Contains(t, textContent.Text, "not a directory") }) t.Run("try to tree non-existent directory", func(t *testing.T) { nonExistentPath := filepath.Join(tmpDir, "non_existent_directory") req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ Arguments: map[string]interface{}{ "path": nonExistentPath, }, }, } res, err := fsHandler.HandleTree(ctx, req) require.NoError(t, err) require.True(t, res.IsError) }) t.Run("path is in a non-allowed directory", func(t *testing.T) { otherDir := t.TempDir() req := mcp.CallToolRequest{ Params: mcp.CallToolParams{ Arguments: map[string]interface{}{ "path": otherDir, }, }, } res, err := fsHandler.HandleTree(ctx, req) require.NoError(t, err) require.True(t, res.IsError) }) }

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