diff --git a/README.md b/README.md index 5625b19..34f5bf6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,9 @@ An Anthropic Model Context Protocol (MCP) server that provides SSH access to rem - Provides SSH connection to remote servers - Enables command execution on remote systems - Secure public key authentication +- SCP/SFTP file transfer (upload, download, directory listing, file removal, disk usage) - Configurable via environment variables or MCP config +- Automatically terminates when the client stops polling for resources/prompts (controlled by `MCP_SSH_POLL_INTERVAL` and `MCP_SSH_MISSED_POLLS_THRESHOLD`) ## Installation @@ -28,6 +30,8 @@ The SSH server can be configured using environment variables or the MCP JSON con | `MCP_SSH_KEY_FILENAME` | Path to SSH private key file | None | | `MCP_SSH_SERVER_NAME` | Custom name for the server instance | "SSH Server" | | `MCP_SSH_TOOL_PREFIX` | Prefix for tool names (e.g., 'server1_' for 'server1_ssh_connect') | "" | +| `MCP_SSH_POLL_INTERVAL` | Interval in seconds between client polling requests (`resources/list` / `prompts/list`) | 60 | +| `MCP_SSH_MISSED_POLLS_THRESHOLD` | Number of missed polling intervals before the server auto-shuts down | 3 | ### Claude Desktop MCP Configuration @@ -101,12 +105,17 @@ This server implements the Anthropic MCP protocol and provides the following too - `ssh_connect`: Connect to an SSH server using public key authentication (using config or explicit parameters) - `ssh_execute`: Execute a command on the SSH server - `ssh_disconnect`: Disconnect from the SSH server +- `scp_upload`: Upload a file to the remote server via SFTP/SCP +- `scp_download`: Download a file from the remote server via SFTP/SCP +- `scp_listdir`: List a remote directory via SFTP +- `scp_remove`: Remove a remote file via SFTP +- `scp_disk_usage`: Get remote disk usage statistics via SFTP ### Example ```python from mcp import ClientSession, StdioServerParameters -from mcpssh.server import SSHServerMCP +from mcpssh.server import SSHServerMCP, FileTransferParams # Start the server in a subprocess server_params = StdioServerParameters( @@ -129,6 +138,37 @@ with ClientSession(server_params) as client: result = client.ssh_execute(command="ls -la") print(result["stdout"]) + # Upload a file + upload_params = FileTransferParams( + local_path="/path/to/local.txt", + remote_path="/tmp/remote.txt", + max_size=1024*1024*10, # 10MB + on_conflict="RENAME" # Options: FAIL, OVERWRITE, RENAME + ) + upload_result = client.scp_upload(upload_params) + print(upload_result) + + # Download a file + download_params = FileTransferParams( + local_path="/tmp/downloaded.txt", + remote_path="/tmp/remote.txt", + on_conflict="OVERWRITE" + ) + download_result = client.scp_download(download_params) + print(download_result) + + # List a remote directory + dir_result = client.scp_listdir("/tmp") + print(dir_result) + + # Remove a remote file + rm_result = client.scp_remove("/tmp/remote.txt") + print(rm_result) + + # Get remote disk usage + disk_result = client.scp_disk_usage("/tmp") + print(disk_result) + # Disconnect client.ssh_disconnect() ``` @@ -152,6 +192,33 @@ server = SSHServerMCP( server.run(transport="stdio") ``` +## File Transfer Parameters + +File transfer tools (`scp_upload`, `scp_download`) use the `FileTransferParams` model: + +```python +class FileTransferParams(BaseModel): + local_path: str + remote_path: str + max_size: Optional[int] = 1073741824 # bytes (default: 1GB) + on_conflict: Literal["FAIL", "OVERWRITE", "RENAME"] = "FAIL" +``` + +- `local_path`: Path to the local file (upload: source, download: destination) +- `remote_path`: Path to the remote file (upload: destination, download: source) +- `max_size`: Optional file size limit (in bytes) +- `on_conflict`: What to do if the file exists (`FAIL`, `OVERWRITE`, `RENAME`) + +## Conflict Resolution +- `FAIL`: Abort if the file exists +- `OVERWRITE`: Replace the existing file +- `RENAME`: Add a timestamp suffix to avoid overwriting + +## Error Handling +- All file operations return a `success` boolean and detailed error messages if applicable. +- Partial transfers are automatically cleaned up unless configured otherwise. +- Disk space and file size are checked before transfer. + ## Security Note This tool provides full access to a remote system. It should only be used with virtual machines in sandbox environments where security implications are well understood. diff --git a/setup.py b/setup.py index fc639a0..7d7cb6a 100644 --- a/setup.py +++ b/setup.py @@ -4,5 +4,6 @@ from setuptools import setup setup( name="mcpssh", + version="0.1.1", package_dir={"": "src"}, ) \ No newline at end of file