# Directory Structure
```
├── .github
│ └── workflows
│ └── node.js.yml
├── .gitignore
├── .roo
│ └── mcp.json
├── Dockerfile
├── examples
│ └── deep-research.md
├── LICENSE
├── memory-bank
│ ├── activeContext.md
│ ├── productContext.md
│ ├── progress.md
│ ├── systemPatterns.md
│ └── techContext.md
├── package-lock.json
├── package.json
├── README.md
├── smithery.yaml
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
node_modules/
build/
*.log
.env*
.kiro
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# Perplexity MCP Server
An intelligent research assistant powered by Perplexity's specialized AI models. Features automatic query complexity detection to route requests to the most appropriate model for optimal results. Unlike the Official server, it has search capabilities FOR EVERY TASK, essentially
It also forces the agent using the MCP to be specific
## Tools
### 1. Search (Sonar Pro)
Quick search for simple queries and basic information lookup. Best for straightforward questions that need concise, direct answers.
```javascript
const result = await use_mcp_tool({
server_name: "perplexity",
tool_name: "search",
arguments: {
query: "What is the capital of France?",
force_model: false // Optional: force using this model even if query seems complex
}
});
```
### 2. Reason (Sonar Reasoning Pro)
Handles complex, multi-step tasks requiring detailed analysis. Perfect for explanations, comparisons, and problem-solving.
```javascript
const result = await use_mcp_tool({
server_name: "perplexity",
tool_name: "reason",
arguments: {
query: "Compare and contrast REST and GraphQL APIs, explaining their pros and cons",
force_model: false // Optional: force using this model even if query seems simple
}
});
```
### 3. Deep Research (Sonar Deep Research)
Conducts comprehensive research and generates detailed reports. Ideal for in-depth analysis of complex topics.
```javascript
const result = await use_mcp_tool({
server_name: "perplexity",
tool_name: "deep_research",
arguments: {
query: "The impact of quantum computing on cryptography",
focus_areas: [
"Post-quantum cryptographic algorithms",
"Timeline for quantum threats",
"Practical mitigation strategies"
],
force_model: false // Optional: force using this model even if query seems simple
}
});
```
## Setup
1. **Prerequisites**
- Node.js (from [nodejs.org](https://nodejs.org))
- Perplexity API key (from [perplexity.ai/settings/api](https://www.perplexity.ai/settings/api))
- clone the repo somewhere
2. **Configure MCP Settings**
Add to your MCP settings file (location varies by platform):
```json
{
"mcpServers": {
"perplexity": {
"command": "node",
"args": ["/path/to/perplexity-server/build/index.js"],
"env": {
"PERPLEXITY_API_KEY": "YOUR_API_KEY_HERE"
},
"disabled": false,
"autoApprove": []
}
}
}
```
Or use NPX to not have to install it locally (recommended for macos):
```json
{
"mcpServers": {
"perplexity": {
"command": "npx",
"args": [
"-y",
"perplexity-mcp"
],
"env": {
"PERPLEXITY_API_KEY": "your_api_key"
}
}
}
}
```
In case the MCP Client is not able to parse the Perplexity API Key from the
environment using methods like `"${env:PERPLEXITY_API_KEY}"` common in modern
AI Coding Agents (e.g. Kiro), there are two fallback solutions:
**Command-Line Argument**: Pass the API key directly as a command-line argument, and you can even try to see whether "${env:PERPLEXITY_API_KEY}" works in there.
```json
{
"mcpServers": {
"perplexity": {
"command": "node",
"args": [
"/path/to/perplexity-server/build/index.js",
"--api-key",
"your_api_key_here"
],
"disabled": false,
"autoApprove": []
}
}
}
```
**Read an explicit `.env` File**: specify the location of the project `.env` file with the environment variables and API keys for your current project with the `--cwd` command-line argument, and the MCP Server will read the `.env` file from the directory finding the Perplexity API Key from there.
```json
{
"mcpServers": {
"perplexity": {
"command": "node",
"args": [
"/path/to/perplexity-server/build/index.js",
"--cwd",
"/path/to/your/project"
],
"disabled": false,
"autoApprove": []
}
}
}
```
**Priority Order**: Command-line argument > Environment variable > .env file with `--cwd` (path needed)
## Star History
[](https://www.star-history.com/#DaInfernalCoder/perplexity-mcp&Timeline)
```
--------------------------------------------------------------------------------
/.roo/mcp.json:
--------------------------------------------------------------------------------
```json
{
"mcpServers": {}
}
```
--------------------------------------------------------------------------------
/memory-bank/activeContext.md:
--------------------------------------------------------------------------------
```markdown
# Recent Changes
- Removed unused better-sqlite3 import from src/index.ts that was causing build errors
- Successfully built the project after removing the dependency
# Current Status
- Build is now working correctly
- Project is ready for further development
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
type: object
required:
- perplexityApiKey
properties:
perplexityApiKey:
type: string
description: The API key for the Perplexity API.
commandFunction:
# A function that produces the CLI command to start the MCP on stdio.
|-
(config) => ({ command: 'node', args: ['build/index.js'], env: { PERPLEXITY_API_KEY: config.perplexityApiKey } })
```
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
```yaml
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
name: Node.js CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm run build --if-present
- run: npm test
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
# Use Node.js LTS version as the base image
FROM node:18-alpine AS builder
# Set the working directory
WORKDIR /app
# Copy package.json and package-lock.json to the working directory
COPY package.json package-lock.json ./
# Install dependencies
RUN npm install --ignore-scripts
# Copy the rest of the application code to the working directory
COPY . .
# Build the TypeScript files
RUN npm run build
# Use a clean Node.js image for the production environment
FROM node:18-alpine AS release
# Set the working directory
WORKDIR /app
# Copy built files and node_modules from the builder stage
COPY --from=builder /app/build ./build
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
# Set environment variables (replace YOUR_API_KEY_HERE with the actual key)
ENV PERPLEXITY_API_KEY=YOUR_API_KEY_HERE
# Expose the port the app runs on
EXPOSE 3000
# Command to run the application
ENTRYPOINT ["node", "build/index.js"]
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "perplexity-mcp",
"version": "0.2.3",
"description": "MCP server providing intelligent search, reasoning, and research capabilities powered by Perplexity's specialized AI models",
"type": "module",
"bin": "./build/index.js",
"files": [
"build",
"README.md",
"LICENSE"
],
"scripts": {
"build": "tsc && chmod +x build/index.js",
"watch": "tsc --watch",
"start": "node build/index.js",
"inspector": "npx @modelcontextprotocol/inspector build/index.js",
"test": "tsc --noEmit",
"prepublishOnly": "npm run build"
},
"dependencies": {
"@modelcontextprotocol/sdk": "0.6.0",
"axios": "^1.7.9",
"dotenv": "^17.2.3",
"minimist": "^1.2.8"
},
"devDependencies": {
"@types/minimist": "^1.2.5",
"@types/node": "^20.19.19",
"typescript": "^5.3.3"
},
"keywords": [
"mcp",
"perplexity",
"ai",
"search",
"reasoning",
"research",
"sonar-pro",
"sonar-reasoning-pro",
"sonar-deep-research",
"model-context-protocol"
],
"author": "MCP Contributors",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/DaInfernalCoder/researcher-mcp"
},
"engines": {
"node": ">=18.0.0"
}
}
```
--------------------------------------------------------------------------------
/memory-bank/progress.md:
--------------------------------------------------------------------------------
```markdown
# Progress
## What Works
- Server initialization and configuration
- MCP protocol implementation
- Intelligent query routing system
- Three specialized tools implemented:
- search (Sonar Pro)
- reason (Sonar Reasoning Pro)
- deep_research (Sonar Deep Research)
- Automatic model selection based on query complexity
- Manual model override with force_model flag
- Build process with TypeScript
- Direct Node.js execution
## Recent Updates
1. **Core Implementation**
- [x] Removed chat history and SQLite
- [x] Implemented query complexity detection
- [x] Added specialized tool handlers
- [x] Updated server version to 0.2.0
2. **Documentation**
- [x] Updated README with new tools
- [x] Added model selection documentation
- [x] Updated setup instructions
- [x] Added usage examples
3. **Memory Bank**
- [x] Updated productContext.md
- [x] Updated systemPatterns.md
- [x] Updated techContext.md
- [x] Updated activeContext.md
- [x] Updated progress.md
## What's Next
1. **Testing**
- [ ] Test query complexity detection
- [ ] Verify model selection logic
- [ ] Test each specialized tool
- [ ] Validate force_model override
2. **Potential Improvements**
- [ ] Expand complexity detection patterns
- [ ] Add more specialized prompts per model
- [ ] Enhance error messages
- [ ] Add query preprocessing
## Current Status
- Streamlined architecture with three specialized tools
- Intelligent query routing system implemented
- All documentation updated
- Ready for testing and validation
```
--------------------------------------------------------------------------------
/memory-bank/techContext.md:
--------------------------------------------------------------------------------
```markdown
# Technical Context
## Technologies Used
### Core Technologies
- **Node.js**: Runtime environment
- **TypeScript**: Type-safe development
- **MCP SDK**: Server implementation
- **Perplexity API**: AI model integration
### Dependencies
- **@modelcontextprotocol/sdk (0.6.0)**: MCP protocol implementation
- **axios (^1.7.9)**: API communication
### Development Dependencies
- **@types/node (^20.11.24)**: Node.js type definitions
- **typescript (^5.3.3)**: TypeScript compiler
## AI Models
1. **Sonar Pro**
- Quick information retrieval
- Simple query processing
- Direct answers
2. **Sonar Reasoning Pro**
- Complex analysis
- Multi-step reasoning
- Detailed explanations
3. **Sonar Deep Research**
- Comprehensive research
- In-depth analysis
- Structured reports
## Development Setup
1. **Build Process**
- TypeScript compilation
- Watch mode for development
- Direct Node.js execution
2. **Testing**
- Type checking
- MCP Inspector integration
- Manual tool testing
3. **Environment Variables**
- PERPLEXITY_API_KEY (required)
## Technical Constraints
1. **API Requirements**
- Valid Perplexity API key
- Rate limits and quotas
- API availability
2. **Runtime Requirements**
- Node.js environment
- Local dependencies
- MCP protocol support
3. **Query Processing**
- Pattern-based analysis
- Model selection logic
- Response formatting
## Local Setup
1. **Installation**
- Clone repository
- Install dependencies
- Build TypeScript
- Configure API key
2. **Configuration**
- Environment setup
- MCP settings
- Model selection rules
3. **Usage**
- Direct Node.js execution
- Tool selection
- Query complexity handling
```
--------------------------------------------------------------------------------
/memory-bank/systemPatterns.md:
--------------------------------------------------------------------------------
```markdown
# System Patterns
## Architecture
The Perplexity Server implements a streamlined MCP architecture focused on intelligent query routing:
1. **Server Initialization**
- Creates MCP server instance
- Sets up API client with authentication
- Registers specialized tool handlers
2. **Query Analysis System**
- Pattern-based complexity detection
- Intelligent model selection
- Override capabilities via force_model flag
3. **Tool Specialization**
- search: Quick lookups (Sonar Pro)
- reason: Complex analysis (Sonar Reasoning Pro)
- deep_research: Comprehensive research (Sonar Deep Research)
4. **API Integration**
- Communicates with Perplexity API
- Model-specific request handling
- Structured response formatting
## Key Technical Decisions
1. **Intelligent Routing**
- Pattern-based query analysis
- Automatic model selection
- Manual override capability
- Optimized response quality
2. **Model Specialization**
- Sonar Pro: Simple queries
- Sonar Reasoning Pro: Complex analysis
- Sonar Deep Research: In-depth research
3. **TypeScript Implementation**
- Strong typing for tool schemas
- Enhanced code reliability
- Better developer experience
4. **Direct Execution**
- Node.js runtime
- Local dependency management
- Simple configuration
5. **Error Handling**
- Detailed error messages
- API error conversion
- Graceful shutdown
- Query validation
## Communication Patterns
1. **MCP Protocol**
- Stdio transport
- Structured request/response
- Tool schema definitions
- Input validation
2. **API Communication**
- RESTful endpoints
- Model-specific formatting
- Bearer authentication
- Error handling
3. **Query Processing**
- Complexity analysis
- Model selection
- Response formatting
- Context management
```
--------------------------------------------------------------------------------
/memory-bank/productContext.md:
--------------------------------------------------------------------------------
```markdown
# Product Context
## Purpose
The Perplexity Server is an MCP (Model Context Protocol) server that provides intelligent research and information retrieval through specialized AI models. It automatically analyzes query complexity to route requests to the most appropriate Perplexity AI model, ensuring optimal responses for different types of queries.
## Problems Solved
1. **Query Optimization**: Automatically selects the best AI model based on query complexity
2. **Information Access**: Provides quick answers to simple questions using Sonar Pro
3. **Complex Analysis**: Handles multi-step reasoning and detailed explanations with Sonar Reasoning Pro
4. **Deep Research**: Conducts comprehensive research and analysis using Sonar Deep Research
5. **Efficiency**: Streamlines information retrieval by matching query complexity to model capabilities
## How It Works
The server analyzes queries and routes them to three specialized tools:
1. **search (Sonar Pro)**
- Quick information lookup
- Simple factual queries
- Direct, concise answers
- Best for straightforward questions
2. **reason (Sonar Reasoning Pro)**
- Complex problem-solving
- Multi-step analysis
- Comparisons and trade-offs
- Detailed explanations
- Best for how/why questions
3. **deep_research (Sonar Deep Research)**
- Comprehensive research
- In-depth analysis
- Multiple perspectives
- Source references
- Best for complex topics
The server features intelligent query analysis that automatically routes requests to the appropriate model based on complexity patterns, with manual override available through the force_model flag.
## Key Features
1. **Automatic Model Selection**
- Pattern-based complexity detection
- Intelligent routing to appropriate model
- Override capability for manual control
2. **Specialized Response Formats**
- Concise answers for simple queries
- Structured analysis for complex questions
- Comprehensive reports for research topics
3. **Query Analysis**
- Keyword and pattern detection
- Complexity assessment
- Context consideration
- Model-specific formatting
```
--------------------------------------------------------------------------------
/examples/deep-research.md:
--------------------------------------------------------------------------------
```markdown
# Deep Research Tool
The `deep_research` tool allows you to conduct comprehensive, in-depth research on complex topics using Perplexity's sonar-deep-research model. This tool is designed for situations where you need thorough analysis and detailed information beyond what a standard search can provide.
## Example Usage
```javascript
const result = await use_mcp_tool({
server_name: "perplexity-ask",
tool_name: "deep_research",
arguments: {
query: "The impact of quantum computing on cryptography",
focus_areas: [
"Post-quantum cryptographic algorithms",
"Timeline for quantum threats to current encryption",
"Practical mitigation strategies for organizations"
]
}
});
console.log(result);
```
## Parameters
- `query` (required): The research query or topic to investigate in depth
- `focus_areas` (optional): An array of specific aspects or areas to focus on during research
- `output_format` (optional): Format of the response (text or dropdown), defaults to "text"
## Example Response
The response will include a comprehensive analysis with:
1. Background and context
2. Key concepts and definitions
3. Current state of knowledge
4. Different perspectives and approaches
5. Recent developments and breakthroughs
6. Practical applications or implications
7. Challenges and limitations
8. Future directions
9. Expert opinions and consensus views
10. References to authoritative sources
## When to Use
Use the `deep_research` tool when:
- You need comprehensive information on complex topics
- Standard search results aren't providing enough depth
- You want analysis that considers multiple perspectives
- You need information on cutting-edge or rapidly evolving fields
- You're looking for expert consensus and authoritative sources
## Comparison with Search Tool
While the `search` tool is great for quick answers and general information, the `deep_research` tool provides:
- Greater depth and breadth of analysis
- More comprehensive coverage of different perspectives
- Better handling of complex, nuanced topics
- More thorough citation of sources and expert opinions
- Ability to focus on specific aspects of a broader topic
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
McpError,
ErrorCode,
} from "@modelcontextprotocol/sdk/types.js";
import axios from "axios";
import minimist from "minimist";
import * as fs from "fs";
import * as path from "path";
// Parse command-line arguments
const args = minimist(process.argv.slice(2), {
string: ['api-key', 'cwd'],
alias: {
'api-key': 'apiKey'
}
});
// Extracts PERPLEXITY_API_KEY from an .env file content
function extractApiKey(content: string): string | null {
const lines = content.split('\n');
for (const line of lines) {
const trimmed = line.trim();
// Skip empty lines and comments
if (!trimmed || trimmed.startsWith('#')) {
continue;
}
// Look specifically for PERPLEXITY_API_KEY
if (trimmed.startsWith('PERPLEXITY_API_KEY=')) {
const value = trimmed.substring('PERPLEXITY_API_KEY='.length).trim();
// Remove surrounding quotes if present
let clean = value;
if ((clean.startsWith('"') && clean.endsWith('"')) ||
(clean.startsWith("'") && clean.endsWith("'"))) {
clean = clean.slice(1, -1);
}
if (clean && clean.length > 0) {
return clean;
}
}
}
return null;
}
// Read .env file from specified directory and extract PERPLEXITY_API_KEY.
// Returns null if file does not exist or does not contain PERPLEXITY_API_KEY.
function readEnvFile(dir: string): string | null {
const envPath = path.join(dir, '.env');
if (!fs.existsSync(envPath)) {
return null;
}
try {
const content = fs.readFileSync(envPath, 'utf8');
return extractApiKey(content);
} catch (error) {
return null;
}
}
/**
* Resolves API key using simple three-step logic. Priority:
* 1. Command-line argument (--api-key),
* 2. Environment variable (PERPLEXITY_API_KEY),
* 3. .env file with --cwd (fallback option)
*/
function getApiKey(): string {
// 1. Command-line argument (highest priority)
if (args['api-key']) {
sanitizeArgs();
return args['api-key'];
}
// 2. Environment variable
if (process.env.PERPLEXITY_API_KEY) {
return process.env.PERPLEXITY_API_KEY;
}
// 3. .env file (with --cwd PROJECT_DIR specified)
if (args.cwd) {
const key = readEnvFile(args.cwd);
if (key) return key;
}
// Error if none found
throw new Error(`
Perplexity API key is required. Please provide it using one of these three methods (in priority order):
1. Environment variable (PREFERRED): PERPLEXITY_API_KEY=your-api-key
2. Command-line argument: --api-key your-api-key
3. .env file with --cwd: Create .env file with PERPLEXITY_API_KEY=your-api-key and specify directory
Examples:
• Environment variable: export PERPLEXITY_API_KEY="your-key-here" && npx perplexity-mcp
• Command-line: npx perplexity-mcp --api-key "your-key-here"
• .env file: npx perplexity-mcp --cwd /path/to/project
The environment variable method is preferred and most secure for production use.
Note: Automatic .env file discovery has been removed - you must use --cwd to specify the directory.
`.trim());
}
// Overwrites API key arguments in process.argv to minimize exposure in process list
// This reduces the window where the API key is visible to other processes
function sanitizeArgs(): void {
for (let i = 0; i < process.argv.length; i++) {
if (process.argv[i] === '--api-key' && i + 1 < process.argv.length) {
process.argv[i + 1] = '***REDACTED***';
} else if (process.argv[i].startsWith('--api-key=')) {
process.argv[i] = '--api-key=***REDACTED***';
}
}
}
const PERPLEXITY_API_KEY = getApiKey();
class PerplexityServer {
private server: Server;
private axiosInstance;
constructor() {
this.server = new Server(
{
name: "perplexity-server",
version: "0.2.3",
},
{
capabilities: {
tools: {},
},
}
);
this.axiosInstance = axios.create({
baseURL: "https://api.perplexity.ai",
headers: {
"Authorization": `Bearer ${PERPLEXITY_API_KEY}`,
"Content-Type": "application/json",
},
});
this.setupToolHandlers();
// Error handling
this.server.onerror = (error) => console.error("[MCP Error]", error);
process.on("SIGINT", async () => {
await this.server.close();
process.exit(0);
});
}
/**
* Checks if a query contains specific details needed for accurate responses
* Returns array of missing details, empty if sufficient
*/
private detectMissingDetails(query: string): string[] {
const queryLower = query.toLowerCase();
const missingDetails: string[] = [];
// Check for error messages, stack traces, or error-related terms
const hasErrors = /error|exception|failure|crash|traceback|stack trace|failed/i.test(query) ||
/Error:|Exception:|at\s+\w+\.\w+/i.test(query);
// Check for code snippets (common patterns)
const hasCode = /```|function\s+\w+|const\s+\w+|let\s+\w+|var\s+\w+|import\s+|require\(|\.\w+\(/i.test(query) ||
/[a-z]\w*\([^)]*\)/i.test(query) || // function calls
/\w+\.\w+\s*=/i.test(query); // property assignments
// Check for version numbers
const hasVersions = /\d+\.\d+(\.\d+)?/i.test(query) || /version\s*\d+|v\d+/i.test(query);
// Check for specific API/function names (camelCase, PascalCase, or names with dots)
const hasSpecificNames = /[A-Z][a-z]+[A-Z]|\.\w+\(|::\w+|api\.|sdk\./i.test(query);
// Check for logs or console output
const hasLogs = /log|console|output|print|trace|debug/i.test(query) &&
(query.length > 100 || /\n/.test(query));
// Check for environment/platform details
const hasEnvironment = /node|python|java|javascript|typescript|react|vue|angular|linux|windows|macos|ubuntu|docker/i.test(queryLower);
// Determine missing details for technical queries
if (hasErrors && !hasCode && !hasLogs) {
missingDetails.push("code snippets showing the error context");
missingDetails.push("relevant logs or stack traces");
}
if (!hasVersions && (hasCode || hasEnvironment)) {
missingDetails.push("version numbers (framework, library, runtime versions)");
}
if (!hasSpecificNames && (hasCode || hasErrors)) {
missingDetails.push("exact function names, API endpoints, or library names");
}
if (hasErrors && !hasEnvironment) {
missingDetails.push("environment details (OS, runtime version, framework)");
}
// If query seems technical/problem-solving but lacks specifics
const seemsTechnical = hasErrors || hasCode || /problem|issue|bug|fix|solution|how to|why|debug/i.test(queryLower);
if (seemsTechnical && missingDetails.length === 0) {
// Still check if it could be more specific
if (query.length < 50 && !hasCode && !hasErrors) {
missingDetails.push("specific error messages or code snippets");
missingDetails.push("exact terminology and context");
}
}
return missingDetails;
}
/**
* Determines the complexity of a query to choose the appropriate model
*/
private determineQueryComplexity(query: string): "simple" | "complex" | "research" {
// Check for research indicators
const researchIndicators = [
"analyze", "research", "investigate", "study", "examine", "explore",
"comprehensive", "detailed", "in-depth", "thorough",
"compare and contrast", "evaluate", "assess"
];
// Check for complex reasoning indicators
const complexIndicators = [
"how", "why", "what if", "explain", "solve", "steps to",
"difference between", "compare", "which is better",
"pros and cons", "advantages", "disadvantages"
];
const query_lower = query.toLowerCase();
// Check for research patterns
if (researchIndicators.some(indicator => query_lower.includes(indicator))) {
return "research";
}
// Check for complex patterns
if (complexIndicators.some(indicator => query_lower.includes(indicator))) {
return "complex";
}
// Default to simple if no complex/research patterns found
return "simple";
}
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "search",
description: "Quick search for simple queries using Perplexity's Sonar Pro model. Best for straightforward questions and basic information lookup.",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "The search query or question. IMPORTANT: Be extremely specific and include all relevant details:\n- Include exact error messages, logs, and stack traces if applicable\n- Provide exact terminology, function names, API names, version numbers\n- Include relevant code snippets showing the problem or context\n- Specify platform, OS, framework versions, and environment details\n- Mention any attempted solutions or workarounds\n- Provide context about what you're trying to achieve\n\nThe more specific details you include, the more accurate and helpful the answer will be.\nIf you don't have enough specific information, prompt the user to provide it before using this tool."
},
force_model: {
type: "boolean",
description: "Optional: Force using this model even if query seems complex",
default: false
}
},
required: ["query"]
}
},
{
name: "reason",
description: "Handles complex, multi-step tasks using Perplexity's Sonar Reasoning Pro model. Best for explanations, comparisons, and problem-solving.",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "The complex query or task to reason about. IMPORTANT: Be extremely specific and include all relevant details:\n- Include exact error messages, logs, and stack traces if applicable\n- Provide exact terminology, function names, API names, version numbers\n- Include relevant code snippets showing the problem or context\n- Specify platform, OS, framework versions, and environment details\n- Mention any attempted solutions or workarounds\n- Provide context about what you're trying to achieve\n- Include relevant data structures, configurations, or inputs\n\nThe more specific details you include, the more accurate and helpful the answer will be.\nIf you don't have enough specific information, prompt the user to provide it before using this tool."
},
force_model: {
type: "boolean",
description: "Optional: Force using this model even if query seems simple/research-oriented",
default: false
}
},
required: ["query"]
}
},
{
name: "deep_research",
description: "Conducts in-depth analysis and generates detailed reports using Perplexity's Sonar Deep Research model. Best for comprehensive research topics.",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "The research topic or question to investigate in depth. IMPORTANT: Be extremely specific and include all relevant details:\n- Include exact error messages, logs, and stack traces if applicable\n- Provide exact terminology, function names, API names, version numbers\n- Include relevant code snippets showing the problem or context\n- Specify platform, OS, framework versions, and environment details\n- Mention any attempted solutions or workarounds\n- Provide context about what you're trying to achieve\n- Include relevant data structures, configurations, or inputs\n- Specify the scope, constraints, or specific requirements\n\nThe more specific details you include, the more accurate and helpful the answer will be.\nIf you don't have enough specific information, prompt the user to provide it before using this tool."
},
focus_areas: {
type: "array",
items: {
type: "string"
},
description: "Optional: Specific aspects or areas to focus on"
},
force_model: {
type: "boolean",
description: "Optional: Force using this model even if query seems simple",
default: false
}
},
required: ["query"]
}
}
]
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const { query, force_model = false } = request.params.arguments as {
query: string;
force_model?: boolean;
};
// Determine which model to use based on query complexity
let selectedTool = request.params.name;
if (!force_model && selectedTool === "search") {
const complexity = this.determineQueryComplexity(query);
if (complexity === "complex") {
selectedTool = "reason";
} else if (complexity === "research") {
selectedTool = "deep_research";
}
}
let model: string;
let prompt: string;
switch (selectedTool) {
case "search": {
model = "sonar-pro";
prompt = `You are answering a query that contains specific details like error messages, logs, code snippets, exact terminology, version numbers, and context. Use all provided details to give the most accurate answer possible.
Query: ${query}
Provide a clear, concise answer that directly addresses the specific details in the query.`;
break;
}
case "reason": {
model = "sonar-reasoning-pro";
prompt = `You are answering a query that contains specific details like error messages, logs, code snippets, exact terminology, version numbers, and context. Carefully analyze all provided details to give the most accurate and helpful answer.
Query: ${query}
Provide a detailed explanation and analysis that:
1. Addresses the specific details provided (errors, logs, code, versions, etc.)
2. Includes step-by-step reasoning based on the actual context
3. Identifies key considerations relevant to the specific situation
4. Provides relevant examples matching the described scenario
5. Offers practical implications based on the exact details provided
6. Suggests potential alternatives or solutions tailored to the specific context`;
break;
}
case "deep_research": {
model = "sonar-deep-research";
const { focus_areas = [] } = request.params.arguments as { focus_areas?: string[] };
prompt = `You are answering a research query that contains specific details like error messages, logs, code snippets, exact terminology, version numbers, and context. Use all provided details to conduct the most accurate and comprehensive research.
Research Query: ${query}`;
if (focus_areas.length > 0) {
prompt += `\n\nFocus areas:\n${focus_areas.map((area, i) => `${i + 1}. ${area}`).join('\n')}`;
}
prompt += `\n\nProvide a detailed analysis that:
1. Incorporates and addresses all specific details provided (errors, logs, code, versions, configurations, etc.)
2. Provides background and context relevant to the specific scenario described
3. Defines key concepts and terminology matching the exact terms used
4. Presents the current state of knowledge relevant to the specific problem or topic
5. Explores different perspectives applicable to the described situation
6. Covers recent developments that relate to the specific details provided
7. Discusses practical applications relevant to the exact context
8. Identifies challenges and limitations specific to the scenario
9. Suggests future directions or solutions based on the specific details
10. Includes expert opinions and references to sources that address the specific issue
11. Tailors all recommendations to the exact technical context, versions, and environment described`;
break;
}
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
const response = await this.axiosInstance.post("/chat/completions", {
model,
messages: [{ role: "user", content: prompt }],
});
// Detect what details were missing from the original query
const missingDetails = this.detectMissingDetails(query);
// response.data can have a string[] .citations
// these are referred to in the return text as numbered citations e.g. [1]
const sourcesText = response.data.citations
? `\n\n## Sources\nPlease keep the numbered citations inline.\n${response.data.citations
.map((c: string, i: number) => `${i + 1}: ${c}`)
.join("\n")}`
: "";
// Add note about missing details if any were detected
let responseText = response.data.choices[0].message.content + sourcesText;
if (missingDetails.length > 0) {
const missingList = missingDetails.map((detail, i) => `${i + 1}. ${detail}`).join('\n');
responseText += `\n\n---\n\n**Note**: I didn't have the following details which would help provide a more specific and accurate answer:\n\n${missingList}\n\nIf you'd like a more precise response, please provide these details and ask again.`;
}
return {
content: [{
type: "text",
text: responseText,
}]
};
} catch (error) {
if (axios.isAxiosError(error)) {
throw new McpError(
ErrorCode.InternalError,
`Perplexity API error: ${error.response?.data?.error?.message || error.message}`
);
}
throw error;
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("Perplexity MCP server running on stdio");
}
}
const server = new PerplexityServer();
server.run().catch(console.error);
```