mirror of
https://github.com/orangecoding/fredy.git
synced 2026-06-16 12:31:07 +00:00
adding tool to receive photo of listing
This commit is contained in:
@@ -220,6 +220,122 @@ export function createMcpServer() {
|
||||
},
|
||||
);
|
||||
|
||||
// ── get_photo_for_listing ─────────────────────────────────────────────────────
|
||||
server.tool(
|
||||
'get_photo_for_listing',
|
||||
'Fetch and return the photo of a listing by its ID as an image for vision analysis.',
|
||||
{
|
||||
listingId: z.string().describe('The listing ID whose photo to fetch'),
|
||||
},
|
||||
async ({ listingId }, extra) => {
|
||||
const { user, error } = authenticateToolCall(extra, 'get_photo_for_listing');
|
||||
if (error) return normalizeError(error, 'get_photo_for_listing');
|
||||
|
||||
const listing = getListingById(listingId, user.id, user.isAdmin);
|
||||
if (!listing) {
|
||||
return normalizeError('Listing not found or access denied.', 'get_photo_for_listing');
|
||||
}
|
||||
|
||||
const imageUrl = listing.image_url;
|
||||
if (!imageUrl) {
|
||||
return normalizeError('No image available for this listing.', 'get_photo_for_listing');
|
||||
}
|
||||
|
||||
const SUPPORTED_MIME_TYPES = new Set(['image/jpeg', 'image/png', 'image/gif', 'image/webp']);
|
||||
|
||||
let response;
|
||||
try {
|
||||
response = await fetch(imageUrl, {
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
Accept: 'image/jpeg,image/png,image/webp,image/gif,image/*,*/*',
|
||||
},
|
||||
});
|
||||
} catch (fetchErr) {
|
||||
return normalizeError(`Failed to fetch image: ${fetchErr.message}`, 'get_photo_for_listing');
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
return normalizeError(
|
||||
`Image fetch returned HTTP ${response.status}. Image URL: ${imageUrl}`,
|
||||
'get_photo_for_listing',
|
||||
);
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('content-type') ?? '';
|
||||
const headerMimeType = contentType.split(';')[0].trim().toLowerCase();
|
||||
|
||||
let buffer;
|
||||
try {
|
||||
buffer = await response.arrayBuffer();
|
||||
} catch (readErr) {
|
||||
return normalizeError(`Failed to read image body: ${readErr.message}`, 'get_photo_for_listing');
|
||||
}
|
||||
|
||||
const bytes = new Uint8Array(buffer);
|
||||
|
||||
if (bytes.length < 12) {
|
||||
return normalizeError(
|
||||
`Downloaded file is too small to determine image type. Image URL: ${imageUrl}`,
|
||||
'get_photo_for_listing',
|
||||
);
|
||||
}
|
||||
|
||||
let resolvedMime;
|
||||
|
||||
if (SUPPORTED_MIME_TYPES.has(headerMimeType)) {
|
||||
resolvedMime = headerMimeType;
|
||||
} else {
|
||||
if (bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {
|
||||
resolvedMime = 'image/jpeg';
|
||||
} else if (
|
||||
bytes[0] === 0x89 &&
|
||||
bytes[1] === 0x50 &&
|
||||
bytes[2] === 0x4e &&
|
||||
bytes[3] === 0x47 &&
|
||||
bytes[4] === 0x0d &&
|
||||
bytes[5] === 0x0a &&
|
||||
bytes[6] === 0x1a &&
|
||||
bytes[7] === 0x0a
|
||||
) {
|
||||
resolvedMime = 'image/png';
|
||||
} else if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38) {
|
||||
resolvedMime = 'image/gif';
|
||||
} else if (
|
||||
bytes[0] === 0x52 &&
|
||||
bytes[1] === 0x49 &&
|
||||
bytes[2] === 0x46 &&
|
||||
bytes[3] === 0x46 &&
|
||||
bytes[8] === 0x57 &&
|
||||
bytes[9] === 0x45 &&
|
||||
bytes[10] === 0x42 &&
|
||||
bytes[11] === 0x50
|
||||
) {
|
||||
resolvedMime = 'image/webp';
|
||||
} else {
|
||||
return normalizeError(
|
||||
`Image format not supported by vision models (header: ${headerMimeType || 'unknown'}). Image URL: ${imageUrl}`,
|
||||
'get_photo_for_listing',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const base64 = Buffer.from(buffer).toString('base64');
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'image',
|
||||
data: base64,
|
||||
mimeType: resolvedMime,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
// ── get_current_date_ime ─────────────────────────────────────────────────────
|
||||
server.tool('get_current_date_time', 'Returns the current date and time.', {}, () => {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user