Make MCP Server

by 3rzy
const express = require('express'); const axios = require('axios'); const dotenv = require('dotenv'); const http = require('http'); const WebSocket = require('ws'); // Załaduj zmienne środowiskowe dotenv.config(); // Konfiguracja const config = { make: { apiToken: process.env.MAKE_API_TOKEN, baseUrl: 'https://eu1.make.com/api/v2' // Może się różnić w zależności od regionu }, port: process.env.PORT || 3001 }; // Inicjalizacja serwera Express const app = express(); app.use(express.json()); const server = http.createServer(app); // Inicjalizacja serwera WebSocket const wss = new WebSocket.Server({ server }); // Funkcje pomocnicze dla API Make const makeApi = { // Autentykacja dla każdego zapytania getHeaders: () => ({ 'Content-Type': 'application/json', 'Authorization': `Token ${config.make.apiToken}` }), // Pobierz listę scenariuszy getScenarios: async () => { try { const response = await axios.get(`${config.make.baseUrl}/scenarios`, { headers: makeApi.getHeaders() }); return response.data; } catch (error) { console.error('Błąd podczas pobierania scenariuszy:', error.message); throw new Error(`Nie udało się pobrać scenariuszy: ${error.message}`); } }, // Pobierz szczegóły scenariusza getScenario: async (scenarioId) => { try { const response = await axios.get(`${config.make.baseUrl}/scenarios/${scenarioId}`, { headers: makeApi.getHeaders() }); return response.data; } catch (error) { console.error(`Błąd podczas pobierania scenariusza ${scenarioId}:`, error.message); throw new Error(`Nie udało się pobrać scenariusza: ${error.message}`); } }, // Uruchom scenariusz runScenario: async (scenarioId) => { try { const response = await axios.post(`${config.make.baseUrl}/scenarios/${scenarioId}/run`, {}, { headers: makeApi.getHeaders() }); return response.data; } catch (error) { console.error(`Błąd podczas uruchamiania scenariusza ${scenarioId}:`, error.message); throw new Error(`Nie udało się uruchomić scenariusza: ${error.message}`); } }, // Włącz/wyłącz scenariusz toggleScenario: async (scenarioId, active) => { try { const response = await axios.patch(`${config.make.baseUrl}/scenarios/${scenarioId}`, { active }, { headers: makeApi.getHeaders() }); return response.data; } catch (error) { console.error(`Błąd podczas przełączania stanu scenariusza ${scenarioId}:`, error.message); throw new Error(`Nie udało się przełączyć stanu scenariusza: ${error.message}`); } }, // Utwórz nowy scenariusz createScenario: async (name, folderID = null) => { try { const payload = { name, ...(folderID && { folderID }) }; const response = await axios.post(`${config.make.baseUrl}/scenarios`, payload, { headers: makeApi.getHeaders() }); return response.data; } catch (error) { console.error('Błąd podczas tworzenia scenariusza:', error.message); throw new Error(`Nie udało się utworzyć scenariusza: ${error.message}`); } }, // Pobierz dostępne aplikacje getApps: async () => { try { const response = await axios.get(`${config.make.baseUrl}/apps`, { headers: makeApi.getHeaders() }); return response.data; } catch (error) { console.error('Błąd podczas pobierania aplikacji:', error.message); throw new Error(`Nie udało się pobrać dostępnych aplikacji: ${error.message}`); } }, // Pobierz historię uruchomień scenariusza getScenarioHistory: async (scenarioId, limit = 10) => { try { const response = await axios.get(`${config.make.baseUrl}/scenarios/${scenarioId}/history`, { params: { limit }, headers: makeApi.getHeaders() }); return response.data; } catch (error) { console.error(`Błąd podczas pobierania historii scenariusza ${scenarioId}:`, error.message); throw new Error(`Nie udało się pobrać historii scenariusza: ${error.message}`); } } }; // Lista narzędzi MCP const tools = [ { name: "list_scenarios", description: "Pobiera listę wszystkich scenariuszy w Make", handler: async () => { const scenarios = await makeApi.getScenarios(); return { scenarios }; } }, { name: "run_scenario", description: "Uruchamia scenariusz w Make", handler: async (params) => { const { scenarioId } = params; if (!scenarioId) { throw new Error("Brak wymaganego parametru 'scenarioId'"); } const result = await makeApi.runScenario(scenarioId); return { success: true, executionId: result.executionId, message: `Scenariusz ${scenarioId} został uruchomiony` }; } }, { name: "toggle_scenario", description: "Włącza lub wyłącza scenariusz w Make", handler: async (params) => { const { scenarioId, active } = params; if (!scenarioId) { throw new Error("Brak wymaganego parametru 'scenarioId'"); } if (active === undefined) { throw new Error("Brak wymaganego parametru 'active'"); } const result = await makeApi.toggleScenario(scenarioId, active); return { success: true, scenarioId: result.id, active: result.active, message: `Scenariusz ${scenarioId} został ${active ? 'aktywowany' : 'dezaktywowany'}` }; } }, { name: "get_scenario", description: "Pobiera szczegóły scenariusza w Make", handler: async (params) => { const { scenarioId } = params; if (!scenarioId) { throw new Error("Brak wymaganego parametru 'scenarioId'"); } const scenario = await makeApi.getScenario(scenarioId); return { scenario }; } }, { name: "create_scenario", description: "Tworzy nowy scenariusz w Make", handler: async (params) => { const { name, folderID } = params; if (!name) { throw new Error("Brak wymaganego parametru 'name'"); } const result = await makeApi.createScenario(name, folderID); return { success: true, scenarioId: result.id, name: result.name, message: `Scenariusz "${name}" został utworzony` }; } }, { name: "list_apps", description: "Pobiera listę wszystkich dostępnych aplikacji w Make", handler: async () => { const apps = await makeApi.getApps(); return { apps }; } }, { name: "get_scenario_history", description: "Pobiera historię uruchomień scenariusza w Make", handler: async (params) => { const { scenarioId, limit } = params; if (!scenarioId) { throw new Error("Brak wymaganego parametru 'scenarioId'"); } const history = await makeApi.getScenarioHistory(scenarioId, limit); return { history }; } } ]; // Obsługa połączeń WebSocket wss.on('connection', (ws) => { console.log('Nowe połączenie WebSocket'); // Funkcja pomocnicza do wysyłania odpowiedzi const sendResponse = (id, result, error) => { const response = { jsonrpc: "2.0", id }; if (error) { response.error = error; } else { response.result = result; } console.log('Wysyłanie odpowiedzi:', JSON.stringify(response)); ws.send(JSON.stringify(response)); }; // Obsługa wiadomości ws.on('message', async (data) => { console.log('Otrzymano dane:', data.toString()); let request; try { request = JSON.parse(data); console.log('Przetworzone żądanie:', request); } catch (error) { console.error('Błąd parsowania JSON:', error); sendResponse(null, null, { code: -32700, message: "Nieprawidłowe żądanie JSON" }); return; } // Sprawdź czy żądanie ma prawidłowy format JSON-RPC 2.0 if (!request.jsonrpc || request.jsonrpc !== "2.0" || !request.method) { sendResponse(request.id || null, null, { code: -32600, message: "Nieprawidłowe żądanie JSON-RPC" }); return; } // Obsługa inicjalizacji if (request.method === 'initialize') { console.log('Obsługa initialize'); sendResponse(request.id, { serverInfo: { name: "make-mcp-server", version: "1.0.0" }, capabilities: {} }); return; } // Obsługa wywołania narzędzia if (request.method === 'tools/invoke') { console.log('Obsługa tools/invoke:', request.params); const { name, parameters } = request.params || {}; if (!name) { sendResponse(request.id, null, { code: -32602, message: "Nieprawidłowe parametry: brak nazwy narzędzia" }); return; } const tool = tools.find(t => t.name === name); if (!tool) { sendResponse(request.id, null, { code: -32601, message: `Nieznane narzędzie: ${name}` }); return; } try { const result = await tool.handler(parameters || {}); sendResponse(request.id, result); } catch (error) { console.error(`Błąd podczas wykonywania narzędzia ${name}:`, error); sendResponse(request.id, null, { code: -32000, message: error.message }); } return; } // Obsługa listy narzędzi if (request.method === 'tools/list') { console.log('Obsługa tools/list'); const toolsList = tools.map(tool => ({ name: tool.name, description: tool.description })); sendResponse(request.id, { tools: toolsList }); return; } // Obsługa shutdown if (request.method === 'shutdown') { console.log('Obsługa shutdown'); sendResponse(request.id, null); return; } // Obsługa exit if (request.method === 'exit') { console.log('Obsługa exit'); sendResponse(request.id, null); ws.close(); return; } // Nieznana metoda console.log('Nieznana metoda:', request.method); sendResponse(request.id, null, { code: -32601, message: `Nieznana metoda: ${request.method}` }); }); // Obsługa błędów ws.on('error', (error) => { console.error('Błąd WebSocket:', error); }); // Obsługa zamknięcia połączenia ws.on('close', () => { console.log('Połączenie WebSocket zamknięte'); }); }); // Obsługa żądań HTTP app.get('/', (req, res) => { res.send('Make MCP Server działa poprawnie. Protokół WebSocket jest aktywny.'); }); // Uruchomienie serwera server.listen(config.port, () => { console.log(`Make MCP Server działa na porcie ${config.port}`); console.log('Serwer obsługuje protokół MCP przez WebSocket'); });