diff --git a/README.md b/README.md new file mode 100644 index 0000000..27ee775 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# mcp-docker-bash + +HTTP-MCP-Server mit persistenten PTY-Sessions und Hintergrund-Jobs. Agenten können interaktive Programme (`htop`, `vim`, …) steuern und deren Bildschirminhalt lesen. + +## Starten + +```bash +docker compose up +# Erster Start: apt + pip (ca. 60 s), danach dank pip-cache schnell +# Server läuft auf http://localhost:8000 +``` + +## Claude Code / Desktop einbinden + +`mcp-config.json` in die Claude-Einstellungen kopieren oder manuell eintragen: + +```json +{ + "mcpServers": { + "bash": { "type": "http", "url": "http://localhost:8000/mcp" } + } +} +``` + +## Tools + +| Tool | Beschreibung | +|---|---| +| `exec` | Einmaliger Shell-Befehl, synchron (Timeout konfigurierbar) | +| `pty_create` | Neues persistentes Terminal öffnen → `session_id` | +| `pty_send` | Text/Tasten senden (`\n` = Enter, `\x03` = Ctrl-C, `\x04` = Ctrl-D) | +| `pty_read` | Aktuellen Bildschirminhalt als Zeilen-Array lesen | +| `pty_kill` | Session beenden | +| `pty_list` | Alle offenen Sessions | +| `job_run` | Hintergrund-Job starten → `job_id` (kehrt sofort zurück) | +| `job_output` | Output-Zeilen lesen, inkrementell per `offset` | +| `job_status` | Status (`running`/`done`/`failed`), Exit-Code, Zeilenzahl | +| `job_kill` | Job per SIGTERM beenden | +| `job_list` | Alle bekannten Jobs | + +## Typischer PTY-Flow + +``` +pty_create → session_id +pty_send { session_id, text: "htop\n" } +pty_read { session_id, wait_ms: 500 } ← gerendertes Grid, kein ANSI-Müll +pty_send { session_id, text: "\x03" } ← Ctrl-C +pty_kill { session_id } +``` + +## Workspace + +`./workspace` wird als `/workspace` in den Container gemountet — Dateien dort sind direkt in jeder PTY-Session sichtbar. + +## Erweiterungspunkte + +- **Auth**: API-Key-Header in `main.py` → FastAPI `Depends` +- **Session-Cleanup**: TTL-Hintergrundtask der tote PTY-Sessions aufräumt +- **Streaming**: SSE-Endpoint für Live-Output ohne Polling +- **Ressourcenlimits**: `ulimit` im Dockerfile oder `mem_limit` in `docker-compose.yml` diff --git a/docker-compose.yml b/docker-compose.yml index 6e28395..529b083 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,16 +1,25 @@ services: mcp-bash: - build: - context: ./server - dockerfile: Dockerfile + image: python:3.12-slim container_name: mcp-bash restart: unless-stopped + working_dir: /app + command: > + bash -c " + apt-get update -qq && + apt-get install -y --no-install-recommends + curl wget git vim nano htop procps jq less unzip && + pip install -q -r requirements.txt && + python -u main.py" ports: - "8000:8000" volumes: - # shared workspace: host files are visible inside the PTY sessions + - ./server:/app:ro - ./workspace:/workspace + - pip-cache:/root/.cache/pip environment: - PYTHONUNBUFFERED=1 - # tty: true keeps stdin open for ptyprocess inside the container tty: true + +volumes: + pip-cache: diff --git a/server/Dockerfile b/server/Dockerfile deleted file mode 100644 index 90e3145..0000000 --- a/server/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM python:3.12-slim - -# tools agents typically need in the PTY -RUN apt-get update && apt-get install -y --no-install-recommends \ - bash curl wget git vim nano htop procps jq less unzip \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /app - -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -COPY main.py . - -EXPOSE 8000 - -CMD ["python", "-u", "main.py"]