import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import express from "express"; import { z } from "zod"; const API_BASE_URL = process.env.PLANILLA_API_URL || "http://localhost:4000"; async function fetchJSON(path, options = {}) { const res = await fetch(`${API_BASE_URL}${path}`, { headers: { 'Content-Type': 'application/json' }, ...options }); if (!res.ok) { const txt = await res.text(); throw new Error(`API request failed (${res.status}): ${txt}`); } return res.json(); } function createServer() { const server = new McpServer({ name: "planilla-mcp", version: "0.1.0", }); // ----- Resources ----- // List all planillas server.resource( "planilla-list", "planilla://list", async (uri) => { const planillas = await fetchJSON('/api/planillas'); return { contents: [{ uri: uri.href, text: JSON.stringify(planillas) }] }; } ); // Get planilla by ID server.resource( "planilla", new ResourceTemplate("planilla://{id}", { list: undefined }), async (uri, { id }) => { const planilla = await fetchJSON(`/api/planillas/${id}`); return { contents: [{ uri: uri.href, text: JSON.stringify(planilla) }] }; } ); // ----- Tools ----- // Create a new planilla server.tool( "create-planilla", "Crea una planilla", { empleado_id: z.number(), fecha_desde: z.string(), fecha_hasta: z.string(), titulo: z.string(), total: z.number().optional(), estado: z.string().optional(), fecha_anulado: z.string().optional(), creador_id: z.number().optional(), anulador_id: z.number().optional(), }, async (params) => { const body = JSON.stringify(params); const planilla = await fetchJSON('/api/planillas', { method: 'POST', body }); return { content: [{ type: 'text', text: JSON.stringify(planilla) }] }; } ); // Update planilla by ID server.tool( "update-planilla", "Actualiza una planilla existente", { id: z.number(), empleado_id: z.number().optional(), fecha_desde: z.string().optional(), fecha_hasta: z.string().optional(), titulo: z.string().optional(), total: z.number().optional(), estado: z.string().optional(), fecha_anulado: z.string().optional(), anulador_id: z.number().optional(), }, async ({ id, ...updates }) => { const body = JSON.stringify(updates); const planilla = await fetchJSON(`/api/planillas/${id}`, { method: 'PUT', body, }); return { content: [{ type: 'text', text: JSON.stringify(planilla) }] }; } ); // Delete planilla by ID server.tool( "delete-planilla", "Elimina una planilla", { id: z.number() }, async ({ id }) => { await fetchJSON(`/api/planillas/${id}`, { method: 'DELETE' }); return { content: [{ type: 'text', text: `Planilla ${id} eliminada` }] }; } ); return server; } async function main() { const useStdio = process.argv.includes("--stdio"); if (useStdio) { const server = createServer(); const transport = new StdioServerTransport(); await server.connect(transport); console.log("MCP Planilla server listo (stdio)"); } else { const app = express(); app.use(express.json()); const port = process.env.PORT || 5000; app.post("/mcp", async (req, res) => { try { const server = createServer(); const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined }); res.on("close", () => { transport.close(); server.close(); }); await server.connect(transport); await transport.handleRequest(req, res, req.body); } catch (error) { console.error("Error handling MCP request:", error); if (!res.headersSent) { res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error" }, id: null, }); } } }); app.get("/mcp", async (_req, res) => { res.writeHead(405).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Method not allowed." }, id: null, })); }); app.delete("/mcp", async (_req, res) => { res.writeHead(405).end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32000, message: "Method not allowed." }, id: null, })); }); app.listen(port, () => { console.log(`MCP Planilla HTTP server listening on port ${port}`); }); } } main().catch((err) => { console.error("Error fatal en MCP server:", err); process.exit(1); });