PluginWorld
Mc

mcp-security-vision

MCP

Enables AI agents to connect to security cameras, detect objects with YOLOv8, recognize known faces, monitor restricted intrusion zones, capture annotated snapshots, and query a persistent SQLite event history.

@kfr0st · MIT License · updated today

SECURITY

B

SCORE

60

STARS

▲ 0

PLUG IN

git clone https://github.com/kfr0st/mcp-security-vision.git

See the README to configure this MCP server

README

🛡️ MCP Security Vision

Servidor MCP (Model Context Protocol) para videovigilancia inteligente en tiempo real: conecta cámaras de seguridad (IP por RTSP, analógicas por DVR o capturadora USB) con agentes de IA como Claude Desktop, Cursor, LangChain, AutoGen o tus propios bots.

YOLOv8 detecta objetos, face_recognition identifica personas conocidas y polígonos de intrusión vigilan zonas restringidas. Todo el historial se guarda en SQLite y se expone al agente como herramientas MCP.

Captura anotada de demostración

Imagen generada por overlay.py con la cámara sintética (CAMERA_SOURCE=synthetic): objetos YOLO, rostro identificado y dos zonas restringidas, una de ellas en alerta (rojo).

CI Docker Hub Python MCP License: MIT


📚 Índice


✨ Características

🎯 Detección de objetos YOLOv8 (personas, autos, mascotas, mochilas, paquetes…) con confianza configurable
🧑 Reconocimiento facial Identifica personas conocidas (known_faces/) vs. desconocidos, con estabilización temporal anti-falsos positivos
🚧 Zonas restringidas Polígonos de intrusión normalizados (0..1), por cámara y por tipo de objeto, con tiempo de permanencia
🗄️ Historial permanente SQLite con eventos, zonas, personas y rutas de evidencia; consultas por fecha, cámara y tipo
🎥 Multicámara Varias cámaras definidas en JSON, cada una con su hilo, tracker y estado
🧵 Multihilo con reconexión Bucle de vídeo en segundo plano, backoff exponencial si se cae el RTSP, nunca bloquea MCP
🖼️ Capturas para agentes multimodales capture_snapshot devuelve una imagen JPEG (con cajas/zonas opcionales)
🔔 Notificaciones Webhook JSON opcional (n8n, Home Assistant, Slack, Telegram vía bot) para alertas
🐳 Docker listo Dockerfile multi-stage + docker-compose + publicación automática en Docker Hub
✅ CI con GitHub Actions Lint, 144 tests (matriz Python 3.10/3.11/3.12 × MCP SDK 1.x/2.x) y smoke test de la imagen
🔒 Controles de seguridad Credenciales solo en .env, contraseñas RTSP enmascaradas en logs, HTTP ligado a loopback con validación de Host/Origin, usuario no-root en Docker

🆕 Novedades de la v2.0 / v2.1

v2.1.0 es la revisión de seguridad y robustez: HTTP en loopback con validación de Host/Origin, cleanup_history con confirmación, esquema SQLite v2, backoff real de cámara, inferencia serializada y 144 tests. Ver CHANGELOG.md.

v2.0.1 corrige la guía de problemas frecuentes tras verificarla contra el código (mensajes de consola reales, RTSP_TRANSPORT, FACE_MODEL, validación de YOLO_IMGSZ, comprobación de puerto antes de cargar modelos) y añade los scripts de publicación. Ver CHANGELOG.md.

La v2.0 incorporó las extensiones solicitadas:

  1. Base de datos SQLite para historial permanente (mcp_security_vision/database.py)
    • Tablas events, zones, people, índices y WAL para escrituras concurrentes.
    • Herramientas get_security_report, filtros por horas/tipo y cleanup_history con retención.
  2. GitHub Actions para pruebas y publicación en Docker Hub
    • .github/workflows/ci.yml: lint + tests + smoke test del contenedor.
    • .github/workflows/docker-publish.yml: build multi-arquitectura (amd64/arm64) y push con tags semánticos.
  3. Zonas restringidas (polígonos de intrusión) (mcp_security_vision/zones.py)
    • Detección punto-en-polígono, tracker por IoU para no duplicar eventos, dwell time y evidencia fotográfica.
    • Herramientas MCP add_restricted_zone, list_restricted_zones, remove_restricted_zone, get_active_intrusions.

Y de la hoja de ruta v2.0 original:

  1. Soporte multicámara con CAMERAS en JSON y camera_id en todas las herramientas.
  2. Arquitectura desacoplada (paquete mcp_security_vision/), lista para combinarse con otros servidores MCP (Telegram, WhatsApp, Notion…) sin tocar el código de visión, más webhook opcional para reaccionar a alertas.

🏗️ Arquitectura

                    ┌──────────────────────────────┐
   Claude Desktop   │                              │
   Cursor / Cline ──┤        Servidor MCP          │
   LangChain/AutoGen│  server.py  (13 tools + 1    │
   HTTP / Docker ───┤  recurso security://status)  │
                    └──────────────┬───────────────┘
                                   │  (mismo proceso, sin bloqueo)
                    ┌──────────────▼───────────────┐
                    │        VisionSystem          │
                    │  cámaras · zonas · registro  │
                    └──────┬─────────────┬─────────┘
        ┌──────────────────┼─────────────┼──────────────────┐
        ▼                  ▼             ▼                  ▼
 ┌────────────┐    ┌─────────────┐  ┌──────────┐    ┌────────────┐
 │ CameraWorker│   │  Pipeline   │  │  SQLite  │    │ Snapshots  │
 │ (1 hilo por │   │ YOLO → IoU  │  │ events   │    │  alertas   │
 │  cámara)    │   │ → caras →   │  │ zones    │    │  .jpg      │
 │ reconexión  │   │ zonas → log │  │ people   │    │ retención  │
 └────────────┘    └─────────────┘  └──────────┘    └────────────┘
        │                  │
        │                  └─────► Webhook JSON (opcional)
        └── RTSP · USB · DVR · synthetic

Estructura del repositorio

mcp-security-vision/
├── mcp_security_vision/          # Paquete instalable
│   ├── server.py                 # Herramientas MCP + arranque (compat. SDK 1.x/2.x)
│   ├── system.py                 # Fachada de negocio (lo que usan las tools)
│   ├── camera.py                 # Hilo por cámara + reconexión + estado
│   ├── pipeline.py               # Detección → tracking → caras → zonas → eventos
│   ├── detectors.py              # YOLOv8 + utilidades de cajas
│   ├── faces.py                  # Registro y reconocimiento facial
│   ├── zones.py                  # Polígonos de intrusión + monitor
│   ├── tracker.py                # Tracker IoU (IDs estables)
│   ├── database.py               # SQLite: eventos, zonas, personas
│   ├── snapshots.py              # Evidencias JPEG y retención
│   ├── overlay.py                # Dibujo de cajas, rostros y zonas
│   ├── notifications.py          # Webhook opcional en segundo plano
│   ├── synthetic.py              # Cámara sintética para demos/CI
│   ├── fakes.py                  # Modelos simulados (CI, sin torch ni dlib)
│   └── config.py                 # Settings desde .env (+ multicámara JSON)
├── config/zones.example.json     # Zonas iniciales (seed)
├── known_faces/                  # Fotos de personas conocidas (nombre_apellido.jpg)
├── snapshots/                    # Evidencias generadas
├── data/                         # security.db (SQLite)
├── docs/
│   ├── TROUBLESHOOTING.md        # Problemas frecuentes (verificados con tests)
│   ├── PUBLICAR_EN_GITHUB.md     # Cómo subir el repo (manual, gh, script, agente)
│   └── demo_annotated.jpg        # Captura anotada de ejemplo
├── scripts/
│   ├── check_setup.py            # Diagnóstico de instalación
│   ├── draw_zones.py             # Editor visual de polígonos
│   ├── configure_repo.py         # Quita placeholders antes de publicar
│   └── publish_github.sh         # Push seguro con verificación de secretos
├── tests/                        # 144 tests (unitarios + integración MCP)
├── .github/workflows/            # ci.yml + docker-publish.yml
├── .github/ISSUE_TEMPLATE/       # plantillas de bug y propuesta
├── SECURITY.md · CONTRIBUTING.md # política de seguridad y guía de contribución
├── server.py                     # Atajo: python server.py
├── Dockerfile · docker-compose.yml · Makefile
├── requirements.txt · requirements-dev.txt · pyproject.toml
└── .env.example

🚀 Instalación

Requisitos

  • Python 3.10, 3.11 o 3.12 (dlib todavía no soporta 3.13+)
  • cmake y compilador C++ (para dlib, que usa face-recognition)
    • Debian/Ubuntu: sudo apt install build-essential cmake libopenblas-dev liblapack-dev libgl1
    • macOS: brew install cmake
    • Windows: Visual Studio Build Tools con la carga de trabajo Desarrollo para el escritorio con C++ + CMake
  • Opcional: GPU NVIDIA con CUDA para acelerar YOLO (YOLO_DEVICE=0)

Pasos

git clone https://github.com/kfr0st/mcp-security-vision.git
cd mcp-security-vision

python -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate

pip install -r requirements.txt
cp .env.example .env              # configura tus cámaras y rutas
python scripts/check_setup.py --read-frames

¿Sin cámara a mano? Prueba todo el flujo con la cámara sintética y modelos simulados:

VISION_FAKE_MODELS=1 CAMERA_SOURCE=synthetic python server.py --transport streamable-http --host 127.0.0.1

O simplemente make demo.

Instalación como paquete (opcional)

pip install ".[vision]"     # código + YOLO + dlib
mcp-security-vision --check # comando instalado

📷 Configuración de cámaras

Todo se define en .env (nunca en el código). Ver .env.example para la lista completa.

Una sola cámara

# Webcam o capturadora USB (índice del dispositivo)
CAMERA_SOURCE=0

# Cámara IP / DVR por RTSP
# CAMERA_SOURCE=rtsp://usuario:clave@192.168.1.50:554/Streaming/Channels/101

Varias cámaras

CAMERAS={"entrada": "rtsp://admin:clave@192.168.1.50:554/Streaming/Channels/101", "patio": 0, "demo": "synthetic"}

# O con nombres visibles y cámaras deshabilitadas:
# CAMERAS=[{"id":"entrada","name":"Puerta principal","source":"rtsp://..."},
#          {"id":"patio","name":"Patio trasero","source":0},
#          {"id":"cochera","name":"Cochera","source":2,"enabled":false}]

Todas las herramientas aceptan camera_id; si se omite se usa la primera cámara habilitada.

URLs RTSP habituales

Marca URL del canal 1 (principal) Substream (menos CPU)
Hikvision (cámara) rtsp://user:pass@IP:554/Streaming/Channels/101 .../102
Hikvision (DVR/NVR) rtsp://user:pass@IP:554/Streaming/Channels/101 .../102
Dahua rtsp://user:pass@IP:554/cam/realmonitor?channel=1&subtype=0 subtype=1
TP-Link Tapo rtsp://user:pass@IP:554/stream1 stream2
Reolink rtsp://user:pass@IP:554/h264Preview_01_main _sub
Amcrest rtsp://user:pass@IP:554/cam/realmonitor?channel=1&subtype=0 subtype=1

🎥 Cámaras analógicas (coaxial/BNC): sin cambiar una línea de código.

  1. Vía DVR/XVR (recomendado): conecta el DVR al router y usa su RTSP por canal.
  2. Vía capturadora RCA/BNC → USB (~10 USD): el sistema la ve como webcam CAMERA_SOURCE=0/1.

⚙️ Variables de entorno principales

Variable Por defecto Descripción
CAMERA_SOURCE / CAMERAS 0 Fuente única o JSON multicámara (RTSP, índice USB, synthetic)
PROCESS_EVERY_N_FRAMES 5 Analiza IA cada N fotogramas (sube si la CPU va justa)
YOLO_MODEL yolov8n.pt yolov8n/s/m/l/x.pt (n = más rápido)
YOLO_CONF 0.5 Umbral de confianza de objetos
YOLO_DEVICE (auto) cpu, 0 (GPU CUDA), mps (Apple)
FACE_TOLERANCE 0.6 Distancia máxima para considerar la misma persona (menor = más estricto)
FACE_MODEL hog hog (rápido, CPU) o cnn (más preciso, GPU)
FACE_SCALE 0.25 Escala a la que se buscan rostros (súbela si las caras son pequeñas)
FACE_STABILIZER_WINDOW / ..._MIN_VOTES 3 / 2 Anti-parpadeo del reconocimiento
FACES_WITHOUT_PERSON false Buscar rostros incluso sin detección de "person"
EVENT_COOLDOWN_SECONDS 20 Anti-spam del mismo evento
INTRUSION_COOLDOWN_SECONDS 30 Anti-spam de reentradas a una zona
PRESENCE_TIMEOUT_SECONDS 2.5 Tiempo sin ver a alguien para considerar que salió de la zona
SNAPSHOT_ON_ALERT true Guardar evidencia JPEG de cada alerta
DB_PATH data/security.db Base de datos SQLite
KNOWN_FACES_DIR known_faces Fotos de referencia
SNAPSHOTS_DIR snapshots Capturas
ZONES_SEED_FILE config/zones.example.json Zonas iniciales (solo se importan si no existen)
RECONNECT_DELAY_SECONDS / MAX_... 5 / 60 Backoff de reconexión RTSP
RTSP_TRANSPORT tcp tcp (estable), udp o auto (deja que FFmpeg decida)
MCP_TRANSPORT stdio stdio, sse o streamable-http
MCP_HOST / MCP_PORT 127.0.0.1 / 8000 Solo para transportes HTTP
ALERT_WEBHOOK_URL (vacío) Webhook JSON para alertas
VISION_FAKE_MODELS false Modo demo/CI sin YOLO ni dlib

🤖 Conexión con agentes de IA

Claude Desktop / Cursor (transporte stdio)

Edita claude_desktop_config.json (~/Library/Application Support/Claude/ en macOS, %APPDATA%\Claude\ en Windows) y añade:

{
  "mcpServers": {
    "security-camera": {
      "command": "/ruta/absoluta/a/mcp-security-vision/venv/bin/python",
      "args": ["/ruta/absoluta/a/mcp-security-vision/server.py"],
      "env": {
        "CAMERA_SOURCE": "rtsp://usuario:clave@192.168.1.50:554/Streaming/Channels/101"
      }
    }
  }
}

En Windows usa C:\\ruta\\...\\venv\\Scripts\\python.exe. Los logs del servidor van a stderr, así que no rompen el protocolo stdio.

Clientes HTTP (Docker, remoto, agentes web)

docker compose up -d --build          # endpoint: http://localhost:8000/mcp
# o en local (solo accesible desde tu máquina):
python server.py --transport streamable-http --host 127.0.0.1 --port 8000

Ejemplo mínimo con el SDK oficial de Python:

import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client


async def main():
    async with streamablehttp_client("http://localhost:8000/mcp") as streams:
        read, write = streams[0], streams[1]  # 3 valores en SDK 1.x, 2 en SDK 2.x
        async with ClientSession(read, write) as session:
            await session.initialize()
            status = await session.call_tool("get_live_status", {})
            print(status.content[0].text)


asyncio.run(main())

El SDK 2.x renombró algunas funciones; el servidor incluye un shim para funcionar con MCP SDK 1.x y 2.x sin cambios.

Prompts de ejemplo

  • «¿Hay alguien en la entrada ahora mismo? ¿Es alguien conocido?»
  • «Toma una captura anotada del patio y dime si el repartidor dejó un paquete en el suelo.»
  • «Revisa los registros de las últimas 24 horas y dime a qué hora pasó Carlos.»
  • «Define una zona restringida llamada "Caja fuerte" con el polígono [[0.6,0.4],[0.95,0.4],[0.95,0.9],[0.6,0.9]] en cam1.»
  • «¿Hay alguna intrusión activa en este momento?»
  • «Estoy frente a la cámara: regístrame como "Administrador".»

🧰 Herramientas MCP

Herramienta Parámetros Devuelve
list_cameras — Cámaras, fuente enmascarada, estado y FPS
get_live_status camera_id="" Objetos, rostros, zonas con intrusos y últimos eventos
capture_snapshot camera_id="", annotate=false, save=false Imagen JPEG (con cajas y zonas si annotate=true); save=true la guarda además en snapshots/
get_security_logs limit=20, camera_id="", hours=0, event_type="" Historial SQLite filtrado
get_security_report hours=24, camera_id="" Totales por tipo/cámara, rostros y zonas
get_active_intrusions camera_id="" Personas/objetos dentro de zonas ahora
list_known_people — Personas registradas (foto + alta)
register_current_face person_name, camera_id="" Guarda el rostro actual (exige 1 sola persona)
forget_known_person person_name Elimina foto, memoria y fila SQLite
list_restricted_zones camera_id="" Polígonos, etiquetas y estado
add_restricted_zone camera_id, name, points, labels=["person"], enabled=true Crea/actualiza zona
remove_restricted_zone camera_id, name Elimina zona
cleanup_history keep_days=30, purge_snapshots=false, confirm=false Purga eventos antiguos (y capturas si purge_snapshots=true); exige confirm=true
(recurso) security://status — JSON con cámaras, personas, zonas y últimos eventos

Eventos registrados (event_type)

intrusion · face_known · face_unknown · person_unidentified · camera_online · camera_offline · face_registered · face_forgotten · zone_added · zone_removed · system


🚧 Zonas restringidas (intrusión)

Las coordenadas son normalizadas (0..1) respecto al ancho y alto de la imagen: (0,0) = esquina superior izquierda, (1,1) = inferior derecha. Así la zona funciona con 720p, 1080p o 4K y sobrevive a reconexiones.

[
  [0.35, 0.30],
  [0.65, 0.30],
  [0.65, 0.95],
  [0.35, 0.95]
]

Cómo crear zonas

  1. Pidiéndoselo al agente (herramienta add_restricted_zone).
  2. Con el editor visual (recomendado para ajustar a mano):
    python scripts/draw_zones.py --camera-id cam1 --name "Puerta trasera" --labels person,car
    # clic = vértice · u = deshacer · c = limpiar · s = guardar
    
  3. Con el archivo semilla config/zones.example.json (se importa al arrancar si la zona no existe).

Mecánica anti-spam

  • El tracker asigna un ID estable (#1, #2…) a cada objeto; solo se registra una intrusión por entrada.
  • Al salir de la zona (sin detección durante PRESENCE_TIMEOUT_SECONDS) se libera el estado, y una nueva entrada vuelve a alertar (respetando INTRUSION_COOLDOWN_SECONDS).
  • Cada intrusión guarda una captura de evidencia y se acompaña de dwell_seconds (tiempo dentro).

🗄️ Base de datos SQLite

Archivo: data/security.db (WAL activado para escrituras concurrentes desde varios hilos de cámara).

-- Esquema resumido
events(id, camera_id, created_at, event_type, severity, description,
       object_label, track_id, zone_names, snapshot_path, metadata)
zones(id, camera_id, name, points, labels, enabled, created_at, updated_at)
people(id, name, slug, file_name, created_at, updated_at)

Consultas útiles:

-- ¿Quién pasó hoy?
SELECT created_at, description FROM events
WHERE event_type = 'face_known' AND created_at >= date('now', 'start of day');

-- Intrusiones por zona en los últimos 7 días
SELECT zone_names, COUNT(*) FROM events
WHERE event_type = 'intrusion' AND created_at >= datetime('now', '-7 days')
GROUP BY zone_names;

-- Agenda de presencia por persona
SELECT json_extract(metadata, '$.person') AS persona, MIN(created_at), MAX(created_at), COUNT(*)
FROM events WHERE event_type = 'face_known' GROUP BY persona;

Las marcas de tiempo son hora local del servidor en ISO-8601 (YYYY-MM-DDTHH:MM:SS), ordenables como texto.

Respaldo: copia data/security.db (con WAL, usa sqlite3 data/security.db ".backup copia.db"). Retención: cleanup_history(keep_days=90, confirm=true); añade purge_snapshots=true para borrar también las capturas. Sin confirm=true la herramienta solo describe lo que haría.


🐳 Docker

# 1. Configura el entorno
cp .env.example .env

# 2. Construye y levanta (endpoint MCP en http://localhost:8000/mcp)
docker compose up -d --build
docker compose logs -f

# 3. Prueba rápida sin cámara (modo demo)
docker run --rm -p 127.0.0.1:8000:8000 -e VISION_FAKE_MODELS=1 -e CAMERA_SOURCE=synthetic \
  kfr0st/mcp-security-vision:latest
  • Imagen multi-stage (dlib se compila en el builder; la imagen final no lleva compiladores).
  • Corre como usuario no-root (vision).
  • Volúmenes: data (SQLite), snapshots, known_faces, config, yolo-models (pesos persistentes).
  • Para cámara USB en Linux añade devices: ["/dev/video0:/dev/video0"] y group_add: ["video"].
  • Por defecto solo se aceptan hosts locales (localhost, 127.0.0.1, [::1] y sus puertos). Si publicas detrás de un proxy, añade tu dominio a MCP_ALLOWED_HOSTS (por ejemplo, MCP_ALLOWED_HOSTS=mcp.midominio.com,mcp.midominio.com:*) y define MCP_ALLOWED_ORIGINS=https://mcp.midominio.com si usas un navegador.
  • El transporte HTTP no trae autenticación. No lo expongas directamente a Internet: usa un proxy con TLS y autenticación, o una red privada/VPN.
  • PRELOAD_MODELS=true (build-arg) incluye yolov8n.pt en la imagen para despliegues sin Internet.
  • El contenedor corre como UID 1000. Si usas bind mounts en Linux y ves Permission denied: sudo chown -R 1000:1000 data snapshots known_faces (o usa volúmenes nombrados).

🔁 GitHub Actions y Docker Hub

CI (.github/workflows/ci.yml)

Se ejecuta en cada push y pull request:

  1. Lint: ruff check + ruff format --check.
  2. Tests: matriz Python 3.10 / 3.11 / 3.12 × MCP SDK 1.x / 2.x, con VISION_FAKE_MODELS=1 (sin torch ni dlib → CI en ~1 min), cobertura y JUnit XML.
  3. Smoke test Docker: construye la imagen, arranca el contenedor en modo demo y espera a que abra el puerto 8000.

Publicación en Docker Hub (.github/workflows/docker-publish.yml)

Se dispara con tags v*.*.*, pushes a main o manualmente. Construye linux/amd64 + linux/arm64 y publica:

Evento Tags
Tag v1.2.3 1.2.3, 1.2, 1, latest
Push a main main, sha-xxxxxxx, latest

Configuración (una sola vez)

  1. Crea un repositorio en Docker Hub: kfr0st/mcp-security-vision.
  2. Genera un Access Token en Docker Hub → Account settings → Security.
  3. En GitHub → Settings → Secrets and variables → Actions, añade:
    • DOCKERHUB_USERNAME
    • DOCKERHUB_TOKEN
    • (opcional) DOCKERHUB_REPO si el repo no se llama mcp-security-vision.
  4. Publica:
    git tag v2.1.0 && git push origin v2.1.0
    

Si faltan los secrets, el workflow solo hace build (con un warning) y no falla.


🧪 Desarrollo y pruebas

pip install -r requirements-dev.txt      # sin torch/dlib
pip install --no-deps -e .

make test      # pytest + cobertura (144 tests, ~25 s)
make lint      # ruff check
make format    # ruff format + fixes
make demo      # servidor con cámara sintética y modelos falsos
make check     # valida .env y rutas

Qué cubren los tests:

  • Parseo de configuración (multicámara, validaciones, enmascarado de credenciales).
  • SQLite: eventos, filtros temporales, resúmenes, zonas, personas y persistencia.
  • Geometría de polígonos + monitor de intrusiones (entrada, dwell, reentrada, etiquetas).
  • Tracker IoU, estabilizador de nombres, registro/reconocimiento facial (con backend falso).
  • Pipeline completo con detectores simulados y evidencias JPEG.
  • Webhook de notificaciones contra un servidor HTTP local.
  • Integración MCP real: levanta server.py en un subproceso, lista herramientas, llama a get_live_status, add_restricted_zone, get_security_logs, capture_snapshot (verifica JPEG) y el recurso security://status.

server.py se ejecuta en un subproceso durante los tests, por eso la cobertura global (~81 %) es menor que la de los módulos de lógica (~90 %); el arranque está cubierto end-to-end por el test de integración.


🔒 Seguridad y privacidad

  • Las contraseñas de cámaras viven solo en .env (ignorado por git) o en variables de entorno.
  • Los logs muestran las URLs con la clave y los tokens de la query enmascarados (rtsp://admin:***@...).
  • known_faces/, snapshots/ y data/ están en .gitignore: no subas fotos de personas al repo.
  • Recomendado: usuarios RTSP de solo lectura y contraseñas fuertes; segmenta las cámaras en una VLAN.
  • El reconocimiento facial biométrico es dato sensible: informa a las personas y cumple tu normativa local (GDPR/RGPD, LFPDPPP, Ley 29733 en Perú, etc.).
  • En Docker el proceso corre sin privilegios y con no-new-privileges.
  • El transporte HTTP escucha en loopback y valida Host/Origin por defecto, pero no incluye autenticación: si lo expones, ponlo detrás de HTTPS + autenticación y limita MCP_ALLOWED_HOSTS.
  • cleanup_history exige confirm=true y no borra capturas salvo que pidas purge_snapshots=true.

Checklist para producción

  • MCP_HOST=127.0.0.1 o proxy con TLS y autenticación.
  • MCP_ALLOWED_HOSTS / MCP_ALLOWED_ORIGINS ajustados a tu dominio real.
  • .env fuera de git y con permisos restringidos (chmod 600).
  • Usuario RTSP de solo lectura y cámaras en una VLAN segmentada.
  • Retención definida (cleanup_history) y copias de seguridad de data/security.db.
  • Aviso de privacidad y base legal para las personas grabadas.
  • Contenedor como usuario no-root y volúmenes con permisos mínimos.
  • Dependencias auditadas (pip-audit, Dependabot) y tokens rotados.

🩺 Problemas frecuentes

Guía completa y verificada ejecutando el código: docs/TROUBLESHOOTING.md. Cada fila está cubierta por tests (tests/test_troubleshooting.py), así que no se queda desfasada.

Síntoma Causa / solución verificada
pip install face-recognition falla compilando dlib face-recognition no tiene binarios: dlib se compila. Instala build-essential cmake python3-dev libopenblas-dev liblapack-dev (el paquete python3-dev es imprescindible y casi siempre se olvida). Windows: Build Tools C++ + CMake. Usa Python 3.10–3.12 (dlib falla en 3.13+). Alternativa sin compilar: imagen Docker o VISION_FAKE_MODELS=1
La cámara entra en bucle de «sin señal» El mensaje real es [cam1] Cámara SIN SEÑAL: … Reintentando en 5s... (no [WARN] Señal de cámara perdida). Comprueba URL/credenciales con ffplay -rtsp_transport tcp <url>, usa el substream (/102, stream2, _sub), revisa el canal del DVR y el códec (H.264 básico). El transporte es TCP por defecto (RTSP_TRANSPORT); si tu equipo necesita UDP: RTSP_TRANSPORT=udp. Diagnóstico: python scripts/check_setup.py --read-frames
La imagen va a tirones / con retraso El análisis corre en el hilo de la cámara. Orden de impacto: FACES_ENABLED=false → FACE_SCALE=0.15 → PROCESS_EVERY_N_FRAMES=10 → YOLO_IMGSZ=480 (debe ser múltiplo de 32, se valida al arrancar) → substream → YOLO_DEVICE=0 (GPU)
No reconoce a nadie / dice «Desconocido» Foto reciente, de frente, iluminada y con un solo rostro. FACE_TOLERANCE=0.65 ayuda, pero también: cara pequeña → FACE_SCALE=0.5; pocas lecturas → FACE_STABILIZER_MIN_VOTES=1; YOLO no ve «person» → FACES_WITHOUT_PERSON=true; FACE_MODEL=cnn (más preciso, lento en CPU). list_known_people avisa si una foto no tiene rostro detectable
Falsos positivos de personas YOLO_CONF=0.6 y FACE_STABILIZER_MIN_VOTES=3 con FACE_STABILIZER_WINDOW=5 (si MIN_VOTES > WINDOW el arranque falla con ConfigError). No subas FACE_TOLERANCE en ese caso
Address already in use Solo ocurre con sse/streamable-http (con stdio no hay puerto). Desde v2.0.1 el servidor comprueba el puerto antes de cargar los modelos y muestra cómo liberarlo o cambiarlo (MCP_PORT=8001)
Claude Desktop no conecta Ruta absoluta al python del venv (.../venv/bin/python o ...\venv\Scripts\python.exe). Logs: macOS ~/Library/Logs/Claude/mcp.log y mcp-server-security-camera.log; Windows %APPDATA%\Claude\logs\. Prueba antes python server.py --check y reinicia la app por completo
El contenedor no ve /dev/video0 Añade devices: ["/dev/video0:/dev/video0"] + group_add: ["video"]. Solo funciona en Linux: Docker Desktop en macOS/Windows no puede pasar dispositivos USB (usa RTSP)
La zona nunca dispara El punto de referencia es el centro inferior de la caja (los pies), no la cabeza. Además: coordenadas 0..1, labels=["person"] (etiqueta exacta de YOLO, no «Persona»), zona enabled, camera_id correcto y respetar el cooldown de 30 s entre entradas. Verifica con capture_snapshot(annotate=true)
Otros avisos de consola y qué significan
Mensaje Significado
[cam1] Primer fotograma recibido: 1920x1080 (...) La cámara entrega vídeo ✅
[cam1] Cámara RECONECTADA (...) Se recuperó de una caída
ConfigError: YOLO_IMGSZ debe ser múltiplo de 32 Resolución de inferencia inválida (usa 320/480/512/640)
SDK sin soporte de transport_security Actualiza mcp a ≥1.9
⚠️ face_recognition no está instalado Falta dlib: instala requirements.txt o usa el modo demo

🗺️ Roadmap

  • Tracking con ByteTrack/BoTSORT para oclusiones y multitudes.
  • Embeddings en SQLite (evitar re-calcular rostros al arrancar) y cifrado en reposo.
  • Panel web (FastAPI + HTMX) con stream anotado y mapa de zonas.
  • Notificaciones nativas a Telegram/WhatsApp como MCP aparte o plugin.
  • Zonas por horario («solo de 22:00 a 06:00») y reglas de dirección (entrar vs. salir).
  • Exportación de clips MP4 alrededor de cada alerta.
  • Autenticación OAuth para el transporte HTTP.

📤 Publicar en GitHub

Guía paso a paso (manual, con gh, con script y prompt listo para el agente de opencode): docs/PUBLICAR_EN_GITHUB.md

# 1) personaliza badges/URLs/autor con tus datos
python scripts/configure_repo.py --github-user kfr0st --name "kfr0st"

# 2) publica (aborta si detecta .env, *.db, *.pt o fotos de known_faces/)
bash scripts/publish_github.sh git@github.com:kfr0st/mcp-security-vision.git

🤝 Contribuir

Lee CONTRIBUTING.md para el entorno de desarrollo, estilo y comprobaciones antes del PR. Para vulnerabilidades usa el reporte privado de SECURITY.md; no abras un issue público.

  1. Haz fork y crea una rama: git checkout -b feature/mi-mejora.
  2. make lint && make test en verde.
  3. Pull request describiendo el cambio y, si aplica, nuevos tests.

📄 Licencia

MIT © 2026 kfr0st. Ver LICENSE.


Hecho con ❤️ para la comunidad MCP hispanohablante · modelcontextprotocol.io

SIMILAR PLUGINS