Skip to main content
Glama

Nanoleaf MCP Server

by srnetadmin
index.ts10.6 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { NanoleafClient } from './nanoleaf-client.js'; const server = new Server( { name: 'nanoleaf-mcp-server', version: '1.0.0', }, { capabilities: { tools: {}, }, }, ); let primaryDevice: NanoleafClient | null = null; // Initialize Nanoleaf connection async function initializeNanoleaf() { // Check for environment variables first const deviceIP = process.env.NANOLEAF_IP; const authToken = process.env.NANOLEAF_AUTH_TOKEN; if (deviceIP && authToken) { console.error(`Using configured device: ${deviceIP} with auth token`); const device = { ip: deviceIP, port: parseInt(process.env.NANOLEAF_PORT || '16021'), protocol: process.env.NANOLEAF_PROTOCOL || 'http', authToken: authToken }; primaryDevice = new NanoleafClient(device); return; } try { const devices = await NanoleafClient.discover(); if (devices.length === 0) { console.error('No Nanoleaf devices found - server will still run but tools will not work until a device is connected'); console.error('You can also set NANOLEAF_IP and NANOLEAF_AUTH_TOKEN environment variables for direct connection'); return; } primaryDevice = new NanoleafClient(devices[0]); console.error('Nanoleaf device discovered:', devices[0]); } catch (error) { console.error('Error discovering Nanoleaf devices:', error); } } // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'get_nanoleaf_info', description: 'Get information about the Nanoleaf device', inputSchema: { type: 'object', properties: {}, }, }, { name: 'turn_on_nanoleaf', description: 'Turn on the Nanoleaf lights', inputSchema: { type: 'object', properties: {}, }, }, { name: 'turn_off_nanoleaf', description: 'Turn off the Nanoleaf lights', inputSchema: { type: 'object', properties: {}, }, }, { name: 'set_brightness', description: 'Set the brightness of the Nanoleaf lights', inputSchema: { type: 'object', properties: { brightness: { type: 'number', description: 'Brightness level (0-100)', minimum: 0, maximum: 100, }, }, required: ['brightness'], }, }, { name: 'set_color', description: 'Set the color of the Nanoleaf lights', inputSchema: { type: 'object', properties: { hue: { type: 'number', description: 'Hue value (0-360)', minimum: 0, maximum: 360, }, saturation: { type: 'number', description: 'Saturation value (0-100)', minimum: 0, maximum: 100, }, }, required: ['hue', 'saturation'], }, }, { name: 'set_effect', description: 'Set an effect on the Nanoleaf lights', inputSchema: { type: 'object', properties: { effect: { type: 'string', description: 'Name of the effect to apply', }, }, required: ['effect'], }, }, { name: 'get_effects', description: 'Get list of available effects', inputSchema: { type: 'object', properties: {}, }, }, { name: 'discover_nanoleaf', description: 'Discover Nanoleaf devices on the network', inputSchema: { type: 'object', properties: {}, }, }, { name: 'connect_to_ip', description: 'Connect to a Nanoleaf device at a specific IP address', inputSchema: { type: 'object', properties: { ip: { type: 'string', description: 'IP address of the Nanoleaf device', }, port: { type: 'number', description: 'Port number (default: 16021)', default: 16021, }, }, required: ['ip'], }, }, { name: 'authorize_nanoleaf', description: 'Authorize connection to Nanoleaf device (device must be in pairing mode)', inputSchema: { type: 'object', properties: {}, }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { switch (request.params.name) { case 'discover_nanoleaf': try { const devices = await NanoleafClient.discover(); if (devices.length > 0) { primaryDevice = new NanoleafClient(devices[0]); return { content: [ { type: 'text', text: `Found ${devices.length} Nanoleaf device(s): ${JSON.stringify(devices, null, 2)}`, }, ], }; } else { return { content: [ { type: 'text', text: 'No Nanoleaf devices found on the network', }, ], }; } } catch (error) { return { content: [ { type: 'text', text: `Error during discovery: ${error}`, }, ], }; } case 'connect_to_ip': try { const ip = request.params.arguments?.ip as string; const port = (request.params.arguments?.port as number) || 16021; const device = await NanoleafClient.connectToIP(ip, port); if (device) { primaryDevice = new NanoleafClient(device); return { content: [ { type: 'text', text: `Successfully connected to Nanoleaf device at ${ip}:${port}`, }, ], }; } else { return { content: [ { type: 'text', text: `No Nanoleaf device found at ${ip}:${port}`, }, ], }; } } catch (error) { return { content: [ { type: 'text', text: `Error connecting to ${request.params.arguments?.ip}: ${error}`, }, ], }; } case 'authorize_nanoleaf': if (!primaryDevice) { return { content: [ { type: 'text', text: 'No device connected. Please run connect_to_ip or discover_nanoleaf first.', }, ], }; } try { const authToken = await primaryDevice.authorize(); return { content: [ { type: 'text', text: `Successfully authorized! Auth token: ${authToken.substring(0, 8)}... (truncated for security)`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Authorization failed: ${error}`, }, ], }; } default: break; } if (!primaryDevice) { return { content: [ { type: 'text', text: 'Nanoleaf device not initialized. Please run discover_nanoleaf or connect_to_ip first.', }, ], }; } switch (request.params.name) { case 'get_nanoleaf_info': return { content: [ { type: 'text', text: JSON.stringify(await primaryDevice.getInfo(), null, 2), }, ], }; case 'turn_on_nanoleaf': await primaryDevice.turnOn(); return { content: [ { type: 'text', text: 'Nanoleaf lights turned on', }, ], }; case 'turn_off_nanoleaf': await primaryDevice.turnOff(); return { content: [ { type: 'text', text: 'Nanoleaf lights turned off', }, ], }; case 'set_brightness': const brightness = request.params.arguments?.brightness as number; await primaryDevice.setBrightness(brightness); return { content: [ { type: 'text', text: `Brightness set to ${brightness}%`, }, ], }; case 'set_color': const hue = request.params.arguments?.hue as number; const saturation = request.params.arguments?.saturation as number; await primaryDevice.setColor(hue, saturation); return { content: [ { type: 'text', text: `Color set to hue: ${hue}, saturation: ${saturation}`, }, ], }; case 'set_effect': const effect = request.params.arguments?.effect as string; await primaryDevice.setEffect(effect); return { content: [ { type: 'text', text: `Effect set to: ${effect}`, }, ], }; case 'get_effects': const effects = await primaryDevice.getEffects(); return { content: [ { type: 'text', text: JSON.stringify(effects, null, 2), }, ], }; default: throw new Error(`Unknown tool: ${request.params.name}`); } }); async function main() { await initializeNanoleaf(); const transport = new StdioServerTransport(); await server.connect(transport); console.error('Nanoleaf MCP server running on stdio'); } main().catch(err => { console.error('Failed to start MCP server:', err); });

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/srnetadmin/nanoleaf-mcp-server'

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