170
src/index.ts
170
src/index.ts
@@ -3,6 +3,7 @@ import cors from 'cors';
|
|||||||
import swaggerUi from 'swagger-ui-express';
|
import swaggerUi from 'swagger-ui-express';
|
||||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
||||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||||
|
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
||||||
import {
|
import {
|
||||||
CallToolRequestSchema,
|
CallToolRequestSchema,
|
||||||
ListToolsRequestSchema,
|
ListToolsRequestSchema,
|
||||||
@@ -16,8 +17,14 @@ const PORT = process.env.PORT || 3000;
|
|||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
// Middleware
|
// Middleware
|
||||||
app.use(cors());
|
app.use(cors({
|
||||||
|
origin: '*',
|
||||||
|
methods: ['GET', 'POST', 'OPTIONS'],
|
||||||
|
allowedHeaders: ['Content-Type', 'Accept'],
|
||||||
|
credentials: false
|
||||||
|
}));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
app.use(express.text());
|
||||||
|
|
||||||
// Swagger Documentation
|
// Swagger Documentation
|
||||||
app.use('/api-docs', ...swaggerUi.serve as any);
|
app.use('/api-docs', ...swaggerUi.serve as any);
|
||||||
@@ -459,62 +466,81 @@ async function handleToolCall(name: string, args: any): Promise<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Endpoint JSON-RPC para MCP
|
* Crea un servidor MCP con los handlers configurados
|
||||||
*/
|
*/
|
||||||
app.post('/mcp/v1', async (req: Request, res: Response) => {
|
function createMCPServer(): Server {
|
||||||
|
const server = new Server(
|
||||||
|
{
|
||||||
|
name: 'ine-mcp-server',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
capabilities: {
|
||||||
|
tools: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Registrar handler para listar herramientas
|
||||||
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
||||||
|
tools
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Registrar handler para llamar herramientas
|
||||||
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||||
|
const { name, arguments: args } = request.params;
|
||||||
|
const result = await handleToolCall(name, args || {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
text: JSON.stringify(result, null, 2)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint MCP HTTP con SSE (Server-Sent Events)
|
||||||
|
* Este es el protocolo oficial de MCP para HTTP
|
||||||
|
*/
|
||||||
|
app.get('/mcp/v1/sse', async (req: Request, res: Response) => {
|
||||||
|
console.log('Nueva conexión SSE MCP');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { jsonrpc, method, params, id } = req.body;
|
const server = createMCPServer();
|
||||||
|
const transport = new SSEServerTransport('/mcp/v1/message', res);
|
||||||
if (jsonrpc !== '2.0') {
|
|
||||||
return res.status(400).json({
|
await server.connect(transport);
|
||||||
jsonrpc: '2.0',
|
|
||||||
error: { code: -32600, message: 'Invalid Request' },
|
// La conexión se mantiene abierta hasta que el cliente se desconecte
|
||||||
id: null
|
req.on('close', () => {
|
||||||
});
|
console.log('Conexión SSE cerrada');
|
||||||
}
|
server.close().catch(console.error);
|
||||||
|
|
||||||
// Listar herramientas
|
|
||||||
if (method === 'tools/list') {
|
|
||||||
return res.json({
|
|
||||||
jsonrpc: '2.0',
|
|
||||||
result: { tools },
|
|
||||||
id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Llamar a una herramienta
|
|
||||||
if (method === 'tools/call') {
|
|
||||||
const { name, arguments: args } = params;
|
|
||||||
const result = await handleToolCall(name, args || {});
|
|
||||||
|
|
||||||
return res.json({
|
|
||||||
jsonrpc: '2.0',
|
|
||||||
result: {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
text: JSON.stringify(result, null, 2)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Método no soportado
|
|
||||||
return res.status(404).json({
|
|
||||||
jsonrpc: '2.0',
|
|
||||||
error: { code: -32601, message: 'Method not found' },
|
|
||||||
id
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error en MCP:', error);
|
console.error('Error en conexión SSE:', error);
|
||||||
return res.status(500).json({
|
if (!res.headersSent) {
|
||||||
jsonrpc: '2.0',
|
res.status(500).json({ error: error.message });
|
||||||
error: { code: -32603, message: error.message || 'Internal error' },
|
}
|
||||||
id: req.body.id || null
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Endpoint para mensajes MCP (usado por SSE transport)
|
||||||
|
*/
|
||||||
|
app.post('/mcp/v1/message', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
// Este endpoint es manejado internamente por SSEServerTransport
|
||||||
|
// Solo necesitamos asegurarnos de que el body parser esté configurado
|
||||||
|
res.status(202).json({ received: true });
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Error procesando mensaje:', error);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -563,43 +589,15 @@ app.get('/api/tablas-operacion/:idOperacion', async (req, res) => {
|
|||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`🚀 MCP INE Server ejecutándose en http://localhost:${PORT}`);
|
console.log(`🚀 MCP INE Server ejecutándose en http://localhost:${PORT}`);
|
||||||
console.log(`📚 Documentación Swagger: http://localhost:${PORT}/api-docs`);
|
console.log(`📚 Documentación Swagger: http://localhost:${PORT}/api-docs`);
|
||||||
console.log(`🔧 Endpoint MCP JSON-RPC: http://localhost:${PORT}/mcp/v1`);
|
console.log(`🔧 Endpoint MCP SSE: http://localhost:${PORT}/mcp/v1/sse`);
|
||||||
|
console.log(`📨 Endpoint MCP Message: http://localhost:${PORT}/mcp/v1/message`);
|
||||||
console.log(`💚 Health check: http://localhost:${PORT}/health`);
|
console.log(`💚 Health check: http://localhost:${PORT}/health`);
|
||||||
|
console.log(`\n📋 Herramientas disponibles: ${tools.length}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Para uso con stdio (AI Toolkit)
|
// Para uso con stdio (AI Toolkit local)
|
||||||
export async function runStdioServer() {
|
export async function runStdioServer() {
|
||||||
const server = new Server(
|
const server = createMCPServer();
|
||||||
{
|
|
||||||
name: 'ine-mcp-server',
|
|
||||||
version: '1.0.0',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
capabilities: {
|
|
||||||
tools: {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Registrar handlers
|
|
||||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
||||||
tools
|
|
||||||
}));
|
|
||||||
|
|
||||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
||||||
const { name, arguments: args } = request.params;
|
|
||||||
const result = await handleToolCall(name, args || {});
|
|
||||||
|
|
||||||
return {
|
|
||||||
content: [
|
|
||||||
{
|
|
||||||
type: 'text',
|
|
||||||
text: JSON.stringify(result, null, 2)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const transport = new StdioServerTransport();
|
const transport = new StdioServerTransport();
|
||||||
await server.connect(transport);
|
await server.connect(transport);
|
||||||
|
|
||||||
|
|||||||
Referencia en una nueva incidencia
Block a user