Skip to main content
Glama

OPNSense MCP Server

leases.ts11 kB
// DHCP Lease Management Resource - FIXED VERSION import { OPNSenseAPIClient } from '../../../api/client.js'; export interface DhcpLease { address: string; // IP address hwaddr: string; // MAC address hostname?: string; // Device hostname descr?: string; // Description starts?: string; // Lease start time ends?: string; // Lease end time state?: string; // active, expired, etc. act?: string; // Action wstatus?: string; // Status if?: string; // Interface // Additional fields that might be in the API response mac?: string; // Alternative MAC field interface?: string; // Alternative interface field description?: string; // Alternative description field type?: string; // Lease type man?: string; // Manufacturer } export interface DhcpStaticMapping { uuid?: string; mac: string; // MAC address ipaddr: string; // IP address hostname?: string; // Hostname descr?: string; // Description winsserver?: string; // WINS server dnsserver?: string; // DNS server gateway?: string; // Gateway domain?: string; // Domain domainsearchlist?: string; // Domain search list ntpserver?: string; // NTP server } export class DhcpLeaseResource { private client: OPNSenseAPIClient; private debugMode: boolean; constructor(client: OPNSenseAPIClient, debugMode: boolean = false) { this.client = client; this.debugMode = debugMode; } /** * Enable/disable debug mode */ setDebugMode(enabled: boolean): void { this.debugMode = enabled; } /** * Normalize lease data from different possible API response formats */ private normalizeLease(rawLease: any): DhcpLease { return { address: rawLease.address || rawLease.ip || rawLease.ipaddr || '', hwaddr: rawLease.hwaddr || rawLease.mac || rawLease.macaddr || '', hostname: rawLease.hostname || rawLease.host || rawLease.name || '', descr: rawLease.descr || rawLease.description || rawLease.man || '', starts: rawLease.starts || rawLease.start || rawLease.startTime || '', ends: rawLease.ends || rawLease.end || rawLease.endTime || '', state: rawLease.state || rawLease.status || 'unknown', act: rawLease.act || rawLease.action || '', wstatus: rawLease.wstatus || rawLease.status || '', if: rawLease.if || rawLease.interface || rawLease.intf || '' }; } /** * List all DHCP leases with improved error handling */ async listLeases(): Promise<DhcpLease[]> { try { if (this.debugMode) { console.log('[DHCP] Calling searchLease endpoint...'); } const response = await this.client.post('/dhcpv4/leases/searchLease', { current: 1, rowCount: 1000, sort: {}, searchPhrase: '' }); if (this.debugMode) { console.log('[DHCP] Raw response:', JSON.stringify(response, null, 2)); } // Handle different response formats let leases: any[] = []; if (response && response.rows && Array.isArray(response.rows)) { leases = response.rows; } else if (response && response.leases && Array.isArray(response.leases)) { leases = response.leases; } else if (Array.isArray(response)) { leases = response; } else if (response && response.data && Array.isArray(response.data)) { leases = response.data; } if (this.debugMode) { console.log(`[DHCP] Found ${leases.length} leases`); if (leases.length > 0) { console.log('[DHCP] First lease:', JSON.stringify(leases[0], null, 2)); } } // Normalize the lease data return leases.map(lease => this.normalizeLease(lease)); } catch (error) { if (this.debugMode) { console.error('[DHCP] Failed to list leases:', error); } // Try alternative endpoint try { if (this.debugMode) { console.log('[DHCP] Trying alternative endpoint...'); } const altResponse = await this.client.get('/dhcpv4/leases'); if (altResponse && Array.isArray(altResponse)) { return altResponse.map((lease: any) => this.normalizeLease(lease)); } } catch (altError) { if (this.debugMode) { console.error('[DHCP] Alternative endpoint also failed:', altError); } } console.error('Failed to list DHCP leases:', error instanceof Error ? error.message : 'Unknown error'); return []; } } /** * Find leases by hostname pattern */ async findByHostname(pattern: string): Promise<DhcpLease[]> { const leases = await this.listLeases(); const searchPattern = pattern.toLowerCase(); return leases.filter(lease => lease.hostname?.toLowerCase().includes(searchPattern) || lease.descr?.toLowerCase().includes(searchPattern) ); } /** * Find leases by MAC address */ async findByMac(mac: string): Promise<DhcpLease[]> { const leases = await this.listLeases(); const searchMac = mac.toLowerCase().replace(/[:-]/g, ''); return leases.filter(lease => { if (!lease.hwaddr) return false; const leaseMac = lease.hwaddr.toLowerCase().replace(/[:-]/g, ''); return leaseMac.includes(searchMac); }); } /** * Get leases for specific interface */ async getInterfaceLeases(interfaceName: string): Promise<DhcpLease[]> { const leases = await this.listLeases(); return leases.filter(lease => lease.if === interfaceName); } /** * Get leases for guest network (VLAN 4) */ async getGuestLeases(): Promise<DhcpLease[]> { // Try multiple possible interface names for VLAN 4 const possibleInterfaces = ['igc2_vlan4', 'vlan4', 'opt3', 'guest']; const leases = await this.listLeases(); return leases.filter(lease => { // Check if the interface matches any of the possible names if (possibleInterfaces.includes(lease.if || '')) { return true; } // Also check if the IP is in the guest network range (10.0.4.x) if (lease.address && lease.address.startsWith('10.0.4.')) { return true; } return false; }); } /** * Get device info summary */ getDeviceInfo(lease: DhcpLease): string { const hostname = lease.hostname || 'Unknown device'; const manufacturer = lease.hwaddr ? this.getMacManufacturer(lease.hwaddr) : 'Unknown'; return `${hostname} - ${lease.address} (${manufacturer})`; } /** * Simple MAC address manufacturer lookup with null checks */ private getMacManufacturer(mac: string): string { if (!mac || typeof mac !== 'string' || mac.length < 8) { return 'Unknown manufacturer'; } const prefix = mac.substring(0, 8).toUpperCase(); // Common manufacturer prefixes const manufacturers: { [key: string]: string } = { '00:1B:63': 'Apple', 'AC:DE:48': 'Apple', '00:23:12': 'Apple', '3C:22:FB': 'Apple', '00:50:56': 'VMware', '00:0C:29': 'VMware', '08:00:27': 'VirtualBox', '00:15:5D': 'Microsoft Hyper-V', '00:1C:42': 'Parallels', 'DC:A6:32': 'Raspberry Pi', 'B8:27:EB': 'Raspberry Pi', '00:50:B6': 'PlayStation', '00:04:20': 'PlayStation', '00:D9:D1': 'Sony', '04:03:D6': 'Nintendo', '00:24:D6': 'Intel', '00:1F:16': 'Intel', '00:13:77': 'Samsung', '00:15:99': 'Samsung', '00:21:19': 'Samsung', '00:26:37': 'Samsung', '94:01:C2': 'Samsung', 'AC:5F:3E': 'Samsung', '00:09:2D': 'HTC', '00:23:76': 'HTC', '00:BB:3A': 'Google', '94:EB:2C': 'Google', '00:1A:11': 'Google', '00:50:F2': 'Microsoft', '00:03:FF': 'Microsoft Xbox', '00:50:8B': 'HP', '00:17:A4': 'HP', '00:21:5A': 'HP', '00:1C:C4': 'HP', '00:23:7D': 'HP', '00:25:B3': 'HP', '3C:D9:2B': 'HP', '00:04:00': 'Lexmark', '00:20:00': 'Lexmark', '00:00:48': 'Epson', '00:1B:11': 'Dell', '00:14:22': 'Dell', '00:19:B9': 'Dell', 'B0:83:FE': 'Dell', '00:0D:56': 'Dell', '00:12:3F': 'Dell', '00:15:C5': 'Dell', '00:18:8B': 'Dell', '00:1A:A0': 'Dell', '00:1C:23': 'Dell', '00:1D:09': 'Dell', '00:1E:4F': 'Dell', '00:1E:C9': 'Dell', '00:21:70': 'Dell', '00:21:9B': 'Dell', '00:23:AE': 'Dell', '00:25:64': 'Dell', '00:26:B9': 'Dell', '14:FE:B5': 'Dell', '18:03:73': 'Dell', '18:A9:9B': 'Dell', '1C:40:24': 'Dell', '20:04:0F': 'Dell', '24:B6:FD': 'Dell', '28:F1:0E': 'Dell' }; // Check for manufacturer for (const [prefixKey, manufacturer] of Object.entries(manufacturers)) { if (prefix.startsWith(prefixKey)) { return manufacturer; } } return 'Unknown manufacturer'; } /** * Format lease for display */ formatLease(lease: DhcpLease): string { const hostname = lease.hostname || 'Unknown'; const state = lease.state || 'unknown'; const manufacturer = lease.hwaddr ? this.getMacManufacturer(lease.hwaddr) : 'Unknown'; return `${hostname} - IP: ${lease.address}, MAC: ${lease.hwaddr} (${manufacturer}), State: ${state}`; } /** * Group leases by interface */ async getLeasesByInterface(): Promise<Map<string, DhcpLease[]>> { const leases = await this.listLeases(); const grouped = new Map<string, DhcpLease[]>(); for (const lease of leases) { const iface = lease.if || 'unknown'; if (!grouped.has(iface)) { grouped.set(iface, []); } grouped.get(iface)!.push(lease); } return grouped; } /** * Debug method to test API endpoints */ async debugApiEndpoints(): Promise<void> { console.log('=== Debugging DHCP API Endpoints ===\n'); // Test various endpoints const endpoints = [ { method: 'POST', path: '/dhcpv4/leases/searchLease', data: { current: 1, rowCount: 10 } }, { method: 'GET', path: '/dhcpv4/leases/get' }, { method: 'GET', path: '/dhcpv4/leases' }, { method: 'GET', path: '/dhcpv4/service/status' }, ]; for (const endpoint of endpoints) { console.log(`Testing ${endpoint.method} ${endpoint.path}...`); try { const response = endpoint.method === 'POST' ? await this.client.post(endpoint.path, endpoint.data || {}) : await this.client.get(endpoint.path); console.log('Success! Response structure:', JSON.stringify(response, null, 2).substring(0, 500)); } catch (error: any) { console.log('Failed:', error.message); } console.log(); } } } export default DhcpLeaseResource;

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/vespo92/OPNSenseMCP'

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