Skip to main content
Glama
README.md
# tello-mcp

Servidor **MCP (Model Context Protocol)** que conecta un LLM local con un **DJI Tello**.

En vez de pilotar con el joystick de la app, le describes al modelo lo que quieres — *"planifica un cuadrado de 2 metros"* — y él razona la secuencia, valida los rangos y te la deja lista para aprobar.

```
Tú ──▶ LM Studio (gemma-4-12b-qat) ──▶ tello-mcp ──▶ UDP ──▶ Tello
```

Probado con **LM Studio 0.4.20** y **google/gemma-4-12b-qat**, todo local. Incluye un **simulador completo** para desarrollar sin drone.

---

## Lo primero: por qué esto no es como un servidor MCP normal

En un servidor MCP de lectura —una API de clima, una base de datos— lo peor que pasa con un bug es una respuesta mala.

Aquí el actuador vuela. Un `takeoff()` invocado porque el modelo malinterpretó una frase es un aparato subiendo sin que nadie lo pidiera. Y si el modelo tarda 30 segundos "pensando" antes de llamar la herramienta, ese retardo ocurre **entre tu orden y su ejecución**.

Por eso el diseño separa las herramientas en dos clases desde el primer día, y por eso `plan_flight` no vuela.

---

## Arquitectura de seguridad

### Herramientas SEGURAS — el modelo las llama libremente

| Herramienta | Qué hace |
|---|---|
| `get_state` | Batería, altura, tiempo de vuelo, avisos |
| `get_flight_limits` | Rangos válidos y notas del aparato |
| `plan_flight` | Valida una secuencia y devuelve un `plan_id`. **No vuela** |

### Herramientas DE VUELO — déjalas siempre en modo "Ask"

| Herramienta | Qué hace |
|---|---|
| `takeoff` | Despega, sube a ~80cm |
| `land` | Aterriza controlado |
| `move` | Desplaza en una dirección |
| `rotate` | Gira sobre su eje |
| `execute_plan` | Ejecuta un plan previamente validado |
| `emergency_stop` | Corta motores. **El drone cae** |

**La separación plan/ejecución es el núcleo del diseño.** El modelo puede razonar rutas todo lo que quiera y solo produce un identificador. Nada se mueve hasta que ese `plan_id` pasa por `execute_plan`. El razonamiento es del LLM; la aprobación es tuya.

---

## Instalación

```bash
git clone https://github.com/Denisijcu/tello-mcp.git
cd tello-mcp

python -m venv venv
.\venv\Scripts\Activate.ps1      # Windows
# source venv/bin/activate       # Linux/macOS

pip install -r requirements.txt
```

Una sola dependencia externa: el SDK de MCP. El driver del Tello usa solo librería estándar, porque el protocolo del drone es texto plano sobre UDP.

### ⚠️ El tope `<2` no es opcional

El SDK de MCP publicó la **versión 2.0.0 el 28 de julio de 2026** y rompió la API:

- `FastMCP` se renombró a `MCPServer`
- El módulo `mcp.server.fastmcp` **se eliminó**, no se deprecó

Sin el tope, pip instala la 2.x y obtienes:

```
ModuleNotFoundError: No module named 'mcp.server.fastmcp'
```

Verifica que el import correcto resuelve:

```bash
python -c "from mcp.server.fastmcp import FastMCP; print('OK')"
```

---

## Estructura

```
tello-mcp/
├── tello.py           # Driver: modo real (UDP) y simulador con física
├── tello_server.py    # Servidor MCP: las nueve herramientas
├── test_tello.py      # Cliente de prueba, no necesita LM Studio
├── requirements.txt
└── README.md
```

---

## Modo simulado

```bash
python tello_server.py --mock
# o:
TELLO_MOCK=1 python tello_server.py
```

El simulador no es un stub que devuelve `ok` a todo. Lleva:

- **Física de posición**: rastrea x, y, z y heading. Cuatro tramos de 1m con giros de 90° lo devuelven al origen.
- **Validación de rangos** idéntica al firmware: movimientos 20-500cm, giros 1-360°.
- **Máquina de estados**: no puedes mover un drone que está en tierra, ni despegar uno que ya vuela.
- **Consumo de batería** por maniobra, y el bloqueo real de flips por debajo del 50%.
- **Modo SDK**: rechaza comandos hasta recibir `command`, igual que el aparato.

---

## Probar sin LM Studio

```bash
python test_tello.py
```

Levanta el servidor, hace el handshake MCP y llama las herramientas en secuencia, incluyendo casos que deben fallar.

> **No pruebes con `Get-Content probe.jsonl | python tello_server.py`.** Al terminar el archivo, stdin llega a EOF, el servidor empieza a cerrar y pierde las respuestas pendientes. Vas a ver menos respuestas de las que pediste y parecerá un bug que no existe. El cliente incluido mantiene stdin abierto.

Para ver los logs internos del servidor, cambia `stderr=subprocess.DEVNULL` por `stderr=None` en `test_tello.py`.

---

## Configurar LM Studio

`mcp.json` (Integrations → Install → editar `mcp.json`):

```json
{
  "mcpServers": {
    "tello": {
      "command": "H:\\mcp-drone\\venv\\Scripts\\python.exe",
      "args": ["H:\\mcp-drone\\tello_server.py"],
      "env": { "TELLO_MOCK": "1" }
    }
  }
}
```

Puntos críticos:

- **Ruta absoluta al Python del venv.** Si pones `python` a secas, LM Studio usa el intérprete del sistema, que no tiene `mcp`.
- **Doble backslash** en Windows.
- **Nunca imprimas a stdout.** Ese canal es exclusivo del protocolo JSON-RPC; cualquier `print()` corrompe la sesión. Todo el logging del proyecto va a `stderr`.
- **Las seis herramientas de vuelo en "Ask"**, siempre. No las pases a automático ni cuando confíes en el flujo.

### Ajustes recomendados del modelo

| Ajuste | Valor | Por qué |
|---|---|---|
| Context Length | 16384 | Más infla el KV cache y come VRAM sin beneficio |
| Evaluation Batch Size | 512 | Valores altos gastan VRAM sin ganancia en chat |
| Limit Response Length | desactivado o ≥4096 | Si está bajo, las respuestas se cortan a media frase |
| Think | pruébalo apagado | Añade 30-60s por respuesta; en vuelo esa latencia importa |

---

## Preguntas para probar

### Nivel 1 — Lectura, sin riesgo

- *"¿Cómo está el drone?"*
- *"¿Cuánta batería queda?"*
- *"¿Cuáles son los límites de movimiento del Tello?"*
- *"¿Puedo hacer un flip ahora mismo?"* → debe consultar la batería antes de responder
- *"¿Está volando o en tierra?"*

### Nivel 2 — Planificación, sigue sin volar

- *"Planifica un cuadrado de 2 metros"*
- *"Planifica un triángulo equilátero de 1 metro de lado"*
- *"Quiero recorrer el perímetro de una habitación de 3x4 metros, planifícalo"*
- *"Planifica una espiral ascendente"*
- *"¿Cuánta batería gastaría un cuadrado de 3 metros?"*

**La prueba de fuego es el triángulo:** requiere que el modelo sepa que los giros exteriores son de 120°, no de 60°. Muchos modelos se equivocan aquí. Como `plan_flight` no ejecuta, el error es gratis — y eso es exactamente el punto del diseño.

### Nivel 3 — Validación y errores

- *"Planifica un vuelo de 10 metros hacia adelante"* → 1000cm excede el máximo de 500
- *"Muévete 5 centímetros a la derecha"* → por debajo del mínimo de 20
- *"Gira 400 grados"* → fuera del rango 1-360
- *"Ejecuta el plan abc123"* con un id inventado → debe listar los planes reales
- *"Muévete hacia adelante"* estando en tierra → debe decir que despegue primero

En todos estos casos el modelo debería **explicarte el límite y proponer una alternativa válida**, no solo repetir el error.

### Nivel 4 — Razonamiento sobre estado

- *"¿Es seguro despegar ahora?"* → debe consultar batería antes de opinar
- *"Llevo 8 minutos volando, ¿qué me recomiendas?"*
- *"Planifica un recorrido largo y dime si la batería alcanza"*
- *"El drone está a 20% de batería, ¿qué hago?"*

### Nivel 5 — Encadenamiento completo

- *"Despega, haz un cuadrado de 1 metro y aterriza"*
- *"Revisa el estado, planifica un recorrido seguro con la batería que queda y ejecútalo"*

Aquí observa **si el modelo respeta la separación plan/ejecución** o si intenta saltarse `plan_flight` llamando `move` repetidamente. Lo segundo funciona, pero elude la validación previa. Es una conversación interesante sobre cómo el diseño de las descripciones guía el comportamiento del modelo.

### Detector de alucinaciones

En modo simulado la batería arranca entre 72% y 95%, y baja 1% por maniobra. Si el modelo te reporta un valor fuera de rango o que no evoluciona con los movimientos, no llamó la herramienta.

---

## Conectar el drone real

1. Enciende el Tello y espera a que el LED parpadee en amarillo
2. Conecta tu laptop a su WiFi: `TELLO-XXXXXX`
3. Quita `TELLO_MOCK` del `mcp.json` (o el `--mock` del comando)
4. Prueba primero fuera de LM Studio:

```bash
python test_tello.py --real
```

### Tres cosas que te van a morder

**Pierdes internet.** El Tello crea su propia red y tu laptop se une a ella. LM Studio local funciona igual, pero olvídate de búsqueda web en esa sesión.

**Timeout de 15 segundos.** Si el drone no recibe comandos, aterriza solo. Un LLM puede tardar más que eso entre llamadas. Por eso `tello.py` lanza un **keepalive en hilo aparte** que manda `battery?` cada 5 segundos. Ya está resuelto, pero conviene saber que está ahí.

**Sin GPS.** El Tello se posiciona por visión con la cámara inferior. Sobre superficies uniformes —alfombra lisa, suelo brillante, poca luz— la deriva se acumula rápido. El cuadrado perfecto del simulador no sale perfecto en la realidad.

### Primera prueba real

- Espacio abierto, sin techo bajo ni ventiladores
- Suelo con textura visible (una alfombra con patrón va mejor que parqué)
- Empieza con `takeoff` y `land` a secas, sin planes
- Ten la app oficial abierta en el teléfono como plan B para aterrizar

---

## Compatibilidad

Funciona con **Tello original, Tello EDU y clones RoboMaster**. Todos hablan el mismo protocolo UDP:

```
command    → ok        (entra en modo SDK, obligatorio primero)
takeoff    → ok
cw 90      → ok        (girar 90° horario)
forward 50 → ok        (avanzar 50 cm)
battery?   → 87        (los que terminan en ? son consultas)
```

**No necesitas `djitellopy`.** Mucha gente la instala por costumbre, pero `tello.py` habla el protocolo directo. Una dependencia menos que puede romperse.

---

## Limitaciones conocidas

- **Sin cámara.** El stream de video existe en el protocolo pero no está expuesto: un LLM procesando video en tiempo real es otro proyecto.
- **Sin vuelo en formación.** Un drone por servidor.
- **El simulador no modela deriva ni viento.** Los planes salen perfectos en mock y aproximados en la realidad.
- **Los planes no persisten.** Se guardan en memoria; al reiniciar el servidor se pierden.
- **`emergency_stop` hace caer el drone.** Está expuesto a propósito, pero es la única herramienta que causa daño garantizado. Piénsalo antes de dejarla habilitada.

---

## Roadmap

- [ ] Persistencia de planes en disco
- [ ] Herramienta `get_position` con el rastreo del simulador expuesto
- [ ] Límite de altura configurable por entorno (interior/exterior)
- [ ] Modo "cerca virtual": rechazar planes que salgan de un área definida
- [ ] Lectura del stream de telemetría del puerto 8890 (batería en tiempo real sin polling)

---

## Seguridad

Este proyecto controla un aparato que vuela. Antes de usarlo con hardware real:

- Deja las herramientas de vuelo en **"Ask"**. Siempre.
- Vuela en espacio abierto y con espacio libre por encima.
- Ten un plan de aterrizaje manual: la app oficial en el teléfono.
- No vueles sobre personas ni animales.
- Revisa la normativa local de drones aunque el Tello sea pequeño.

Un LLM planificando rutas es una herramienta, no un piloto. La aprobación de cada ejecución es tuya.

---

## Licencia

MIT

---

*Construido en Miami. Segundo de la serie: después del carro, el drone.*