"""Data models and result types for the File System MCP Server."""
from dataclasses import dataclass
from datetime import datetime
from typing import Optional, List, Dict, Any, Union
import stat
import os
@dataclass
class FileMetadata:
"""Metadata information for a file or directory."""
size: int
modified_time: str
permissions: str
is_directory: bool
mime_type: str
created_time: Optional[str] = None
accessed_time: Optional[str] = None
owner: Optional[str] = None
group: Optional[str] = None
@classmethod
def from_path(cls, path: str, mime_type: str = "unknown") -> "FileMetadata":
"""Create FileMetadata from a file path."""
try:
stat_result = os.stat(path)
# Format permissions as octal string
permissions = oct(stat.S_IMODE(stat_result.st_mode))
# Format timestamps
modified_time = datetime.fromtimestamp(stat_result.st_mtime).isoformat()
created_time = datetime.fromtimestamp(stat_result.st_ctime).isoformat()
accessed_time = datetime.fromtimestamp(stat_result.st_atime).isoformat()
# Get owner and group if available (Unix-like systems)
owner = None
group = None
try:
import pwd
import grp
owner = pwd.getpwuid(stat_result.st_uid).pw_name
group = grp.getgrgid(stat_result.st_gid).gr_name
except (ImportError, KeyError):
# Not available on Windows or if user/group doesn't exist
pass
return cls(
size=stat_result.st_size,
modified_time=modified_time,
permissions=permissions,
is_directory=stat.S_ISDIR(stat_result.st_mode),
mime_type=mime_type,
created_time=created_time,
accessed_time=accessed_time,
owner=owner,
group=group
)
except OSError as e:
raise ValueError(f"Cannot get metadata for {path}: {e}")
@dataclass
class ErrorResponse:
"""Structured error response with recovery suggestions."""
error_type: str
message: str
details: Optional[Dict[str, Any]] = None
recovery_suggestions: Optional[List[str]] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
result = {
"error_type": self.error_type,
"message": self.message
}
if self.details:
result["details"] = self.details
if self.recovery_suggestions:
result["recovery_suggestions"] = self.recovery_suggestions
return result
@dataclass
class FileReadResult:
"""Result of a file read operation."""
content: Optional[str]
metadata: Optional[FileMetadata]
success: bool
error: Optional[ErrorResponse] = None
is_binary: bool = False
encoding: Optional[str] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
result = {
"success": self.success,
"is_binary": self.is_binary
}
if self.content is not None:
result["content"] = self.content
if self.metadata:
result["metadata"] = {
"size": self.metadata.size,
"modified_time": self.metadata.modified_time,
"permissions": self.metadata.permissions,
"is_directory": self.metadata.is_directory,
"mime_type": self.metadata.mime_type,
"created_time": self.metadata.created_time,
"accessed_time": self.metadata.accessed_time,
"owner": self.metadata.owner,
"group": self.metadata.group
}
if self.encoding:
result["encoding"] = self.encoding
if self.error:
result["error"] = self.error.to_dict()
return result
@dataclass
class FileWriteResult:
"""Result of a file write operation."""
path: str
metadata: Optional[FileMetadata]
success: bool
backup_created: Optional[str] = None
error: Optional[ErrorResponse] = None
bytes_written: Optional[int] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
result = {
"path": self.path,
"success": self.success
}
if self.metadata:
result["metadata"] = {
"size": self.metadata.size,
"modified_time": self.metadata.modified_time,
"permissions": self.metadata.permissions,
"is_directory": self.metadata.is_directory,
"mime_type": self.metadata.mime_type
}
if self.backup_created:
result["backup_created"] = self.backup_created
if self.bytes_written is not None:
result["bytes_written"] = self.bytes_written
if self.error:
result["error"] = self.error.to_dict()
return result
@dataclass
class FileUpdateResult:
"""Result of a file update operation."""
path: str
metadata: Optional[FileMetadata]
success: bool
backup_created: Optional[str] = None
error: Optional[ErrorResponse] = None
bytes_written: Optional[int] = None
restored_from_backup: bool = False
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
result = {
"path": self.path,
"success": self.success,
"restored_from_backup": self.restored_from_backup
}
if self.metadata:
result["metadata"] = {
"size": self.metadata.size,
"modified_time": self.metadata.modified_time,
"permissions": self.metadata.permissions,
"is_directory": self.metadata.is_directory,
"mime_type": self.metadata.mime_type
}
if self.backup_created:
result["backup_created"] = self.backup_created
if self.bytes_written is not None:
result["bytes_written"] = self.bytes_written
if self.error:
result["error"] = self.error.to_dict()
return result
@dataclass
class DirectoryEntry:
"""Information about a directory entry."""
name: str
path: str
metadata: FileMetadata
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
return {
"name": self.name,
"path": self.path,
"metadata": {
"size": self.metadata.size,
"modified_time": self.metadata.modified_time,
"permissions": self.metadata.permissions,
"is_directory": self.metadata.is_directory,
"mime_type": self.metadata.mime_type
}
}
@dataclass
class DirectoryListResult:
"""Result of a directory listing operation."""
path: str
entries: List[DirectoryEntry]
success: bool
total_entries: int
filtered_entries: int
error: Optional[ErrorResponse] = None
pattern_used: Optional[str] = None
recursive: bool = False
max_depth_reached: bool = False
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
result = {
"path": self.path,
"success": self.success,
"total_entries": self.total_entries,
"filtered_entries": self.filtered_entries,
"recursive": self.recursive,
"max_depth_reached": self.max_depth_reached,
"entries": [entry.to_dict() for entry in self.entries]
}
if self.pattern_used:
result["pattern_used"] = self.pattern_used
if self.error:
result["error"] = self.error.to_dict()
return result
@dataclass
class FileDeleteResult:
"""Result of a file deletion operation."""
path: str
success: bool
backup_location: Optional[str] = None
error: Optional[ErrorResponse] = None
was_directory: bool = False
files_deleted: int = 1
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
result = {
"path": self.path,
"success": self.success,
"was_directory": self.was_directory,
"files_deleted": self.files_deleted
}
if self.backup_location:
result["backup_location"] = self.backup_location
if self.error:
result["error"] = self.error.to_dict()
return result
@dataclass
class FileInfoResult:
"""Result of a file info operation."""
path: str
metadata: Optional[FileMetadata]
success: bool
error: Optional[ErrorResponse] = None
exists: bool = False
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary for JSON serialization."""
result = {
"path": self.path,
"success": self.success,
"exists": self.exists
}
if self.metadata:
result["metadata"] = {
"size": self.metadata.size,
"modified_time": self.metadata.modified_time,
"permissions": self.metadata.permissions,
"is_directory": self.metadata.is_directory,
"mime_type": self.metadata.mime_type,
"created_time": self.metadata.created_time,
"accessed_time": self.metadata.accessed_time,
"owner": self.metadata.owner,
"group": self.metadata.group
}
if self.error:
result["error"] = self.error.to_dict()
return result
# Type aliases for common result types
OperationResult = Union[
FileReadResult,
FileWriteResult,
FileUpdateResult,
DirectoryListResult,
FileDeleteResult,
FileInfoResult
]