From c6b05658acbf0fe3e1d86cc16aace6002ec35ca0 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sun, 27 Jul 2025 13:10:00 +0000 Subject: [PATCH] feat: add Perplexity MCP server for web search and deep research - Implements MCP server with Perplexity API integration - Adds three tools: web_search, deep_research, and ask_followup - Includes comprehensive documentation and example configuration - Supports various search options including domain filtering and recency - Provides deep research capabilities with Pro models - Closes #6271 --- examples/mcp-servers/perplexity/.gitignore | 25 ++ examples/mcp-servers/perplexity/README.md | 208 ++++++++++++ .../perplexity/example-config.json | 14 + examples/mcp-servers/perplexity/package.json | 31 ++ examples/mcp-servers/perplexity/src/index.ts | 315 ++++++++++++++++++ examples/mcp-servers/perplexity/tsconfig.json | 19 ++ 6 files changed, 612 insertions(+) create mode 100644 examples/mcp-servers/perplexity/.gitignore create mode 100644 examples/mcp-servers/perplexity/README.md create mode 100644 examples/mcp-servers/perplexity/example-config.json create mode 100644 examples/mcp-servers/perplexity/package.json create mode 100644 examples/mcp-servers/perplexity/src/index.ts create mode 100644 examples/mcp-servers/perplexity/tsconfig.json diff --git a/examples/mcp-servers/perplexity/.gitignore b/examples/mcp-servers/perplexity/.gitignore new file mode 100644 index 0000000000..91f664ce7c --- /dev/null +++ b/examples/mcp-servers/perplexity/.gitignore @@ -0,0 +1,25 @@ +# Dependencies +node_modules/ + +# Build output +build/ +dist/ + +# Environment files +.env +.env.local + +# IDE files +.vscode/ +.idea/ + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* + +# Test coverage +coverage/ \ No newline at end of file diff --git a/examples/mcp-servers/perplexity/README.md b/examples/mcp-servers/perplexity/README.md new file mode 100644 index 0000000000..1d283ad05d --- /dev/null +++ b/examples/mcp-servers/perplexity/README.md @@ -0,0 +1,208 @@ +# Perplexity MCP Server + +An MCP (Model Context Protocol) server that integrates with the Perplexity API to provide web search and deep research capabilities to AI assistants. + +## Features + +This MCP server provides three main tools: + +### 1. `web_search` + +Performs real-time web searches using Perplexity's Sonar model. + +**Parameters:** + +- `query` (required): Search query for web search +- `search_domain_filter` (optional): List of domains to restrict search to +- `return_citations` (optional, default: true): Whether to return source citations +- `return_images` (optional, default: false): Whether to return relevant images +- `return_related_questions` (optional, default: true): Whether to return related questions +- `search_recency_filter` (optional): Filter results by recency ('month', 'week', 'day', 'hour') +- `temperature` (optional, default: 0.2): Temperature for response generation (0-2) + +### 2. `deep_research` + +Conducts comprehensive research using Perplexity's Pro models for in-depth analysis. + +**Parameters:** + +- `topic` (required): Research topic or question for in-depth analysis +- `model` (optional, default: 'sonar-pro'): Model to use ('sonar-pro' or 'sonar-reasoning') +- `focus_areas` (optional): Specific areas to focus the research on +- `max_tokens` (optional, default: 2000): Maximum tokens for response (100-4000) +- `temperature` (optional, default: 0.1): Temperature for response generation (0-2) +- `return_citations` (optional, default: true): Whether to return source citations + +### 3. `ask_followup` + +Asks follow-up questions based on previous research context. + +**Parameters:** + +- `context` (required): Previous research context or conversation +- `question` (required): Follow-up question to ask +- `model` (optional, default: 'sonar'): Model to use ('sonar' or 'sonar-pro') +- `temperature` (optional, default: 0.3): Temperature for response generation (0-2) + +## Prerequisites + +- Node.js 18 or higher +- A Perplexity API key (get one at https://www.perplexity.ai/settings/api) + +## Installation + +### For Development + +1. Clone this repository and navigate to the server directory: + +```bash +cd examples/mcp-servers/perplexity +``` + +2. Install dependencies: + +```bash +npm install +``` + +3. Build the server: + +```bash +npm run build +``` + +### For Use with Roo Code + +The server can be configured in your MCP settings file. See the Configuration section below. + +## Configuration + +### Getting a Perplexity API Key + +1. Sign up for a Perplexity account at https://www.perplexity.ai +2. Navigate to Settings → API +3. Generate a new API key +4. Copy the API key for use in the configuration + +### MCP Settings Configuration + +Add the following to your MCP settings file: + +#### For Roo Code Extension + +Location: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json` (macOS) + +```json +{ + "mcpServers": { + "perplexity": { + "command": "node", + "args": ["/absolute/path/to/examples/mcp-servers/perplexity/build/index.js"], + "env": { + "PERPLEXITY_API_KEY": "your-perplexity-api-key-here" + } + } + } +} +``` + +#### For Claude Desktop App + +Location: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) + +```json +{ + "mcpServers": { + "perplexity": { + "command": "node", + "args": ["/absolute/path/to/examples/mcp-servers/perplexity/build/index.js"], + "env": { + "PERPLEXITY_API_KEY": "your-perplexity-api-key-here" + } + } + } +} +``` + +## Usage Examples + +Once configured, the AI assistant can use these tools: + +### Web Search Example + +``` +"Search for the latest developments in quantum computing" +``` + +### Deep Research Example + +``` +"Conduct deep research on the environmental impact of electric vehicles, focusing on battery production and recycling" +``` + +### Follow-up Question Example + +``` +"Based on the previous research about electric vehicles, what are the most promising battery technologies being developed?" +``` + +## Development + +### Running in Development Mode + +```bash +npm run dev +``` + +### Building + +```bash +npm run build +``` + +### Project Structure + +``` +perplexity/ +├── src/ +│ └── index.ts # Main server implementation +├── build/ # Compiled JavaScript (generated) +├── package.json # Dependencies and scripts +├── tsconfig.json # TypeScript configuration +└── README.md # This file +``` + +## API Rate Limits + +Please be aware of Perplexity API rate limits: + +- Check your plan's rate limits at https://www.perplexity.ai/settings/api +- The server includes error handling for rate limit errors +- Consider implementing caching for frequently requested information + +## Troubleshooting + +### Common Issues + +1. **"PERPLEXITY_API_KEY environment variable is required"** + + - Ensure you've added your API key to the MCP settings configuration + - Verify the key is valid and has not expired + +2. **Connection errors** + + - Check your internet connection + - Verify the Perplexity API is accessible + - Ensure your API key has the necessary permissions + +3. **TypeScript errors during build** + - Run `npm install` to ensure all dependencies are installed + - Check that you're using Node.js 18 or higher + +## Contributing + +Contributions are welcome! Please feel free to submit issues or pull requests to improve this MCP server. + +## License + +This MCP server is part of the Roo Code project and follows the same license terms. diff --git a/examples/mcp-servers/perplexity/example-config.json b/examples/mcp-servers/perplexity/example-config.json new file mode 100644 index 0000000000..90543114b9 --- /dev/null +++ b/examples/mcp-servers/perplexity/example-config.json @@ -0,0 +1,14 @@ +{ + "mcpServers": { + "perplexity": { + "command": "node", + "args": ["./examples/mcp-servers/perplexity/build/index.js"], + "env": { + "PERPLEXITY_API_KEY": "pplx-YOUR_API_KEY_HERE" + }, + "disabled": false, + "alwaysAllow": [], + "disabledTools": [] + } + } +} diff --git a/examples/mcp-servers/perplexity/package.json b/examples/mcp-servers/perplexity/package.json new file mode 100644 index 0000000000..24e2eb8386 --- /dev/null +++ b/examples/mcp-servers/perplexity/package.json @@ -0,0 +1,31 @@ +{ + "name": "@roo-code/mcp-server-perplexity", + "version": "0.1.0", + "description": "MCP server for Perplexity API integration - enables web search and deep research capabilities", + "type": "module", + "main": "build/index.js", + "scripts": { + "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"", + "dev": "tsx src/index.ts", + "prepare": "npm run build" + }, + "keywords": [ + "mcp", + "perplexity", + "search", + "research", + "ai" + ], + "author": "Roo Code", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.4", + "axios": "^1.7.9", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.10.5", + "tsx": "^4.19.2", + "typescript": "^5.7.3" + } +} diff --git a/examples/mcp-servers/perplexity/src/index.ts b/examples/mcp-servers/perplexity/src/index.ts new file mode 100644 index 0000000000..4cae4cccbf --- /dev/null +++ b/examples/mcp-servers/perplexity/src/index.ts @@ -0,0 +1,315 @@ +#!/usr/bin/env node +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" +import { z } from "zod" +import axios, { AxiosInstance } from "axios" + +// Get API key from environment +const API_KEY = process.env.PERPLEXITY_API_KEY +if (!API_KEY) { + throw new Error("PERPLEXITY_API_KEY environment variable is required") +} + +// Define types for Perplexity API +interface PerplexityMessage { + role: "system" | "user" | "assistant" + content: string +} + +interface PerplexitySearchOptions { + model?: string + temperature?: number + top_p?: number + search_domain_filter?: string[] + return_citations?: boolean + return_images?: boolean + return_related_questions?: boolean + search_recency_filter?: "month" | "week" | "day" | "hour" + top_k?: number + stream?: boolean + presence_penalty?: number + frequency_penalty?: number +} + +interface PerplexityResponse { + id: string + model: string + created: number + usage: { + prompt_tokens: number + completion_tokens: number + total_tokens: number + } + citations?: string[] + choices: Array<{ + index: number + finish_reason: string + message: { + role: string + content: string + } + delta?: { + role?: string + content?: string + } + }> +} + +// Create MCP server +const server = new McpServer({ + name: "perplexity-server", + version: "0.1.0", +}) + +// Create axios instance for Perplexity API +const perplexityApi: AxiosInstance = axios.create({ + baseURL: "https://api.perplexity.ai", + headers: { + Authorization: `Bearer ${API_KEY}`, + "Content-Type": "application/json", + }, +}) + +// Tool for web search using Perplexity +server.tool( + "web_search", + { + query: z.string().describe("Search query for web search"), + search_domain_filter: z.array(z.string()).optional().describe("List of domains to restrict search to"), + return_citations: z.boolean().optional().default(true).describe("Whether to return source citations"), + return_images: z.boolean().optional().default(false).describe("Whether to return relevant images"), + return_related_questions: z.boolean().optional().default(true).describe("Whether to return related questions"), + search_recency_filter: z + .enum(["month", "week", "day", "hour"]) + .optional() + .describe("Filter results by recency"), + temperature: z.number().min(0).max(2).optional().default(0.2).describe("Temperature for response generation"), + }, + async ({ + query, + search_domain_filter, + return_citations, + return_images, + return_related_questions, + search_recency_filter, + temperature, + }) => { + try { + const messages: PerplexityMessage[] = [ + { + role: "system", + content: + "You are a helpful search assistant. Provide accurate and relevant information based on web search results.", + }, + { + role: "user", + content: query, + }, + ] + + const searchOptions: PerplexitySearchOptions = { + model: "sonar", + temperature, + return_citations, + return_images, + return_related_questions, + search_domain_filter, + search_recency_filter, + } + + const response = await perplexityApi.post("/chat/completions", { + messages, + ...searchOptions, + }) + + const result = response.data.choices[0].message.content + const citations = response.data.citations || [] + + let formattedResult = `## Search Results\n\n${result}` + + if (citations.length > 0) { + formattedResult += `\n\n## Sources\n` + citations.forEach((citation, index) => { + formattedResult += `${index + 1}. ${citation}\n` + }) + } + + return { + content: [ + { + type: "text", + text: formattedResult, + }, + ], + } + } catch (error) { + if (axios.isAxiosError(error)) { + return { + content: [ + { + type: "text", + text: `Perplexity API error: ${error.response?.data?.error?.message || error.message}`, + }, + ], + isError: true, + } + } + throw error + } + }, +) + +// Tool for deep research using Perplexity Pro models +server.tool( + "deep_research", + { + topic: z.string().describe("Research topic or question for in-depth analysis"), + model: z + .enum(["sonar-pro", "sonar-reasoning"]) + .optional() + .default("sonar-pro") + .describe("Model to use for research"), + focus_areas: z.array(z.string()).optional().describe("Specific areas to focus the research on"), + max_tokens: z.number().min(100).max(4000).optional().default(2000).describe("Maximum tokens for response"), + temperature: z.number().min(0).max(2).optional().default(0.1).describe("Temperature for response generation"), + return_citations: z.boolean().optional().default(true).describe("Whether to return source citations"), + }, + async ({ topic, model, focus_areas, max_tokens, temperature, return_citations }) => { + try { + let systemPrompt = + "You are an expert research assistant. Provide comprehensive, well-structured, and accurate information based on the latest available data. Include relevant details, examples, and explanations." + + if (focus_areas && focus_areas.length > 0) { + systemPrompt += ` Focus particularly on these areas: ${focus_areas.join(", ")}.` + } + + const messages: PerplexityMessage[] = [ + { + role: "system", + content: systemPrompt, + }, + { + role: "user", + content: `Please provide a comprehensive research report on: ${topic}`, + }, + ] + + const response = await perplexityApi.post("/chat/completions", { + model, + messages, + max_tokens, + temperature, + return_citations, + }) + + const result = response.data.choices[0].message.content + const citations = response.data.citations || [] + const usage = response.data.usage + + let formattedResult = `# Research Report: ${topic}\n\n${result}` + + if (citations.length > 0) { + formattedResult += `\n\n## References\n` + citations.forEach((citation, index) => { + formattedResult += `[${index + 1}] ${citation}\n` + }) + } + + formattedResult += `\n\n---\n_Model: ${model} | Tokens used: ${usage.total_tokens}_` + + return { + content: [ + { + type: "text", + text: formattedResult, + }, + ], + } + } catch (error) { + if (axios.isAxiosError(error)) { + return { + content: [ + { + type: "text", + text: `Perplexity API error: ${error.response?.data?.error?.message || error.message}`, + }, + ], + isError: true, + } + } + throw error + } + }, +) + +// Tool for asking follow-up questions based on previous research +server.tool( + "ask_followup", + { + context: z.string().describe("Previous research context or conversation"), + question: z.string().describe("Follow-up question to ask"), + model: z.enum(["sonar", "sonar-pro"]).optional().default("sonar").describe("Model to use"), + temperature: z.number().min(0).max(2).optional().default(0.3).describe("Temperature for response generation"), + }, + async ({ context, question, model, temperature }) => { + try { + const messages: PerplexityMessage[] = [ + { + role: "system", + content: + "You are a helpful research assistant. Answer follow-up questions based on the provided context and any additional information you can find.", + }, + { + role: "user", + content: `Context: ${context}\n\nQuestion: ${question}`, + }, + ] + + const response = await perplexityApi.post("/chat/completions", { + model, + messages, + temperature, + return_citations: true, + }) + + const result = response.data.choices[0].message.content + const citations = response.data.citations || [] + + let formattedResult = `## Follow-up Answer\n\n${result}` + + if (citations.length > 0) { + formattedResult += `\n\n## Additional Sources\n` + citations.forEach((citation, index) => { + formattedResult += `${index + 1}. ${citation}\n` + }) + } + + return { + content: [ + { + type: "text", + text: formattedResult, + }, + ], + } + } catch (error) { + if (axios.isAxiosError(error)) { + return { + content: [ + { + type: "text", + text: `Perplexity API error: ${error.response?.data?.error?.message || error.message}`, + }, + ], + isError: true, + } + } + throw error + } + }, +) + +// Start the server +const transport = new StdioServerTransport() +await server.connect(transport) +console.error("Perplexity MCP server running on stdio") diff --git a/examples/mcp-servers/perplexity/tsconfig.json b/examples/mcp-servers/perplexity/tsconfig.json new file mode 100644 index 0000000000..cb2ccb537c --- /dev/null +++ b/examples/mcp-servers/perplexity/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "./build", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "build"] +}