Fastapi Fastmcp Autogen
Write once, get both REST API and MCP tools automatically
Ask AI about Fastapi Fastmcp Autogen
Powered by Claude · Grounded in docs
I know everything about Fastapi Fastmcp Autogen. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
FastAPI + FastMCP Auto-Generator
Harto de escribir el mismo código dos veces para tener una API REST y un servidor MCP? Este proyecto resuelve eso generando automáticamente una cosa desde la otra.
El problema
Cuando trabajas con FastAPI y FastMCP, terminas duplicando lógica:
# Primero defines la tool MCP
@mcp.tool()
def sumar(a: float, b: float) -> dict:
return {"resultado": a + b}
# Luego defines el endpoint (el mismo código otra vez)
@app.post("/sumar")
async def sumar_endpoint(req: Request):
return {"resultado": req.a + req.b}
Esto es tedioso y propenso a errores. Si cambias algo, tienes que acordarte de cambiarlo en ambos lados.
La solución
Dos enfoques dependiendo de tu caso de uso:
main.py - Escribes las herramientas MCP, se generan los endpoints automáticamente main2.py - Escribes los endpoints FastAPI, se generan las herramientas MCP automáticamente
Eliges el punto de partida que te convenga más y el otro lado se crea solo.
Instalación
git clone <este-repo>
cd mcp_test
uv sync
Uso
Opción 1: Empezar desde MCP (main.py)
uv run uvicorn main:app --reload
Defines herramientas asÃ:
@mcp.tool()
def multiplicar(a: float, b: float) -> dict:
"""Multiplica dos números."""
return {"resultado": a * b}
Y automáticamente tienes POST /multiplicar disponible.
Opción 2: Empezar desde FastAPI (main2.py)
uv run uvicorn main2:app --port 8001 --reload
Defines endpoints asÃ:
@app.post("/multiplicar")
async def multiplicar(req: OperacionRequest):
"""Multiplica dos números."""
return {"resultado": req.a * req.b}
Y automáticamente tienes la herramienta MCP multiplicar disponible.
Ejemplos
Ambos servidores exponen las mismas operaciones:
# Llamar al endpoint REST
curl -X POST "http://localhost:8000/sumar" \
-H "Content-Type: application/json" \
-d '{"a": 10, "b": 5}'
# Respuesta: {"a":10.0,"b":5.0,"operacion":"suma","resultado":15.0}
El servidor MCP está montado en /calculadora/mcp/ y la documentación Swagger en /docs.
Cómo funciona
main.py (MCP → FastAPI)
- Registras funciones con
@mcp.tool() - Una función inspecciona todas las tools del registry de FastMCP
- Para cada una, crea un modelo Pydantic con los mismos parámetros
- Registra un endpoint POST en FastAPI que llama a la tool original
main2.py (FastAPI → MCP)
- Defines endpoints POST normales en FastAPI
- Una función recorre todas las rutas registradas
- Extrae el modelo Pydantic que usa cada endpoint
- Genera dinámicamente una función con parámetros explÃcitos (FastMCP no acepta **kwargs)
- Registra esa función como tool MCP que llama al endpoint original
El truco en main2.py es que FastMCP requiere parámetros explÃcitos, asà que uso exec() para generar el código de la función dinámicamente. No es lo más elegante pero funciona.
Cuál usar?
Depende de tu situación:
Usa main.py si:
- Estás empezando un proyecto nuevo enfocado en MCP
- Prefieres el estilo minimalista de FastMCP
- No necesitas control fino sobre HTTP
Usa main2.py si:
- Ya tienes una API FastAPI y quieres agregarle MCP
- Necesitas controlar códigos de estado, headers, etc.
- Prefieres trabajar con FastAPI directamente
Yo personalmente prefiero main.py porque es más limpio, pero main2.py es útil si ya tienes código FastAPI existente.
Agregar operaciones nuevas
Solo defines en tu lado preferido:
main.py:
@mcp.tool()
def dividir(a: float, b: float) -> dict:
if b == 0:
return {"error": "División por cero"}
return {"resultado": a / b}
main2.py:
@app.post("/dividir")
async def dividir(req: OperacionRequest):
if req.b == 0:
raise HTTPException(400, "División por cero")
return {"resultado": req.a / req.b}
El resto se genera automáticamente.
Estructura
.
├── main.py # MCP → FastAPI
├── main2.py # FastAPI → MCP
├── pyproject.toml # Dependencias (FastAPI, FastMCP, Uvicorn)
└── uv.lock
Limitaciones
- Los parámetros tienen que ser tipos básicos (int, float, str, bool)
- En main2.py cada endpoint necesita exactamente un parámetro Pydantic
- Solo endpoints POST se convierten en tools (GET no tiene mucho sentido como tool)
- El código generado con
exec()en main2.py es medio feo pero funciona
TecnologÃas
- FastAPI para el servidor HTTP
- FastMCP para el servidor MCP
- Pydantic para validación
- Uvicorn como servidor ASGI
Notas
Este es un proyecto de demostración. En producción probablemente querrÃas agregar:
- Autenticación
- Rate limiting
- Logging apropiado
- Tests
- Manejo de errores más robusto
Pero el concepto base de auto-generación funciona bien y ahorra bastante código repetitivo.
License
MIT - haz lo que quieras con esto.
