UNPKG

@mixio-pro/kalaasetu-mcp

Version:

A powerful Model Context Protocol server providing AI tools for content generation and analysis

191 lines (163 loc) 6.94 kB
import { z } from "zod"; export const perplexityImages = { name: "perplexityImages", description: "Searches for images using the Perplexity API. Returns a formatted text response that includes a summary and a numbered list of image URLs with citations mapped to the text.", parameters: z.object({ query: z.string().describe("The search query for images."), image_domain_filter: z.array(z.string()).optional().describe("A list of domains to include or exclude. To exclude, prefix with '-'. E.g., ['wikimedia.org', '-gettyimages.com']."), image_format_filter: z.array(z.string()).optional().describe("A list of allowed image formats. E.g., ['jpg', 'png', 'gif']."), }), execute: async (args: { query: string; image_domain_filter?: string[]; image_format_filter?: string[] }) => { const apiKey = process.env.PERPLEXITY_API_KEY; if (!apiKey) { throw new Error("PERPLEXITY_API_KEY environment variable is not set."); } const url = "https://api.perplexity.ai/chat/completions"; const headers = { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json", "accept": "application/json" }; const payload: any = { model: "sonar", messages: [ { role: "user", content: `Show me images of ${args.query}` } ], return_images: true }; if (args.image_domain_filter) { payload.image_domain_filter = args.image_domain_filter; } if (args.image_format_filter) { payload.image_format_filter = args.image_format_filter; } const res = await fetch(url, { method: "POST", headers: headers, body: JSON.stringify(payload), }); if (!res.ok) { const text = await res.text(); throw new Error(`Perplexity API request failed: ${res.status} ${text}`); } const data: any = await res.json(); let content = data.choices?.[0]?.message?.content; const images = data.images; const citations = data.citations; if (!images || images.length === 0) { return `No direct image URLs found in the API response. The text content was: ${content}`; } // Create a map of origin_url -> new 1-based index const originUrlToImageIndex: { [key: string]: number } = {}; images.forEach((img: any, index: number) => { if (img.origin_url) { originUrlToImageIndex[img.origin_url] = index + 1; } }); // Create a map of old citation index -> new image index const oldToNewCitationMap: { [key: number]: number } = {}; if (citations && Array.isArray(citations)) { citations.forEach((citationUrl: string, index: number) => { if (originUrlToImageIndex[citationUrl]) { oldToNewCitationMap[index + 1] = originUrlToImageIndex[citationUrl]; } }); } // Replace citations in the content if (content && typeof content === 'string') { content = content.replace(/\[(\d+)\]/g, (match: string, oldIndexStr: string) => { const oldIndex = parseInt(oldIndexStr, 10); const newIndex = oldToNewCitationMap[oldIndex]; if (newIndex) { return `[${newIndex}]`; } return ''; // Remove citation if it doesn't correspond to an image }).replace(/(\s\s+)/g, ' ').trim(); // Clean up extra spaces } // Build the final formatted output let output = content + "\n\n--- Images ---\n"; images.forEach((img: any, index: number) => { output += `${index + 1}. ${img.image_url}\n (Source: ${img.origin_url})\n`; }); return output; }, }; export const perplexityVideos = { name: "perplexityVideos", description: "Searches for videos using the Perplexity API. Returns a formatted text response that includes a summary and a numbered list of video URLs with citations mapped to the text.", parameters: z.object({ query: z.string().describe("The search query for videos."), search_domain_filter: z.array(z.string()).optional().describe("A list of domains to limit the search to (e.g., ['youtube.com']). Use a '-' prefix to exclude a domain."), }), execute: async (args: { query: string; search_domain_filter?: string[] }) => { const apiKey = process.env.PERPLEXITY_API_KEY; if (!apiKey) { throw new Error("PERPLEXITY_API_KEY environment variable is not set."); } const url = "https://api.perplexity.ai/chat/completions"; const headers = { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json", "accept": "application/json" }; const payload: any = { model: "sonar-pro", messages: [ { role: "user", content: `Show me videos of ${args.query}` } ], media_response: { overrides: { return_videos: true } } }; if (args.search_domain_filter) { payload.search_domain_filter = args.search_domain_filter; } const res = await fetch(url, { method: "POST", headers: headers, body: JSON.stringify(payload), }); if (!res.ok) { const text = await res.text(); throw new Error(`Perplexity API request failed: ${res.status} ${text}`); } const data: any = await res.json(); let content = data.choices?.[0]?.message?.content; const videos = data.videos; const citations = data.citations; if (!videos || videos.length === 0) { return `No direct video URLs found in the API response. Full API Response: ${JSON.stringify(data, null, 2)}`; } // Create a map of video url -> new 1-based index const urlToVideoIndex: { [key: string]: number } = {}; videos.forEach((video: any, index: number) => { if (video.url) { urlToVideoIndex[video.url] = index + 1; } }); // Create a map of old citation index -> new video index const oldToNewCitationMap: { [key: number]: number } = {}; if (citations && Array.isArray(citations)) { citations.forEach((citationUrl: string, index: number) => { if (urlToVideoIndex[citationUrl]) { oldToNewCitationMap[index + 1] = urlToVideoIndex[citationUrl]; } }); } // Replace citations in the content if (content && typeof content === 'string') { content = content.replace(/\[(\d+)\]/g, (match: string, oldIndexStr: string) => { const oldIndex = parseInt(oldIndexStr, 10); const newIndex = oldToNewCitationMap[oldIndex]; if (newIndex) { return `[${newIndex}]`; } return ''; // Remove citation if it doesn't correspond to a video }).replace(/(\s\s+)/g, ' ').trim(); // Clean up extra spaces } // Build the final formatted output let output = content + "\n\n--- Videos ---\n"; videos.forEach((video: any, index: number) => { output += `${index + 1}. ${video.url}\n`; }); return output; }, };