Skip to main content
Glama
index.js6.75 kB
#!/usr/bin/env node "use strict"; /** * MCP Bridge para HandsAI * Conecta Claude Desktop con HandsAI Spring Boot server via HTTP */ Object.defineProperty(exports, "__esModule", { value: true }); const readline = require("readline"); const node_fetch_1 = require("node-fetch"); class HandsAIMcpBridge { constructor(handsaiUrl = 'http://localhost:8080') { this.handsaiBaseUrl = handsaiUrl; this.rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: false }); // Solo logs de debug van a stderr console.error('🚀 HandsAI MCP Bridge iniciando...'); console.error(`📡 Conectando a: ${this.handsaiBaseUrl}`); } async start() { this.rl.on('line', async (line) => { try { const request = JSON.parse(line.trim()); const response = await this.handleMcpRequest(request); if (response) { // Solo JSON puro a stdout console.log(JSON.stringify(response)); } } catch (error) { console.error(`❌ Error processing request: ${error}`); const errorResponse = { jsonrpc: '2.0', error: { code: -32700, message: 'Parse error' } }; console.log(JSON.stringify(errorResponse)); } }); this.rl.on('close', () => { console.error('👋 HandsAI MCP Bridge cerrando...'); process.exit(0); }); } async handleMcpRequest(request) { console.error(`🔧 Método: ${request.method}, ID: ${request.id}`); switch (request.method) { case 'initialize': return this.handleInitialize(request); case 'list_tools': return await this.handleListTools(request); case 'call_tool': return await this.handleCallTool(request); default: return { jsonrpc: '2.0', id: request.id, error: { code: -32601, message: 'Method not found' } }; } } handleInitialize(request) { console.error('🔧 Initialize'); return { jsonrpc: '2.0', id: request.id, result: { protocolVersion: '2024-11-05', capabilities: { tools: { listChanged: false } }, serverInfo: { name: 'HandsAI MCP Bridge', version: '1.0.0' } } }; } async handleListTools(request) { console.error('📋 List tools - llamando a HandsAI API'); try { const response = await (0, node_fetch_1.default)(`${this.handsaiBaseUrl}/mcp/tools/list`); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); console.error(`✅ HandsAI respondió con ${data.result?.tools?.length || 0} herramientas`); // Convertir respuesta de HandsAI a formato MCP estándar const mcpTools = data.result?.tools?.map((tool) => ({ name: tool.name, description: tool.description, inputSchema: tool.inputSchema || { type: 'object', properties: {}, required: [] } })) || []; return { jsonrpc: '2.0', id: request.id, result: { tools: mcpTools } }; } catch (error) { console.error(`❌ Error llamando HandsAI list tools: ${error}`); return { jsonrpc: '2.0', id: request.id, error: { code: -32603, message: `Internal error: ${error instanceof Error ? error.message : String(error)}` } }; } } async handleCallTool(request) { if (!request.params?.name) { return { jsonrpc: '2.0', id: request.id, error: { code: -32602, message: 'Invalid params: tool name required' } }; } const toolName = request.params.name; const arguments_ = request.params.arguments || {}; console.error(`🔧 Call tool: ${toolName} con args:`, arguments_); try { const mcpCallRequest = { jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name: toolName, arguments: arguments_ } }; const response = await (0, node_fetch_1.default)(`${this.handsaiBaseUrl}/mcp/tools/call`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(mcpCallRequest) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); console.error(`✅ HandsAI ejecutó ${toolName} exitosamente`); // La respuesta de HandsAI ya debería estar en formato MCP correcto return { jsonrpc: '2.0', id: request.id, result: data.result || { content: [{ type: 'text', text: 'No result returned' }] } }; } catch (error) { console.error(`❌ Error ejecutando ${toolName}: ${error}`); return { jsonrpc: '2.0', id: request.id, error: { code: -32603, message: `Execution error: ${error instanceof Error ? error.message : String(error)}` } }; } } } // Inicializar bridge const bridge = new HandsAIMcpBridge(); bridge.start().catch((error) => { console.error(`💥 Error fatal: ${error}`); process.exit(1); });

Latest Blog Posts

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/Vrivaans/handsai-bridge'

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