Skip to main content
Glama

Gotas Commerce MCP Server

smithery-mcp-server.js9.99 kB
/** * Servidor MCP para Smithery seguindo a especificação MCP 2025-03-26 * * Este servidor implementa o transporte Streamable HTTP conforme a especificação * e é compatível com o Smithery para listagem de ferramentas sem autenticação. */ const express = require('express'); const cors = require('cors'); const app = express(); const port = process.env.PORT || 3000; // Definição das ferramentas const tools = [ { name: 'create-payment', description: 'Creates a new payment in the Gotas Commerce API', parameters: { type: 'object', properties: { amount: { type: 'number', description: 'Payment amount (e.g., 100.50)' }, currency: { type: 'string', description: 'Currency code (e.g., "USDT")' }, return_url: { type: 'string', description: 'URL to redirect customer after payment' }, description: { type: 'string', description: 'Optional description of the payment' } }, required: ['amount', 'currency', 'return_url'] } }, { name: 'check-payment-status', description: 'Checks the status of an existing payment', parameters: { type: 'object', properties: { payment_id: { type: 'string', description: 'Identifier of the payment to check' } }, required: ['payment_id'] } } ]; // Middleware para processar JSON app.use(express.json()); // Middleware para CORS app.use(cors({ origin: '*', methods: ['GET', 'POST', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Accept', 'Mcp-Session-Id', 'Origin', 'X-Requested-With'] })); // Middleware para processar configuração em base64 app.use((req, res, next) => { if (req.query.config) { try { const configStr = Buffer.from(req.query.config, 'base64').toString('utf-8'); req.smitheryConfig = JSON.parse(configStr); console.log('Smithery config detected and parsed successfully'); } catch (error) { console.error('Error parsing Smithery config:', error); } } else { req.smitheryConfig = {}; } next(); }); // Middleware para logging app.use((req, res, next) => { console.log(`${new Date().toISOString()} - ${req.method} ${req.originalUrl}`); next(); }); // Endpoint MCP principal (Streamable HTTP) app.all('/mcp', (req, res) => { // Verificar se o cliente aceita JSON ou SSE const acceptsJson = req.headers.accept && req.headers.accept.includes('application/json'); const acceptsSSE = req.headers.accept && req.headers.accept.includes('text/event-stream'); // Método GET para SSE ou para listar ferramentas if (req.method === 'GET') { if (req.query.action === 'list-tools') { // Listar ferramentas via GET (para compatibilidade com Smithery) console.log('Listando ferramentas via GET com action=list-tools'); return res.json({ jsonrpc: "2.0", id: req.query.id || "1", result: { tools: tools } }); } else if (acceptsSSE) { handleSSE(req, res); return; } else { // Resposta padrão para GET sem action=list-tools return res.json({ jsonrpc: "2.0", id: "1", result: { name: 'Gotas Commerce', description: 'Cryptocurrency payment gateway for USDT transactions', version: '1.0.0' } }); } } // Método POST para enviar mensagens JSON-RPC if (req.method === 'POST' && (acceptsJson || acceptsSSE)) { handleJsonRpc(req, res); return; } // Método DELETE para encerrar sessão if (req.method === 'DELETE') { handleSessionTermination(req, res); return; } // Método OPTIONS para preflight CORS if (req.method === 'OPTIONS') { res.status(200).end(); return; } // Método não suportado res.status(405).json({ jsonrpc: "2.0", id: null, error: { code: -32601, message: "Method not allowed", data: { method: req.method } } }); }); // Manipulador para SSE function handleSSE(req, res) { console.log('Iniciando stream SSE'); // Configurar cabeçalhos para SSE res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // Manter a conexão viva const keepAlive = setInterval(() => { res.write(':\n\n'); // Comentário SSE para manter a conexão }, 30000); // Limpar quando a conexão for fechada req.on('close', () => { console.log('Conexão SSE fechada'); clearInterval(keepAlive); }); } // Manipulador para JSON-RPC function handleJsonRpc(req, res) { const body = req.body; // Verificar se é uma requisição de listagem de ferramentas if (isListToolsRequest(body)) { console.log('Processando requisição de listagem de ferramentas'); // Responder com a lista de ferramentas no formato JSON-RPC 2.0 return res.json({ jsonrpc: "2.0", id: getRequestId(body), result: { tools: tools } }); } // Verificar se é uma requisição de execução de ferramenta if (isToolExecutionRequest(body)) { console.log('Processando requisição de execução de ferramenta'); // Extrair informações da requisição const method = getMethod(body); const params = getParams(body); const requestId = getRequestId(body); // Verificar se a ferramenta existe const toolDef = tools.find(t => t.name === method); if (!toolDef) { return res.json({ jsonrpc: "2.0", id: requestId, error: { code: -32601, message: `Method '${method}' not found`, data: { method: method } } }); } // Verificar autenticação (lazy loading) const apiKey = process.env.GOTAS_API_KEY || req.smitheryConfig?.GOTAS_API_KEY; if (!apiKey) { return res.json({ jsonrpc: "2.0", id: requestId, error: { code: -32000, message: "API key is required to execute tools", data: { method: method } } }); } // Executar a ferramenta return executeToolAndRespond(method, params, requestId, res); } // Resposta padrão para outras requisições res.json({ jsonrpc: "2.0", id: getRequestId(body) || null, error: { code: -32600, message: "Invalid request", data: { reason: "Unsupported request format" } } }); } // Manipulador para encerramento de sessão function handleSessionTermination(req, res) { console.log('Encerrando sessão'); // Responder com sucesso res.status(200).json({ jsonrpc: "2.0", id: "1", result: { success: true, message: 'Session closed successfully' } }); } // Função para verificar se é uma requisição de listagem de ferramentas function isListToolsRequest(body) { return ( body.method === 'list-tools' || body.action === 'list-tools' || !body.method || body.method === '' ); } // Função para verificar se é uma requisição de execução de ferramenta function isToolExecutionRequest(body) { return ( body.method && body.method !== 'list-tools' && body.method !== '' ); } // Função para obter o ID da requisição function getRequestId(body) { return body.id || "1"; } // Função para obter o método da requisição function getMethod(body) { return body.method || body.tool; } // Função para obter os parâmetros da requisição function getParams(body) { return body.params || body.arguments || {}; } // Função para executar a ferramenta e responder function executeToolAndRespond(method, params, requestId, res) { if (method === 'create-payment') { const { amount, currency, return_url, description } = params; return res.json({ jsonrpc: "2.0", id: requestId, result: { message: "Payment created successfully", details: { amount, currency, return_url, description, status: "pending", payment_url: `https://commerce.gotas.com/pay?session=example-id`, wallet_address: "0x79Dc4e370298e0ff2563972c2d4e8350a31Fe851", } } }); } if (method === 'check-payment-status') { const { payment_id } = params; return res.json({ jsonrpc: "2.0", id: requestId, result: { id: payment_id, amount: "250.00", currency: "USDT", status: "pending", payment_url: `https://commerce.gotas.com/pay?session=${payment_id}`, wallet_address: "0x79Dc4e370298e0ff2563972c2d4e8350a31Fe851", created_at: new Date().toISOString(), expires_at: new Date(Date.now() + 30 * 60 * 1000).toISOString(), } }); } // Ferramenta não implementada return res.json({ jsonrpc: "2.0", id: requestId, error: { code: -32000, message: `The method '${method}' is not implemented yet`, data: { method: method } } }); } // Rota raiz para verificação de saúde app.get('/', (req, res) => { res.json({ status: 'ok', message: 'Gotas Commerce MCP Server is running', version: '1.0.0', protocol: 'MCP Streamable HTTP', endpoints: { mcp: '/mcp' } }); }); // Iniciar o servidor const server = app.listen(port, () => { console.log(`MCP Server running on port ${port}`); console.log(`MCP endpoint available at: http://localhost:${port}/mcp`); }); // Lidar com sinais de encerramento process.on('SIGTERM', () => { console.log('SIGTERM signal received: closing HTTP server'); server.close(() => { console.log('HTTP server closed'); }); }); process.on('SIGINT', () => { console.log('SIGINT signal received: closing HTTP server'); server.close(() => { console.log('HTTP server closed'); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/caiovicentino/mcpGOTAS'

If you have feedback or need assistance with the MCP directory API, please join our Discord server