analyzeListingPhotos
Analyze Airbnb listing photos to assess property quality and features for informed booking decisions.
Instructions
Analyze photos from an Airbnb listing
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Airbnb listing ID |
Implementation Reference
- photoTools.ts:56-71 (handler)Handler logic for the 'analyzeListingPhotos' tool. Extracts photos using extractListingPhotos, formats analysis prompt, and returns structured JSON response.if (toolName === 'analyzeListingPhotos') { return { content: [ { type: 'text', text: JSON.stringify({ success: photos.extractionSuccess, photoCount: photos.photoCount, analysisPrompt: formatPhotosForAnalysis(photos), photoUrls: photos.photoUrls, }), }, ], isError: !photos.extractionSuccess, }; }
- photoTools.ts:15-26 (schema)Schema definition for the analyzeListingPhotos tool, including name, description, and input schema requiring 'id'.{ name: 'analyzeListingPhotos', description: 'Analyze photos from an Airbnb listing', inputSchema: { type: 'object', properties: { id: { type: 'string', description: 'Airbnb listing ID' }, }, required: ['id'], }, }, ];
- index.ts:666-670 (registration)Registration and dispatch in the main tool call handler switch statement, routing analyzeListingPhotos calls to handlePhotoAnalysisTool.case "getListingPhotos": case "analyzeListingPhotos": { result = await handlePhotoAnalysisTool(request.params.name, request.params.arguments); break; }
- index.ts:137-141 (registration)Tool registration by including photoAnalysisTools (containing analyzeListingPhotos schema) in the main AIRBNB_TOOLS list used for ListTools response.const AIRBNB_TOOLS = [ AIRBNB_SEARCH_TOOL, AIRBNB_LISTING_DETAILS_TOOL, ...photoAnalysisTools, ];
- photoAnalyzer.ts:4-42 (helper)Core helper function extractListingPhotos that fetches the listing page and parses photo URLs using cheerio.export async function extractListingPhotos(listingId: string) { try { const url = `https://www.airbnb.com/rooms/${listingId}`; const response = await fetch(url, { headers: { 'User-Agent': 'Mozilla/5.0' }, }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const html = await response.text(); const $ = cheerio.load(html); const photoUrls: string[] = []; $('img[src*="airbnb"]').each((_: any, el: any) => { const src = $(el).attr('src'); const alt = $(el).attr('alt'); if (src && alt?.includes('photo') && photoUrls.length < 50) { if (!photoUrls.includes(src)) photoUrls.push(src); } }); return { listingId, photoUrls, photoCount: photoUrls.length, extractionSuccess: photoUrls.length > 0, timestamp: new Date().toISOString(), }; } catch (error) { return { listingId, photoUrls: [], photoCount: 0, extractionSuccess: false, error: (error instanceof Error ? error.message : 'Unknown error'), timestamp: new Date().toISOString(), }; } }