Metadata-Version: 2.4
Name: ssh-mcp-vn
Version: 0.2.1
Summary: High-performance SSH & SFTP MCP Server for Linux
Author-email: NamHT <namht.dev@gmail.com>
License-Expression: MIT
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: mcp<2,>=1.2.0
Requires-Dist: asyncssh>=2.14.0
Requires-Dist: pydantic>=2.8.0
Requires-Dist: pydantic-settings>=2.4.0
Requires-Dist: python-dotenv>=1.0.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
Requires-Dist: ruff>=0.5.0; extra == "dev"
Requires-Dist: mypy>=1.10.0; extra == "dev"

# SSH & SFTP MCP Server (ssh-mcp-vn)

Một MCP (Model Context Protocol) Server mạnh mẽ dùng để thực thi lệnh (SSH) và truyền tải file (SFTP) trên **nhiều máy chủ Linux từ xa (multi-host)** cùng lúc.

## Hướng dẫn cài đặt

### Chạy qua UVX (Được khuyến nghị)
Bạn có thể chạy trực tiếp server này qua `uvx` mà không cần phải tải hay cài đặt thủ công:

```bash
uvx ssh-mcp-vn
```

## Các tính năng chính

- **Hỗ trợ Multi-host**: Có thể tương tác với nhiều máy chủ Linux mà không cần chạy nhiều instance.
- **Thực thi qua SSH**: Chạy lệnh bash/shell tùy ý trên máy chủ từ xa.
- **Quản lý thư mục**: Dễ dàng tạo cấu trúc thư mục từ xa (hoạt động giống lệnh `mkdir -p`).
- **Tải lên qua SFTP**: Đẩy file từ máy tính cá nhân lên máy chủ từ xa.
- **Tải về qua SFTP**: Tải file từ máy chủ từ xa về máy tính.
- **Liệt kê file bằng SFTP**: Xem danh sách các file trong một thư mục bất kỳ trên máy chủ.

## Cấu hình (Khuyên dùng qua File JSON)

Vì mảng cấu hình có thể khá dài và việc viết chuỗi JSON dính liền với các dấu `\` (escape) trong cấu hình môi trường sẽ phức tạp, bạn nên tách cấu hình ra một file JSON riêng biệt.

**Bước 1:** Tạo một file có tên `ssh_servers.json` ở bất kỳ đâu trên máy của bạn (ví dụ `C:\Users\BiNgo\.codex\ssh_servers.json`) và điền danh sách các server dạng mảng thuần túy.

Ưu tiên dùng SSH key qua `key_path` và user vận hành riêng có sudo. Không nên để `root` + `password` plaintext làm cấu hình mặc định.

```json
[
  {
    "name": "toppion-linux",
    "host": "192.168.1.100",
    "user": "ops",
    "key_path": "C:\\Users\\BiNgo\\.ssh\\toppion_ops_ed25519",
    "port": 22
  },
  {
    "name": "toppion-registry",
    "host": "192.168.1.101",
    "user": "ops",
    "key_path": "C:\\Users\\BiNgo\\.ssh\\toppion_ops_ed25519"
  },
  {
    "name": "toppion-master",
    "host": "192.168.1.102",
    "user": "ops",
    "key_path": "C:\\Users\\BiNgo\\.ssh\\toppion_ops_ed25519"
  }
]
```

**Bước 2:** Cập nhật `mcp_config.json` của ArcticFactory chỉ truyền vào biến `SSH_CONFIG_FILE` trỏ đến file đó:

```json
{
  "mcpServers": {
    "ssh-server": {
      "command": "uvx",
      "args": [
        "ssh-mcp-vn"
      ],
      "env": {
        "SSH_CONFIG_FILE": "C:\\Users\\NamHT\\ssh_servers.json"
      }
    }
  }
}
```

### Thêm server KHÔNG dùng file JSON (nhập thẳng)

Nếu chỉ có 1 server và không muốn tạo file cấu hình hay escape chuỗi JSON, bạn có thể khai báo **từng biến env riêng** trong `mcp_config.json`:

**Dạng 1 — Một server (từng biến env riêng, khuyên dùng):**

```json
{
  "mcpServers": {
    "ssh-server": {
      "command": "uvx",
      "args": [
        "ssh-mcp-vn"
      ],
      "env": {
        "SSH_NAME": "production",
        "SSH_HOST": "192.168.1.10",
        "SSH_USER": "ops",
        "SSH_PORT": "22",
        "SSH_KEY_PATH": "~/.ssh/toppion_ops_ed25519"
      }
    }
  }
}
```

Các biến hỗ trợ: `SSH_NAME` (bắt buộc), `SSH_HOST` (bắt buộc), `SSH_USER`, `SSH_PORT` (mặc định 22), `SSH_KEY_PATH`, `SSH_PASSWORD`.

**Dạng 2 — Nhiều server (mảng JSON):**

```json
{
  "mcpServers": {
    "ssh-server": {
      "command": "uvx",
      "args": [
        "ssh-mcp-vn"
      ],
      "env": {
        "SSH_SERVERS": "[{\"name\": \"production\", \"host\": \"192.168.1.10\", \"user\": \"ops\", \"key_path\": \"~/.ssh/toppion_ops_ed25519\"}, {\"name\": \"staging\", \"host\": \"192.168.1.11\", \"user\": \"ops\", \"key_path\": \"~/.ssh/toppion_ops_ed25519\"}]"
      }
    }
  }
}
```

> Thứ tự ưu tiên: `SSH_CONFIG_FILE` > `SSH_SERVERS` > các biến `SSH_NAME`/`SSH_HOST` riêng lẻ. Nếu chỉ có 1 server, dùng Dạng 1 cho gọn, không cần escape JSON.

Hoặc chạy trực tiếp qua CLI với tham số `--servers` (dùng để test nhanh, hỗ trợ cả object đơn lẫn array):

```bash
uvx ssh-mcp-vn --servers '{"name": "production", "host": "192.168.1.10", "user": "ops", "key_path": "~/.ssh/toppion_ops_ed25519"}' --smoke-test production
```

### Các trường trong từng block JSON cấu hình
Mỗi object của một server hỗ trợ các trường sau:
- `name` (bắt buộc): Tên gọi riêng cho server này (VD: "production", "staging") để giúp công cụ AI nhận biết server muốn thao tác.
- `host` (bắt buộc): Địa chỉ IP hoặc Hostname của máy chủ SSH.
- `user` (bắt buộc): Tên đăng nhập (username).
- `port` (tùy chọn): Cổng SSH, mặc định là 22.
- `key_path` (khuyên dùng): Đường dẫn tuyệt đối đến file khóa bí mật (private key).
- `password` (tùy chọn, không khuyên dùng): Mật khẩu đăng nhập. Chỉ nên dùng tạm khi chưa chuyển xong sang SSH key.

### Kiểm tra cấu hình và smoke test

Sau khi sửa `ssh_servers.json`, hãy restart/reload Codex MCP trước khi kết luận lỗi config vì MCP server đọc cấu hình khi process khởi động.

Kiểm tra config đã được đọc đúng và không in secret:

```bash
uvx ssh-mcp-vn --config-file "C:\Users\BiNgo\.codex\ssh_servers.json" --validate-config
```

Chạy smoke test chuẩn cho toàn bộ server:

```bash
uvx ssh-mcp-vn --config-file "C:\Users\BiNgo\.codex\ssh_servers.json" --smoke-test all
```

Smoke test dùng lệnh:

```bash
hostname && whoami && pwd && uptime
```

Nếu đang làm trực tiếp trong source repo, có thể dùng script:

```bash
python3 uvx/ssh/scripts/smoke_test.py --config-file "C:\Users\BiNgo\.codex\ssh_servers.json" --server all
```

Checklist vận hành ngắn:

1. Dùng alias MCP thống nhất: `toppion-linux`, `toppion-registry`, `toppion-master`.
2. Không phụ thuộc shell alias; MCP chỉ dùng `name` trong `ssh_servers.json`.
3. Ưu tiên `key_path`; hạn chế `password` plaintext.
4. Dùng user vận hành riêng có sudo; chỉ dùng `root` khi thật cần.
5. Sửa config xong thì restart/reload Codex MCP rồi chạy smoke test.

## Danh sách công cụ (Tools)

- `ssh_execute_command`: Chạy một lệnh shell trên máy Linux từ xa.
- `ssh_create_folder`: Tạo thư mục từ xa.
- `sftp_upload_file`: Chuyển một file từ local lên máy chủ.
- `sftp_download_file`: Lấy một file từ máy chủ về local.
- `sftp_list_directory`: Liệt kê tất cả file trong một thư mục từ xa.
- `list_ssh_servers`: Liệt kê server đã cấu hình. **Chỉ xuất hiện khi cấu hình nhiều hơn 1 server.**

> Khi chỉ cấu hình **1 server**, tham số `server_name` của các tool là **tùy chọn** (bỏ trống cũng được, hệ thống tự dùng server duy nhất). Khi có **nhiều server**, `server_name` là bắt buộc để biết thao tác trên máy nào.

## Hướng dẫn sử dụng (Usage)

Sau khi server đã được khai báo, bạn thao tác qua **`server_name`** — giá trị phải khớp chính xác trường `name` trong cấu hình. Ví dụ với server có `"name": "toppion-linux"`:

**1. Chạy lệnh shell trên server**

```text
ssh_execute_command
  server_name: toppion-linux
  command: df -h
```

*Chỉ có 1 server?* Bỏ qua `server_name`:

```text
ssh_execute_command
  command: df -h
```

**2. Tạo thư mục từ xa**

```text
ssh_create_folder
  server_name: toppion-linux
  remote_path: /var/www/backend/logs
```

**3. Truyền file qua SFTP**

Upload file từ máy local lên server:

```text
sftp_upload_file
  server_name: toppion-linux
  local_path: /Users/namho/config/app.yml
  remote_path: /etc/app/config.yml
```

Download file từ server về máy local:

```text
sftp_download_file
  server_name: toppion-linux
  remote_path: /var/log/nginx/error.log
  local_path: /Users/namho/downloads/error.log
```

Liệt kê file trong một thư mục trên server:

```text
sftp_list_directory
  server_name: toppion-linux
  remote_path: /var/log
```

**4. Xem danh sách server đã cấu hình** (chỉ có khi cấu hình nhiều server)

```text
list_ssh_servers
```

> Mẹo: Muốn thao tác nhanh với nhiều server, hãy đặt `name` ngắn gọn và ổn định (VD: `production`, `staging`, `toppion-linux`) và dùng đúng alias đó trong mọi lời gọi tool.

## Changelog

### 0.2.0 - 2026-08-10

**Breaking**
- Xoá tool `ssh_smoke_test` và `ssh_smoke_test_all` khỏi MCP (vẫn dùng được qua CLI `--smoke-test`).

**Changed**
- Chỉ cấu hình 1 server → `server_name` là tùy chọn ở mọi tool; `list_ssh_servers` chỉ xuất hiện khi có nhiều server.

### 0.1.8 - 2026-08-10

**New**
- `SSH_SERVERS`, `--servers` và file JSON giờ chấp nhận cả object đơn `{...}` lẫn array `[{...}]`, không bắt buộc bọc mảng khi chỉ có 1 server.

### 0.1.7 - 2026-08-10

**Fixed**
- Khóa `mcp>=1.2.0,<2` để tránh `uvx` resolve nhầm lên `mcp` 2.0.0, bản này đã bỏ `mcp.server.fastmcp` khiến server crash với `ModuleNotFoundError: No module named 'mcp.server.fastmcp'`.

### 0.1.5 - 2026-07-05

**New**
- `list_ssh_servers`: Xem danh sách server đã cấu hình với thông tin auth đã sanitize, không in password.
- `ssh_smoke_test`: Kiểm tra nhanh một server bằng lệnh `hostname && whoami && pwd && uptime`.
- `ssh_smoke_test_all`: Kiểm tra nhanh toàn bộ server trong `ssh_servers.json`.
- CLI `--validate-config`: Kiểm tra config và cảnh báo vận hành mà không cần mở MCP client.

**Improved**
- Khuyến nghị cấu hình bằng `key_path` và user vận hành riêng thay vì mặc định `root` + password plaintext.
- Publish scripts dùng `UV_PUBLISH_TOKEN` thay vì lưu PyPI token trong source.
- `key_path` hỗ trợ `~` và biến môi trường.
- SSH command có timeout mặc định để tránh treo lâu khi server không phản hồi.

**Fixed**
- Sửa tương thích MCP SDK bằng cách bỏ tham số `description` không được hỗ trợ trong `FastMCP(...)`.
- `ssh_create_folder` quote remote path trước khi chạy `mkdir -p`.

Xem lịch sử đầy đủ tại [CHANGELOG.md](CHANGELOG.md).

## License
MIT
