MCP: se retira por completo (endpoint, paquete api/app/mcp/ y dependencia).
Tres razones, en orden de peso:
- Nadie lo usa.
- Llevaba roto desde antes de este trabajo. Con credencial valida devolvia 500:
el handler construia un StreamableHTTPServerTransport nuevo por peticion, sin
manejo de sesion. Lo que estaba expuesto a internet era la puerta abierta de un
cuarto averiado.
- Su SDK sin fijar tumbo el API entero en produccion al saltar a 2.0.0.
Retirarlo es una mitigacion mas fuerte que autenticarlo, que fue lo que hizo el
commit anterior. Recuperable con `git show edb500f5:api/app/mcp/server.py`.
Se limpia tambien la ruta "/mcp" que el endpoint raiz seguia anunciando, y las
menciones a MCP del docstring y la descripcion de Swagger.
Dependencias: once de las doce eran rangos `>=` sin techo, o sea que cada
reconstruccion era una tirada de dados contra PyPI. `pydantic>=2.0` habria
aceptado pydantic 3 con la misma alegria con la que `mcp>=1.0.0` acepto 2.0.0.
Ahora todas van fijadas a la version exacta que corre sana en produccion,
capturada con pip freeze del contenedor healthy.
El lado Next.js ya era reproducible via package-lock.json; por eso el web nunca
se cayo durante el incidente y el api si.
Docs actualizados: AGENTS.md, README.md, api/COTIZADOR_API_SKILL.md y el spec,
que ademas registra en su seccion 0 las tres desviaciones de Fase 0 respecto a
lo disenado (el Despliegue B cancelado, la retirada del MCP y la deriva de
dependencia).
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
El primer rebuild del api en dos semanas trajo mcp 2.0.0, que elimino
Server.list_tools(). app/mcp/server.py lo usa como decorador en su linea 27,
asi que el import lanzaba AttributeError al arrancar.
El agravante: main.py envolvia el montaje del MCP en `except ImportError`.
Un AttributeError no es ImportError, asi que se escapaba y tumbaba toda la
aplicacion. En produccion el contenedor quedo en crash-loop y el REST dejo de
responder por completo, no solo el MCP.
Dos arreglos:
- requirements.txt fija `mcp>=1.28.1,<2.0.0`. La imagen anterior que llevaba dos
semanas sana tenia 1.28.1; el rango abierto `>=1.0.0` permitio el salto mayor.
- main.py captura cualquier excepcion al montar el MCP y la registra. El servidor
MCP es opcional; el REST no. Si el MCP no monta, la API sigue de pie.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Primera fase del plugin de propuesta consultiva (docs/superpowers/specs/
2026-07-28-propuesta-consultiva-ia-fase0-fase1-design.md). Recupera el contexto
humano que hoy se captura y se descarta, y cierra los bloqueadores que el
analisis previo destapo.
Seguridad (lo mas urgente):
- POST /mcp no tenia NINGUNA autenticacion: cero Depends() en main.py, mientras
el servicio recibe dominio publico en produccion (SERVICE_FQDN_API_8000).
Cualquiera en internet podia leer y escribir cotizaciones. Ahora exige
require_auth, que ya existia en app/auth.py y no se estaba usando ahi.
- _obtener_cotizacion hacia SELECT c.* y devolvia dict(cot) al agente, asi que
cualquier columna nueva se publicaba sola. Ahora usa lista blanca espejo de
CotizacionResponse, excluyendo observacionesInternas.
Totales:
- Nueva calcularTotalesCotizacion() en calculators.ts como fuente de verdad
unica. El calculo estaba duplicado a mano en siete consumidores.
- conIva() sustituye a los `* 1.16` hardcodeados de pdf-generator y
excel-builder, que ignoraban IVA_RATE y el flag incluirIva.
Contexto humano:
- Campo nuevo Cotizacion.observacionesInternas. La migracion es PURAMENTE
ADITIVA: no mueve ni una fila. El movimiento de datos no hace falta porque la
unica fila de produccion con observaciones ya contiene texto dirigido al
cliente, y separarlo en dos despliegues mantiene el rollback limpio.
- Dos textareas visualmente inconfundibles en el formulario.
- observaciones se imprime por primera vez en el PDF y el Excel; el dato ya
viajaba hasta las rutas de borrador y se tiraba.
- observacionesInternas solo se ve en la app. La garantia es estructural: el
campo no existe en CotizacionPDFData ni en ExcelData, asi que el generador no
puede filtrarlo aunque alguien lo intente.
Bugs vecinos:
- orderBy explicito en las cuatro rutas de export: el PDF asume las partidas
agrupadas por fase y sin orderBy podia diferir del Excel del mismo envio.
- El PDF de borrador leia solo el branding, no los datos bancarios, e imprimia
los hardcodeados del generador.
- detalleModelo local en PDF y Excel omitia la rama "demanda": esa partida
salia en $0 y sin explicacion. Ahora delegan en la version canonica.
- Los bonos salen de la tabla Bono; la lista hardcodeada queda de respaldo y su
texto ya no coincidia con el seed.
- La palomita de los bonos mide 0pt en las fuentes base de PDFKit (verificado),
o sea que salia como dos espacios. Sustituida por una vineta.
Ademas: zod pasa a ser dependencia declarada. Se importaba en schemas.ts y
resolvia transitivamente, asi que un npm ci --omit=dev reventaba.
Verificado con build, lint y 22 comprobaciones funcionales sobre PDF y Excel
reales (texto del cliente presente, texto interno ausente incluso inyectandolo
a la fuerza, texto largo multipagina, y totales con y sin IVA).
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>