SSH MCP Server
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é.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues