Skip to main content
Glama

Algorand MCP Server

by Tairon-ai
server.js9.12 kB
#!/usr/bin/env node // Load .env file if it exists require('dotenv').config(); const express = require('express'); const cors = require('cors'); const { spawn } = require('child_process'); const { randomUUID } = require('crypto'); const app = express(); const PORT = process.env.PORT || 3000; console.log('🚀 Starting Algorand MCP Server...'); console.log(`📍 Port: ${PORT}`); console.log(`🌐 ALGORAND_NETWORK: ${process.env.ALGORAND_NETWORK || 'mainnet'}`); app.use(cors()); app.use(express.json({ limit: '10mb' })); let mcpProcess = null; let mcpInitialized = false; const pendingRequests = new Map(); async function initializeMCP() { return new Promise((resolve, reject) => { console.log('Initializing MCP...'); console.log('Environment for MCP:', { ALGORAND_NETWORK: process.env.ALGORAND_NETWORK || 'mainnet' }); mcpProcess = spawn('node', ['./mcp/index.js'], { env: { ...process.env, ALGORAND_NETWORK: process.env.ALGORAND_NETWORK || 'mainnet' }, stdio: ['pipe', 'pipe', 'pipe'] }); let buffer = ''; let initTimeout = setTimeout(() => { reject(new Error('MCP initialization timeout')); }, 30000); mcpProcess.stdout.on('data', (data) => { buffer += data.toString(); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.trim()) { try { const response = JSON.parse(line); if (response.id === 'init' && response.result) { clearTimeout(initTimeout); console.log('✅ MCP initialized'); // Send initialized notification mcpProcess.stdin.write(JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized', params: {} }) + '\n'); // Mark as initialized after notification setTimeout(() => { mcpInitialized = true; console.log('✅ MCP ready for requests'); }, 500); resolve(response.result); } if (response.id && pendingRequests.has(response.id)) { const { resolve } = pendingRequests.get(response.id); pendingRequests.delete(response.id); resolve(response); } } catch (e) { // Ignore parse errors for non-JSON lines } } } }); mcpProcess.stderr.on('data', (data) => { console.error('MCP stderr:', data.toString()); }); mcpProcess.on('error', (err) => { clearTimeout(initTimeout); console.error('MCP error:', err); reject(err); }); mcpProcess.on('exit', (code) => { console.log(`MCP exited: ${code}`); mcpInitialized = false; }); // Send initialize request setTimeout(() => { mcpProcess.stdin.write(JSON.stringify({ jsonrpc: '2.0', method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'algorand-mcp-http-server', version: '1.0.0' } }, id: 'init' }) + '\n'); }, 1000); }); } async function sendMCPRequest(method, params = {}) { if (!mcpInitialized) { throw new Error('MCP not initialized'); } return new Promise((resolve, reject) => { const id = randomUUID(); const timeout = setTimeout(() => { pendingRequests.delete(id); reject(new Error('Request timeout')); }, 30000); pendingRequests.set(id, { resolve: (response) => { clearTimeout(timeout); resolve(response); }, reject }); mcpProcess.stdin.write(JSON.stringify({ jsonrpc: '2.0', method, params, id }) + '\n'); }); } // Root endpoint app.get('/', (req, res) => { res.json({ name: 'Algorand MCP Server', version: '0.1.0', status: mcpInitialized ? 'operational' : 'offline', blockchain: 'Algorand', supportedNetworks: ['mainnet', 'testnet', 'betanet'], protocol: 'MCP', endpoints: ['/health', '/info', '/mcp'] }); }); // Health check endpoint app.get('/health', (req, res) => { const healthy = mcpInitialized || mcpProcess !== null; res.status(healthy ? 200 : 503).json({ status: healthy ? 'healthy' : 'unhealthy', service: 'Algorand MCP', version: '1.0.0', blockchain: 'Algorand', timestamp: new Date().toISOString() }); }); // Info endpoint app.get('/info', (req, res) => { res.json({ name: 'Algorand MCP', description: 'MCP server for Algorand blockchain integration', version: '1.0.0', supportedNetworks: ['mainnet', 'testnet', 'betanet'], network: process.env.ALGORAND_NETWORK || 'mainnet', capabilities: [ 'Account management and creation', 'Transaction processing and tracking', 'Asset creation and management (ASA)', 'Smart contract deployment (TEAL/Python/TypeScript)', 'NFT minting and transfers', 'DeFi operations and staking', 'Network monitoring and analytics' ] }); }); // MCP Protocol endpoint (JSON-RPC) app.post('/mcp', async (req, res) => { try { const { jsonrpc, method, params, id, tool } = req.body; // Handle legacy format (tool + params) if (tool && !method) { if (!mcpInitialized) { return res.status(503).json({ error: 'MCP not ready', message: 'Please wait for initialization' }); } try { const response = await sendMCPRequest('tools/call', { name: tool, arguments: params || {} }); return res.json({ result: response.result, error: response.error || null }); } catch (error) { return res.status(500).json({ error: error.message }); } } // Handle standard JSON-RPC format if (jsonrpc !== '2.0') { return res.status(400).json({ jsonrpc: '2.0', error: { code: -32600, message: 'Invalid Request' }, id: id || null }); } switch (method) { case 'initialize': res.json({ jsonrpc: '2.0', result: { protocolVersion: '2024-11-05', serverInfo: { name: 'Algorand MCP', version: '1.0.0' }, capabilities: { tools: {} } }, id }); break; case 'tools/list': if (!mcpInitialized) throw new Error('MCP not ready'); const listResponse = await sendMCPRequest('tools/list'); res.json({ jsonrpc: '2.0', result: listResponse.result, id }); break; case 'tools/call': if (!mcpInitialized) throw new Error('MCP not ready'); const callResponse = await sendMCPRequest('tools/call', params); res.json({ jsonrpc: '2.0', result: callResponse.result, id }); break; default: res.status(404).json({ jsonrpc: '2.0', error: { code: -32601, message: `Method not found: ${method}` }, id }); } } catch (error) { console.error('MCP error:', error); res.status(500).json({ jsonrpc: '2.0', error: { code: -32603, message: error.message }, id: req.body.id || null }); } }); // MCP Discovery endpoint (GET) app.get('/mcp', (req, res) => { res.json({ name: 'Algorand MCP Server', version: '1.0.0', protocol_version: '2024-11-05', endpoint: '/mcp', status: mcpInitialized ? 'ready' : 'offline', description: 'Full-featured MCP server for Algorand blockchain', features: [ 'Account management and creation', 'Transaction processing', 'Asset and NFT operations', 'Smart contract interaction', 'Network monitoring' ] }); }); // Initialize MCP initializeMCP() .then(() => console.log('✅ MCP ready')) .catch((error) => { console.error('⚠️ MCP init failed:', error.message); console.log('Server will run with limited functionality'); }); // Start server const server = app.listen(PORT, '0.0.0.0', () => { console.log(`\n🚀 Algorand MCP Server running on port ${PORT}`); console.log('📍 Health check: http://localhost:' + PORT + '/health'); console.log('📍 Info: http://localhost:' + PORT + '/info'); console.log('📍 MCP endpoint: http://localhost:' + PORT + '/mcp'); console.log('\n✨ Ready for MCP connections!\n'); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('SIGTERM received, shutting down...'); server.close(() => console.log('HTTP server closed')); if (mcpProcess) mcpProcess.kill('SIGTERM'); setTimeout(() => process.exit(0), 5000); }); process.on('SIGINT', () => { console.log('SIGINT received, shutting down...'); server.close(() => console.log('HTTP server closed')); if (mcpProcess) mcpProcess.kill('SIGTERM'); setTimeout(() => process.exit(0), 5000); });

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/Tairon-ai/algorand-mcp'

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