Skip to main content
Glama
vait90
by vait90
README.md
# SSH MCP Szerver (paramiko)

Paramiko-alapú **SSH MCP szerver**, amely távoli gépeken tud parancsot futtatni és
fájlokat mozgatni SFTP-n keresztül. Kétféle transporton érhető el, ami környezeti
változóval / kapcsolóval választható:

- **`http`** – MCP *streamable-http* végpont a `/mcp` útvonalon (ide csatlakozik a
  Cherry Studio), plusz dokumentált **OpenAPI/Swagger** felület (`/docs`, `/openapi.json`).
- **`stdio`** – klasszikus MCP stdio transport (helyi indításhoz / `docker exec`-hez).

> **FONTOS a portokról:** a **2222** az az MCP szerver portja, ahová a Cherry Studio
> csatlakozik. Ez **NEM** a távoli gép SSH portja! A távoli gép SSH portja általában
> a **22** (`SSH_PORT`). Tehát: Cherry Studio → `http://<host-IP>:2222/mcp` → MCP szerver
> → paramiko → távoli gép `22`-es SSH portja.

---

## Elérhető MCP tool-ok

### Állapotmentes (stateless) eszközök — egyszerű, egyszeri műveletek

| Tool | Leírás |
|------|--------|
| `ssh_test` | Kapcsolat és hitelesítés tesztelése egy távoli géppel. |
| `ssh_execute` | **EGYETLEN** shell parancs futtatása friss kapcsolatban (stdout / stderr / exit kód). Nincs memória: a `cd` / `export` **nem** öröklődik a következő hívásra, és **nem** tud interaktív promptra válaszolni. |
| `ssh_upload` | Helyi fájl feltöltése a távoli gépre SFTP-vel. |
| `ssh_download` | Fájl letöltése a távoli gépről SFTP-vel. |

### Állapottartó (stateful), interaktív session eszközök — élő shell

Ezek egy **élő shellt** tartanak nyitva, ahol az állapot megmarad hívások között
(könyvtárváltás `cd` után, `export`-ált változók, interaktív promptok kezelése:
sudo jelszó, apt `[Y/n]` stb.).

| Tool | Leírás |
|------|--------|
| `ssh_open_session` | **1. lépés** – új interaktív shell nyitása, visszaad egy `session_id`-t. |
| `ssh_send` | **2. lépés** – szöveg (parancs vagy prompt-válasz) küldése a session-be. A `session_id`-t **mindig** át kell adni. |
| `ssh_read` | **3. lépés (opcionális)** – további kimenet beolvasása küldés nélkül (lassú/hosszú parancsokhoz). |
| `ssh_close_session` | **4. lépés** – a session lezárása. Mindig zárd le, ha végeztél. |
| `ssh_list_sessions` | A nyitott session-ök listázása (host, felhasználó, tétlenség), pl. ha elveszett a `session_id`. |

> A tool-leírások (docstringek) szándékosan nagyon részletes, egyszerű angol
> nyelvű "USE THIS WHEN..." útmutatót tartalmaznak, hogy a fogyasztó modell
> egyértelműen tudja, **mikor** és **hogyan** használja az egyes eszközöket.

Minden tool paraméterei (`host`, `port`, `username`, `password`, `private_key`,
`private_key_path`, `passphrase`, `timeout`) megadhatók:
- **hívásonként** külön-külön, vagy
- **alapértelmezettként** a `.env` fájlban (`SSH_*` változók). Ami a hívásban nincs
  megadva, azt a rendszer az `SSH_*` környezeti változókból veszi.

Támogatott hitelesítés: **jelszó** és **kulcs** (inline PEM vagy fájlútvonal, opcionális
jelszóval). Az ismeretlen host kulcsokat a szerver automatikusan elfogadja
(`AutoAddPolicy`), hogy az automatizálás gördülékeny legyen.

---

## Állapotmentes vs. állapottartó (interaktív) használat

**Melyiket mikor?**

- **Egyetlen, önálló parancs** (pl. `ls`, `uptime`, `df -h`) → `ssh_execute`.
  Minden hívás friss kapcsolatot nyit, lefuttat egy parancsot, majd bezár.
  **Nincs memória**: a `cd` és `export` **nem** él túl a következő hívásig, és
  interaktív promptra **sem** tud válaszolni.
- **Bármi interaktív vagy több lépéses** (állapotmegőrzés `cd`/`export` után,
  sudo jelszó megadása, apt `[Y/n]` megválaszolása, egymásra épülő parancsok) →
  **interaktív session**: `ssh_open_session` → `ssh_send` → `ssh_read` →
  `ssh_close_session`.

### Ajánlott munkafolyamat (session)

1. **`ssh_open_session`** → visszakapsz egy `session_id`-t (és a belépési
   bannert / első promptot az `initial_output`-ban).
2. **`ssh_send`** → parancsot gépelsz be vagy promptra válaszolsz. A
   `session_id`-t **minden** híváskor át kell adni. Alapból Entert is küld.
3. **`ssh_read`** (opcionális) → lassú/hosszan futó parancsnál további kimenet
   begyűjtése küldés nélkül.
4. **`ssh_close_session`** → ha végeztél, zárd le a session-t.

`ssh_list_sessions`-nel bármikor megnézheted a nyitott session-öket
(host, felhasználó, tétlenség), ha elveszett egy `session_id`.

### Példák (REST végpontokon keresztül)

Session nyitása:

```bash
curl -X POST http://localhost:2222/api/ssh/session/open \
  -H "Content-Type: application/json" \
  -d '{"host":"192.168.1.100","username":"user","password":"secret"}'
# -> {"ok":true,"session_id":"<ID>", "initial_output":"...prompt..."}
```

Könyvtárváltás, ami megmarad (állapottartás):

```bash
curl -X POST http://localhost:2222/api/ssh/session/send \
  -H "Content-Type: application/json" \
  -d '{"session_id":"<ID>","input":"cd /var/log && pwd"}'
# a következő ssh_send már a /var/log-ban futna
```

Sudo parancs + jelszó-prompt megválaszolása:

```bash
# 1) elindítod a sudo parancsot
curl -X POST http://localhost:2222/api/ssh/session/send \
  -H "Content-Type: application/json" \
  -d '{"session_id":"<ID>","input":"sudo apt-get update"}'
# 2) a kimenetben megjelenik a "[sudo] password for user:" prompt -> beküldöd a jelszót
curl -X POST http://localhost:2222/api/ssh/session/send \
  -H "Content-Type: application/json" \
  -d '{"session_id":"<ID>","input":"my_sudo_password"}'
```

Apt `[Y/n]` kérdés megválaszolása:

```bash
curl -X POST http://localhost:2222/api/ssh/session/send \
  -H "Content-Type: application/json" \
  -d '{"session_id":"<ID>","input":"sudo apt-get install htop","read_timeout":5}'
# amikor jön a "Do you want to continue? [Y/n]" kérdés:
curl -X POST http://localhost:2222/api/ssh/session/send \
  -H "Content-Type: application/json" \
  -d '{"session_id":"<ID>","input":"Y"}'
```

Session lezárása:

```bash
curl -X POST http://localhost:2222/api/ssh/session/close \
  -H "Content-Type: application/json" \
  -d '{"session_id":"<ID>"}'
```

> **Időtúllépés / tétlenség / hibák:** minden session-művelet opportunistán
> lezárja a `SSH_SESSION_IDLE_TIMEOUT`-nál (alap 600 mp) régebb óta tétlen
> session-öket, valamint azokat, amelyeknek a csatornája elhalt. Egyszerre
> legfeljebb `SSH_MAX_SESSIONS` (alap 20) session lehet nyitva — a limit elérése
> egyértelmű hibaüzenetet ad. Ha egy `session_id` már nem létezik, a válasz
> pontosan megmondja, mit tegyél (nyiss újat, vagy nézd meg `ssh_list_sessions`-nel).

---

## Projekt felépítés

```
ssh-mcp-server/
├── app/
│   ├── __init__.py
│   ├── ssh_ops.py     # paramiko SSH/SFTP műveletek (közös logika)
│   └── server.py      # MCP tool-ok + FastAPI/OpenAPI + transport választás
├── requirements.txt
├── Dockerfile
├── docker-compose.yml # 2222:2222 publikálás
├── .env.example
└── README.md
```

---

## 1. Gyors indítás Docker-rel (ajánlott)

### Előkészítés

```bash
cd ssh-mcp-server
cp .env.example .env
# szerkeszd a .env-et: add meg a távoli gép adatait (SSH_HOST, SSH_USERNAME, stb.)
```

### Build és indítás (HTTP mód)

```bash
docker compose up -d --build
```

Ez elindítja a szervert **HTTP** módban, és a **2222**-es portot kipublikálja a hosztra
(`ports: "2222:2222"`).

### Ellenőrzés

```bash
curl http://localhost:2222/health
# {"status":"ok","service":"ssh-mcp-server","mcp_endpoint":"/mcp"}
```

- Swagger UI (böngészőben):  `http://localhost:2222/docs`
- OpenAPI JSON:             `http://localhost:2222/openapi.json`
- MCP végpont (Cherry Studio): `http://<host-IP>:2222/mcp`

### Leállítás

```bash
docker compose down
```

---

## 2. HTTP mód kézzel (Docker nélkül, fejlesztéshez)

```bash
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export TRANSPORT=http HOST=0.0.0.0 PORT=2222
python -m app.server
```

---

## 3. stdio mód

Konténerben futó szerver mellett `docker exec`-kel:

```bash
docker exec -i -e TRANSPORT=stdio ssh-mcp-server python -m app.server
```

Vagy közvetlenül, Docker nélkül:

```bash
TRANSPORT=stdio python -m app.server
```

---

## 4. Cherry Studio integráció

### A) HTTP (streamable-http) mód – ajánlott, hálózaton át is működik

A konténer a laptopodon fut Dockerben, a Cherry Studio pedig a **host IP-jét** és a
**2222**-es portot használja.

1. Indítsd el a szervert: `docker compose up -d --build`
2. Derítsd ki a gép (host) IP-címét, amin a Docker fut:
   - Linux: `hostname -I` → pl. `192.168.1.50`
   - Ha a Cherry Studio ugyanazon a gépen fut, a `localhost` / `127.0.0.1` is jó.
3. Cherry Studio → **Beállítások (Settings)** → **MCP Servers** → **Add / Új szerver**.
4. Add meg az alábbiakat:
   - **Type / Típus:** `Streamable HTTP` (ha nincs, akkor `SSE` / `HTTP`)
   - **URL / Endpoint:** `http://<host-IP>:2222/mcp`
     - pl. `http://192.168.1.50:2222/mcp`
     - ugyanazon a gépen: `http://localhost:2222/mcp`
5. Mentsd el és **engedélyezd (Enable)** a szervert. A Cherry Studio betölti a
   `ssh_test`, `ssh_execute`, `ssh_upload`, `ssh_download` tool-okat.

> Ha távoli gépről csatlakozol, győződj meg róla, hogy a **2222**-es port elérhető
> (tűzfal engedélyezze), és a Docker a `0.0.0.0`-ra hallgat (alapból így van).

### B) stdio mód

Ha a Cherry Studio stdio MCP szervert vár (parancsot indít):

- **Command:** `docker`
- **Arguments:**
  ```
  exec -i -e TRANSPORT=stdio ssh-mcp-server python -m app.server
  ```

(Ehhez a `ssh-mcp-server` konténernek futnia kell — `docker compose up -d`.)

---

## 5. `.env` konfiguráció

| Változó | Leírás | Alapértelmezés |
|---------|--------|----------------|
| `TRANSPORT` | `http` vagy `stdio` | `http` |
| `HOST` | MCP HTTP bind cím | `0.0.0.0` |
| `PORT` | MCP HTTP port (amit a Cherry Studio elér) | `2222` |
| `SSH_HOST` | Távoli gép címe | – |
| `SSH_PORT` | Távoli gép SSH portja | `22` |
| `SSH_USERNAME` | SSH felhasználó | – |
| `SSH_PASSWORD` | SSH jelszó (vagy használj kulcsot) | – |
| `SSH_PRIVATE_KEY` | Inline privát kulcs (PEM) | – |
| `SSH_PRIVATE_KEY_PATH` | Privát kulcs fájl útvonala (a konténerben) | – |
| `SSH_PASSPHRASE` | Privát kulcs jelszava | – |
| `SSH_TIMEOUT` | Kapcsolat timeout (mp) | `15` |
| `SSH_SESSION_IDLE_TIMEOUT` | Tétlen interaktív session automatikus lezárása ennyi mp után (0 = nincs) | `600` |
| `SSH_MAX_SESSIONS` | Egyszerre nyitható interaktív session-ök maximuma | `20` |

### Kulcsos hitelesítés Dockerben

Csatold be a kulcsokat a konténerbe, és állítsd be az útvonalat. A
`docker-compose.yml`-ben vedd ki a kommentet a `volumes` sornál:

```yaml
    volumes:
      - ./keys:/keys:ro
```

majd a `.env`-ben:

```
SSH_PRIVATE_KEY_PATH=/keys/id_ed25519
```

---

## 6. REST végpontok teszteléshez (OpenAPI)

A HTTP mód a Cherry Studio MCP végpont mellett REST végpontokat is kínál — ezek
ugyanazokat az SSH műveleteket végzik, és jól használhatók `curl`-lel / Swagger UI-ból:

| Metódus | Útvonal | Művelet |
|---------|---------|---------|
| GET | `/health` | Állapot |
| GET | `/` | Szerver infó |
| POST | `/api/ssh/test` | Kapcsolat teszt |
| POST | `/api/ssh/execute` | Parancs futtatás |
| POST | `/api/ssh/upload` | Fájl feltöltés (SFTP) |
| POST | `/api/ssh/download` | Fájl letöltés (SFTP) |
| POST | `/api/ssh/session/open` | Interaktív session nyitása (1. lépés) |
| POST | `/api/ssh/session/send` | Bemenet küldése a session-be (2. lépés) |
| POST | `/api/ssh/session/read` | Kimenet olvasása küldés nélkül (3. lépés) |
| POST | `/api/ssh/session/close` | Session lezárása (4. lépés) |
| GET | `/api/ssh/session/list` | Nyitott session-ök listája |

Példa (állapotmentes egyparancsos futtatás):

```bash
curl -X POST http://localhost:2222/api/ssh/execute \
  -H "Content-Type: application/json" \
  -d '{"host":"192.168.1.100","username":"user","password":"secret","command":"uname -a"}'
```

---

## Biztonsági megjegyzések

- **Titkok soha nincsenek a kódban** — mindent `.env`-ből / hívási paraméterből olvas.
- A `.env` fájlt a `.dockerignore` és jellemzően a `.gitignore` is kizárja — ne
  kommitold verziókezelőbe.
- A szerver `AutoAddPolicy`-t használ (ismeretlen host kulcsok automatikus elfogadása).
  Zárt hálózaton kényelmes; szigorúbb környezetben érdemes ismert host kulcsokat használni.
- A `2222`-es MCP portot csak megbízható hálózaton tedd elérhetővé.