Metadata-Version: 2.5
Name: kdeconnect-mcp
Version: 0.1.0
Summary: MCP server que expone llamadas, SMS y notificaciones de un movil via KDE Connect, con redaccion de PII (OTP, tarjetas, IBAN, telefonos).
Project-URL: Homepage, https://github.com/DaBlitzStein/kdeconnect-mcp
Project-URL: Issues, https://github.com/DaBlitzStein/kdeconnect-mcp/issues
License-Expression: MIT
License-File: LICENSE
Keywords: agents,kdeconnect,llm,mcp,notifications,privacy,redaction,sms
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications
Classifier: Topic :: Home Automation
Requires-Python: >=3.11
Requires-Dist: dbus-next>=0.2.3
Requires-Dist: mcp>=2.0.0
Requires-Dist: pyyaml>=6.0
Description-Content-Type: text/markdown

# kdeconnect-mcp

[![GitHub stars](https://img.shields.io/github/stars/DaBlitzStein/kdeconnect-mcp?style=social)](https://github.com/DaBlitzStein/kdeconnect-mcp)

MCP server que expone **llamadas, SMS y notificaciones** de un movil Android a
un agente, via **KDE Connect**, con un sistema de **PII** que impide que los
codigos de autorizacion (OTP), numeros de tarjeta, IBAN y telefonos completos
lleguen al agente o toquen el disco. Los nombres de contacto y de app si son
visibles.

```
movil Android ──KDE Connect v8──> kcd (Go, headless) ──socket Unix──> listener ──PII──> SQLite ──MCP──> agente
```

## Que redacta (y que no)

| Categoria | Ejemplo | Resultado |
|---|---|---|
| `otp` | `Tu codigo de autorizacion es 483920` | `[REDACTADO:otp]` |
| `card` | `407-1234567-8901234` | `[REDACTADO:card]` |
| `iban` | `ES91 2100 0418 4502 0005 1332` | `[REDACTADO:iban]` |
| `phone` | `+34 600 123 456` | `+34 ***456` (configurable) |
| Nombres | `Ana`, `Mama`, `BBVA` | sin cambios |

Garantias:

1. **Redaccion en la ingesta**: el listener redacta antes de `INSERT`. El texto
   original solo existe en memoria durante el evento.
2. **Hash con HMAC** (`content_hash`) para deduplicar/auditar sin guardar texto.
3. **Defensa en profundidad**: las respuestas MCP vuelven a pasar por el
   redactor, tambien las lecturas en vivo de DBus.
4. **Logs sin contenido**: el listener registra kind/app/contadores, nunca el
   texto. Los tests E2E escanean el fichero SQLite (incluido `-wal`) buscando
   los secretos simulados y fallan si aparecen.

El detector de OTP combina palabras clave (codigo, autorizacion, verificacion,
otp, code, password...) con ventana de contexto y lista de **apps sensibles**
(authenticator, authy, bitwarden...). Anade tus bancos a `sensitive_apps` en la
config para que cualquier codigo suyo se redacte aunque falte la palabra clave.

## Requisitos

- Linux; vale **headless** (sin sesion grafica, sin Qt/KDE).
- Sesion de usuario con systemd (`systemctl --user`) para kcd y el listener.
- [uv](https://docs.astral.sh/uv/) para instalar/ejecutar (gestiona Python >= 3.11).
- Movil Android con la app **KDE Connect** y los plugins **Notificaciones**,
  **SMS** y **Telefonia** activos (en la app: Ajustes > Plugins).
- Opcional: backend DBus legacy si ya usas `kdeconnectd` (`backend: dbus`).

El daemon headless [`kcd`](https://github.com/bethropolis/kcd) lo instala
`kdeconnect-mcp provision` (binario unico, sin Qt). Verifica el estado con:

```bash
kdeconnect-mcp doctor      # o: uv run kdeconnect-mcp doctor, desde el repo
```

## Instalacion

```bash
git clone https://github.com/DaBlitzStein/kdeconnect-mcp.git
cd kdeconnect-mcp
uv sync
uv run kdeconnect-mcp demo        # prueba el pipeline con datos simulados
```

### Sin clonar el repo (uvx, recomendado)

`uvx` es el equivalente a `npx` en Python: ejecuta el paquete sin clonar ni instalar.

```bash
# desde GitHub (disponible ya)
uvx --from git+https://github.com/DaBlitzStein/kdeconnect-mcp kdeconnect-mcp serve

# cuando este publicado en PyPI
uvx kdeconnect-mcp serve
```

Configuracion en un agente MCP (opencode, Claude Code, Cursor, LibreFang...):

```json
"kdeconnect": {
  "type": "local",
  "command": ["uvx", "--from", "git+https://github.com/DaBlitzStein/kdeconnect-mcp", "kdeconnect-mcp", "serve"],
  "enabled": true
}
```

Listener permanente (captura aunque no haya agente abierto): instala la herramienta y provisiona:

```bash
uv tool install git+https://github.com/DaBlitzStein/kdeconnect-mcp   # o: uv tool install kdeconnect-mcp
kdeconnect-mcp provision
```

### Registrar en opencode

En `~/.config/opencode/opencode.json`:

```json
{
  "mcp": {
    "kdeconnect": {
      "type": "local",
      "command": [
        "uv", "--directory", "/ruta/a/kdeconnect-mcp",
        "run", "kdeconnect-mcp", "serve"
      ],
      "enabled": true
    }
  }
}
```

### Listener permanente (recomendado)

El servidor MCP captura mientras hay una sesion de agente. Para capturar
siempre (aunque el agente este cerrado), instala la herramienta y provisiona:

```bash
uv tool install git+https://github.com/DaBlitzStein/kdeconnect-mcp   # o: uv tool install kdeconnect-mcp
kdeconnect-mcp provision   # instala kcd + unidades systemd de usuario y las arranca
```

`provision` escribe la unidad con el interprete real de la instalacion, asi que
funciona igual desde el repo o desde `uv tool`. El lock (`listener.lock`)
garantiza un unico escritor; si el service ya corre, el servidor MCP solo lee la
misma base de datos. Al terminar, el CLI te recuerda dejar una estrella en el repo.

## Operacion con kcd

[`kcd`](https://github.com/bethropolis/kcd) es un daemon de KDE Connect
(protocolo v8) escrito en Go, headless: binario unico, sin Qt ni sesion grafica.
Escucha en el socket Unix `$XDG_RUNTIME_DIR/kcd/kcd.sock` (o el que fije
`KDCONNECT_SOCKET`).

### Provision

```bash
uv run kdeconnect-mcp provision --dry-run   # plan completo, no descarga ni escribe
uv run kdeconnect-mcp provision             # instala y arranca
```

`provision` trabaja sobre la release fijada **v1.20.0** (`--version vX.Y.Z` para
cambiarla):

1. Descarga `kcd_<version>_linux_x86_64.tar.gz` y `checksums.txt` de GitHub a
   un directorio temporal.
2. Verifica el SHA256 del tarball contra `checksums.txt` y aborta con error si
   no coincide.
3. Extrae el binario y lo instala en `~/.local/bin/kcd` (chmod +x).
4. Escribe `~/.config/systemd/user/kcd.service`
   (`ExecStart=%h/.local/bin/kcd daemon`) y `kdeconnect-mcp-listen.service`
   (`ExecStart=<interprete-instalado> -m kdeconnect_mcp listen`, sin depender
   del PATH de systemd).
5. `systemctl --user daemon-reload`; salvo `--no-start`, hace
   `enable --now` de ambos servicios.

### Emparejamiento

```bash
~/.local/bin/kcd devices              # lista dispositivos y estado
~/.local/bin/kcd pair                 # escucha y acepta solicitudes del movil
~/.local/bin/kcd pair <deviceId>      # inicia el pairing desde el escritorio
```

El flujo es TLS con huella SHA-256: confirma la huella en el movil cuando
aparezca la solicitud. Desde el agente, las tools `scan_devices`,
`request_pair`, `accept_pairing` y `reject_pairing` cubren lo mismo.

### Plugins en el movil

En la app KDE Connect del movil, Ajustes > Plugins, activa al menos
**Notificaciones**, **SMS** y **Telefonia**. Sin ellos kcd no reenvia eventos,
aunque el emparejamiento exista.

### Refresco

- El listener re-sincroniza dispositivos al conectar y mantiene abierto el
  stream de pairing; si el socket cae, reconecta con backoff (1s-30s).
- `kcd devices` lista los dispositivos vistos por el daemon.
- `kdeconnect-mcp doctor` (o `uv run kdeconnect-mcp doctor` desde el repo)
  muestra socket, version de kcd, estado de las unidades systemd y el lock del
  listener.
- Refresco forzado: `systemctl --user restart kdeconnect-mcp-listen.service`.

### Limites con kcd

- **SMS por polling**: kcd no empuja los SMS; el listener pide las
  conversaciones (`sms_request_conversations`) al conectar y cada
  `capture.sms_poll_seconds` (300 s por defecto; `0` lo desactiva). El movil
  reenvia su historial cacheado en cada ciclo: el dedup lo absorbe, pero con
  intervalos muy bajos hay trafico/CPU/bateria de mas (60 s funciona bien).
- **`list_active_notifications` no soportado en kcd**: usa `get_activity`
  (la captura en vivo si trae las notificaciones nuevas).
- **`sync_sms_history` no soportado en kcd**: devuelve un error explicito; el
  polling ya trae el historial.
- La release v1.20.0 de kcd solo publica binario Linux x86_64; en otras
  arquitecturas `provision` falla con error claro.

## Herramientas MCP

| Tool | Para que |
|---|---|
| `get_status` | Estado del listener, captura y PII |
| `list_devices` | Dispositivos conocidos (BD) |
| `get_activity` | Timeline filtrable (`kind`, `app`, `since_minutes`, ...) |
| `get_events` / `wait_for_events` | Consumo incremental por cursor (`after_id`); long-poll |
| `search_activity` | Busqueda de texto sobre lo redactado |
| `get_conversation` | Hilo de SMS por telefono/contacto |
| `get_call_log` | Llamadas; `only_missed=true` para perdidas |
| `list_active_notifications` | Notificaciones activas en el movil (solo backend DBus; en kcd, cache) |
| `acknowledge_events` | Marca leidos por ids o antiguedad |
| `get_redaction_stats` | Redacciones por categoria |
| `sync_sms_history` | Pide al movil las conversaciones cacheadas (solo DBus) |
| `scan_devices` / `request_pair` | Emparejamiento: listar y solicitar |
| `accept_pairing` / `reject_pairing` | Aceptar/rechazar solicitudes entrantes (kcd) |

## CLI

```bash
kdeconnect-mcp serve          # MCP por stdio (por defecto)
kdeconnect-mcp listen         # captura en primer plano
kdeconnect-mcp sync           # sincroniza SMS cacheados
kdeconnect-mcp events         # timeline reciente
kdeconnect-mcp redact-test "Tu codigo es 123456"
kdeconnect-mcp demo           # datos simulados, sin KDE Connect
kdeconnect-mcp doctor         # diagnostico (config, kcd, systemd, KDE Connect)
kdeconnect-mcp provision      # instala kcd y los services systemd de usuario
kdeconnect-mcp config-init    # escribe config de ejemplo
```

Todas aceptan `--data-dir`, `--config` y `--fake`.

## Configuracion

Ver `config/config.example.yaml`. Se carga de
`~/.config/kdeconnect-mcp/config.yaml` (o `KDCONNECT_MCP_CONFIG`).

Claves utiles:

- `backend`: `kcd` (por defecto) | `dbus` | `fake`.
- `kcd.socket_path`: ruta al socket de kcd (por defecto `$XDG_RUNTIME_DIR/kcd/kcd.sock`).
- `capture.sms_poll_seconds`: cada cuanto se piden las conversaciones SMS (0 = off).
- `redaction.phone.mode`: `off` | `partial` (por defecto, ultimos 3) | `full`.
- `redaction.keywords`: palabras que activan la redaccion de codigos cercanos.
- `redaction.sensitive_apps`: apps donde todo codigo se redacta siempre.
- `capture.ignore_apps`: apps cuyas notificaciones no se capturan.

## Desarrollo

```bash
uv run pytest          # 93 tests: PII, store, ingesta, backends, poll SMS, tools MCP, provision
```

Estructura:

- `pii.py` — motor de redaccion (categorias, solapes, enmascarado de telefono).
- `listener.py` — ingesta: redacta, deduplica, mergea llamadas, persiste.
- `store.py` — SQLite WAL + FTS5, solo texto redactado.
- `kcd_backend.py` — cliente del socket de kcd (watch NDJSON + comandos IPC).
- `backends.py` — factoria `kcd` | `dbus` | `fake`.
- `dbus_backend.py` — DBus KDE Connect (legacy; interfaces verificadas contra master y v24.02).
- `fake_backend.py` — movil simulado para desarrollo/tests.
- `server.py` — tools MCP; `cli.py` — comandos; `config.py` — config YAML.

## Limitaciones

- Las llamadas se exponen como eventos `ringing`/`missedCall` (no hay audio ni
  estado "en curso" persistente en KDE Connect).
- Los SMS se reciben pidiendo las conversaciones al movil (polling; ver
  "Limites con kcd").
- No hay tool MCP de envio de SMS ni de respuesta a notificaciones (el backend
  lo soporta; extension pendiente).
- El escritorio debe estar encendido y con KDE Connect conectado al movil.

## Diagramas (mermaid con Firefox headless, sin Chrome)

`mermaid-cli` usa Puppeteer, que por defecto baja `chrome-headless-shell`. Aquí se
usa el Firefox de Puppeteer en su lugar:

```bash
# una vez: descarga el Firefox de Puppeteer (~90 MB, sin Chrome)
PUPPETEER_SKIP_DOWNLOAD=1 npx -y puppeteer browsers install firefox

# renderizar cualquier .mmd (svg o png; pdf es Chromium-only)
./tools/render-mermaid.sh docs/arquitectura-kcd.mmd docs/arquitectura-kcd.png
```

La config `tools/puppeteer.firefox.json` fija `{"browser": "firefox", "headless": true}`
(necesario para pisar el `headless: "shell"` por defecto de mermaid-cli, que es Chrome).

Para ver diagramas en la terminal (flowchart y sequence) sin visor gráfico:

```bash
./tools/mmd.sh docs/flujo-ingesta.mmd
```

Nota: `mermaid-ascii` no soporta `subgraph` ni formas no rectangulares; los
flowcharts para terminal se escriben planos (ver `docs/arquitectura-kcd-plano.mmd`).
Para ERD/gantt o el diagrama con subgraphs, usar el PNG y `chafa`.
