Skip to main content
Glama

EnriVision

EnriVision is a Model Context Protocol (MCP) server over stdio that uploads local media to EnriProxy and returns server-side extraction + model analysis.

This is useful for media types that many MCP clients cannot read reliably (videos, audio, scanned PDFs, HEIC/AVIF, large files), while keeping the MCP server itself lightweight.

What this project is

  • An MCP server process your MCP host launches (OpenCode, Claude Code, Codex, etc.)

  • A thin client for EnriProxy (resumable upload + structured output)

Related MCP server: multimodal-reader-mcp

Requirements

  • Node.js >= 24

  • A reachable EnriProxy server with these endpoints enabled:

    • POST /v1/uploads

    • HEAD /v1/uploads/:id

    • PATCH /v1/uploads/:id

    • DELETE /v1/uploads/:id (best-effort cleanup of orphaned upload sessions)

    • POST /v1/vision/analyze

    • POST /v1/vision/segments (cursor continuation for long analyses)

    • GET /v1/account/models (fail-open vision-capability probe before upload)

  • An EnriProxy API key (configured on the EnriProxy side)

Install

# Global install
npm install -g @bedolla/enrivision

# Or run without installing
npx -y @bedolla/enrivision@latest --help

Build

npm install
npm run typecheck
npm run build

Usage

1) Configure your MCP host

EnriVision runs as an MCP server over stdio. Your MCP host is responsible for launching the process.

Example: global install

{
  "EnriVision": {
    "type": "stdio",
    "command": "enrivision",
    "args": [],
    "env": {
      "ENRIPROXY_URL": "http://127.0.0.1:8787",
      "ENRIPROXY_API_KEY": "YOUR_ENRIPROXY_API_KEY",
      "ENRIVISION_DEFAULT_LANGUAGE": "es"
    }
  }
}

Example: no install (always uses whatever npm currently tags as latest)

{
  "EnriVision": {
    "type": "stdio",
    "command": "npx",
    "args": ["-y", "@bedolla/enrivision@latest"],
    "env": {
      "ENRIPROXY_URL": "http://127.0.0.1:8787",
      "ENRIPROXY_API_KEY": "YOUR_ENRIPROXY_API_KEY",
      "ENRIVISION_DEFAULT_LANGUAGE": "es"
    }
  }
}
{
  "EnriVision": {
    "type": "stdio",
    "command": "node",
    "args": ["C:\\Users\\Administrator\\Projects\\EnriVision\\dist\\index.js"],
    "env": {
      "ENRIPROXY_URL": "http://127.0.0.1:8787",
      "ENRIPROXY_API_KEY": "YOUR_ENRIPROXY_API_KEY",
      "ENRIVISION_DEFAULT_LANGUAGE": "es"
    }
  }
}

Configuration

EnriVision is configured via environment variables:

  • ENRIPROXY_URL (string, optional, default: http://127.0.0.1:8787)

  • ENRIPROXY_API_KEY (string, required)

  • ENRIVISION_TIMEOUT_MS (string, optional, default: 1800000)

    • Parsed as an integer (milliseconds). This is the operator cap: the per-call analyze timeout is min(operator, mode budget) with single = 10 min (one pass, fast/cheap), multipass/auto = 20 min (per-segment/batch map + reduce; auto may escalate to multipass server-side). Uploads are performed in chunks; per-chunk timeouts honor min(operator, derived 30s..300s) floored at 30 s (an operator budget below 30 s never forces tighter single-chunk budgets).

  • ENRIVISION_DEFAULT_LANGUAGE (string, optional)

    • Default language to send when the tool call does not provide language.

  • ENRIVISION_DENY_SYMLINKS (string, optional)

    • Set to 1 to reject symlinked path/paths inputs. Strict mode opens with O_NOFOLLOW (POSIX) and compares the dev:ino handle identity from fstat. On Windows (win32) O_NOFOLLOW is 0 (advisory only), so strict mode there rests solely on the lstat-vs-fstat comparison with a small swap window: prefer POSIX hosts when symlink races are in scope.

  • ENRIVISION_MODEL (string, optional)

    • Model id for server-side dispatch affinity; omit for auto-dispatch.

  • ENRIVISION_QUIET (string, optional)

    • Set to 1 to silence upload/retry progress lines on stderr.

Analysis budgets

The client analyze timeout is min(ENRIVISION_TIMEOUT_MS, mode budget):

  • single → 10 min (mirrors EnriCode and the EnriProxy single-pass stage budget).

  • multipass → 20 min (mirrors EnriCode and the server multipass wall-clock budget).

  • auto (default) → 20 min: the server picks the mode and may escalate to multipass, so the client cannot assume the short budget. If unsure, omit tuning (auto).

Error shape

Tool failures return MCP isError with Spanish-first bilingual text (ES first, EN second) plus machine-readable structuredContent: { code, retryable, httpStatus? } reusing the EnriCode vocabulary:

  • ENRICODE_ERR_TOOL_INPUT_INVALID — argument/tuning errors (including proxy 400/422). Never retry unchanged (retryable: false).

  • ENRICODE_ERR_TOOL_EXECUTION_FAILED — server/transport failures. retryable is true for 408/429/5xx, false otherwise.

  • ENRICODE_ERR_TOOL_EXECUTION_TIMEOUT — expired upload/analyze budgets (retryable: true; retry with a smaller scope).

  • ENRICODE_ERR_TOOL_EXECUTION_ABORTED — caller-cancelled (retryable: false).

httpStatus is present only when the failure carries a proxy HTTP status.

MCP tools

EnriVision exposes this MCP tool:

  • analyze_media

General notes:

  • The tool accepts a single JSON object as its input (the MCP arguments).

  • At least one of path, paths, or cursor is required. When paths carries at least one valid entry, path is ignored (explicit ignore-path contract: sending both is allowed, path is silently ignored — prefer oneOf semantics and send only one). A cursor (from a truncated response) reads the next window of the list without uploading or analyzing anything.

  • Paths must be absolute on the machine running the MCP server, or http(s) URLs. URLs are downloaded to a temporary directory on the MCP host (up to 64 MiB each; localhost and private-network destinations are blocked) and deleted after analysis. A solitary URL above 64 MiB escalates to EnriProxy's server-side source_url ingestion (resumable download with extra hops); local files use resumable upload up to 4 GiB.

  • EnriVision does not accept per-call server_url/api_key overrides (these are configured via env vars).

  • Nothing you send is dropped quietly: any unrecognized parameter is rejected with the Spanish coaching Parámetro no reconocido: <keys>. Parámetros aceptados: <real list> (top level and inside video/audio/document/images/region), closed enums (analysis_mode, delivery) reject any present non-null value that is not exactly a valid selector (non-strings included; null/blank count as omitted), and two spellings of the same knob with different values (e.g. max_frames + maxFrames) are rejected — send a single spelling. A flat value that differs from its nested counterpart applies the flat (documented precedence) and declares it in warnings.

analyze_media

Inputs:

  • path (string, optional): absolute local file path, or one http(s) URL to download and analyze (up to 64 MiB; a solitary larger URL escalates to server-side source_url ingestion).

  • paths (string[], optional): absolute local image paths or http(s) image URLs (useful for UI screenshot sets).

  • context (string, optional): high-level hint (examples: ui, diagram, chart, error, code, meeting, tutorial, photo).

  • question (string, optional): what you want to extract/answer.

  • language (string, optional): preferred response language (ISO 639-1; e.g., es, en). If omitted, uses ENRIVISION_DEFAULT_LANGUAGE when set.

  • analysis_mode (string, optional): auto | single | multipass.

  • max_frames (number, optional): single-pass video frames (1..20).

  • model (string, optional): model id for server-side dispatch affinity (max 128 chars; env ENRIVISION_MODEL; omit for auto-dispatch).

  • region (object, optional): relative [0,1] zoom box {x, y, width, height} for one image (native-resolution reading of small text); single images only.

  • transcribe (boolean, optional): enable/disable transcription (videos). Has no effect on images/documents (declared in warnings, ignored).

  • transcription_language (string, optional): whisper hint (auto, es, en, ...).

Continuation:

  • cursor (string, optional): opaque cursor from a truncated response (segment_summaries_cursor or transcription_segments_cursor); reads the next window without re-analyzing.

  • offset (integer, optional): continuation start index (defaults to the response next_offset).

  • limit (integer, optional): continuation window length (1..100; defaults to the server window size).

Video targeting:

  • video.clip_start_seconds (number, optional)

  • video.clip_duration_seconds (number, optional)

  • video.clip_end_seconds (number, optional; end = start + duration, wins over clip_duration_seconds)

Multipass tuning (advanced; used only for analysis_mode: multipass):

  • video.segment_seconds (number, optional)

  • video.max_segments (number, optional)

  • video.max_frames_per_segment (number, optional)

  • document.max_pages_total (number, optional)

  • document.pages_per_batch (number, optional)

  • document.max_images_per_batch (number, optional)

  • document.scanned_text_threshold_chars (number, optional)

  • audio.timestamps (boolean, optional)

  • audio.segment_seconds (number, optional)

  • audio.max_segments (number, optional)

  • images.max_images_total (number, optional)

  • images.images_per_batch (number, optional)

  • images.max_dimension (number, optional)

Output:

  • analysis (string): model-produced analysis.

  • media_type (string): detected media type (video, audio, image, document, image_set).

  • extraction (object): safe metadata summary (internal routing details are stripped).

Example arguments object:

{
  "path": "C:\\path\\to\\video.mp4",
  "question": "What are the key steps demonstrated?",
  "analysis_mode": "auto",
  "transcribe": true,
  "language": "es"
}

Many MCP clients include a built-in Read(...) tool that can ingest local files and attach them to the model request. This is convenient, but the set of supported formats is limited and can change across client versions.

If the file you need to analyze is not reliably supported by your client (for example .avif, .heic, .svg, videos, audio, or Office documents), prefer EnriVision MCP so the client can upload bytes and EnriProxy can do extraction reliably.

EnriProxy determines media type using content-type and extension allow-lists.

Videos:

  • .mp4, .mov, .avi, .mkv, .webm, .m4v, .wmv, .flv, .3gp, .3g2, .ts, .mts, .m2ts, .mpeg, .mpg, .gif

Audio:

  • .mp3, .mp1, .mp2, .mpa, .mpga, .wav, .aiff, .aif, .aifc, .caf, .flac, .m4a, .m4b, .m4r, .aac, .ogg, .oga, .wma, .opus, .weba, .mka

Images:

  • .png, .apng, .jpg, .jpeg, .gif, .webp, .avif, .heic, .heif, .tiff, .tif, .bmp, .svg, .ico

Documents:

  • .pdf, .docx, .pptx, .xlsx

Available Tools

1 tool
analyze_mediaAnálisis de medios EnriVisionA
Read-onlyIdempotent

Sube y analiza un archivo mediante EnriProxy (extracción del lado servidor + análisis con modelo). / Upload and analyze a media file via EnriProxy (server-side extraction + model analysis). IMPORTANTE para modelos sin visión: la respuesta es SIEMPRE TEXTO (descripción visual generada del lado del servidor y/o transcripción del audio); nunca se devuelven bloques de imagen, así que CUALQUIER modelo puede consumirla — si no puedes ver imágenes, esta herramienta es tu vía para 'ver' archivos multimedia pidiendo la descripción en question. / IMPORTANT for models without vision: the response is ALWAYS TEXT (server-side visual description and/or audio transcription); image blocks are never returned, so ANY model can consume it — if you cannot see images, this tool is how you 'see' media by asking for the description in question. Formatos aceptados — Imágenes estáticas: PNG, JPEG, WebP, TIFF, BMP, AVIF, HEIC/HEIF, SVG. Imágenes animadas: GIF animado, WebP animado, APNG, SVG animado (se extraen fotogramas clave y se describen sus cambios). Video: MP4, WebM, MKV, MOV, AVI y cualquier contenedor/códec decodificable, CON o SIN audio (en video con audio se procesan juntos: fotogramas + transcripción sobre la misma línea de tiempo). Audio: WAV, MP3, OGG (Vorbis/Opus), M4A/AAC, FLAC, ALAC, Opus y cualquier formato común (todo se normaliza a WAV 16 kHz mono antes de transcribir). Documentos: PDF (de texto, escaneado o mixto), Word (.docx), Excel (.xlsx), PowerPoint (.pptx) — con texto e imágenes embebidas — y JSONL. Conjuntos de varias imágenes: use paths. / Accepted formats — Static images: PNG, JPEG, WebP, TIFF, BMP, AVIF, HEIC/HEIF, SVG. Animated images: animated GIF, animated WebP, APNG, animated SVG (key frames are extracted and their changes described). Video: MP4, WebM, MKV, MOV, AVI and any decodable container/codec, WITH or WITHOUT audio (video with audio processes both together: frames + transcription on the same timeline). Audio: WAV, MP3, OGG (Vorbis/Opus), M4A/AAC, FLAC, ALAC, Opus and any common format (everything is normalized to 16 kHz mono WAV before transcription). Documents: PDF (text, scanned, or mixed), Word (.docx), Excel (.xlsx), PowerPoint (.pptx) — with embedded text and images — and JSONL. Multiple-image sets: use paths. Cuándo usarla: PDFs grandes o escaneados donde el Read puede truncar; video/audio u otros binarios que el cliente no puede leer; HEIC/AVIF/TIFF/BMP/APNG/SVG/Office cuando el Read no es confiable; archivos muy grandes con subidas reanudables (hasta 4 GiB). / When to use: large or scanned PDFs where client Read may truncate; video/audio or binary media the client cannot Read; HEIC/AVIF/TIFF/BMP/APNG/SVG/Office docs when client Read is unreliable; very large files needing resumable uploads (up to 4 GiB). Reglas: use path para un archivo, paths para varias imágenes. Cuando paths trae al menos una entrada válida, path se ignora (contrato explícito: mandar ambos se permite, path se ignora en silencio — prefiera semántica oneOf y mande solo uno). Las entradas en blanco se descartan; claves desconocidas en video/audio/document/images se rechazan. question es opcional aquí (obligatoria en EnriCode); attachmentIndex/attachmentId no existen aquí (sólo EnriCode). / Rules: use path for one file, paths for several images (UI screenshots/photo sets). When paths carries at least one valid entry, path is ignored (explicit ignore-path contract: sending both is allowed, path is silently ignored — prefer oneOf semantics and send only one). Blank paths entries are discarded; unknown keys inside video/audio/document/images are rejected (check typos like max_pages_totall). question is optional here (required in EnriCode vision.analyze_media); attachmentIndex/attachmentId do not exist here (EnriCode-only). Presupuestos (timeout = min(ENRIVISION_TIMEOUT_MS del operador, presupuesto del modo)): single = 10 min (una pasada, rápida y barata); multipass = 20 min (por segmentos/lotes + reducción); auto = 20 min (el servidor elige y puede escalar a multipass). Si no sabe cuál usar, omita el afinado (auto). / Analysis budgets (client analyze timeout = min(operator ENRIVISION_TIMEOUT_MS, mode budget)): single = 10 min (one pass, fast and cheap, 1 image or simple questions); multipass = 20 min (per-segment/batch map + reduce; PDFs over ~20 pages, long videos, image sets); auto = 20 min (the server picks and may escalate to multipass). If unsure, omit tuning (auto). Clip de video: para preguntas en un tiempo específico ("¿qué pasa en 12:34?") use video.clip_start_seconds + video.clip_duration_seconds: convierta a segundos (12:34 = 1260+34 = 754), por ejemplo clip_start_seconds=754 y clip_duration_seconds=30. O dé video.clip_end_seconds (fin = inicio + duración, 0-86400 s). / Video clip targeting: for time-specific questions ("what happens at 12:34?") use video.clip_start_seconds + video.clip_duration_seconds: convert to seconds (12:34 = 1260+34 = 754) and request a window, e.g. clip_start_seconds=754 and clip_duration_seconds=30. Or give video.clip_end_seconds instead (end = start + duration, 0-86400 s). Enteros estrictos: los knobs enteros aceptan números o strings enteras completas ("60" vale; "8.0", "8abc" y 7.9 fallan). Los flotantes aceptan decimales ("12.5" vale). transcribe vale true por defecto y no tiene efecto en imágenes/documentos (se declara en warnings, se ignora). Requiere API key válida de EnriProxy (env ENRIPROXY_API_KEY). / Strict integers: integer knobs accept numbers or complete integer strings ("60" works; "8.0", "8abc", 7.9 fail). Floats accept decimals ("12.5" works). transcribe defaults to true and has no effect on images/documents (declared in warnings, ignored). Requires a valid EnriProxy API key (env ENRIPROXY_API_KEY, sent as Authorization: Bearer ...). Errores: las fallas devuelven isError con texto bilingüe más structuredContent {code, retryable, httpStatus?} con el vocabulario EnriCode; retryable marca 429/5xx/timeouts. Si el mensaje trae Detalle del servidor: en el idioma del proxy, repórtelo tal cual. Fotogramas y transcripción comparten la MISMA línea de tiempo. model es el id del modelo para afinidad de dispatch (máximo 128 caracteres o env ENRIVISION_MODEL; omita para auto-dispatch). language controla el idioma de la RESPUESTA; transcription_language aparte el idioma que Whisper espera al TRANSCRIBIR ("auto" = detectar solo). / Errors: failures return isError with bilingual text plus structuredContent {code, retryable, httpStatus?} reusing the EnriCode vocabulary (ENRICODE_ERR_TOOL_INPUT_INVALID / EXECUTION_FAILED / EXECUTION_TIMEOUT / EXECUTION_ABORTED); retryable marks 429/5xx/timeouts. If the message carries a Detalle del servidor: fragment in the proxy language, report it verbatim. Video frames and transcription share the SAME video timeline. Animated GIF/WebP/APNG/SVG become representative key frames. model is the active model id for server-side dispatch affinity (max 128 chars, or env ENRIVISION_MODEL; omit for auto-dispatch). Set language (e.g. "es") to match the user language and avoid drift: language controls the analysis RESPONSE language; transcription_language separately controls the language Whisper expects when TRANSCRIBING audio ("auto" = detect only). Ejemplos mínimos: (1) una imagen: {"path": "/tmp/foto.png", "question": "..."}. (2) clip de video 12:34->754s: {"path": "/tmp/charla.mp4", "question": "...", "video": {"clip_start_seconds": 754, "clip_duration_seconds": 30}}. (3) PDF largo multipass: {"path": "/tmp/manual.pdf", "question": "...", "analysis_mode": "multipass"}. Rutas absolutas del host MCP (en Windows valen C:/...; en POSIX lanzarían error). / Minimal examples: (1) single image: {"path": "/tmp/shot.png", "question": "What does each capture show?"}. (2) video clip 12:34->754s: {"path": "/tmp/talk.mp4", "question": "What happens at 12:34?", "video": {"clip_start_seconds": 754, "clip_duration_seconds": 30}}. (3) long PDF multipass: {"path": "/tmp/manual.pdf", "question": "Summarize each chapter.", "analysis_mode": "multipass"}. Absolute MCP-host paths (C:/... drive paths only work on a Windows host; POSIX rejects them). Continuación: si la respuesta trae has_more con cursor (segment_summaries_cursor o transcription_segments_cursor), pida el resto con solo cursor (+ offset opcional, por defecto next_offset; también limit opcional 1-100 para acotar la ventana). Con cursor no mande path/paths. / Continuation: when the response carries has_more with a cursor (segment_summaries_cursor or transcription_segments_cursor), ask for the rest with only cursor (+ optional offset, defaults to next_offset; optional limit 1-100 bounds the window); never send path/paths with cursor. Depuración de capturas de UI: abra con un veredicto de una línea; describa zona por zona; aproxime colores como hex; cuantifique defectos de layout; transcriba etiquetas, botones y errores visibles; compare observado vs esperado cuando aplique. / UI-screenshot debugging (when the media are app screenshots): open with a one-line plain verdict; describe zone by zone (header, sidebar, main content, modals, notifications), not as a general scene; approximate colors as hex values (e.g. #1F6FEB) and name them; quantify layout defects (overflows, clipping, overlaps, misalignments, missing spacing, cut text) estimating pixel magnitudes when possible; transcribe labels, buttons, and any visible error/status text; when the request states what was expected, compare observed vs expected explicitly.

Inputs: path, audio, limit, model, +33 more. Returns: analysis, elements, warnings, extraction, +3 more

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.8/5.0

    Scored across 1 tool

    Disambiguation5/5

    Only one tool exists, so there is zero risk of an agent selecting the wrong tool for a task. The single analyze_media tool unambiguously covers any media-analysis request, with no overlapping purposes to confuse.

    Naming Consistency5/5

    analyze_media follows the standard verb_noun convention used across well-designed MCP servers. While a single tool offers little evidence of a broader pattern, the name is self-consistent, descriptive, and aligned with common conventions.

    Tool Count3/5

    A single tool sits at the thin end of the calibration range, where 1-2 tools feels sparse. This is partially redeemed by the tool being a deliberately consolidated mega-tool that absorbs image, video, audio, and document analysis into one entry, but that pushes substantial complexity into a single schema.

    Completeness5/5

    For the server's stated purpose — media analysis — coverage is remarkably complete: static and animated images, video with shared audio timeline, transcription, PDFs, Office documents, multi-image sets, video clipping, multipass budgets, and continuation cursors for long outputs. An agent can fully accomplish the server's job with this one tool and no obvious dead ends.

    Maintenance

    ActivitySlowing
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers