Skip to main content
Glama
josesiiii

forms-vision

by josesiiii

forms-vision — MCP para ver y manejar Oracle Forms

Permite a Claude abrir SAFIX, fotografiar la pantalla y los elementos de una forma, y navegar por ella (clicks, texto, teclas).

⚠ Maneja una sesión REAL del ERP

Esta herramienta inyecta entrada real: mueve el puntero físico y el foco del teclado de verdad, porque AWT ignora los mensajes sintéticos. Un click mal calculado pulsa lo que haya en ese píxel.

Solo actúa en los ambientes de ambientes_permitidosSAFIXDEMOS de fábrica—. Esto ya no es un consejo del README: lee el ambiente del título de la ventana y rechaza todo lo demás, incluida la captura. Falla cerrado: si no puede leer el ambiente, no actúa.

Guardas que trae puestas: F10 y CTRL+S bloqueadas, se niega a pulsar botones cuyo WHEN-BUTTON-PRESSED contiene COMMIT/RUN_PRODUCT/DELETE, no inyecta nada si la ventana no logra ponerse en primer plano, y ante «¿Desea salvar los cambios?» la respuesta correcta es siempre No.

Los controles técnicos y —más importante— los que una herramienta no puede implementar están en SEGURIDAD.md.

Precondición: SAFIX tiene que declarar el ambiente

El control anterior lee el ambiente de títulos como Administración del Sistema [XENCO/Safix@SAFIXDEMOS/2026-09]. SAFIX solo rotula así una vez completado el inicio de sesión con empresa y período; antes de eso el título es XENCO - Administracion del Sistema y no declara nada, así que la herramienta se niega a trabajar.

No es un defecto: es no actuar sin saber contra qué base se trabaja. Completa el inicio de sesión antes de empezar. forms_ventanas —la única herramienta sin restricción, porque para saber dónde estás hay que poder mirar— dice qué ambiente ve.

Por qué está hecho así

Verificado en este equipo el 2026-09-01 con probe_forms.py:

Hecho comprobado

Consecuencia de diseño

El runtime es Java Web Start (jp2launcher.exe), no un navegador

Playwright y las herramientas de navegador no sirven

La ventana es un SunAwtFrame con un solo hijo, un SunAwtCanvas de 1366×720

No hay árbol de items para Windows: la posición de cada item hay que deducirla

PrintWindow sobre ventanas Java suele devolver negro

Se captura de pantalla; la ventana debe estar al frente y despejada

AWT ignora los mensajes sintéticos

La entrada se inyecta con SendInput (entrada real: el puntero se mueve de verdad)

Java Access Bridge está deshabilitado y solo existe WindowsAccessBridge-32.dll (JRE 8u501 de 32 bits), mientras el Python del equipo es de 64 bits

La ruta "árbol accesible" está descartada por ahora (ver Mejora pendiente)

Por eso la ubicación de los items no se descubre, se deduce: sale de los XPosition/YPosition/Width/Height que 06-frontend/forms/extraer_forma.py ya extrae del .fmb, traducidos a píxeles con una calibración medida una vez por forma. No se asume ninguna conversión de unidades — se mide, porque coordenadas_modulo viene None en los extracts existentes.

Related MCP server: Computer Use MCP Server

Flujo obligatorio para una sesión de fotos

Añadido 2026-09-02, después de medir dónde se iba el tiempo en la primera sesión real. Las decisiones se toman con el extract, no frente a la forma.

[1] forms_plan(forma)      -> qué fotos hacen falta, cómo llegar a cada una,
                              qué NO necesita foto y por qué, y qué botones
                              NO se pueden pulsar porque escriben en la BD
[2] revisar el plan         -> cerrar la lista ANTES de tocar la forma
[3] forms_secuencia(...)    -> ejecutar por lotes: click, tecla, capturar
[4] lo que no salga         -> anotarlo y seguir; nada de explorar la forma

Prohibido en el paso 3: ir a la forma "a ver qué hay". Si aparece algo que el extract no anunció, se anota y se continúa con el resto.

Por qué es más rápido que la primera versión

Costo medido en la sesión del 2026-09-01

Cómo se eliminó

Medir el recuadro con un script de PIL externo antes de cada foto

forms_capturar(auto=True) lo detecta solo

Abrir cada PNG para comprobar que servía

la captura devuelve diagnóstico (negra / plana / encuadre OK)

Un viaje ida y vuelta por cada click, espera y captura

forms_secuencia ejecuta la tanda en una llamada

Leer triggers a mano para saber qué botón era seguro

forms_plan clasifica el riesgo de los 54 botones

Las capas

winauto.py    833   Win32, pixeles, ajustes            no sabe nada de MCP
nucleo.py     143   configuracion, extract, contrato   depende solo de winauto
                    ambiente permitido (A.8.31)
plan.py       366   planificacion desde el .fmb        no toca la forma
calibra.py    168   calibracion .fmb -> pixel
server.py    1143   las 15 herramientas MCP

Cada capa depende solo de las de abajo. nucleo.py existe justamente para romper el círculo: si la configuración y el contrato vivieran en server.py, plan.py tendría que importar server y server importar plan.

El servidor MCP se declara solo en server.py, así que plan y calibra se pueden importar desde una prueba sin levantar nada de MCP — que es lo que permite que las 67 comprobaciones corran sin sesión de Forms.

El contrato de éxito y fallo

Las herramientas devuelven texto, porque quien las lee es un modelo. El problema era que devolvían texto también al fallar, así que un lote tenía que adivinar por subcadenas si un paso había salido bien. Eso costó tres defectos en un solo día, y en dos de ellos se guardaron fotos con el nombre de una lista que nunca se abrió.

Ahora cada fallo va prefijado con [FALLO] y cada aviso que invalida lo que venga detrás con [AVISO]. La lista de marcas_fallo queda como red de seguridad y, cuando salta, lo dice: «…(por marcador de texto, sin token)».

pruebas_contrato.py audita el código fuente de las cuatro capas y falla si alguien añade un retorno de fallo sin token. Ya cazó cuatro que se me escaparon en la conversión.

Herramientas

15 herramientas, agrupadas por lo que hacen:

Grupo

Herramienta

Qué hace

Planificación

forms_plan

Empezar aquí. Secciones, rutas, riesgos y descartes. Con seccion= da el detalle: LOVs, select lists, checkboxes y botones con nombre propuesto

forms_pendientes

Terminar aquí. Escribe el .txt cruzando plan y disco

Inspección

forms_ventanas

Estado de la sesión: canvas, ventana activa, tira de pestañas, bloqueo

forms_items

Inventario de items con su etiqueta de pantalla y su LOV

forms_tabs

Blancos de click de la tira de pestañas

Sesión

forms_abrir · forms_foco

Lanzar SAFIX y traer la ventana al frente

Captura

forms_capturar

PNG con auto=True; se autoverifica el encuadre. Con comparar_con= mide cuánto cambió la pantalla

Navegación

forms_click · forms_escribir · forms_tecla

Entrada real; relativo=True para lo que esté dentro de la ventana

forms_calibrar · forms_click_item

Pulsar un control por su nombre del .fmb, incluido GRUPO.RADIO

forms_cerrar_popup

Cierra el recuadro de encima identificándolo por su tamaño

forms_secuencia

Varios pasos en una sola llamada

forms_esperar existe solo como paso de forms_secuencia, que es donde se usa. forms_ventana_activa desapareció: su información está en forms_ventanas.

Las capturas se guardan en 06-frontend/forms/_capturas/<fecha>/ y las herramientas devuelven la ruta, no la imagen: una imagen en línea cuesta mucho contexto y casi siempre basta con abrir la que interesa.

Los dos fallos que no daban ninguna señal

Lo más valioso de la herramienta no es lo que hace, es lo que se niega a hacer a ciegas. Dos fallos producían fotos impecables con el nombre equivocado, y ninguno de los dos daba error:

Fallo

Cómo se caza ahora

Un click no mueve el foco → el Ctrl+L siguiente abre la lista de otro campo. Pedí Centro Costos y salió Barrios

forms_click_item mira el resalte amarillo después de pulsar y avisa si no cubre el punto pulsado. Siempre, no a petición

Un control no se movió → la pasada entera se hace creyendo que el estado cambió

forms_capturar(comparar_con=...). Si sale IDENTICAS cuando se esperaba un cambio, el control no se movió

forms_secuencia detiene el lote ante [FALLO] o [AVISO]: seguir después de uno de estos solo produce fotos mal rotuladas.

El mismo mecanismo decide si un control necesita dos fotos: se mide el área que cambió. CAMBIO ESTRUCTURAL (aparece una pestaña o un panel) → las dos versiones; CAMBIO MENOR (uno o dos campos) → una sola. El umbral está en píxeles, no en porcentaje: el mismo cambio es 0,26% de la ventana de datos y 1,6% de un recuadro de LOV, y con un umbral porcentual la misma diferencia se clasificaba distinto según dónde ocurriera.

Bitácora

Cada captura y cada click_item dejan una línea en _capturas/_bitacora/<fecha>.log con la hora, el usuario de Windows, el pid del servidor, el ambiente, lo que se pidió y los avisos. Los rechazos por ambiente quedan también, marcados XX. Una corrida de ~90 fotos es larga y desatendida: sin registro no hay forma de saber después qué salió y qué quedó a medias. Se cambia con FORMS_VISION_BITACORA.

La escritura del registro se traga sus errores —tumbar una captura por no poder anotarla sería peor— pero los anota y los expone: forms_ventanas muestra siempre la ruta, porque el modo de fallo real no es una excepción sino escribir en el sitio equivocado sin quejarse. Eso pasó de verdad: una prueba con una raíz inexistente creó un árbol entero en Z:\NoExisteEsteProyecto\ sin un solo error.

Ajustes — lo que se afina sin reiniciar

Trece valores viven en ajustes.json y se releen en cada llamada, con caché por fecha de modificación. Son justo los que se afinan probando:

Ajuste

Qué decide

marcas_fallo

qué textos detienen un lote de forms_secuencia

menor_pixeles · menor_tope

cuándo un cambio en pantalla es MENOR y no ESTRUCTURAL

clase_por_parent

el mapa parent_name → clase de control

prefijo

el prefijo del nombre de archivo por clase

sufijos_rango

qué sufijos marcan un extremo de rango

rejilla_minima

desde cuántas casillas un panel es una rejilla de selección

encajes_fiables

cuántos encajes hacen fiable una calibración propia

verbos_peligrosos

qué hace que un botón sea NO TOCAR

ambientes_permitidos

en qué ambientes se permite actuar (A.8.31)

popup_lov_cancel · popup_mensaje_aceptar

dónde está el botón que cierra cada recuadro

popup_alto_lov · ancho_datos_respaldo

las medidas que distinguen un recuadro de la ventana de datos

Dejar ambientes_permitidos vacío inhabilita la herramienta a propósito: una lista vacía no significa «todos». Es la única forma de apagarla sin desinstalar.

El archivo aguanta que se edite mal: si falta, no es JSON válido, trae una clave desconocida o un tipo equivocado, se usan los valores de respaldo que lleva winauto.py dentro y se avisa del problema. Un JSON roto a mitad de una corrida de 90 fotos sería peor que el reinicio que evita.

Pruebas

python pruebas_deteccion.py      # 19 comprobaciones, sin sesión de Forms
python pruebas_ajustes.py        # 16 comprobaciones, sin sesión de Forms
python pruebas_contrato.py       # 16 comprobaciones, sin sesión de Forms
python pruebas_ambiente.py       # 16 comprobaciones, sin sesión de Forms
python verificar_entorno.py      # entorno + captura real

Las 67 comprobaciones corren sin abrir SAFIX. Dos de ellas no comprueban un resultado sino el código: pruebas_contrato.py audita las cuatro capas buscando retornos de fallo sin token, y pruebas_ajustes.py compara las claves contra AJUSTES_DEFECTO en vez de contra un número fijo — porque una prueba que hay que editar cada vez que el código crece acaba fallando por estar vieja, no por un defecto.

pruebas_ambiente.py demuestra que el control de ambientes impide en vez de avisar, y lo hace sustituyendo la lectura del título por títulos de prueba, uno de ellos de producción. Comprobar un control de separación de entornos entrando en producción sería el incidente que el control evita.

pruebas_deteccion.py sintetiza las imágenes que necesita, así que corre en cualquier momento. Existe porque lo que verifica está puesto para cazar fallos silenciosos, y una verificación que se creyera a sí misma no serviría de nada — de hecho la primera vez tumbó el umbral porcentual.

verificar_entorno.py necesita las mismas variables que .mcp.json. Sin FORMS_VISION_PROYECTO la raíz se deduce y los chequeos de extracts no valen: eso sale como aviso, no como fallo, para no mandar a buscar un problema que no existe.

Flujo típico

forms_ventanas                                   # ver qué hay abierto
forms_capturar  nombre="estado"                  # foto del canvas
forms_items     forma="ipedidosopt" canvas="LOTES"
forms_calibrar  forma="ipedidosopt" \
                item_a="FECHA_VENCIMIENTO" px_a=... py_a=... \
                item_b="..."            px_b=... py_b=...
forms_capturar_item forma="ipedidosopt" item="FECHA_VENCIMIENTO"

La calibración se guarda en calibraciones.json y deja de valer si mueves o redimensionas la ventana de Forms, o si cambias de resolución.

Si una forma no tiene extract todavía:

python 06-frontend/forms/extraer_forma.py <ruta>/<forma>.xml

Guardas

  • Solo se actúa en los ambientes autorizados. El ambiente se lee del título de la ventana —no de la configuración: la configuración dice a qué ambiente se quiso apuntar, el título dice en cuál se está— y lo comprueba _exigir_frente, por donde pasa toda inyección de entrada, más forms_capturar. Falla cerrado. La única herramienta sin restricción es forms_ventanas, que solo mira.

  • Ninguna tecla ni click se inyecta si la ventana de Forms no logra ponerse en primer plano. Evita escribir en la aplicación equivocada.

  • F10 y CTRL+S están bloqueadas por defecto: son candidatas a confirmar o guardar contra la base de datos. Es una precaución de esta herramienta, no una regla de Forms — se cambia con FORMS_VISION_TECLAS_BLOQUEADAS en .mcp.json.

  • La sesión que se maneja es una sesión real, y las capturas contienen los datos reales que estuvieran a la vista. Eso no lo resuelve el código: es el riesgo abierto que documenta SEGURIDAD.md.

El semáforo de la barra — qué significa de verdad

Digitar en los campos no depende del semáforo: se puede escribir en cualquier momento y en cualquier parte. El color importa en el momento de guardar, actualizar o cambiar de página habiendo digitado algo:

Semáforo

Qué pasa al guardar

🔴 rojo

modo edición/creación: modifica los datos

🟢 verde

modo consulta: lo digitado es solo criterio de búsqueda

De ahí que la guarda de esta herramienta esté en las teclas de guardado, no en el teclear. Antes de pulsar cualquier cosa que confirme, comprobar el color.

Ojo con un patrón frecuente en las formas SAFIX: navegar entre pestañas o mover un radio puede marcar el registro como modificado, y entonces al entrar a modo consulta la forma pregunta si guardar aunque nadie haya escrito. La respuesta segura es siempre No.

Nada está atado a una forma concreta

Todo lo que se descubrió trabajando una forma quedó como mecanismo general:

Se descubrió en una forma

Cómo quedó

posición y tamaño de la ventana

se detecta en cada captura; da igual si la forma es más grande o está corrida

los colores del tema (título, borde)

se aprenden de la imagen; no hay RGB fijo en el código

dónde está la tira de pestañas

se busca la banda con más bloques; no hay desplazamiento fijo

qué botones son peligrosos

se leen los WHEN-BUTTON-PRESSED del extract de esa forma

qué pestañas son condicionales

se leen los SET_TAB_PAGE_PROPERTY y su IF

qué no tiene camino

se calcula quién invoca a quién en ese extract

rutas de fotos y de pendientes

son parámetros, no constantes

Para trabajar otra forma no hay que tocar código: basta su extract y sus rutas.

Instalación

git clone <repo> && cd MCP-Capturas-Oracle-Forms
powershell -ExecutionPolicy Bypass -File instalar.ps1 -Proyecto Z:\Projects

El instalador crea el venv, copia el código a %LOCALAPPDATA%\forms-vision e imprime el JSON exacto para el panel Servidores MCP locales de la app.

Por qué el código se copia y no se ejecuta desde el repo

Qué

Dónde

Por qué

el código que ejecuta

%LOCALAPPDATA%\forms-vision\

el servidor arranca en cada llamada; si el repo está en red, eso cruza la red constantemente

el intérprete

%LOCALAPPDATA%\forms-vision-venv\

un venv en red es tan lento que el pip install no termina

los datos — extracts, fotos, pendientes

donde apunte FORMS_VISION_PROYECTO

ahí viven y ahí los espera el equipo

En el equipo donde se desarrolló, Z: es un recurso de red compartido, de ahí la separación. En una máquina con el repo en disco local da igual, pero el instalador hace lo mismo: una sola forma de instalar, sin casos especiales.

Lo que NO se versiona, y por qué

  • calibraciones.json — la conversión unidades del .fmb → píxeles, medida contra la ventana viva. Depende de la resolución, del tamaño de la ventana y hasta de la corrida: la misma sección dio escala 1.335 / off 6 en una y 1.33 / off 7 en otra. Versionarlo haría que otra máquina arrancara con una geometría ajena, y los clicks caerían desplazados sin dar ningún error. Se regenera con forms_calibrar.

  • el venv y los __pycache__ — binarios de una máquina y una versión de Python concretas.

  • .mcp.json y claude_desktop_config.json — configuración de máquina, y el segundo puede llevar credenciales en texto plano.

Tras editar server.py o winauto.py hay que reinstalar y reiniciar la app: el proceso del MCP ya tiene el módulo en memoria y Python no recarga nada por su cuenta. Se comprueba comparando la fecha del archivo con la hora de arranque del proceso — si el proceso es anterior, corre código viejo. Los 9 ajustes de ajustes.json son la excepción: esos se releen en caliente (ver Ajustes).

Configuración

Registrado en Z:\Projects\.mcp.json (ámbito de proyecto: carga cuando la sesión está abierta en Z:\Projects). Variables de entorno:

Variable

Por defecto

FORMS_VISION_PROYECTO

tres niveles arriba del servidor — hay que declararla

FORMS_VISION_JNLP

C:\Scripdominio\SAFIXV4.jnlp

FORMS_VISION_SALIDA

Z:\Projects\06-frontend\forms\_capturas

FORMS_VISION_BITACORA

<SALIDA>\_bitacora

FORMS_VISION_TECLAS_BLOQUEADAS

F10,CTRL+S

FORMS_VISION_PROYECTO es obligatoria con la copia local: subir tres niveles desde %LOCALAPPDATA%\forms-vision da una ruta absurda. verificar_entorno.py lo avisa en vez de reportar fallos falsos por extracts que no encuentra.

Dependencias: mcp, mss, pillow. Sin pywin32: la capa Win32 es ctypes puro en winauto.py.

Mejora pendiente — Java Access Bridge

Daría el árbol de componentes en vivo (rol, nombre y rectángulo por item), sin calibrar y sin depender del .fmb. Requiere dos cosas que hoy no están:

  1. Habilitar el puente: jabswitch -enable y reiniciar la sesión de Forms.

  2. Resolver el choque de arquitectura — instalar un Python de 32 bits, o un JRE 8 de 64 bits para que aparezca WindowsAccessBridge-64.dll.

Ambas son instalaciones en la máquina, así que no se hicieron sin permiso.

Diagnóstico

python probe_forms.py <carpeta_salida>

Enumera las ventanas Java, sus hijos nativos y captura cada una. No envía teclas ni clicks: es seguro contra una sesión en uso.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/josesiiii/mcp_forms_vision'

If you have feedback or need assistance with the MCP directory API, please join our Discord server