#
tokens: 4915/50000 9/9 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── Dockerfile
├── FileManagerMcp.sln
├── LICENSE
├── README.md
├── smithery.yaml
└── src
    ├── appsettings.json
    ├── FileManagerMcp.csproj
    ├── Login.cs
    ├── Program.cs
    ├── ServiceConfiguration.cs
    └── Toolkits
        └── FtpTool.cs
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | packages/
 4 | *.nuget/
 5 | bin/
 6 | obj/
 7 | dist/
 8 | build/
 9 | 
10 | # IDE - VSCode
11 | .vscode/*
12 | !.vscode/settings.json
13 | !.vscode/tasks.json
14 | !.vscode/launch.json
15 | !.vscode/extensions.json
16 | 
17 | # IDE - Visual Studio / Rider
18 | .vs/
19 | .idea/
20 | *.suo
21 | *.user
22 | *.userosscache
23 | *.sln.docstates
24 | *.userprefs
25 | 
26 | # Compiled output
27 | *.dll
28 | *.exe
29 | *.pdb
30 | *.cache
31 | 
32 | # Logs and databases
33 | *.log
34 | logs/
35 | *.sqlite
36 | *.db
37 | 
38 | # Environment files
39 | .env
40 | .env.local
41 | .env.development.local
42 | .env.test.local
43 | .env.production.local
44 | 
45 | # OS generated files
46 | .DS_Store
47 | .DS_Store?
48 | ._*
49 | .Spotlight-V100
50 | .Trashes
51 | ehthumbs.db
52 | Thumbs.db
53 | 
54 | # Build results
55 | [Dd]ebug/
56 | [Dd]ebugPublic/
57 | [Rr]elease/
58 | [Rr]eleases/
59 | x64/
60 | x86/
61 | [Aa][Rr][Mm]/
62 | [Aa][Rr][Mm]64/
63 | bld/
64 | [Bb]in/
65 | [Oo]bj/
66 | [Ll]og/
67 | [Ll]ogs/
68 | 
69 | # NuGet Packages
70 | *.nupkg
71 | # NuGet Symbol Packages
72 | *.snupkg
73 | # The packages folder can be ignored because of Package Restore
74 | **/[Pp]ackages/*
75 | # except build/, which is used as an MSBuild target.
76 | !**/[Pp]ackages/build/
77 | 
78 | # User-specific files
79 | *.rsuser
80 | *.suo
81 | *.user
82 | *.userosscache
83 | *.sln.docstates
84 | 
85 | # Test Results
86 | [Tt]est[Rr]esult*/
87 | [Bb]uild[Ll]og.* 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # File Manager MCP 📂
  2 | 
  3 | [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
  4 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/yourusername/FileManagerMcp/graphs/commit-activity)
  5 | [![smithery badge](https://smithery.ai/badge/@taha-ghadirian/filemanagermcp)](https://smithery.ai/server/@taha-ghadirian/filemanagermcp)
  6 | 
  7 | A powerful and user-friendly File Manager application that provides a modern interface for managing FTP file operations.
  8 | 
  9 | > 🤖 **AI-Powered Development**: This project is a result of vibe coding through AI prompt engineering. The entire codebase was developed by collaborating with AI, showcasing the potential of modern AI-assisted development practices.
 10 | 
 11 | ## 🚀 Features
 12 | 
 13 | - 📁 Browse and manage remote FTP directories
 14 | - ⬆️ Upload files and directories
 15 | - ⬇️ Download files and directories
 16 | - 🗑️ Delete files and directories
 17 | - 📝 Create new directories
 18 | - 🔄 Recursive file operations support
 19 | - 💻 Clean and intuitive user interface
 20 | 
 21 | ## 🚀 Usage
 22 | 
 23 | ### Using Smithery Hosted Service (Recommended) 
 24 | 
 25 | 1. Visit [File Manager on smithery](https://smithery.ai/server/@taha-ghadirian/filemanagermcp)
 26 | 
 27 | 2. Create an account or sign in
 28 | 
 29 | 3. Connect using your preferred development environment:
 30 |    - Visual Studio Code
 31 |    - Cursor
 32 |    - Any IDE or tool with MCP integration
 33 | 
 34 | ### Alternative: Local Installation
 35 | 
 36 | If you prefer running the application locally, follow these steps:
 37 | 
 38 | 1. Make sure you have the [.NET 9.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) installed
 39 | 2. Follow the installation steps below to build and run the application
 40 | 
 41 | ### 🛠️ Build and Run Locally
 42 | 
 43 | 1. Clone the repository:
 44 |    
 45 |    HTTPS:
 46 |    ```bash
 47 |    git clone https://github.com/taha-ghadirian/FileManagerMcp.git
 48 |    ```
 49 |    
 50 |    SSH:
 51 |    ```bash
 52 |    git clone [email protected]:taha-ghadirian/FileManagerMcp.git
 53 |    ```
 54 | 
 55 |    Then navigate to the project directory:
 56 |    ```bash
 57 |    cd FileManagerMcp
 58 |    ```
 59 | 
 60 | 2. Install dependencies:
 61 | ```bash
 62 | dotnet restore
 63 | ```
 64 | 
 65 | 3. Build the project:
 66 | ```bash
 67 | dotnet build
 68 | ```
 69 | 
 70 | 4. Run the application in inspector:
 71 | ```bash
 72 | npx @modelcontextprotocol/inspector dotnet run
 73 | ```
 74 | 
 75 | ## 🔧 Configuration
 76 | 
 77 | The application uses environment variables for configuration. Here are the required environment variables:
 78 | 
 79 | | Option | Description | Required | Default |
 80 | |----------|-------------|----------|---------|
 81 | | `ftpHost` | FTP server hostname or IP address | Yes | - |
 82 | | `ftpUsername` | FTP account username | Yes | - |
 83 | | `ftpPassword` | FTP account password | Yes | - |
 84 | | `ftpPort` | FTP server port | No | 21 |
 85 | 
 86 | You can set these environment variables in several ways:
 87 | 
 88 | 1. Setting them inline when running the application:
 89 |    ```bash
 90 |    ftpHost=ftp.example.com ftpUsername=myuser ftpPassword=mypassword npx @modelcontextprotocol/inspector dotnet run
 91 |    ```
 92 | 
 93 | ⚠️ **Security Note**: Never commit sensitive information like passwords to version control. Always use environment variables or secure secrets management for production deployments.
 94 | 
 95 | 
 96 | ## 🤝 Contributing
 97 | 
 98 | Contributions are welcome! Please feel free to submit a Pull Request.
 99 | 
100 | 1. Fork the project
101 | 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
102 | 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
103 | 4. Push to the branch (`git push origin feature/AmazingFeature`)
104 | 5. Open a Pull Request
105 | 
106 | ## 📝 License
107 | 
108 | This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details. This means:
109 | 
110 | - You can freely use, modify, and distribute this software
111 | - If you modify and distribute this software, you must:
112 |   - Make your source code available
113 |   - License your modifications under GPL v3.0
114 |   - Document your changes
115 |   - Preserve the original copyright notices
116 | 
117 | ## 📞 Support
118 | 
119 | If you have any questions or need support, please open an issue in the GitHub repository.
120 | 
121 | ## ✨ Acknowledgments
122 | 
123 | - Thanks to all contributors who have helped shape this project
124 | - Built with .NET and modern best practices
125 | 
126 | ---
127 | 
128 | Made with ❤️ by Taha Ghadirian
```

--------------------------------------------------------------------------------
/src/Login.cs:
--------------------------------------------------------------------------------

```csharp
1 | namespace FileManagerMcp;
2 | 
3 | public class FtpCredential
4 | {
5 |     public int port = 21;
6 |     public string host = null!;
7 |     public string username = null!;
8 |     public string password = null!;
9 | }
```

--------------------------------------------------------------------------------
/src/appsettings.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "Logging": {
 3 |     "LogLevel": {
 4 |       "Default": "Information",
 5 |       "Microsoft.AspNetCore": "Warning"
 6 |     }
 7 |   },
 8 |   "AllowedHosts": "*",
 9 |   "FTP": {
10 |       "Host": "ftp://example.com",
11 |       "User": "username",
12 |       "Password": "password",
13 |       "Port": 21
14 |   }
15 | }
```

--------------------------------------------------------------------------------
/src/Program.cs:
--------------------------------------------------------------------------------

```csharp
 1 | using FileManagerMcp;
 2 | using Microsoft.Extensions.DependencyInjection;
 3 | using Microsoft.Extensions.Hosting;
 4 | using Microsoft.Extensions.Logging;
 5 | using Microsoft.Extensions.Configuration;
 6 | 
 7 | 
 8 | var builder = Host.CreateApplicationBuilder(args);
 9 | 
10 | builder.ConfigureServices();
11 | 
12 | var loginDetail = builder.Configuration.GetSection("FTP").Get<FtpCredential>();
13 | builder.Services.AddSingleton(loginDetail ?? throw new ArgumentNullException(nameof(loginDetail)));
14 | 
15 | await builder.Build().RunAsync();
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
 2 | # Build stage
 3 | FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
 4 | WORKDIR /src
 5 | 
 6 | # Copy csproj and restore dependencies
 7 | COPY ["src/FileManagerMcp.csproj", "./"]
 8 | RUN dotnet restore
 9 | 
10 | # Copy the rest of the code
11 | COPY src/ ./
12 | 
13 | # Build and publish
14 | RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
15 | 
16 | # Runtime stage
17 | FROM mcr.microsoft.com/dotnet/runtime:9.0 AS final
18 | WORKDIR /app
19 | 
20 | # Copy the published files from build stage
21 | COPY --from=build /app/publish .
22 | 
23 | # Set the entry point
24 | ENTRYPOINT ["dotnet", "FileManagerMcp.dll"]
25 | 
```

--------------------------------------------------------------------------------
/src/ServiceConfiguration.cs:
--------------------------------------------------------------------------------

```csharp
 1 | // Create a new file to configure services
 2 | using Microsoft.Extensions.DependencyInjection;
 3 | using Microsoft.Extensions.Hosting;
 4 | using Microsoft.Extensions.Logging;
 5 | using Microsoft.Extensions.Configuration;
 6 | using FileManagerMcp.Toolkits;
 7 | 
 8 | public static class ServiceConfiguration
 9 | {
10 |     public static HostApplicationBuilder ConfigureServices(this HostApplicationBuilder builder)
11 |     {
12 |         builder.Configuration
13 |             .SetBasePath(Directory.GetCurrentDirectory())
14 |             .AddEnvironmentVariables()
15 |             .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
16 |             .AddUserSecrets<Program>();
17 |         
18 |         builder.Logging.AddConsole(consoleLogOptions =>
19 |         {
20 |             // Configure all logs to go to stderr
21 |             consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
22 |         });
23 | 
24 |         builder.Services
25 |             .AddMcpServer()
26 |             .WithStdioServerTransport()
27 |             .WithTools<FtpTool>();
28 | 
29 |         return builder;
30 |     }
31 | }
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
 2 | 
 3 | startCommand:
 4 |   type: stdio
 5 |   configSchema:
 6 |     # JSON Schema defining the configuration options for the MCP.
 7 |     type: object
 8 |     required:
 9 |       - ftpHost
10 |       - ftpUsername
11 |       - ftpPassword
12 |     properties:
13 |       ftpHost:
14 |         type: string
15 |         description: FTP host address.
16 |       ftpUsername:
17 |         type: string
18 |         description: FTP username.
19 |       ftpPassword:
20 |         type: string
21 |         description: FTP password.
22 |       ftpPort:
23 |         type: number
24 |         default: 21
25 |         description: FTP port, default is 21.
26 |   commandFunction:
27 |     # A JS function that produces the CLI command based on the given config to start the MCP on stdio.
28 |     |-
29 |     (config) => ({
30 |       command: 'dotnet',
31 |       args: ['FileManagerMcp.dll'],
32 |       env: {
33 |         FTP__HOST: config.ftpHost,
34 |         FTP__USERNAME: config.ftpUsername,
35 |         FTP__PASSWORD: config.ftpPassword,
36 |         FTP__PORT: String(config.ftpPort)
37 |       }
38 |     })
39 |   exampleConfig:
40 |     ftpHost: ftp.example.com
41 |     ftpUsername: user123
42 |     ftpPassword: pass123
43 |     ftpPort: 21
44 | 
```

--------------------------------------------------------------------------------
/src/Toolkits/FtpTool.cs:
--------------------------------------------------------------------------------

```csharp
  1 | using System.ComponentModel;
  2 | using FluentFTP;
  3 | using ModelContextProtocol.Server;
  4 | 
  5 | namespace FileManagerMcp.Toolkits;
  6 | 
  7 | [McpServerToolType]
  8 | public class FtpTool : IDisposable
  9 | {
 10 |     private readonly FtpClient _client;
 11 |     private readonly FtpCredential _credential;
 12 |     public FtpTool(FtpCredential credential)
 13 |     {
 14 |         _credential = credential;
 15 |         _client = new FtpClient(credential.host, credential.username, credential.password, credential.port);
 16 |         _client.Connect();
 17 |     }
 18 | 
 19 | 
 20 |     
 21 |     [McpServerTool, Description("Return list of files in the given directory.")]
 22 |     public string ListFiles(
 23 |         [Description("The directory to list files from.")] string directory = "/",
 24 |         [Description("Whether to list files recursively.")] bool recursive = false)
 25 |     {
 26 |         if (!_client.DirectoryExists(directory))
 27 |         {
 28 |             return $"Error: Directory '{directory}' does not exist";
 29 |         }
 30 | 
 31 |         var items = _client.GetListing(directory, recursive ? FtpListOption.Recursive : FtpListOption.AllFiles);
 32 |         var fileList = items.Select(item => $"{item.Type},{item.FullName},{item.Size},{item.Modified}").ToList();
 33 | 
 34 |         return "Type,Name,Size,Modified\n" + string.Join("\n", fileList);
 35 |     }
 36 | 
 37 |     [McpServerTool, Description("Download a file from the given directory.")]
 38 |     public string DownloadFile(
 39 |         [Description("The file path to download. Example: '/path/to/file.txt'")] string filePath,
 40 |         [Description("The local path to download the file to, example: 'downloads/file.txt'")] string localPath = ".")
 41 |     {
 42 |         if (!_client.FileExists(filePath))
 43 |         {
 44 |             return $"Error: File '{filePath}' does not exist.";
 45 |         }
 46 | 
 47 |         var directory = Path.GetDirectoryName(localPath) ?? ".";
 48 |         if (!_client.DirectoryExists(directory))
 49 |         {
 50 |             Directory.CreateDirectory(directory);
 51 |         }
 52 | 
 53 |         if (File.Exists(localPath))
 54 |         {
 55 |             return $"Error: Local file '{localPath}' already exists, can't overwrite it.";
 56 |         }
 57 | 
 58 |         _client.DownloadFile(filePath, localPath);
 59 | 
 60 |         return $"Downloaded {filePath} to {localPath}";
 61 |     }
 62 | 
 63 |     [McpServerTool, Description("Upload a file to the given directory.")]
 64 |     public string UploadFile(
 65 |         [Description("The file path to upload. Example: 'downloads/file.txt'")] string filePath,
 66 |         [Description("The remote path to upload the file to. Example: '/path/to/file.txt'")] string remotePath = "/")
 67 |     {    
 68 |         if (!File.Exists(filePath))
 69 |         {
 70 |             return $"Error: Local file '{filePath}' does not exist";
 71 |         }
 72 | 
 73 |         if (!_client.DirectoryExists(Path.GetDirectoryName(remotePath)))
 74 |         {
 75 |             return $"Error: Remote directory '{Path.GetDirectoryName(remotePath)}' does not exist";
 76 |         }
 77 | 
 78 |         if (_client.FileExists(remotePath))
 79 |         {
 80 |             return $"Error: Remote file '{remotePath}' already exists";
 81 |         }
 82 | 
 83 |         _client.UploadFile(filePath, remotePath);
 84 | 
 85 |         return $"Uploaded {filePath} to {remotePath}";
 86 |     }
 87 | 
 88 |     [McpServerTool, Description("Delete a file from the given directory.")]
 89 |     public string DeleteFile(
 90 |         [Description("The file path to delete. Example: '/path/to/file.txt'")] string filePath)
 91 |     {
 92 |         if (!_client.FileExists(filePath))
 93 |         {
 94 |             return $"Error: File '{filePath}' does not exist.";
 95 |         }
 96 | 
 97 |         _client.DeleteFile(filePath);
 98 | 
 99 |         return $"Deleted {filePath}";
100 |     }
101 | 
102 |     [McpServerTool, Description("Delete multiple files from the given paths.")]
103 |     public string DeleteFiles(
104 |         [Description("The file paths to delete, separated by commas. Example: '/path/file1.txt,/path/file2.txt'")] string filePaths)
105 |     {
106 |         var paths = filePaths.Split(',').Select(p => p.Trim()).ToList();
107 |         var deleted = new List<string>();
108 |         var failed = new List<string>();
109 | 
110 |         foreach (var path in paths)
111 |         {
112 |             try
113 |             {
114 |                 _client.DeleteFile(path);
115 |                 deleted.Add(path);
116 |             }
117 |             catch (Exception)
118 |             {
119 |                 failed.Add(path);
120 |             }
121 |         }
122 | 
123 |         var result = $"Deleted {deleted.Count} files: {string.Join(", ", deleted)}";
124 |         if (failed.Count != 0)
125 |         {
126 |             result += $"\nFailed to delete {failed.Count} files: {string.Join(", ", failed)}";
127 |         }
128 |         return result;
129 |     }
130 | 
131 |     [McpServerTool, Description("Create a directory in the given path.")]
132 |     public string CreateDirectory(
133 |         [Description("The path to create the directory in. Example: '/path/to/directory'")] string path)
134 |     {
135 |         if (_client.DirectoryExists(path))
136 |         {
137 |             return $"Error: Directory '{path}' already exists.";
138 |         }
139 | 
140 |         _client.CreateDirectory(path);
141 | 
142 |         return $"Created directory {path}";
143 |     }
144 | 
145 |     [McpServerTool, Description("Delete a directory from the given path.")]
146 |     public string DeleteDirectory(
147 |         [Description("The path to delete the directory from. Example: '/path/to/directory'")] string path)
148 |     {
149 |         _client.DeleteDirectory(path);
150 | 
151 |         return $"Deleted directory {path}";
152 |     }
153 | 
154 |     public void Dispose()
155 |     {
156 |         _client.Disconnect();
157 |     }
158 | }
```