Skip to main content
Glama
LauOtero

Klipper MCP Server

by LauOtero
README.md
# 🔌 Klipper MCP Server

**Servidor Model Context Protocol de nivel industrial para el ecosistema Klipper / Moonraker.**

[![CI](https://img.shields.io/badge/CI-passing-brightgreen)](#)
[![Licencia: GPLv3](https://img.shields.io/badge/licencia-GPLv3-blue)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)](#requisitos-técnicos)
[![MCP](https://img.shields.io/badge/MCP-2026--07--28-purple)](https://modelcontextprotocol.io)

`klipper-mcp-server` expone, mediante una API uniforme (tools, resources
y prompts), las capacidades necesarias para que clientes LLM (Claude,
GPT, Cursor, VS Code) operen sobre el stack Klipper + Moonraker con
garantías industriales de resiliencia, observabilidad, seguridad y
**rendimiento extremo**.

---

## 📑 Tabla de contenidos

1. [Estado del proyecto](#-estado-del-proyecto)
2. [Descripción y propósito](#-descripción-y-propósito)
3. [Casos de uso](#-casos-de-uso)
4. [Optimizaciones de rendimiento](#-optimizaciones-de-rendimiento)
5. [Requisitos técnicos](#-requisitos-técnicos)
6. [Instalación y puesta en marcha](#-instalación-y-puesta-en-marcha)
7. [Configuración](#-configuración)
8. [Manual de uso](#-manual-de-uso)
9. [Benchmarks y perfilado](#-benchmarks-y-perfilado)
10. [Contribuir](#-contribuir)
11. [Créditos y financiación](#-créditos-y-financiación)
12. [Contacto](#-contacto)
13. [Licencia](#-licencia)

---

## 📌 Estado del proyecto

> **Importante:** el repositorio contiene actualmente la
> **especificación técnica v0.1**
> ([`docs/mcp-klipper.md`](docs/mcp-klipper.md)) como fuente única de
> verdad. Dicha especificación incluye la implementación de referencia
> del núcleo y los contratos de tools, resources, prompts, agentes y
> skills. El código ejecutable se genera a partir de ella.
>
> Las optimizaciones de rendimiento descritas en la
> [sección 4](#-optimizaciones-de-rendimiento) están **especificadas y
> diseñadas**; sus objetivos son metas de ingeniería verificables
> mediante el arnés de benchmark incluido en el plan de medida. Los
> valores de línea base deben capturarse en CI antes de certificar la
> mejora (ver [Benchmarks](#-benchmarks-y-perfilado)).

| Campo | Valor |
|---|---|
| Artefacto | `klipper-mcp-server` |
| Versión | 0.1.0 (baseline) · optimización objetivo v0.2 |
| Protocolo | MCP 2026-07-28 (stateless core) |
| Runtime | Python ≥ 3.10 · asyncio · aiohttp |
| Licencia | GPLv3 |

---

## 🎯 Descripción y propósito

### El problema

El desarrollo de extensiones para Klipper y plugins para Moonraker
presenta tres barreras recurrentes:

1. **Reglas de oro no verificables automáticamente**: muchas
   contribuciones fallan en revisión por usar `time.sleep()`,
   variables globales o I/O síncrono en callbacks del reactor.
2. **Errores silenciosos de configuración**: `SAVE_CONFIG` solo escribe
   en `printer.cfg`; los parámetros auto-calibrados en includes
   provocan fallos de arranque difíciles de diagnosticar.
3. **Ausencia de tooling LLM-aware**: los asistentes genéricos no
   disponen de contexto estructural del ecosistema Klipper/Moonraker.

### La solución

Un servidor MCP que actúa como **capa de abstracción tipada** entre el
cliente LLM y:

- La API REST/JSON-RPC de Moonraker.
- El sistema de archivos de configuración Klipper.
- El log de eventos de Klipper (`klippy.log`).
- Validadores AST y plantillas canónicas de código conforme.

### Objetivos principales

- **Contratos verificables**: cada tool valida su entrada contra schema
  y devuelve resultados deterministas.
- **Resiliencia activa**: circuit breaker por fábrica, cache con purga
  periódica y detector de anomalías EWMA con histéresis.
- **Seguridad por defecto**: comandos G-code peligrosos requieren
  `confirm=True`; las API keys nunca se registran en logs.
- **Rendimiento extremo**: cache multinivel, coalescencia de peticiones
  y economía de tokens (ver [sección 4](#-optimizaciones-de-rendimiento)).
- **Asincronía estricta**: el event loop nunca se bloquea.

### Capacidades entregadas (v0.1)

| Categoría | Cantidad | Descripción |
|---|---:|---|
| Tools | 17 | Herramientas invocables por el modelo |
| Resources | 8 | Recursos direccionables (URIs) |
| Prompts | 10 | Plantillas de interacción predefinidas |
| Agentes | 3 | System prompts especializados |
| Skills | 3 | Guías ejecutables con checklist |
| Core | 6 | Cache multinivel, single-flight, circuit breaker, EWMA, cliente Moonraker, token budget |

---

## 🧩 Casos de uso

1. **Validación offline de configuración**: comprobar `printer.cfg` y
   todo su sistema de `[include]` antes de reiniciar la impresora,
   detectando parámetros auto-calibrados mal ubicados.
2. **Desarrollo de extras y componentes**: generar plantillas canónicas
   y validar con el linter AST que se respetan las reglas del reactor.
3. **Diagnóstico asistido**: analizar `klippy.log` y telemetría con
   prompts especializados y detección de anomalías.
4. **Operación controlada**: ejecutar G-code con salvaguardas para
   comandos peligrosos (movimientos, calibración, cambios de estado).
5. **Telemetría y SLO**: exponer latencias, hit-rate de cache y estado
   del circuit breaker para integrarlos en observabilidad.

---

## ⚡ Optimizaciones de rendimiento

Las siguientes técnicas están especificadas en
[`docs/mcp-klipper.md`](docs/mcp-klipper.md) (§6.2 y §6.6) y son las que
habilitan los objetivos medibles del proyecto.

| Área | Técnica | Archivo | Beneficio |
|---|---|---|---|
| Cache | **Multinivel L1 (RAM) + L2 (disco)** con doble cota (entradas y bytes) | `cache.py` | Reduce latencia de acceso y persistencia entre reinicios |
| Concurrencia | **Single-flight** (coalescencia de peticiones idénticas) | `coalescer.py` | Elimina el *cache stampede*; N llamadas → 1 a Moonraker |
| Red | **Pool de conexiones** persistente (keep-alive, DNS cacheado) | `moonraker_client.py` | Menos handshakes TCP/TLS, menor latencia p95 |
| Serialización | **orjson** con fallback a stdlib `json` | `moonraker_client.py` | Serialización/deserialización más rápida |
| Compresión | **zstd/gzip** en tránsito y en reposo | `compression.py` | Menos bytes en red y disco |
| Paralelización | `asyncio.gather(return_exceptions=True)` + semáforo | `moonraker_client.py` | Solapa latencias con aislamiento de fallos |
| Memoria | Cotas duras, purga activa, streaming, referencias débiles | `cache.py` | RSS estable, sin fugas en ejecución prolongada |
| Tokens | Compactación, delta `unchanged`, rotación O(1) | `token_budget.py` | Menos tokens por respuesta, sin sobrecarga |
| Arranque | **Lazy loading** memoizado | `resources/`, `prompts/` | Menor tiempo de arranque y RSS en reposo |

### Objetivos declarados (metas, no resultados medidos)

| Objetivo | Meta | Métrica |
|---|---|---|
| Reducción de latencia (p50/p95) | ≥ 60 % vs línea base v0.1 | PERF-1…3 |
| Consumo de recursos (CPU/RAM) | ≤ 30 % de los máximos v0.1 | PERF-6…7 |
| Hit-rate combinado L1+L2 | ≥ 95 % | PERF-4 |
| Throughput | ≥ 250 req/s | PERF-5 |
| Bloqueo del event loop | 0 | PERF-8 |

> La metodología completa de perfilado y medición está en
> [§6.6.6 de la especificación](docs/mcp-klipper.md) y en
> [Benchmarks y perfilado](#-benchmarks-y-perfilado).

---

## 🛠 Requisitos técnicos

| Componente | Versión mínima | Versión recomendada | Notas |
|---|---|---|---|
| Python | 3.10 | 3.11 | 3.10 · 3.11 · 3.12 soportadas |
| Klipper | Últimas 2 stable | stable | También `dev` |
| Moonraker | Últimas 2 stable | stable | API REST/JSON-RPC accesible |
| aiohttp | ≥ 3.9 | última 3.x | Cliente HTTP asíncrono |
| mcp | ≥ 2.3 | última 2.x | SDK MCP (`mcp.server.mcpserver.MCPServer`) |
| orjson | ≥ 3.9 (opcional) | última | Fallback automático a `json` |
| zstandard | ≥ 0.22 (opcional) | última | Fallback automático a `gzip` |
| uv | ≥ 0.4 | última | Gestor de entorno recomendado |

Herramientas de desarrollo:

| Herramienta | Uso |
|---|---|
| `pytest` + `pytest-asyncio` | Tests unitarios e integración |
| `pytest-benchmark` | Micro-benchmarks reproducibles |
| `ruff` | Lint y formato |
| `mypy` | Tipado estático |
| `py-spy` / `memray` | Perfilado de CPU y memoria |
| `locust` / `vegeta` | Pruebas de carga y throughput |

Sistemas operativos soportados: **Debian 12**, **Ubuntu 22/24** y
**Raspberry Pi OS**.

---

## 🚀 Instalación y puesta en marcha

### 1. Clonar el repositorio

```bash
git clone https://github.com/<org>/klipper-mcp-server.git
cd klipper-mcp-server
```

### 2. Crear el entorno e instalar dependencias

Se recomienda `uv` por velocidad y reproducibilidad:

```bash
uv venv
# Linux / macOS
source .venv/bin/activate
# Windows (PowerShell)
.\.venv\Scripts\Activate.ps1

# Instalación editable con dependencias de desarrollo y rendimiento
uv pip install -e ".[dev,perf]"
```

Alternativa con `pip` estándar:

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev,perf]"
```

### 3. Configurar las impresoras

Cada impresora se declara con dos variables de entorno: la URL de
Moonraker y, opcionalmente, su API key.

```bash
# Linux / macOS
export PRINTER_DEFAULT="http://localhost:7125"
export PRINTER_VORON="http://192.168.1.100:7125"
export PRINTER_VORON_API_KEY="tu-api-key"

# Windows (PowerShell)
$env:PRINTER_DEFAULT = "http://localhost:7125"
$env:PRINTER_VORON   = "http://192.168.1.100:7125"
$env:PRINTER_VORON_API_KEY = "tu-api-key"
```

### 4. Arrancar el servidor

```bash
python -m klipper_mcp.server
```

El servidor se comunica por `stdio` con el cliente MCP. Si no se
declara ninguna impresora, se usa `http://localhost:7125` por defecto.

### 5. Verificar la instalación

```bash
pytest tests/ -q
```

---

## ⚙️ Configuración

Todas las variables se leen del entorno y se validan de forma tipada en
`src/klipper_mcp/config.py`.

### Impresoras

| Variable | Descripción | Ejemplo |
|---|---|---|
| `PRINTER_<NAME>` | URL base de Moonraker para la impresora `<NAME>` | `http://192.168.1.100:7125` |
| `PRINTER_<NAME>_API_KEY` | API key opcional (`X-Api-Key`) | `abc123…` |

`<NAME>` se normaliza a minúsculas. Si solo hay una impresora, no es
necesario indicar su nombre en las llamadas.

### Núcleo runtime

| Variable | Default | Descripción |
|---|---|---|
| `MCP_CACHE_SIZE` | `512` | Nº de entradas del cache L1 en memoria |
| `MCP_CACHE_TTL` | `30` | TTL del cache L1 en segundos |
| `MCP_CACHE_DISK` | *(vacío)* | Ruta del cache L2 en disco. Vacío = sin L2 |
| `MCP_CACHE_L2_TTL` | `300` | TTL del cache L2 en segundos |
| `MCP_CB_THRESHOLD` | `5` | Fallos consecutivos antes de abrir el circuit breaker |
| `MCP_CB_RECOVERY` | `30` | Segundos antes de pasar a `HALF_OPEN` |
| `MCP_MAX_CONCURRENCY` | `32` | Concurrencia máxima por host (semáforo) |
| `MCP_TOKEN_BUDGET` | `4000` | Tokens máximos por respuesta |
| `MCP_POLL_INTERVAL` | `2` | Intervalo de polling en segundos |
| `MCP_LOG_LEVEL` | `INFO` | Nivel de logging (`DEBUG`…`CRITICAL`) |

### Ejemplo completo

```bash
export PRINTER_DEFAULT="http://localhost:7125"
export MCP_CACHE_DISK="/var/cache/klipper-mcp"
export MCP_CACHE_TTL="30"
export MCP_CACHE_L2_TTL="300"
export MCP_MAX_CONCURRENCY="32"
export MCP_TOKEN_BUDGET="4000"
export MCP_LOG_LEVEL="INFO"
```

---

## 📖 Manual de uso

### Integración con Claude Desktop

Edita `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "klipper": {
      "command": "python",
      "args": ["-m", "klipper_mcp.server"],
      "env": {
        "PRINTER_DEFAULT": "http://192.168.1.100:7125",
        "PRINTER_DEFAULT_API_KEY": "tu-api-key",
        "MCP_CACHE_SIZE": "512",
        "MCP_CACHE_TTL": "30"
      }
    }
  }
}
```

### Instalación en TRAE IDE (install link)

TRAE permite instalar un servidor MCP desde un enlace con esquema propio
([documentación oficial](https://docs.trae.ai/ide/mcp-server-install-links)).
El proyecto incluye un generador reproducible en
[`install_link.py`](src/klipper_mcp/install_link.py).

**Formato del enlace:**

```
trae://trae.ai-ide/mcp-import?type=<TYPE>&name=<NAME>&config=<B64_URLENCODED>
```

| Componente | Obligatorio | Descripción |
|---|---|---|
| `trae://trae.ai-ide/mcp-import` | Sí | Esquema y ruta fijos del handler de TRAE |
| `type` | Sí | `stdio` o `http` |
| `name` | No | Nombre del servidor en TRAE |
| `config` | Sí | JSON de configuración en Base64 y URL-encoded |

**Generar el enlace:**

```bash
# Con el script de consola instalado (pip install -e .)
klipper-mcp-install-link --name klipper \
  --printer-url http://192.168.1.100:7125

# Equivalente sin instalar el entry point
python -m klipper_mcp.install_link --name klipper \
  --printer-url http://192.168.1.100:7125

# Con API key y variables extras (repetible)
klipper-mcp-install-link --name voron \
  --printer-url http://192.168.1.100:7125 \
  --env PRINTER_VORON_API_KEY=TU_API_KEY \
  --env MCP_CACHE_DISK=/var/cache/klipper-mcp

# Emitir un badge Markdown para el README
klipper-mcp-install-link --markdown
```

**Ejemplo de enlace generado:**

```
trae://trae.ai-ide/mcp-import?type=stdio&name=klipper&config=eyJjb21tYW5kIjoicHl0aG9uIiwiYXJncyI6WyItbSIsImtsaXBwZXJfbWNwLnNlcnZlciJdLCJlbnYiOnsiUFJJTlRFUl9ERUZBVUxUIjoiaHR0cDovLzE5Mi4xNjguMS4xMDA6NzEyNSJ9fQ%3D%3D
```

**Importar en TRAE:**

1. Pega el enlace en la barra de direcciones del navegador y pulsa Enter.
2. Confirma en el diálogo del navegador que quieres abrir TRAE.
3. En la ventana **Configure Manually** de TRAE, revisa la configuración
   y pulsa **Confirm**.

> **Seguridad:** `config` va codificado en Base64, **no cifrado**. No
> compartas enlaces que incluyan API keys; genera el enlace en local y
> pasa los secretos vía `--env` solo cuando lo vayas a usar.

### Tools

| Tool | Propósito | Ejemplo de invocación |
|---|---|---|
| `validate_config` | Valida `printer.cfg` y sus includes | `{"config_content": "...", "filename": "printer.cfg"}` |
| `lint_extra` | Linter AST de reglas de oro | `{"source_code": "..."}` |
| `generate_extra` | Genera plantilla de extra | `{"name": "mi_extra", "description": "..."}` |
| `get_printer_status` | Estado de la impresora | `{"printer": "voron"}` |
| `query_objects` | Consulta objetos Moonraker | `{"objects": {"print_stats": null}}` |
| `run_gcode` | Ejecuta G-code (con salvaguardas) | `{"script": "G28", "confirm": true}` |

> Consulta la especificación (§7) para el contrato completo de las 17
> tools, sus parámetros y sus valores de retorno.

### Resources

Los recursos se direccionan por URI, por ejemplo:

```
klipper://config/printer.cfg
klipper://logs/klippy.log
klipper://docs/index
```

### Prompts

Plantillas predefinidas para diagnóstico, optimización y generación:

- **Troubleshooting**: análisis guiado de `klippy.log`.
- **Optimización**: tuning de `pressure_advance`, `input_shaper`, etc.
- **Generación**: scaffolding de extras y componentes conforme a las
  reglas de Klipper/Moonraker.

### Docker Compose

```bash
# Definir la API key en el entorno del host
export VORON_API_KEY="tu-api-key"
docker compose up --build
```

---

## 📊 Benchmarks y perfilado

El proyecto incluye un arnés offline (`benchmarks/profile_harness.py`)
que ejecuta las operaciones contra un Moonraker simulado
(`tests/harness/sim_moonraker.py`) para eliminar ruido de red.

```bash
# CPU: flamegraph sin instrumentar el proceso
py-spy record -o profile.svg -- python -m klipper_mcp.server

# Línea base de CPU
python -m cProfile -o baseline.prof -m klipper_mcp.server

# Memoria (detección de fugas en ejecución prolongada)
memray run -o mem.bin benchmarks/profile_harness.py
memray flamegraph mem.bin

# Benchmarks reproducibles y comparables en CI
pytest benchmarks/ --benchmark-only --benchmark-json=out.json
```

### Protocolo de medición

1. **Congelar la línea base** en la revisión v0.1 y versionar
   `benchmarks/baseline.json`.
2. **Medir la candidata** en el mismo hardware, con el mismo corpus e
   idénticas iteraciones.
3. **Comparar** p50/p95/p99, throughput y RSS. Se acepta la candidata
   solo si cumple los objetivos y no introduce regresiones > 10 %.
4. **Verificar estabilidad**: soak de 24 h sin crecimiento de RSS
   (pendiente ≤ 0.1 %/h) con hit-rate estable.

---

## 🤝 Contribuir

### Estándares de código

- **Estilo**: `ruff` (lint + format) sin errores y `mypy` sin errores.
- **Longitud de línea**: ≤ 80 caracteres.
- **Cabeceras**: todo archivo `.py` incluye cabecera GPLv3.
- **Asincronía**: prohibido `time.sleep()` en código del reactor; usar
  mecanismos del reactor o `asyncio`. Sin variables globales mutables.
- **I/O**: nunca bloqueante dentro del event loop.
- **Tipado**: anotaciones en todas las funciones públicas.

### Flujo de trabajo con Git

1. Crea una rama descriptiva desde `main`:
   `git checkout -b feat/single-flight-cache`.
2. Realiza commits atómicos siguiendo
   [Conventional Commits](https://www.conventionalcommits.org/):
   `feat:`, `fix:`, `perf:`, `docs:`, `refactor:`, `test:`.
3. Asegura que los tests y linters pasan antes de subir la rama.
4. Abre un Pull Request hacia `main`.

### Proceso de revisión de Pull Requests

- Al menos **una aprobación** de un maintainer.
- La **CI debe estar en verde** (lint, mypy, tests y benchmarks).
- Todo cambio de rendimiento debe incluir **evidencia de medición**
  (JSON de benchmark) y no introducir regresiones > 10 %.
- Los PR deben ser pequeños y con un único propósito.

### Requisitos de pruebas

- Cobertura mínima: **80 %**.
- Tests nuevos obligatorios para toda funcionalidad: casos válidos,
  inválidos y de borde.
- Tests de integración con el Moonraker simulado para todo acceso de
  red.
- Los tests de rendimiento usan `pytest-benchmark` y no dependen de red
  real.

```bash
ruff check src/ tests/
mypy --ignore-missing-imports src/
pytest tests/ -v --cov=src/klipper_mcp --cov-fail-under=80
```

---

## 🙌 Créditos y financiación

### Autores

- **Equipo Klipper MCP Server** — diseño de arquitectura, núcleo runtime
  y especificación técnica.

### Agradecimientos

- A los proyectos **Klipper** y **Moonraker** por su documentación
  abierta y su ecosistema.
- A la comunidad de **Model Context Protocol** por el estándar y los
  SDKs de referencia.
- A los mantenedores de **aiohttp**, **orjson** y **zstandard**.

### Fuentes de financiación

Este proyecto es de desarrollo independiente y no cuenta actualmente con
financiación institucional. Si deseas apoyar su mantenimiento,
contacta con el equipo (ver [Contacto](#-contacto)).

---

## 📬 Contacto

- **Issues y bugs**:
  [GitHub Issues](https://github.com/<org>/klipper-mcp-server/issues)
- **Solicitudes de funcionalidad**: usa la plantilla de *feature
  request* en el mismo repositorio.
- **Consultas generales y seguridad**: escribe a
  `maintainers@klipper-mcp.dev`.

Por favor, reporta vulnerabilidades de forma **privada** y no abras un
issue público hasta que se haya evaluado el impacto.

---

## 📄 Licencia

Este proyecto se distribuye bajo la **GNU General Public License v3.0**.
Consulta el archivo [LICENSE](LICENSE) para el texto completo.

La elección de GPLv3 garantiza que las mejoras derivadas —incluidas las
optimizaciones de rendimiento— permanezcan disponibles para la comunidad
del ecosistema Klipper/Moonraker.

---

<p align="center">
  <sub>Hecho con ❤️ para la comunidad de impresión 3D y agentes LLM.</sub>
</p>

TDQS

C2.7/5.0

Scored across 17 tools

Disambiguation4/5

Most tools target distinct resources and actions (config files, validation, server info, runtime status, logs, G-code execution). However, verify_component_loaded vs verify_extra_loaded and get_printer_status vs list_objects/get_print_progress have somewhat overlapping scopes that could cause occasional misselection.

Naming Consistency5/5

Tool names consistently use snake_case with a predictable verb_noun pattern (get_*, list_*, verify_*, validate_*, run_*). There is no mixed casing or vague naming, making the set easy to scan.

Tool Count4/5

17 tools is slightly above the ideal range but reasonable for a server covering Klipper config validation, Moonraker diagnostics, telemetry, and printer control. Each tool appears to have a distinct purpose, though the surface is on the heavier side.

Completeness3/5

The server covers reading, listing, searching, validating configs, plus diagnostics, logs, and basic control. However, it lacks config write/update/delete operations and print lifecycle controls like pause/resume/cancel, leaving some common Klipper management workflows incomplete.

Maintenance

ActivityMaintained
ResponsivenessNo issues