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
This commit is contained in:
Roo Code 2025-07-27 13:10:00 +00:00
parent 7a6e852248
commit c6b05658ac
6 changed files with 612 additions and 0 deletions

View file

@ -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/

View file

@ -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.

View file

@ -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": []
}
}
}

View file

@ -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"
}
}

View file

@ -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<PerplexityResponse>("/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<PerplexityResponse>("/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<PerplexityResponse>("/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")

View file

@ -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"]
}