Skip to main content
Glama
Rana-X
by Rana-X

request_cleaning

Schedule cleaning services in San Francisco by submitting customer details for automated booking requests to service partners.

Instructions

Request cleaning service in San Francisco

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nameYesCustomer name
phoneYesPhone number (10-digit US format)
addressYesService address in San Francisco

Implementation Reference

  • Executes the 'request_cleaning' tool: extracts arguments, validates and sanitizes inputs (name, phone, address), checks if address is in San Francisco, sends email notification to partners using Resend, handles errors and timeouts.
    if (request.params.name === 'request_cleaning') { console.error('[INFO] Processing cleaning request'); try { const { name, phone, address } = request.params.arguments || {}; // Validate required fields if (!name || !phone || !address) { console.error('[WARN] Missing required fields'); return { content: [{ type: 'text', text: 'Error: All fields (name, phone, address) are required.' }] }; } // Sanitize inputs const cleanName = sanitizeString(name, 100); const cleanAddress = sanitizeString(address, 200); // Validate name if (cleanName.length < 2) { console.error('[WARN] Invalid name provided'); return { content: [{ type: 'text', text: 'Error: Please provide a valid name (at least 2 characters).' }] }; } // Validate phone const phoneValidation = validatePhone(phone); if (!phoneValidation.valid) { console.error('[WARN] Invalid phone number:', phone); return { content: [{ type: 'text', text: 'Error: Please provide a valid 10-digit US phone number.' }] }; } // Validate address if (cleanAddress.length < 10) { console.error('[WARN] Address too short'); return { content: [{ type: 'text', text: 'Error: Please provide a complete address.' }] }; } // Check if in SF if (!validateSFAddress(cleanAddress)) { console.error('[INFO] Non-SF address:', cleanAddress); return { content: [{ type: 'text', text: "Sorry, we only serve San Francisco currently. We're expanding - stay tuned!" }] }; } // Log the request console.error('[INFO] Sending email for:', { name: cleanName, phone: phoneValidation.cleaned, address: cleanAddress.substring(0, 50) + '...' }); // Send email with timeout const emailPromise = resend.emails.send({ from: process.env.FROM_EMAIL, to: process.env.PARTNER_EMAILS.split(',').map(e => e.trim()), subject: 'Cleaning Request - SF', text: `New cleaning service request: Customer Details: - Name: ${cleanName} - Phone: ${phoneValidation.cleaned} - Address: ${cleanAddress} Request Time: ${new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })} Please contact the customer within 1 hour.`, html: ` <h2>New Cleaning Service Request</h2> <p><strong>Customer Details:</strong></p> <ul> <li><strong>Name:</strong> ${cleanName}</li> <li><strong>Phone:</strong> ${phoneValidation.cleaned}</li> <li><strong>Address:</strong> ${cleanAddress}</li> </ul> <p><strong>Request Time:</strong> ${new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })}</p> <p><em>Please contact the customer within 1 hour.</em></p> ` }); // Add timeout to prevent hanging const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Email timeout')), 10000) ); try { await Promise.race([emailPromise, timeoutPromise]); console.error('[SUCCESS] Email sent successfully'); return { content: [{ type: 'text', text: 'Successfully booked the maid. Will get confirmation shortly.' }] }; } catch (emailError) { console.error('[ERROR] Failed to send email:', emailError.message); return { content: [{ type: 'text', text: 'Booking received but email notification failed. The service will still process your request.' }] }; } } catch (error) { console.error('[CRITICAL] Unexpected error:', error); return { content: [{ type: 'text', text: 'Service temporarily unavailable. Please try again later.' }] }; } }
  • Input schema definition for the 'request_cleaning' tool, specifying required fields and constraints.
    name: 'request_cleaning', description: 'Request cleaning service in San Francisco', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Customer name', minLength: 2, maxLength: 100 }, phone: { type: 'string', description: 'Phone number (10-digit US format)', pattern: '^[0-9\\s\\-\\(\\)\\+]+$' }, address: { type: 'string', description: 'Service address in San Francisco', minLength: 10, maxLength: 200 } }, required: ['name', 'phone', 'address'] } }]
  • index.js:100-131 (registration)
    Registers the 'request_cleaning' tool by handling ListToolsRequestSchema and returning the tool list with schema.
    server.setRequestHandler(ListToolsRequestSchema, async () => { console.error('[INFO] Listing tools'); return { tools: [{ name: 'request_cleaning', description: 'Request cleaning service in San Francisco', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Customer name', minLength: 2, maxLength: 100 }, phone: { type: 'string', description: 'Phone number (10-digit US format)', pattern: '^[0-9\\s\\-\\(\\)\\+]+$' }, address: { type: 'string', description: 'Service address in San Francisco', minLength: 10, maxLength: 200 } }, required: ['name', 'phone', 'address'] } }] }; });
  • index.js:134-280 (registration)
    Registers the tool call handler via CallToolRequestSchema, dispatching to 'request_cleaning' logic if matched.
    server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === 'request_cleaning') { console.error('[INFO] Processing cleaning request'); try { const { name, phone, address } = request.params.arguments || {}; // Validate required fields if (!name || !phone || !address) { console.error('[WARN] Missing required fields'); return { content: [{ type: 'text', text: 'Error: All fields (name, phone, address) are required.' }] }; } // Sanitize inputs const cleanName = sanitizeString(name, 100); const cleanAddress = sanitizeString(address, 200); // Validate name if (cleanName.length < 2) { console.error('[WARN] Invalid name provided'); return { content: [{ type: 'text', text: 'Error: Please provide a valid name (at least 2 characters).' }] }; } // Validate phone const phoneValidation = validatePhone(phone); if (!phoneValidation.valid) { console.error('[WARN] Invalid phone number:', phone); return { content: [{ type: 'text', text: 'Error: Please provide a valid 10-digit US phone number.' }] }; } // Validate address if (cleanAddress.length < 10) { console.error('[WARN] Address too short'); return { content: [{ type: 'text', text: 'Error: Please provide a complete address.' }] }; } // Check if in SF if (!validateSFAddress(cleanAddress)) { console.error('[INFO] Non-SF address:', cleanAddress); return { content: [{ type: 'text', text: "Sorry, we only serve San Francisco currently. We're expanding - stay tuned!" }] }; } // Log the request console.error('[INFO] Sending email for:', { name: cleanName, phone: phoneValidation.cleaned, address: cleanAddress.substring(0, 50) + '...' }); // Send email with timeout const emailPromise = resend.emails.send({ from: process.env.FROM_EMAIL, to: process.env.PARTNER_EMAILS.split(',').map(e => e.trim()), subject: 'Cleaning Request - SF', text: `New cleaning service request: Customer Details: - Name: ${cleanName} - Phone: ${phoneValidation.cleaned} - Address: ${cleanAddress} Request Time: ${new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })} Please contact the customer within 1 hour.`, html: ` <h2>New Cleaning Service Request</h2> <p><strong>Customer Details:</strong></p> <ul> <li><strong>Name:</strong> ${cleanName}</li> <li><strong>Phone:</strong> ${phoneValidation.cleaned}</li> <li><strong>Address:</strong> ${cleanAddress}</li> </ul> <p><strong>Request Time:</strong> ${new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' })}</p> <p><em>Please contact the customer within 1 hour.</em></p> ` }); // Add timeout to prevent hanging const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Email timeout')), 10000) ); try { await Promise.race([emailPromise, timeoutPromise]); console.error('[SUCCESS] Email sent successfully'); return { content: [{ type: 'text', text: 'Successfully booked the maid. Will get confirmation shortly.' }] }; } catch (emailError) { console.error('[ERROR] Failed to send email:', emailError.message); return { content: [{ type: 'text', text: 'Booking received but email notification failed. The service will still process your request.' }] }; } } catch (error) { console.error('[CRITICAL] Unexpected error:', error); return { content: [{ type: 'text', text: 'Service temporarily unavailable. Please try again later.' }] }; } } // Unknown tool console.error('[WARN] Unknown tool requested:', request.params.name); return { content: [{ type: 'text', text: 'Unknown tool requested.' }] }; });
  • Helper function to sanitize string inputs by removing HTML tags, special characters, trimming, and limiting length.
    const sanitizeString = (str, maxLength = 200) => { if (typeof str !== 'string') return ''; return str .replace(/<[^>]*>/g, '') // Remove HTML tags .replace(/[<>\"']/g, '') // Remove special chars .trim() .slice(0, maxLength); };
  • Helper to validate and format US phone numbers to 10 digits, returning cleaned formatted version.
    const validatePhone = (phone) => { if (typeof phone !== 'string') return { valid: false, cleaned: null }; const cleaned = phone.replace(/\D/g, ''); if (cleaned.length !== 10) { return { valid: false, cleaned: null }; } const formatted = `${cleaned.slice(0,3)}-${cleaned.slice(3,6)}-${cleaned.slice(6)}`; return { valid: true, cleaned: formatted }; };
  • Helper to check if address is in San Francisco by keywords or zip code patterns.
    const validateSFAddress = (address) => { if (typeof address !== 'string') return false; const addr = address.toLowerCase(); const sfIndicators = ['sf', 'san francisco', 'sanfrancisco']; const hasSFText = sfIndicators.some(indicator => addr.includes(indicator)); const hasSFZip = /94[01]\d{2}/.test(addr); return hasSFText || hasSFZip; };

Other Tools

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/Rana-X/irl'

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