mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
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:
parent
7a6e852248
commit
c6b05658ac
6 changed files with 612 additions and 0 deletions
25
examples/mcp-servers/perplexity/.gitignore
vendored
Normal file
25
examples/mcp-servers/perplexity/.gitignore
vendored
Normal 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/
|
||||
208
examples/mcp-servers/perplexity/README.md
Normal file
208
examples/mcp-servers/perplexity/README.md
Normal 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.
|
||||
14
examples/mcp-servers/perplexity/example-config.json
Normal file
14
examples/mcp-servers/perplexity/example-config.json
Normal 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": []
|
||||
}
|
||||
}
|
||||
}
|
||||
31
examples/mcp-servers/perplexity/package.json
Normal file
31
examples/mcp-servers/perplexity/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
315
examples/mcp-servers/perplexity/src/index.ts
Normal file
315
examples/mcp-servers/perplexity/src/index.ts
Normal 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")
|
||||
19
examples/mcp-servers/perplexity/tsconfig.json
Normal file
19
examples/mcp-servers/perplexity/tsconfig.json
Normal 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"]
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue