Files
cotizador/api/main.py
T
urieljarethandClaude Opus 5 20f535d485 Retirar el servidor MCP y fijar todas las dependencias del API
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]>
2026-07-28 17:51:21 -06:00

123 lines
3.8 KiB
Python

"""Cotizador E3 — FastAPI Application
REST API for digital marketing quotation management.
Optimized for n8n workflows and other integrations.
"""
from __future__ import annotations
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.config import settings
from app.database import close_db, init_db
@asynccontextmanager
async def lifespan(app: FastAPI):
await init_db()
yield
await close_db()
app = FastAPI(
title="Cotizador E3 API",
description=(
"API REST para el sistema de cotizaciones de marketing digital de Consultoría E3. "
"Optimizada para integración con n8n y otros consumidores. "
"Autenticación por X-API-Key o Bearer JWT."
),
version="1.0.0",
lifespan=lifespan,
docs_url="/docs",
redoc_url="/redoc",
openapi_url="/openapi.json",
openapi_tags=[
{"name": "Health", "description": "Health check y información de la API"},
{"name": "Auth", "description": "Autenticación (API Key + JWT)"},
{"name": "Clientes", "description": "Gestión de clientes"},
{"name": "Catálogo", "description": "Catálogo de servicios de marketing digital"},
{"name": "Categorías", "description": "Categorías de servicios"},
{"name": "Cotizaciones", "description": "Cotizaciones — CRUD completo con servicios y plan CRM"},
{"name": "Paquetes", "description": "Paquetes de servicios con fases"},
{"name": "Configuración", "description": "Configuración de la empresa (key-value)"},
{"name": "Bonos", "description": "Bonos disponibles para cotizaciones"},
{"name": "Financiamiento", "description": "Planes y cálculo de financiamiento"},
{"name": "Export", "description": "Exportación de PDF, Excel y CSV"},
{"name": "Import", "description": "Importación masiva de servicios"},
],
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
return JSONResponse(
status_code=500,
content={"error": "Error interno del servidor", "detail": str(exc)},
)
# Register routers
from app.routers import (
auth,
bonos,
catalogo,
categorias,
clientes,
configuracion,
cotizaciones,
export_,
financiamiento,
health,
import_,
paquetes,
)
app.include_router(health.router)
app.include_router(auth.router)
app.include_router(clientes.router)
app.include_router(catalogo.router)
app.include_router(categorias.router)
app.include_router(cotizaciones.router)
app.include_router(paquetes.router)
app.include_router(configuracion.router)
app.include_router(bonos.router)
app.include_router(financiamiento.router)
app.include_router(export_.router)
app.include_router(import_.router)
# El servidor MCP se retiro (2026-07-28). No se estaba usando, su transporte
# llevaba tiempo roto (instanciaba un StreamableHTTPServerTransport por peticion,
# sin manejo de sesion, y devolvia 500 con credencial valida) y su SDK sin fijar
# tumbo el API entero en produccion al saltar a 2.0.0. Si algun dia se retoma,
# el codigo esta en el historial: `git show edb500f5:api/app/mcp/server.py`.
@app.get("/", include_in_schema=False)
async def root():
return {
"name": "Cotizador E3 API",
"version": "1.0.0",
"docs": "/docs",
"openapi": "/openapi.json",
"health": "/health",
}
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host=settings.API_HOST, port=settings.API_PORT, reload=True)