/** * Client MCP - Streamable HTTP transport (MCP 2025-03-26) * La réponse initialize est en SSE, les suivantes aussi. */ import http from 'http'; const MCP_HOST = '192.168.1.150'; const MCP_PORT = 9002; let sessionId = null; let msgId = 1; function postMCP(body) { return new Promise((resolve, reject) => { const payload = JSON.stringify(body); const headers = { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload), 'Accept': 'application/json, text/event-stream', }; if (sessionId) headers['mcp-session-id'] = sessionId; const req = http.request({ hostname: MCP_HOST, port: MCP_PORT, path: '/mcp', method: 'POST', headers }, res => { // Récupère session ID depuis header if (res.headers['mcp-session-id']) sessionId = res.headers['mcp-session-id']; let data = ''; res.setEncoding('utf8'); res.on('data', c => data += c); res.on('end', () => { const ct = res.headers['content-type'] || ''; if (ct.includes('text/event-stream')) { // Parser SSE let result = null; let error = null; for (const line of data.split('\n')) { if (line.startsWith('data:')) { const raw = line.slice(5).trim(); if (!raw || raw === '[DONE]') continue; try { const j = JSON.parse(raw); if (j.result !== undefined) result = j.result; if (j.error) error = j.error; } catch {} } } if (error) reject(new Error(JSON.stringify(error))); else resolve(result); } else { // JSON direct try { const j = JSON.parse(data); if (j.error) reject(new Error(JSON.stringify(j.error))); else resolve(j.result ?? j); } catch { resolve(data || null); } } }); }); req.on('error', reject); req.write(payload); req.end(); }); } export async function init() { await postMCP({ jsonrpc: '2.0', id: msgId++, method: 'initialize', params: { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'nox', version: '1.0' } } }); // Notification initialized await postMCP({ jsonrpc: '2.0', method: 'notifications/initialized', params: {} }).catch(() => {}); console.log('✅ MCP Session:', sessionId); } export async function toolRaw(name, args = {}) { return postMCP({ jsonrpc: '2.0', id: msgId++, method: 'tools/call', params: { name, arguments: args } }); } export async function tool(name, args = {}) { const result = await toolRaw(name, args); if (!result) return null; if (result.content && result.content[0]) { const text = result.content[0].text; try { return JSON.parse(text); } catch { return text; } } return result; } export { sessionId };