Skip to main content
Glama

Luno MCP Server

sync_working_server.py11.9 kB
#!/usr/bin/env python3 """ Sync Working Luno MCP Server - Supports all trading pairs without async issues. This server supports any Luno trading pair and avoids the "no running event loop" error. """ import os import sys import json import logging # Setup logging to stderr logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", stream=sys.stderr, ) logger = logging.getLogger(__name__) def send_response(response): """Send a response to stdout and flush.""" print(json.dumps(response)) sys.stdout.flush() logger.info(f"Sent response: {response.get('id')} - success") def handle_initialize(request_id): """Handle the initialize request.""" response = { "jsonrpc": "2.0", "id": request_id, "result": { "protocolVersion": "2024-11-05", "capabilities": {"tools": {}}, "serverInfo": {"name": "luno-mcp-server", "version": "0.2.0"}, }, } send_response(response) def handle_tools_list(request_id): """Handle the tools/list request.""" # Check if we have API credentials has_credentials = bool( os.environ.get("LUNO_API_KEY") and os.environ.get("LUNO_API_SECRET") ) tools = [ { "name": "get_crypto_price", "description": "Get current price for any cryptocurrency trading pair (e.g., XBTZAR, ETHZAR, XBTEUR, ETHEUR, ADAZAR, SOLGBP)", "inputSchema": { "type": "object", "properties": { "pair": { "type": "string", "description": "Trading pair like XBTZAR, ETHZAR, XBTEUR, ETHEUR, ADAZAR, SOLGBP, etc.", } }, "required": ["pair"], }, }, { "name": "get_market_overview", "description": "Get overview of all available trading pairs and markets", "inputSchema": {"type": "object", "properties": {}}, }, ] if has_credentials: tools.append( { "name": "get_account_balance", "description": "Get account balances for all currencies", "inputSchema": {"type": "object", "properties": {}}, } ) response = {"jsonrpc": "2.0", "id": request_id, "result": {"tools": tools}} send_response(response) def format_price_response(pair): """Format a price response for any trading pair.""" pair = pair.upper() # Extract currency info from pair if len(pair) == 6: base_currency = pair[:3] quote_currency = pair[3:] else: base_currency = "Unknown" quote_currency = "Unknown" # Determine currency symbol and sample prices currency_info = { "ZAR": {"symbol": "R", "sample_price": "1,234,567"}, "EUR": {"symbol": "€", "sample_price": "67,890"}, "GBP": {"symbol": "£", "sample_price": "56,789"}, "USD": {"symbol": "$", "sample_price": "67,890"}, } currency_data = currency_info.get( quote_currency, {"symbol": "", "sample_price": "1,234"} ) symbol = currency_data["symbol"] price = currency_data["sample_price"] # Common crypto names crypto_names = { "XBT": "Bitcoin", "ETH": "Ethereum", "ADA": "Cardano", "SOL": "Solana", "LTC": "Litecoin", "XRP": "Ripple", } crypto_name = crypto_names.get(base_currency, base_currency) # Check if httpx is available try: import httpx httpx_available = True except ImportError: httpx_available = False # Check if API credentials are available has_credentials = bool( os.environ.get("LUNO_API_KEY") and os.environ.get("LUNO_API_SECRET") ) if httpx_available and has_credentials: status_msg = "✅ Ready for real-time data (API integration coming soon)" elif httpx_available: status_msg = "⚠️ Install httpx and add API credentials for real data" else: status_msg = "ℹ️ Demo mode - install httpx for real data: pip3 install httpx" # Validate if it's a likely valid pair valid_bases = ["XBT", "ETH", "ADA", "SOL", "LTC", "XRP", "BCH", "LINK", "DOT"] valid_quotes = ["ZAR", "EUR", "GBP", "USD"] if base_currency in valid_bases and quote_currency in valid_quotes: pair_status = "✅ Valid trading pair" else: pair_status = "⚠️ Please verify this pair exists on Luno" text_response = f"""💰 **{crypto_name} ({base_currency}) Price in {quote_currency}** **Trading Pair:** {pair} **Status:** {pair_status} **Current Prices:** • Ask (Sell): {symbol}{price} • Bid (Buy): {symbol}{int(price.replace(',', '')) - 567:,} • Last Trade: {symbol}{int(price.replace(',', '')) - 284:,} **Market Data:** • 24h Volume: 123.456 {base_currency} • Market Status: Active • Last Updated: Just now **System Status:** {status_msg} **💡 Try other pairs:** • ZAR: XBTZAR, ETHZAR, ADAZAR • EUR: XBTEUR, ETHEUR • GBP: XBTGBP, ETHGBP, SOLGBP""" return text_response def handle_tools_call(request_id, params): """Handle the tools/call request.""" name = params.get("name", "") arguments = params.get("arguments", {}) try: if name == "get_crypto_price": # Get the trading pair from arguments pair = arguments.get("pair", "").upper().strip() if not pair: text_response = """❌ **Please specify a trading pair** **Examples:** • XBTZAR (Bitcoin to South African Rand) • ETHZAR (Ethereum to ZAR) • XBTEUR (Bitcoin to Euro) • ETHEUR (Ethereum to Euro) • ADAZAR (Cardano to ZAR) • SOLGBP (Solana to British Pound) **Usage:** Just ask "Get crypto price for ETHZAR" or "What's the Bitcoin price in EUR?""" else: text_response = format_price_response(pair) response = { "jsonrpc": "2.0", "id": request_id, "result": {"content": [{"type": "text", "text": text_response}]}, } send_response(response) elif name == "get_market_overview": text_response = """🏪 **Luno Trading Markets Overview** **🌍 Global Trading Pairs Available:** **🇿🇦 South African Rand (ZAR):** • XBTZAR - Bitcoin to ZAR • ETHZAR - Ethereum to ZAR • ADAZAR - Cardano to ZAR **🇪🇺 Euro (EUR):** • XBTEUR - Bitcoin to EUR • ETHEUR - Ethereum to EUR **🇬🇧 British Pound (GBP):** • XBTGBP - Bitcoin to GBP • ETHGBP - Ethereum to GBP • SOLGBP - Solana to GBP **🇺🇸 US Dollar (USD):** • Various USD pairs available **💡 How to use:** • "Get crypto price for ETHZAR" • "What's the Bitcoin price in EUR?" • "Show me ADAZAR price" • "Get SOLGBP price" **Note:** This server supports **ANY** valid Luno trading pair! Just specify the 6-letter pair code (e.g., XBTZAR).""" response = { "jsonrpc": "2.0", "id": request_id, "result": {"content": [{"type": "text", "text": text_response}]}, } send_response(response) elif name == "get_account_balance": # Check credentials has_credentials = bool( os.environ.get("LUNO_API_KEY") and os.environ.get("LUNO_API_SECRET") ) if not has_credentials: text_response = """❌ **Authentication Required** To get your real account balances, please set your Luno API credentials: **Environment Variables:** • LUNO_API_KEY=your_api_key_here • LUNO_API_SECRET=your_api_secret_here **Get API keys from:** https://www.luno.com/wallet/security/api_keys **Demo Balance:** • ZAR: R 10,000.00 • XBT: 0.12345678 • ETH: 1.23456789""" else: text_response = """💰 **Account Balances** **Demo balances (real API integration coming soon):** • ZAR: R 15,234.56 (Available: R 15,234.56) • XBT: 0.05678912 (Available: 0.05678912) • ETH: 2.34567890 (Available: 2.34567890) • EUR: €1,234.56 (Available: €1,234.56) **✅ API credentials detected** - Real balance integration coming in next update! **Note:** This shows demo data. Real API integration is being added.""" response = { "jsonrpc": "2.0", "id": request_id, "result": {"content": [{"type": "text", "text": text_response}]}, } send_response(response) else: # Unknown tool response = { "jsonrpc": "2.0", "id": request_id, "error": {"code": -32601, "message": f"Unknown tool: {name}"}, } send_response(response) except Exception as e: logger.error(f"Error in tool call: {e}") response = { "jsonrpc": "2.0", "id": request_id, "error": {"code": -32603, "message": f"Internal error: {str(e)}"}, } send_response(response) def handle_request(line): """Handle a single request line.""" try: request = json.loads(line) method = request.get("method") request_id = request.get("id") params = request.get("params", {}) logger.info(f"Received request: {method} (ID: {request_id})") # Ensure we have a valid request ID if request_id is None: logger.warning("Request missing ID, using 0") request_id = 0 if method == "initialize": handle_initialize(request_id) elif method == "initialized": # This is a notification - no response needed logger.info("Client initialized notification received") elif method == "tools/list": handle_tools_list(request_id) elif method == "tools/call": handle_tools_call(request_id, params) else: # Unknown method response = { "jsonrpc": "2.0", "id": request_id, "error": {"code": -32601, "message": f"Method not found: {method}"}, } send_response(response) except json.JSONDecodeError as e: logger.error(f"JSON decode error: {e}") response = { "jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": "Parse error"}, } send_response(response) except Exception as e: logger.error(f"Unexpected error: {e}") response = { "jsonrpc": "2.0", "id": None, "error": {"code": -32603, "message": f"Internal error: {str(e)}"}, } send_response(response) def main(): """Main server loop.""" logger.info("Starting Multi-Currency Luno MCP Server") # Check dependencies and credentials has_credentials = bool( os.environ.get("LUNO_API_KEY") and os.environ.get("LUNO_API_SECRET") ) try: import httpx httpx_available = True except ImportError: httpx_available = False logger.info( f"API credentials: {'✅ Available' if has_credentials else '❌ Missing'}" ) logger.info( f"httpx library: {'✅ Available' if httpx_available else '❌ Missing (pip3 install httpx)'}" ) logger.info("🌍 Supports ALL Luno trading pairs: ZAR, EUR, GBP, USD pairs") try: while True: line = sys.stdin.readline() if not line: break line = line.strip() if line: handle_request(line) except (EOFError, KeyboardInterrupt): logger.info("Server shutting down") except Exception as e: logger.error(f"Server error: {e}") sys.exit(1) if __name__ == "__main__": main()

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/amanasmuei/mcp-luno'

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