/** * GoHighLevel Phone System Tools * Tools for managing phone numbers, call settings, and IVR */ import { GHLApiClient } from '../clients/ghl-api-client.js'; export class PhoneTools { constructor(private ghlClient: GHLApiClient) {} getToolDefinitions() { return [ // Phone Numbers { name: 'get_phone_numbers', description: 'Get all phone numbers for a location', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' } } } }, { name: 'get_phone_number', description: 'Get a specific phone number by ID', inputSchema: { type: 'object', properties: { phoneNumberId: { type: 'string', description: 'Phone Number ID' }, locationId: { type: 'string', description: 'Location ID' } }, required: ['phoneNumberId'] } }, { name: 'search_available_numbers', description: 'Search for available phone numbers to purchase', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' }, country: { type: 'string', description: 'Country code (e.g., US, CA)' }, areaCode: { type: 'string', description: 'Area code to search' }, contains: { type: 'string', description: 'Number pattern to search for' }, type: { type: 'string', enum: ['local', 'tollfree', 'mobile'], description: 'Number type' } }, required: ['country'] } }, { name: 'purchase_phone_number', description: 'Purchase a phone number', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' }, phoneNumber: { type: 'string', description: 'Phone number to purchase' }, name: { type: 'string', description: 'Friendly name for the number' } }, required: ['phoneNumber'] } }, { name: 'update_phone_number', description: 'Update phone number settings', inputSchema: { type: 'object', properties: { phoneNumberId: { type: 'string', description: 'Phone Number ID' }, locationId: { type: 'string', description: 'Location ID' }, name: { type: 'string', description: 'Friendly name' }, forwardingNumber: { type: 'string', description: 'Number to forward calls to' }, callRecording: { type: 'boolean', description: 'Enable call recording' }, whisperMessage: { type: 'string', description: 'Whisper message played to agent' } }, required: ['phoneNumberId'] } }, { name: 'release_phone_number', description: 'Release/delete a phone number', inputSchema: { type: 'object', properties: { phoneNumberId: { type: 'string', description: 'Phone Number ID' }, locationId: { type: 'string', description: 'Location ID' } }, required: ['phoneNumberId'] } }, // Call Forwarding { name: 'get_call_forwarding_settings', description: 'Get call forwarding configuration', inputSchema: { type: 'object', properties: { phoneNumberId: { type: 'string', description: 'Phone Number ID' }, locationId: { type: 'string', description: 'Location ID' } }, required: ['phoneNumberId'] } }, { name: 'update_call_forwarding', description: 'Update call forwarding settings', inputSchema: { type: 'object', properties: { phoneNumberId: { type: 'string', description: 'Phone Number ID' }, locationId: { type: 'string', description: 'Location ID' }, enabled: { type: 'boolean', description: 'Enable forwarding' }, forwardTo: { type: 'string', description: 'Number to forward to' }, ringTimeout: { type: 'number', description: 'Ring timeout in seconds' }, voicemailEnabled: { type: 'boolean', description: 'Enable voicemail on no answer' } }, required: ['phoneNumberId'] } }, // IVR/Call Menu { name: 'get_ivr_menus', description: 'Get all IVR/call menus', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' } } } }, { name: 'create_ivr_menu', description: 'Create an IVR/call menu', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' }, name: { type: 'string', description: 'Menu name' }, greeting: { type: 'string', description: 'Greeting message (text or URL)' }, options: { type: 'array', items: { type: 'object', properties: { digit: { type: 'string', description: 'Digit to press (0-9, *, #)' }, action: { type: 'string', description: 'Action type' }, destination: { type: 'string', description: 'Action destination' } } }, description: 'Menu options' } }, required: ['name', 'greeting'] } }, { name: 'update_ivr_menu', description: 'Update an IVR menu', inputSchema: { type: 'object', properties: { menuId: { type: 'string', description: 'IVR Menu ID' }, locationId: { type: 'string', description: 'Location ID' }, name: { type: 'string', description: 'Menu name' }, greeting: { type: 'string', description: 'Greeting message' }, options: { type: 'array', description: 'Menu options' } }, required: ['menuId'] } }, { name: 'delete_ivr_menu', description: 'Delete an IVR menu', inputSchema: { type: 'object', properties: { menuId: { type: 'string', description: 'IVR Menu ID' }, locationId: { type: 'string', description: 'Location ID' } }, required: ['menuId'] } }, // Voicemail { name: 'get_voicemail_settings', description: 'Get voicemail settings', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' } } } }, { name: 'update_voicemail_settings', description: 'Update voicemail settings', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' }, enabled: { type: 'boolean', description: 'Enable voicemail' }, greeting: { type: 'string', description: 'Voicemail greeting (text or URL)' }, transcriptionEnabled: { type: 'boolean', description: 'Enable transcription' }, notificationEmail: { type: 'string', description: 'Email for voicemail notifications' } } } }, { name: 'get_voicemails', description: 'Get voicemail messages', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' }, phoneNumberId: { type: 'string', description: 'Filter by phone number' }, status: { type: 'string', enum: ['new', 'read', 'archived'], description: 'Filter by status' }, limit: { type: 'number', description: 'Max results' }, offset: { type: 'number', description: 'Pagination offset' } } } }, { name: 'delete_voicemail', description: 'Delete a voicemail message', inputSchema: { type: 'object', properties: { voicemailId: { type: 'string', description: 'Voicemail ID' }, locationId: { type: 'string', description: 'Location ID' } }, required: ['voicemailId'] } }, // Caller ID { name: 'get_caller_ids', description: 'Get verified caller IDs', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' } } } }, { name: 'add_caller_id', description: 'Add a caller ID for verification', inputSchema: { type: 'object', properties: { locationId: { type: 'string', description: 'Location ID' }, phoneNumber: { type: 'string', description: 'Phone number to verify' }, name: { type: 'string', description: 'Friendly name' } }, required: ['phoneNumber'] } }, { name: 'verify_caller_id', description: 'Submit verification code for caller ID', inputSchema: { type: 'object', properties: { callerIdId: { type: 'string', description: 'Caller ID record ID' }, locationId: { type: 'string', description: 'Location ID' }, code: { type: 'string', description: 'Verification code' } }, required: ['callerIdId', 'code'] } }, { name: 'delete_caller_id', description: 'Delete a caller ID', inputSchema: { type: 'object', properties: { callerIdId: { type: 'string', description: 'Caller ID record ID' }, locationId: { type: 'string', description: 'Location ID' } }, required: ['callerIdId'] } } ]; } async handleToolCall(toolName: string, args: Record): Promise { const config = this.ghlClient.getConfig(); const locationId = (args.locationId as string) || config.locationId; switch (toolName) { // Phone Numbers case 'get_phone_numbers': { return this.ghlClient.makeRequest('GET', `/phone-numbers/?locationId=${locationId}`); } case 'get_phone_number': { return this.ghlClient.makeRequest('GET', `/phone-numbers/${args.phoneNumberId}?locationId=${locationId}`); } case 'search_available_numbers': { const params = new URLSearchParams(); params.append('locationId', locationId); params.append('country', String(args.country)); if (args.areaCode) params.append('areaCode', String(args.areaCode)); if (args.contains) params.append('contains', String(args.contains)); if (args.type) params.append('type', String(args.type)); return this.ghlClient.makeRequest('GET', `/phone-numbers/available?${params.toString()}`); } case 'purchase_phone_number': { return this.ghlClient.makeRequest('POST', `/phone-numbers/`, { locationId, phoneNumber: args.phoneNumber, name: args.name }); } case 'update_phone_number': { const body: Record = { locationId }; if (args.name) body.name = args.name; if (args.forwardingNumber) body.forwardingNumber = args.forwardingNumber; if (args.callRecording !== undefined) body.callRecording = args.callRecording; if (args.whisperMessage) body.whisperMessage = args.whisperMessage; return this.ghlClient.makeRequest('PUT', `/phone-numbers/${args.phoneNumberId}`, body); } case 'release_phone_number': { return this.ghlClient.makeRequest('DELETE', `/phone-numbers/${args.phoneNumberId}?locationId=${locationId}`); } // Call Forwarding case 'get_call_forwarding_settings': { return this.ghlClient.makeRequest('GET', `/phone-numbers/${args.phoneNumberId}/forwarding?locationId=${locationId}`); } case 'update_call_forwarding': { const body: Record = { locationId }; if (args.enabled !== undefined) body.enabled = args.enabled; if (args.forwardTo) body.forwardTo = args.forwardTo; if (args.ringTimeout) body.ringTimeout = args.ringTimeout; if (args.voicemailEnabled !== undefined) body.voicemailEnabled = args.voicemailEnabled; return this.ghlClient.makeRequest('PUT', `/phone-numbers/${args.phoneNumberId}/forwarding`, body); } // IVR case 'get_ivr_menus': { return this.ghlClient.makeRequest('GET', `/phone-numbers/ivr?locationId=${locationId}`); } case 'create_ivr_menu': { return this.ghlClient.makeRequest('POST', `/phone-numbers/ivr`, { locationId, name: args.name, greeting: args.greeting, options: args.options }); } case 'update_ivr_menu': { const body: Record = { locationId }; if (args.name) body.name = args.name; if (args.greeting) body.greeting = args.greeting; if (args.options) body.options = args.options; return this.ghlClient.makeRequest('PUT', `/phone-numbers/ivr/${args.menuId}`, body); } case 'delete_ivr_menu': { return this.ghlClient.makeRequest('DELETE', `/phone-numbers/ivr/${args.menuId}?locationId=${locationId}`); } // Voicemail case 'get_voicemail_settings': { return this.ghlClient.makeRequest('GET', `/phone-numbers/voicemail/settings?locationId=${locationId}`); } case 'update_voicemail_settings': { const body: Record = { locationId }; if (args.enabled !== undefined) body.enabled = args.enabled; if (args.greeting) body.greeting = args.greeting; if (args.transcriptionEnabled !== undefined) body.transcriptionEnabled = args.transcriptionEnabled; if (args.notificationEmail) body.notificationEmail = args.notificationEmail; return this.ghlClient.makeRequest('PUT', `/phone-numbers/voicemail/settings`, body); } case 'get_voicemails': { const params = new URLSearchParams(); params.append('locationId', locationId); if (args.phoneNumberId) params.append('phoneNumberId', String(args.phoneNumberId)); if (args.status) params.append('status', String(args.status)); if (args.limit) params.append('limit', String(args.limit)); if (args.offset) params.append('offset', String(args.offset)); return this.ghlClient.makeRequest('GET', `/phone-numbers/voicemail?${params.toString()}`); } case 'delete_voicemail': { return this.ghlClient.makeRequest('DELETE', `/phone-numbers/voicemail/${args.voicemailId}?locationId=${locationId}`); } // Caller ID case 'get_caller_ids': { return this.ghlClient.makeRequest('GET', `/phone-numbers/caller-id?locationId=${locationId}`); } case 'add_caller_id': { return this.ghlClient.makeRequest('POST', `/phone-numbers/caller-id`, { locationId, phoneNumber: args.phoneNumber, name: args.name }); } case 'verify_caller_id': { return this.ghlClient.makeRequest('POST', `/phone-numbers/caller-id/${args.callerIdId}/verify`, { locationId, code: args.code }); } case 'delete_caller_id': { return this.ghlClient.makeRequest('DELETE', `/phone-numbers/caller-id/${args.callerIdId}?locationId=${locationId}`); } default: throw new Error(`Unknown tool: ${toolName}`); } } }