Perplexity Tool for Claude Desktop

#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; // Check for API key const PERPLEXITY_API_KEY = "YOUR-API-KEY-HERE"; if (!PERPLEXITY_API_KEY) { console.error("Error: PERPLEXITY_API_KEY environment variable is required"); process.exit(1); } const PERPLEXITY_TOOL = { name: "ask_perplexity", description: "Ask a question to Perplexity AI", inputSchema: { type: "object", properties: { question: { type: "string", description: "The question to ask" }, temperature: { type: "number", description: "Response randomness (0-2)", default: 0.2 }, max_tokens: { type: "integer", description: "Maximum tokens in response", default: 1000 }, search_domain_filter: { type: "array", items: { type: "string" }, description: "Limit search to specific domains", default: [] }, search_recency_filter: { type: "string", enum: ["day", "week", "month", "year"], description: "Filter results by recency", default: "month" } }, required: ["question"], }, }; // Server implementation const server = new Server({ name: "perplexity-tool", version: "0.1.0", }, { capabilities: { tools: {}, }, }); function isPerplexityArgs(args) { return (typeof args === "object" && args !== null && "question" in args && typeof args.question === "string"); } async function askPerplexity(args) { const { question, temperature = 0.2, max_tokens = 1000, search_domain_filter = [], search_recency_filter = "month" } = args; const response = await fetch("https://api.perplexity.ai/chat/completions", { method: "POST", headers: { "Authorization": `Bearer ${PERPLEXITY_API_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "llama-3.1-sonar-small-128k-online", messages: [ { role: "system", content: "You are a world-class researcher with strong attention to details" }, { role: "user", content: question } ], max_tokens, temperature, top_p: 0.9, stream: false, search_domain_filter, search_recency_filter, return_images: false, return_related_questions: false, frequency_penalty: 1, presence_penalty: 0 }) }); if (!response.ok) { throw new Error(`Perplexity API error: ${response.status} ${response.statusText}`); } const result = await response.json(); // Extract answer and citations const answer = result.choices[0].message.content; const citations = result.citations || []; const tokenUsage = result.usage || {}; // Format response const fullResponse = [ `Answer: ${answer}\n`, "\nSources:", ...citations.map((citation, i) => `${i + 1}. ${citation}`), "\nToken Usage:", `- Prompt tokens: ${tokenUsage.prompt_tokens || 'N/A'}`, `- Completion tokens: ${tokenUsage.completion_tokens || 'N/A'}`, `- Total tokens: ${tokenUsage.total_tokens || 'N/A'}` ].join('\n'); return fullResponse; } // Tool handlers server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [PERPLEXITY_TOOL], })); server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; if (!args) { throw new Error("No arguments provided"); } if (name === "ask_perplexity") { if (!isPerplexityArgs(args)) { throw new Error("Invalid arguments for ask_perplexity"); } const results = await askPerplexity(args); return { content: [{ type: "text", text: results }], isError: false, }; } return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true, }; } catch (error) { return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }); async function runServer() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Perplexity MCP Server running on stdio"); } runServer().catch((error) => { console.error("Fatal error running server:", error); process.exit(1); });