Skip to main content
Glama
users.ts6.92 kB
import { Tool } from '@modelcontextprotocol/sdk/types.js'; import { HueClient } from '../hue-client.js'; import { log } from '../utils/logger.js'; export function createUserTools(_client: HueClient): Tool[] { const tools: Tool[] = [ { name: 'list_users', description: 'List all whitelisted users with authentication details, creation dates, and usage information', inputSchema: { type: 'object', properties: { detail: { type: 'string', enum: ['minimal', 'standard', 'verbose'], description: 'Level of detail to include (default: standard)', default: 'standard', }, sortBy: { type: 'string', enum: ['name', 'created', 'lastUsed', 'username'], description: 'Sort users by field (default: name)', default: 'name', }, includeInactive: { type: 'boolean', description: 'Include users that have never been used (default: true)', default: true, }, }, }, }, { name: 'get_user', description: 'Get detailed information about a specific whitelisted user', inputSchema: { type: 'object', properties: { username: { type: 'string', description: 'The username/ID of the user to retrieve', }, }, required: ['username'], }, }, ]; return tools; } export async function handleListUsers(client: HueClient, args: any = {}): Promise<any> { try { const detail = args.detail || 'standard'; const sortBy = args.sortBy || 'name'; const includeInactive = args.includeInactive !== false; log.debug('Listing bridge users', { detail, sortBy, includeInactive }); // Get all users from bridge const users = await client.getBridgeUsers(); if (!users || Object.keys(users).length === 0) { return { users: [], count: 0, message: 'No whitelisted users found', }; } // Convert to array and add metadata let userList = Object.entries(users).map(([username, userData]: [string, any]) => { const hasBeenUsed = userData['last use date'] && userData['last use date'] !== 'none'; return { username, name: userData.name || 'Unknown', created: userData['create date'] || 'Unknown', lastUsed: userData['last use date'] || 'Never', hasBeenUsed, applicationName: userData.name ? userData.name.split('#')[0] : 'Unknown', deviceName: userData.name ? userData.name.split('#')[1] : undefined, raw: userData, }; }); // Filter inactive users if requested if (!includeInactive) { userList = userList.filter(user => user.hasBeenUsed); } // Sort users userList.sort((a, b) => { switch (sortBy) { case 'created': return new Date(a.created).getTime() - new Date(b.created).getTime(); case 'lastUsed': if (a.lastUsed === 'Never' && b.lastUsed === 'Never') return 0; if (a.lastUsed === 'Never') return 1; if (b.lastUsed === 'Never') return -1; return new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime(); case 'username': return a.username.localeCompare(b.username); case 'name': default: return a.name.localeCompare(b.name); } }); // Minimal response for quick checks if (detail === 'minimal') { return { count: userList.length, active: userList.filter(u => u.hasBeenUsed).length, inactive: userList.filter(u => !u.hasBeenUsed).length, users: userList.map(u => ({ username: u.username, name: u.name, status: u.hasBeenUsed ? 'active' : 'inactive', })), }; } // Standard response with key user information const standardResponse = { summary: { total: userList.length, active: userList.filter(u => u.hasBeenUsed).length, inactive: userList.filter(u => !u.hasBeenUsed).length, sortedBy: sortBy, }, users: userList.map(u => ({ username: u.username, name: u.name, applicationName: u.applicationName, deviceName: u.deviceName, created: u.created, lastUsed: u.lastUsed, status: u.hasBeenUsed ? 'active' : 'inactive', })), }; if (detail === 'standard') { return standardResponse; } // Verbose includes all raw user data return { ...standardResponse, users: userList.map(u => ({ ...standardResponse.users.find(su => su.username === u.username), raw: u.raw, })), metadata: { retrieved: new Date().toISOString(), apiEndpoint: 'configuration.whitelist', includedInactive: includeInactive, }, }; } catch (error) { log.error('Failed to list bridge users', error); throw new Error(`Failed to list bridge users: ${error}`); } } export async function handleGetUser(client: HueClient, args: any): Promise<any> { try { const { username } = args; if (!username) { throw new Error('Username is required'); } log.debug('Getting user details', { username }); // Get specific user from bridge const user = await client.getBridgeUser(username); if (!user) { throw new Error(`User '${username}' not found in bridge whitelist`); } const hasBeenUsed = user['last use date'] && user['last use date'] !== 'none'; const nameParts = user.name ? user.name.split('#') : ['Unknown']; return { username, details: { name: user.name || 'Unknown', applicationName: nameParts[0] || 'Unknown', deviceName: nameParts[1] || undefined, created: user['create date'] || 'Unknown', lastUsed: user['last use date'] || 'Never', status: hasBeenUsed ? 'active' : 'inactive', hasBeenUsed, }, analysis: { isCurrentUser: username === client.getCurrentUsername(), daysSinceCreated: user['create date'] ? Math.floor((Date.now() - new Date(user['create date']).getTime()) / (1000 * 60 * 60 * 24)) : null, daysSinceLastUsed: user['last use date'] && user['last use date'] !== 'Never' ? Math.floor((Date.now() - new Date(user['last use date']).getTime()) / (1000 * 60 * 60 * 24)) : null, }, raw: user, metadata: { retrieved: new Date().toISOString(), apiEndpoint: `configuration.whitelist.${username}`, }, }; } catch (error) { log.error('Failed to get user details', error, { username: args.username }); throw new Error(`Failed to get user details: ${error}`); } }

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/rmrfslashbin/hue-mcp'

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