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]>
This commit is contained in:
urieljareth
2026-07-28 17:51:21 -06:00
co-authored by Claude Opus 5
parent edb500f517
commit 20f535d485
9 changed files with 118 additions and 862 deletions
+10 -47
View File
@@ -1,7 +1,7 @@
"""Cotizador E3 — FastAPI Application
REST API + MCP Server for digital marketing quotation management.
Optimized for n8n workflows and AI agents (OpenClaw, Claude, ChatGPT).
REST API for digital marketing quotation management.
Optimized for n8n workflows and other integrations.
"""
from __future__ import annotations
@@ -9,11 +9,10 @@ from __future__ import annotations
from contextlib import asynccontextmanager
from datetime import datetime, timezone
from fastapi import Depends, FastAPI, Request
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from app.auth import require_auth
from app.config import settings
from app.database import close_db, init_db
@@ -29,8 +28,8 @@ 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 agentes de IA (OpenClaw, Claude, ChatGPT) "
"via MCP (Model Context Protocol)."
"Optimizada para integración con n8n y otros consumidores. "
"Autenticación por X-API-Key o Bearer JWT."
),
version="1.0.0",
lifespan=lifespan,
@@ -99,46 +98,11 @@ app.include_router(financiamiento.router)
app.include_router(export_.router)
app.include_router(import_.router)
# Mount MCP server
try:
from app.mcp.server import server as mcp_server
from mcp.server.streamable_http import StreamableHTTPServerTransport
@app.post("/mcp")
async def mcp_endpoint(request: Request, _auth: dict = Depends(require_auth)):
"""MCP (Model Context Protocol) endpoint for AI agents.
Requiere autenticacion (X-API-Key o Bearer JWT), igual que el resto del API.
Sin esto el endpoint quedaba abierto a internet: el servicio recibe dominio
publico en produccion (docker-compose.coolify.yml, SERVICE_FQDN_API_8000) y
las herramientas MCP leen y escriben cotizaciones directamente.
"""
transport = StreamableHTTPServerTransport(mcp_server)
return await transport.handle_request(request)
@app.get("/mcp")
async def mcp_info():
"""MCP server info. Use POST for actual MCP communication."""
return {
"name": "cotizador-e3",
"version": "1.0.0",
"protocol": "mcp",
"description": "MCP server for Cotizador E3 quotation system",
}
except Exception as exc: # noqa: BLE001
# Antes esto capturaba solo ImportError. Cuando el SDK de MCP salto a 2.0.0 y
# elimino Server.list_tools(), el import lanzo AttributeError, se escapo del
# except y tumbo TODA la aplicacion: el REST tambien dejo de responder.
# El servidor MCP es opcional; el REST no. Si el MCP no monta, se registra y
# la API sigue de pie.
import logging
logging.getLogger("uvicorn.error").error(
"No se pudo montar el servidor MCP (%s: %s). El REST sigue disponible.",
type(exc).__name__,
exc,
)
# 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)
@@ -149,7 +113,6 @@ async def root():
"docs": "/docs",
"openapi": "/openapi.json",
"health": "/health",
"mcp": "/mcp",
}