Commit 3ab02d5

mo khan <mo@mokhan.ca>
2025-08-18 14:39:15
fix: filesystem mime types
1 parent 0bf769b
Changed files (2)
pkg
pkg/filesystem/server.go
@@ -88,6 +88,10 @@ func New(allowedDirs []string) *mcp.Server {
 			}
 
 			filePath := req.URI[7:]
+			// Ensure absolute path for file:// URIs
+			if !strings.HasPrefix(filePath, "/") {
+				filePath = "/" + filePath
+			}
 			validPath, err := tree.validatePath(filePath)
 			if err != nil {
 				return mcp.ReadResourceResult{}, fmt.Errorf("access denied: %v", err)
pkg/filesystem/tree.go
@@ -24,23 +24,262 @@ func (fs *Tree) discoverFiles(dirPath string) []mcp.Resource {
 	var resources []mcp.Resource
 
 	programmingMimeTypes := map[string]string{
+		// Go
 		".go":   "text/x-go",
+		".mod":  "text/x-go-mod",
+		".sum":  "text/plain",
+		
+		// Rust
 		".rs":   "text/x-rust",
+		".rlib": "application/x-rust-library",
+		
+		// Python
 		".py":   "text/x-python",
-		".java": "text/x-java",
-		".c":    "text/x-c",
-		".cpp":  "text/x-c++",
-		".h":    "text/x-c",
-		".hpp":  "text/x-c++",
-		".sh":   "text/x-shellscript",
+		".pyc":  "application/x-python-bytecode",
+		".pyo":  "application/x-python-bytecode",
+		".pyd":  "application/x-python-extension",
+		".pyw":  "text/x-python",
+		".pyz":  "application/x-python-archive",
+		".whl":  "application/x-wheel+zip",
+		
+		// Java ecosystem
+		".java":       "text/x-java",
+		".class":      "application/java-vm",
+		".jar":        "application/java-archive",
+		".war":        "application/java-archive",
+		".scala":      "text/x-scala",
+		".kotlin":     "text/x-kotlin",
+		".kt":         "text/x-kotlin",
+		".kts":        "text/x-kotlin",
+		".gradle":     "text/x-gradle",
+		".groovy":     "text/x-groovy",
+		".clj":        "text/x-clojure",
+		".cljs":       "text/x-clojure",
+		
+		// C/C++ family
+		".c":     "text/x-c",
+		".cpp":   "text/x-c++",
+		".cxx":   "text/x-c++",
+		".cc":    "text/x-c++",
+		".c++":   "text/x-c++",
+		".h":     "text/x-c",
+		".hpp":   "text/x-c++",
+		".hxx":   "text/x-c++",
+		".hh":    "text/x-c++",
+		".h++":   "text/x-c++",
+		".inc":   "text/x-c",
+		".cs":    "text/x-csharp",
+		".vb":    "text/x-vb",
+		".fs":    "text/x-fsharp",
+		
+		// JavaScript/TypeScript ecosystem
+		".js":    "text/javascript",
+		".mjs":   "text/javascript",
+		".cjs":   "text/javascript",
+		".jsx":   "text/jsx",
+		".ts":    "text/typescript",
+		".tsx":   "text/tsx",
+		".vue":   "text/x-vue",
+		".svelte": "text/x-svelte",
+		
+		// Ruby
+		".rb":      "text/x-ruby",
+		".rbw":     "text/x-ruby",
+		".rake":    "text/x-ruby",
+		".gemspec": "text/x-ruby",
+		".ru":      "text/x-ruby",
+		
+		// PHP
+		".php":   "text/x-php",
+		".php3":  "text/x-php",
+		".php4":  "text/x-php",
+		".php5":  "text/x-php",
+		".phtml": "text/x-php",
+		
+		// Shell scripting
+		".sh":    "text/x-shellscript",
+		".bash":  "text/x-shellscript",
+		".zsh":   "text/x-shellscript",
+		".fish":  "text/x-shellscript",
+		".ksh":   "text/x-shellscript",
+		".csh":   "text/x-shellscript",
+		".tcsh":  "text/x-shellscript",
+		".bat":   "text/x-msdos-batch",
+		".cmd":   "text/x-msdos-batch",
+		".ps1":   "text/x-powershell",
+		".psm1":  "text/x-powershell",
+		
+		// Perl
+		".pl":   "text/x-perl",
+		".pm":   "text/x-perl",
+		".pod":  "text/x-perl",
+		".t":    "text/x-perl",
+		
+		// Swift
+		".swift": "text/x-swift",
+		
+		// Objective-C
+		".m":  "text/x-objc",
+		".mm": "text/x-objc++",
+		
+		// Functional languages
+		".hs":   "text/x-haskell",
+		".lhs":  "text/x-haskell",
+		".ml":   "text/x-ocaml",
+		".mli":  "text/x-ocaml",
+		".elm":  "text/x-elm",
+		".erl":  "text/x-erlang",
+		".hrl":  "text/x-erlang",
+		".ex":   "text/x-elixir",
+		".exs":  "text/x-elixir",
+		".f":    "text/x-fortran",
+		".f77":  "text/x-fortran",
+		".f90":  "text/x-fortran",
+		".f95":  "text/x-fortran",
+		".for":  "text/x-fortran",
+		
+		// Lisp family
+		".lisp": "text/x-lisp",
+		".lsp":  "text/x-lisp",
+		".cl":   "text/x-common-lisp",
+		".scm":  "text/x-scheme",
+		".ss":   "text/x-scheme",
+		
+		// R and statistics
+		".r":   "text/x-r",
+		".R":   "text/x-r",
+		".rmd": "text/x-r-markdown",
+		".Rmd": "text/x-r-markdown",
+		
+		// Assembly
+		".asm": "text/x-asm",
+		".s":   "text/x-asm",
+		".S":   "text/x-asm",
+		
+		// Other programming languages
+		".lua":    "text/x-lua",
+		".dart":   "text/x-dart",
+		".pas":    "text/x-pascal",
+		".pp":     "text/x-pascal",
+		".dpr":    "text/x-pascal",
+		".v":      "text/x-verilog",
+		".vhd":    "text/x-vhdl",
+		".vhdl":   "text/x-vhdl",
+		".ada":    "text/x-ada",
+		".adb":    "text/x-ada",
+		".ads":    "text/x-ada",
+		".cob":    "text/x-cobol",
+		".cbl":    "text/x-cobol",
+		".d":      "text/x-d",
+		".nim":    "text/x-nim",
+		".nims":   "text/x-nim",
+		".zig":    "text/x-zig",
+		".jl":     "text/x-julia",
+		".cr":     "text/x-crystal",
+		".tcl":    "text/x-tcl",
+		".tk":     "text/x-tcl",
+		
+		// Markup and data formats
+		".html":   "text/html",
+		".htm":    "text/html",
+		".xhtml":  "application/xhtml+xml",
+		".xml":    "text/xml",
+		".xsl":    "text/xsl",
+		".xslt":   "text/xsl",
+		".json":   "application/json",
+		".jsonl":  "application/jsonl",
+		".ndjson": "application/x-ndjson",
+		".yaml":   "text/yaml",
+		".yml":    "text/yaml",
+		".toml":   "application/toml",
+		".ini":    "text/plain",
+		".cfg":    "text/plain",
+		".conf":   "text/plain",
+		".config": "text/plain",
+		".properties": "text/plain",
+		".env":    "text/plain",
+		".csv":    "text/csv",
+		".tsv":    "text/tab-separated-values",
+		
+		// Stylesheets
+		".css":  "text/css",
+		".scss": "text/x-scss",
+		".sass": "text/x-sass",
+		".less": "text/x-less",
+		".styl": "text/x-stylus",
+		
+		// Documentation
+		".md":       "text/markdown",
+		".markdown": "text/markdown",
+		".mdown":    "text/markdown",
+		".mkd":      "text/markdown",
+		".rst":      "text/x-rst",
+		".asciidoc": "text/x-asciidoc",
+		".adoc":     "text/x-asciidoc",
+		".tex":      "text/x-tex",
+		".latex":    "text/x-tex",
+		".org":      "text/x-org",
+		
+		// Database
+		".sql":  "text/x-sql",
+		".ddl":  "text/x-sql",
+		".dml":  "text/x-sql",
+		".nosql": "text/x-nosql",
+		
+		// Plain text variations
+		".txt":  "text/plain",
+		".text": "text/plain",
+		".log":  "text/plain",
+		".out":  "text/plain",
+		".err":  "text/plain",
+		
+		// Protocol buffers and IDL
+		".proto": "text/x-protobuf",
+		".thrift": "text/x-thrift",
+		".avro":   "text/x-avro",
+		".idl":    "text/x-idl",
+		
+		// Build and deployment
+		".dockerfile": "text/x-dockerfile",
+		".containerfile": "text/x-dockerfile",
+		".makefile": "text/x-makefile",
+		".mk":       "text/x-makefile",
+		".cmake":    "text/x-cmake",
+		".bazel":    "text/x-bazel",
+		".bzl":      "text/x-bazel",
+		".ant":      "text/xml",
+		".maven":    "text/xml",
+		
+		// Specialized formats
+		".graphql": "application/graphql",
+		".gql":     "application/graphql",
+		".prisma":  "text/x-prisma",
+		".tf":      "text/x-terraform",
+		".tfvars":  "text/x-terraform",
+		".hcl":     "text/x-hcl",
+		".nomad":   "text/x-nomad",
+		".consul":  "text/x-consul",
 	}
 
 	specialFiles := map[string]string{
-		"Makefile":   "text/x-makefile",
-		"README":     "text/plain",
-		"LICENSE":    "text/plain",
-		"go.mod":     "text/x-go-mod",
-		"Cargo.toml": "application/toml",
+		"Makefile":     "text/x-makefile",
+		"README":       "text/plain",
+		"LICENSE":      "text/plain",
+		"go.mod":       "text/x-go-mod",
+		"go.sum":       "text/plain",
+		"Cargo.toml":   "application/toml",
+		"Cargo.lock":   "application/toml",
+		"package.json": "application/json",
+		"package-lock.json": "application/json",
+		"yarn.lock":    "text/plain",
+		"Gemfile":      "text/x-ruby",
+		"Gemfile.lock": "text/plain",
+		"requirements.txt": "text/plain",
+		"Dockerfile":   "text/x-dockerfile",
+		"docker-compose.yml": "text/yaml",
+		"docker-compose.yaml": "text/yaml",
+		".gitignore":   "text/plain",
+		".gitattributes": "text/plain",
 	}
 
 	const maxFiles = 500
@@ -81,7 +320,33 @@ func (fs *Tree) discoverFiles(dirPath string) []mcp.Resource {
 		} else {
 			mimeType = mime.TypeByExtension(ext)
 			if mimeType == "" {
-				return nil
+				// Default to text/plain for common text-like extensions
+				textExtensions := []string{".conf", ".config", ".ini", ".log", ".env", ".properties", ".cfg", ".spec"}
+				for _, textExt := range textExtensions {
+					if ext == textExt {
+						mimeType = "text/plain"
+						break
+					}
+				}
+				// If still no MIME type and it looks like a text file, include it
+				if mimeType == "" && (ext == "" || len(ext) <= 4) {
+					// Skip files that are likely binary based on extension
+					binaryExtensions := []string{".exe", ".bin", ".so", ".dll", ".dylib", ".img", ".iso", ".zip", ".tar", ".gz", ".bz2", ".xz", ".7z", ".rar", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".ico", ".pdf", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx"}
+					isBinary := false
+					for _, binExt := range binaryExtensions {
+						if ext == binExt {
+							isBinary = true
+							break
+						}
+					}
+					if !isBinary {
+						mimeType = "text/plain"
+					}
+				}
+				// If still no MIME type, skip the file
+				if mimeType == "" {
+					return nil
+				}
 			}
 		}