28
src/index.ts
28
src/index.ts
@@ -833,11 +833,14 @@ function createMCPServer(): Server {
|
||||
* Endpoint MCP JSON-RPC directo (más compatible y simple)
|
||||
*/
|
||||
app.post('/mcp/v1', async (req: Request, res: Response) => {
|
||||
console.log('MCP JSON-RPC Request:', req.body);
|
||||
console.log('MCP JSON-RPC Request:', JSON.stringify(req.body, null, 2));
|
||||
|
||||
try {
|
||||
const { jsonrpc, method, params, id } = req.body;
|
||||
|
||||
// Establecer headers correctos
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
|
||||
// Validar JSON-RPC 2.0
|
||||
if (jsonrpc !== '2.0') {
|
||||
return res.status(400).json({
|
||||
@@ -852,7 +855,8 @@ app.post('/mcp/v1', async (req: Request, res: Response) => {
|
||||
|
||||
// Initialize - Handshake inicial de MCP
|
||||
if (method === 'initialize') {
|
||||
return res.json({
|
||||
console.log('MCP Initialize received');
|
||||
return res.status(200).json({
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
protocolVersion: '2024-11-05',
|
||||
@@ -868,15 +872,16 @@ app.post('/mcp/v1', async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Initialized - Confirmación del cliente
|
||||
// Initialized - Confirmación del cliente (es una notificación, no requiere respuesta)
|
||||
if (method === 'notifications/initialized') {
|
||||
console.log('Cliente MCP inicializado');
|
||||
return res.status(204).send();
|
||||
return res.status(200).json({});
|
||||
}
|
||||
|
||||
// Listar herramientas
|
||||
if (method === 'tools/list') {
|
||||
return res.json({
|
||||
console.log('MCP tools/list received');
|
||||
return res.status(200).json({
|
||||
jsonrpc: '2.0',
|
||||
result: { tools },
|
||||
id
|
||||
@@ -887,6 +892,8 @@ app.post('/mcp/v1', async (req: Request, res: Response) => {
|
||||
if (method === 'tools/call') {
|
||||
const { name, arguments: args } = params || {};
|
||||
|
||||
console.log(`MCP tools/call: ${name}`, args);
|
||||
|
||||
if (!name) {
|
||||
return res.status(400).json({
|
||||
jsonrpc: '2.0',
|
||||
@@ -900,7 +907,9 @@ app.post('/mcp/v1', async (req: Request, res: Response) => {
|
||||
|
||||
const result = await handleToolCall(name, args || {});
|
||||
|
||||
return res.json({
|
||||
console.log(`MCP tools/call result for ${name}: success`);
|
||||
|
||||
return res.status(200).json({
|
||||
jsonrpc: '2.0',
|
||||
result: {
|
||||
content: [
|
||||
@@ -916,7 +925,7 @@ app.post('/mcp/v1', async (req: Request, res: Response) => {
|
||||
|
||||
// Ping/Pong
|
||||
if (method === 'ping') {
|
||||
return res.json({
|
||||
return res.status(200).json({
|
||||
jsonrpc: '2.0',
|
||||
result: {},
|
||||
id
|
||||
@@ -924,7 +933,8 @@ app.post('/mcp/v1', async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
// Método no soportado
|
||||
return res.status(404).json({
|
||||
console.log(`MCP method not found: ${method}`);
|
||||
return res.status(200).json({
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: -32601,
|
||||
@@ -935,7 +945,7 @@ app.post('/mcp/v1', async (req: Request, res: Response) => {
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('Error en MCP:', error);
|
||||
return res.status(500).json({
|
||||
return res.status(200).json({
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: -32603,
|
||||
|
||||
@@ -12,12 +12,14 @@ import type { Idioma } from './types/ine.types.js';
|
||||
const PORT = process.env.PORT || 3001;
|
||||
const app = express();
|
||||
|
||||
app.use(cors());
|
||||
app.use(cors({
|
||||
origin: '*',
|
||||
methods: ['GET', 'POST', 'OPTIONS'],
|
||||
allowedHeaders: ['Content-Type', 'Accept'],
|
||||
credentials: false
|
||||
}));
|
||||
app.use(express.json());
|
||||
|
||||
// Almacenamiento de sesiones SSE
|
||||
const sessions = new Map<string, { server: Server; transport: SSEServerTransport }>();
|
||||
|
||||
/**
|
||||
* Maneja las llamadas a las herramientas MCP
|
||||
*/
|
||||
@@ -128,15 +130,9 @@ const tools = [
|
||||
* Endpoint SSE para conexión MCP
|
||||
*/
|
||||
app.get('/sse', async (req: Request, res: Response) => {
|
||||
const sessionId = Math.random().toString(36).substring(7);
|
||||
|
||||
// Configurar SSE
|
||||
res.writeHead(200, {
|
||||
'Content-Type': 'text/event-stream',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive'
|
||||
});
|
||||
console.log('Nueva conexión SSE iniciada');
|
||||
|
||||
try {
|
||||
// Crear servidor MCP
|
||||
const server = new Server(
|
||||
{
|
||||
@@ -151,10 +147,15 @@ app.get('/sse', async (req: Request, res: Response) => {
|
||||
);
|
||||
|
||||
// Registrar handlers
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
||||
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
||||
console.log('SSE: ListTools request received');
|
||||
return { tools };
|
||||
});
|
||||
|
||||
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
||||
const { name, arguments: args } = request.params;
|
||||
console.log(`SSE: CallTool request - ${name}`, args);
|
||||
|
||||
const result = await handleToolCall(name, args || {});
|
||||
|
||||
return {
|
||||
@@ -167,34 +168,48 @@ app.get('/sse', async (req: Request, res: Response) => {
|
||||
};
|
||||
});
|
||||
|
||||
// Crear transporte SSE
|
||||
const transport = new SSEServerTransport('/messages', res);
|
||||
// Crear transporte SSE - este configura los headers automáticamente
|
||||
const transport = new SSEServerTransport('/message', res);
|
||||
|
||||
// Conectar servidor con transporte
|
||||
await server.connect(transport);
|
||||
|
||||
sessions.set(sessionId, { server, transport });
|
||||
console.log('SSE: Servidor MCP conectado');
|
||||
|
||||
// Limpiar al cerrar conexión
|
||||
// Manejar cierre de conexión
|
||||
req.on('close', () => {
|
||||
sessions.delete(sessionId);
|
||||
console.log(`Sesión SSE cerrada: ${sessionId}`);
|
||||
console.log('SSE: Conexión cerrada por el cliente');
|
||||
server.close().catch(err => console.error('Error cerrando servidor:', err));
|
||||
});
|
||||
|
||||
console.log(`Nueva sesión SSE iniciada: ${sessionId}`);
|
||||
} catch (error: any) {
|
||||
console.error('Error en conexión SSE:', error);
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Endpoint para enviar mensajes (POST)
|
||||
* Este endpoint recibe los mensajes JSON-RPC del cliente
|
||||
*/
|
||||
app.post('/messages', async (req: Request, res: Response) => {
|
||||
try {
|
||||
// Este endpoint procesa mensajes enviados por el cliente
|
||||
const message = req.body;
|
||||
console.log('Mensaje recibido:', message);
|
||||
app.post('/message', express.json(), async (req: Request, res: Response) => {
|
||||
console.log('SSE Message received:', JSON.stringify(req.body, null, 2));
|
||||
|
||||
res.json({ success: true });
|
||||
try {
|
||||
// El SDK de MCP maneja esto internamente a través del SSEServerTransport
|
||||
// Solo necesitamos confirmar la recepción
|
||||
res.status(202).json({ received: true });
|
||||
} catch (error: any) {
|
||||
console.error('Error procesando mensaje:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
console.error('Error procesando mensaje SSE:', error);
|
||||
res.status(500).json({
|
||||
error: error.message,
|
||||
stack: error.stack
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -203,8 +218,14 @@ app.get('/health', (req: Request, res: Response) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
transport: 'SSE',
|
||||
sessions: sessions.size,
|
||||
timestamp: new Date().toISOString()
|
||||
timestamp: new Date().toISOString(),
|
||||
service: 'MCP INE Server (SSE)',
|
||||
version: '1.0.0',
|
||||
endpoints: {
|
||||
sse: '/sse',
|
||||
message: '/message',
|
||||
health: '/health'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Referencia en una nueva incidencia
Block a user