description: Problemas técnicos reales al desplegar servidores MCP en Cloud Run
tags: mcp, cloudrun, serverless, review
Análisis Crítico: Servidores MCP en Google Cloud Run
🎯 Contexto
Revisé un tutorial sobre desplegar servidores MCP (Model Context Protocol) en Google Cloud Run. Encontré varios problemas técnicos críticos que impiden su funcionamiento real.
🚨 Problema #1: Transporte Incompatible
Error Fatal: El tutorial usa StdioServerTransport en Cloud Run.
Por qué falla:
- Cloud Run requiere HTTP/HTTPS
- StdioServerTransport usa stdin/stdout
- Resultado: servidor no funcional
✅ Solución:
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
const app = express();
const PORT = process.env.PORT || 8080;
const server = new Server({
name: "weather-mcp-server",
version: "1.0.0"
});
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/messages", res);
await server.connect(transport);
});
app.post("/messages", express.json(), async (req, res) => {
// Manejar mensajes MCP
});
app.listen(PORT);
🚨 Problema #2: package.json Incompleto
El tutorial menciona npm run build pero nunca lo define.
✅ Solución:
{
"name": "mcp-server",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/server.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^0.5.0",
"express": "^4.18.2"
},
"devDependencies": {
"typescript": "^5.3.3",
"@types/node": "^20.10.0"
}
}
🚨 Problema #3: Configuración Cliente Incorrecta
Error: Usa curl como comando MCP
{
"mcpServers": {
"weather": {
"command": "curl",
"args": [...]
}
}
}
Claude Desktop necesita un proceso stdio, no HTTP directo.
✅ Solución: Proxy Local
// mcp-proxy.ts
import fetch from "node-fetch";
import { stdin, stdout } from "process";
const SERVER = process.env.MCP_SERVER_URL;
stdin.on("data", async (chunk) => {
const req = JSON.parse(chunk.toString());
const res = await fetch(`${SERVER}/messages`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(req)
});
const data = await res.json();
stdout.write(JSON.stringify(data) + "\n");
});
Configuración:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/path/to/mcp-proxy.js"],
"env": {
"MCP_SERVER_URL": "https://your-server.run.app"
}
}
}
}
🚨 Problema #4: Sin Seguridad
Tutorial menciona autenticación pero no la implementa.
✅ API Keys:
function authenticateAPIKey(req, res, next) {
const apiKey = req.headers["x-api-key"];
const validKeys = process.env.API_KEYS?.split(",") || [];
if (!validKeys.includes(apiKey)) {
return res.status(403).json({ error: "Invalid key" });
}
next();
}
app.use("/sse", authenticateAPIKey);
app.use("/messages", authenticateAPIKey);
🚨 Problema #5: Dockerfile Básico
✅ Versión Optimizada:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci
COPY src/ ./src/
RUN npm run build
FROM node:18-alpine
WORKDIR /app
RUN addgroup -g 1001 nodejs && adduser -S nodejs -u 1001
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/package*.json ./
RUN npm ci --only=production
USER nodejs
EXPOSE 8080
CMD ["node", "dist/server.js"]
💰 Costos Reales
Cloud Run Pricing:
- 100K requests/mes
- 200ms promedio
- 512MB memoria
Costo: ~$0.55/mes
Free tier cubre:
- 2M requests/mes
- 360K vCPU-segundos
- 180K GiB-segundos
Para desarrollo: $0 🎉
🧪 Tests Esenciales
import { describe, it, expect } from "vitest";
describe("MCP Server", () => {
it("lista herramientas", async () => {
const res = await server.handleRequest({
method: "tools/list"
});
expect(res.tools).toHaveLength(1);
});
it("maneja errores", async () => {
const res = await server.handleRequest({
method: "tools/call",
params: { arguments: { city: "" } }
});
expect(res.isError).toBe(true);
});
});
🎯 Caso de Uso: Firestore
import { Firestore } from "@google-cloud/firestore";
const db = new Firestore();
server.setRequestHandler(CallToolRequest, async (req) => {
if (req.params.name === "query_db") {
const { collection } = req.params.arguments;
const snapshot = await db.collection(collection).limit(10).get();
return {
content: [{
type: "text",
text: JSON.stringify(snapshot.docs.map(d => d.data()))
}]
};
}
});
📊 Valoración
Problemas:
- ❌ Transporte incompatible
- ❌ Config cliente incorrecta
- ❌ Sin seguridad implementada
- ❌ Scripts faltantes
Fortalezas:
- ✅ Buena estructura
- ✅ Ejemplo claro
- ✅ Menciona best practices
Puntuación: 6.5/10
Con correcciones: 9/10
⏱️ Tiempo Real
- Básico funcional: 30-45 min
- Production-ready: 2-4 horas
- Completo con CI/CD: 1-2 días
La promesa de "10 minutos" es muy optimista.
🔗 Recursos
💭 Conclusión
El tutorial tiene buenas intenciones pero fallas técnicas críticas. Con las correcciones aquí propuestas, puede convertirse en un recurso valioso para la comunidad MCP.
Top comments (0)