perplexity-mcp-server
Version:
A Perplexity API Model Context Protocol (MCP) server that unlocks Perplexity's search-augmented AI capabilities for LLM agents. Features robust error handling, secure input validation, and transparent reasoning with the showThinking parameter. Built with
74 lines (69 loc) • 5.07 kB
JavaScript
/**
* @fileoverview Defines the core logic, schemas, and types for the `perplexity_deep_research` tool.
* This tool interfaces with the Perplexity API to perform exhaustive, multi-source research.
* @module src/mcp-server/tools/perplexityDeepResearch/logic
*/
import { z } from 'zod';
import { perplexityApiService } from '../../../services/index.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/index.js';
import { PerplexitySearchResponseSchema } from '../perplexitySearch/logic.js';
// 1. DEFINE Zod input and output schemas.
export const PerplexityDeepResearchInputSchema = z.object({
query: z.string().min(1).describe("The detailed research query or topic for Perplexity's deep research engine."),
reasoning_effort: z.enum(['low', 'medium', 'high']).optional().default('medium').describe("Controls the computational effort and depth of the research. 'high' provides the most thorough analysis but costs more."),
}).describe("Performs an exhaustive, multi-source research query using the Perplexity Deep Research API. This tool is for complex topics requiring in-depth analysis and report generation, not for simple questions. Use the `reasoning_effort` parameter to control the depth. (Ex. 'Create a detailed document on utilizing '@modelcontextprotocol/sdk' v1.15.0')");
// The response schema is identical to the search response, so we can reuse it.
export const PerplexityDeepResearchResponseSchema = PerplexitySearchResponseSchema;
// --- System Prompt ---
const SYSTEM_PROMPT = `You are an expert-level AI research assistant using the Perplexity deep research engine. Your primary directive is to conduct exhaustive, multi-source research and generate detailed, well-structured, and impeccably cited reports suitable for an expert audience.
**Core Directives:**
1. **Systematic & Exhaustive Research:** Conduct a comprehensive, multi-faceted search to build a deep and nuanced understanding of the topic. Synthesize information from a wide array of sources to ensure the final report is complete.
2. **Source Vetting:** Apply rigorous standards to source evaluation. Prioritize primary sources, peer-reviewed literature, and authoritative contemporary reports. Scrutinize sources for bias and accuracy.
3. **Accurate & Robust Citations:** Every piece of information, data point, or claim must be attributed with a precise, inline citation. Ensure all citation metadata (URL, title) is captured correctly and completely.
**Final Report Formatting Rules:**
1. **Synthesize and Structure:** Your answer must be a comprehensive synthesis of the information gathered. Structure the response logically with clear headings, subheadings, and paragraphs to create a professional-grade document.
2. **Depth and Detail:** Provide a thorough and detailed analysis. Avoid superficiality and demonstrate a deep command of the subject matter.
3. **Clarity and Precision:** Use clear, precise, and professional language.
4. **Stand-Alone Report:** The final answer must be a complete, stand-alone report, ready for publication. Do not include conversational filler or meta-commentary on your research process.`;
/**
* 3. IMPLEMENT and export the core logic function.
* It must remain pure: its only concerns are its inputs and its return value or thrown error.
* @throws {McpError} If the logic encounters an unrecoverable issue.
*/
export async function perplexityDeepResearchLogic(params, context) {
logger.debug("Executing perplexityDeepResearchLogic...", { ...context, toolInput: params });
const requestPayload = {
model: 'sonar-deep-research',
messages: [
{ role: 'system', content: SYSTEM_PROMPT },
{ role: 'user', content: params.query },
],
reasoning_effort: params.reasoning_effort,
stream: false,
};
logger.info("Calling Perplexity API with deep research model", { ...context, reasoningEffort: params.reasoning_effort });
logger.debug("API Payload", { ...context, payload: requestPayload });
const response = await perplexityApiService.chatCompletion(requestPayload, context);
const choice = response.choices?.[0];
const rawResultText = choice?.message?.content;
if (!rawResultText) {
logger.warning("Perplexity API returned empty content", { ...context, responseId: response.id });
throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, 'Perplexity API returned an empty response.', { ...context, responseId: response.id });
}
const toolResponse = {
rawResultText,
responseId: response.id,
modelUsed: response.model,
usage: response.usage,
searchResults: response.search_results,
};
logger.info("Perplexity deep research logic completed successfully.", {
...context,
responseId: toolResponse.responseId,
model: toolResponse.modelUsed,
usage: toolResponse.usage,
searchResultCount: toolResponse.searchResults?.length ?? 0,
});
return toolResponse;
}