An intelligent MCP (Model Context Protocol) server that enables AI assistants to query Perplexity AI with web search specialization - providing up-to-date information with citations, sources, and related questions for research and fact-checking.
This server provides:
- Web-Grounded Answers: Access real-time web information via Perplexity's sonar models
- Citation Tracking: Automatic extraction of sources and citations from responses
- Search Filtering: Domain restrictions, recency filters, and academic mode
- Multi-turn Conversations: Maintain context across queries with session management
- OpenAI MCP Compatibility: Standard
searchandfetchtools for integration
Perplexity's sonar models are built for web search:
- Always Up-to-Date: Searches the web for current information
- Automatic Citations: Every answer includes source citations
- Related Questions: Suggests follow-up questions for deeper exploration
- Cannot Be Disabled: Web search is built into sonar models (this is the killer feature!)
Fine-tune search behavior with powerful options:
- Recency Filter: Limit results by time ('day', 'week', 'month', 'year')
- Domain Filter: Restrict to trusted domains (e.g., ['github.com', 'stackoverflow.com'])
- Country Filter: Geographic location filtering (e.g., 'US', 'GB', 'KR')
- Academic Mode: Search scholarly sources for research purposes
- Result Limits: Control number of search results used (1-20)
- Token Control: Adjust content detail with max tokens per page
- Image Support: Include relevant images in responses
Every response includes structured metadata:
- Citations: Indexed references ([1], [2]) with URLs, titles, snippets
- Sources: Deduplicated list of domains used
- Related Questions: AI-generated follow-up suggestions
- Search Stats: Applied filters and result counts for transparency
Intelligent conversation handling:
- Auto Session Creation: New sessions created automatically
- Context Preservation: Maintain conversation history across queries
- Automatic Cleanup: Expired sessions removed every minute
- Configurable Limits: Control timeout and history size
Defensive Measures:
- Input Validation: Prompt limits (50KB), domain filter limits (20 max), result limits (1-20)
- Session Isolation: Conversation history separated by session ID
- Logging Sanitization: API keys masked, sensitive data excluded from logs
- Boundary Checks: Non-negative numbers, non-empty strings, ISO country codes enforced via Zod schemas
- File-based logging (
logs/perplexity-mcp.log) - Configurable log directory or disable logging for npx/containerized environments
- Detailed query traces for debugging
- Node.js 18 or higher
- Perplexity API key (Get one here)
npx -y github:mnthe/perplexity-mcp-servergit clone https://github.com/mnthe/perplexity-mcp-server.git
cd perplexity-mcp-server
npm install
npm run buildSet your Perplexity API key:
export PERPLEXITY_API_KEY="pplx-your-api-key-here"Required Environment Variable:
export PERPLEXITY_API_KEY="pplx-your-api-key-here"Optional Model Settings:
export PERPLEXITY_MODEL="sonar" # Default: sonar, also: sonar-pro, sonar-reasoningOptional Conversation Settings:
export ENABLE_CONVERSATIONS="true" # Default: true
export SESSION_TIMEOUT="1800" # Seconds, default: 30 minutes
export MAX_HISTORY="20" # Messages per sessionOptional Logging Configuration:
# Default: Console logging to stderr (recommended for npx/MCP usage)
export LOG_TO_STDERR="true" # Default: true (console logging)
# For file-based logging instead:
export LOG_TO_STDERR="false" # Disable console, use file logging
export LOG_DIR="./logs" # Custom log directory (default: ./logs)
# To disable logging completely:
export DISABLE_LOGGING="true"Add to your MCP client configuration:
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"perplexity": {
"command": "npx",
"args": ["-y", "github:mnthe/perplexity-mcp-server"],
"env": {
"PERPLEXITY_API_KEY": "pplx-your-api-key-here",
"PERPLEXITY_MODEL": "sonar",
"ENABLE_CONVERSATIONS": "true"
}
}
}
}Claude Code (.claude.json in project root):
{
"mcpServers": {
"perplexity": {
"command": "npx",
"args": ["-y", "github:mnthe/perplexity-mcp-server"],
"env": {
"PERPLEXITY_API_KEY": "pplx-your-api-key-here",
"PERPLEXITY_MODEL": "sonar"
}
}
}
}Other MCP Clients (Generic stdio):
# Command to run
npx -y github:mnthe/perplexity-mcp-server
# Or direct execution
node /path/to/perplexity-mcp-server/build/index.jsThis MCP server provides 3 core tools for web-grounded information retrieval:
Main interface for web-grounded question answering with rich metadata.
Parameters:
prompt(string, required): The question to ask PerplexitysessionId(string, optional): Conversation session ID for multi-turn conversationssearchOptions(object, optional): Search configurationrecencyFilter(string): 'day', 'week', 'month', or 'year'domainFilter(string[]): List of allowed domains (max 20)maxResults(number): Maximum search results (1-20, default: 5)maxTokensPerPage(number): Max tokens extracted per page (default: 1024)country(string): Geographic filter - 2-3 letter ISO code (e.g., 'US', 'GB', 'KR')returnImages(boolean): Include image URLsreturnRelatedQuestions(boolean): Include follow-up suggestions (default: true)mode(string): 'default' or 'academic' for scholarly sources
How It Works:
- Searches the web for current information
- Analyzes results and generates answer
- Extracts citations and sources
- Suggests related questions
- Returns structured response with metadata
Examples:
# Simple query
query: "What is the capital of France?"
# Current events with recency filter
query: "Latest developments in quantum computing"
searchOptions: { recencyFilter: "week" }
# Domain-restricted search
query: "How to use React hooks"
searchOptions: { domainFilter: ["react.dev", "github.com"] }
# Academic research
query: "Climate change impact studies"
searchOptions: { mode: "academic", recencyFilter: "year" }
# Country-specific search
query: "Latest tech startup news"
searchOptions: { country: "US", recencyFilter: "week" }
# Control response detail
query: "Explain quantum computing"
searchOptions: { maxTokensPerPage: 2048, maxResults: 10 }
# Multi-turn conversation (session auto-created)
query: "What is TypeScript?"
→ Returns: Answer + Session ID: abc123...
# Follow-up (uses context)
query: "What are its main benefits?"
sessionId: "abc123..."
→ Understands we're asking about TypeScriptResponse Includes:
- Answer content with inline citations
- Metadata: citations, sources, related questions, search stats
- Session ID (if conversations enabled)
Search for information using Perplexity. Returns a list of relevant search results following the OpenAI MCP specification for search tools.
Parameters:
query(string, required): Search query
Response Format:
- Array of search results with document IDs, titles, and URLs
- Results are cached for 30 minutes for fetch tool access
Examples:
# Basic search
search: "TypeScript generics tutorial"
→ Returns: [
{ id: "abc123...", title: "TypeScript Handbook - Generics", url: "https://..." },
{ id: "def456...", title: "Understanding TypeScript Generics", url: "https://..." }
]
# Follow-up with fetch
fetch: { id: "abc123..." }
→ Returns: Full document contentFetch the full contents of a search result document by its ID. Follows the OpenAI MCP specification for fetch tools.
Parameters:
id(string, required): The unique identifier for the document from search results
Response Format:
- Full document content with metadata
- Includes original URL and query context
Examples:
# After searching, fetch a specific result
fetch: { id: "abc123..." }
→ Returns: {
id: "abc123...",
title: "TypeScript Handbook - Generics",
text: "Full Perplexity analysis of the topic...",
url: "https://...",
metadata: { query: "TypeScript generics", timestamp: "..." }
}{
"content": "Main answer text...",
"metadata": {
"citations": [
{
"index": 1,
"url": "https://example.com",
"title": "Example Title",
"snippet": "..."
}
],
"relatedQuestions": [
"What is...?",
"How does...?"
],
"sources": [
{
"url": "https://example.com",
"domain": "example.com",
"title": "Example"
}
],
"searchStats": {
"resultsUsed": 5,
"recencyFilter": "week"
}
},
"session": {
"sessionId": "abc123",
"messageCount": 2
}
}Simple, focused architecture without complex agentic loops:
- ❌ No Agentic Loop (Perplexity SDK handles reasoning internally)
- ❌ No Tool Registry
- ❌ No External MCP Client
- ✅ Clean Service → Handler → Server layers
- ✅ Focus on web search metadata (citations, sources, related questions)
MCP Protocol (Server)
↓
Business Logic (Handler)
↓
SDK Wrapper (Service)
↓
Perplexity API
src/
├── config/ # Configuration loading
│ └── index.ts # Environment variable parsing
│
├── types/ # TypeScript type definitions
│ ├── config.ts # Configuration and search types
│ ├── conversation.ts # Session and message types
│ ├── search.ts # Search/fetch result types
│ └── index.ts # Type exports
│
├── schemas/ # Zod validation schemas
│ └── index.ts # Tool input schemas with boundaries
│
├── managers/ # Business logic
│ └── ConversationManager.ts # Session and history management
│
├── services/ # External services
│ └── PerplexityService.ts # Perplexity SDK wrapper
│
├── handlers/ # Tool handlers
│ ├── QueryHandler.ts # Main query logic
│ ├── SearchHandler.ts # OpenAI MCP search
│ └── FetchHandler.ts # OpenAI MCP fetch
│
├── server/ # MCP server
│ └── PerplexityMCPServer.ts # Server orchestration
│
├── utils/ # Utilities
│ ├── Logger.ts # File-based logging
│ ├── ResponseFormatter.ts # Citation/source extraction
│ └── securityLimits.ts # Input validation
│
└── index.ts # Entry point
- Loads environment variables
- Validates required API key
- Provides defaults for optional settings
- PerplexityService: Wraps Perplexity SDK
- Handles message formatting
- Maps searchOptions to API parameters
- Manages API calls with retry logic (2 retries, 60s timeout)
- ConversationManager: Session management
- Creates and tracks sessions
- Stores conversation history
- Automatic cleanup (30-minute timeout)
- Limits history size (20 messages default)
- QueryHandler: Main query processing
- Coordinates session management
- Calls Perplexity service
- Formats responses with metadata
- SearchHandler: OpenAI MCP search implementation
- Extracts citations from responses
- Caches results for fetch
- FetchHandler: OpenAI MCP fetch implementation
- Retrieves cached search results
- ResponseFormatter: Metadata extraction
- Parses citations from response text
- Extracts sources and deduplicates by domain
- Builds search statistics
- Logger: Structured logging with JSON output
- securityLimits: Input validation and sanitization
- PerplexityMCPServer: MCP protocol implementation
- Registers tools with MCP
- Routes tool calls to handlers
- Manages search cache (30-minute TTL)
- Handles errors gracefully
# News from the past day
query: "Latest AI news"
searchOptions: { recencyFilter: "day" }
# Academic papers from this year
query: "Machine learning research"
searchOptions: { mode: "academic", recencyFilter: "year" }# Trust only specific sources
query: "Python best practices"
searchOptions: {
domainFilter: ["python.org", "realpython.com", "github.com"]
}
# Tech news from major outlets
query: "Silicon Valley trends"
searchOptions: {
domainFilter: ["techcrunch.com", "wired.com", "arstechnica.com"],
recencyFilter: "week"
}# Get visual results
query: "Modern UI design trends"
searchOptions: { returnImages: true }
→ Response includes images array with URLsConversations are automatically managed when enabled:
export ENABLE_CONVERSATIONS="true"
export SESSION_TIMEOUT="1800" # 30 minutes
export MAX_HISTORY="20" # Keep last 20 messagesSession Lifecycle:
- Creation: New session created on first query (or if sessionId not provided)
- Usage: Pass sessionId to subsequent queries to maintain context
- Expiration: Sessions expire after timeout period of inactivity
- Cleanup: Expired sessions automatically removed every minute
Example Multi-Turn Conversation:
// First query - creates session
const response1 = await query({
prompt: "What is dependency injection?"
});
// response1.session.sessionId: "abc123..."
// Second query - uses context
const response2 = await query({
prompt: "Show me a practical example",
sessionId: "abc123..."
});
// Perplexity knows we're discussing dependency injection
// Third query - continues context
const response3 = await query({
prompt: "What are the drawbacks?",
sessionId: "abc123..."
});
// Understands we're asking about DI drawbacksThe rich metadata enables advanced workflows:
const response = await query({
prompt: "Latest React 19 features",
searchOptions: { recencyFilter: "month" }
});
// Extract citations for verification
response.metadata.citations.forEach(cite => {
console.log(`[${cite.index}] ${cite.title}: ${cite.url}`);
});
// Follow related questions
response.metadata.relatedQuestions.forEach(q => {
console.log(`Suggested: ${q}`);
});
// Check sources for credibility
response.metadata.sources.forEach(source => {
console.log(`Domain: ${source.domain}`);
});Control how the server logs information:
Default: Console Logging
Logs are sent to stderr by default, making them visible in MCP client logs.
For File-Based Logging:
export LOG_TO_STDERR="false" # Disable console, use files
export LOG_DIR="./logs" # Log directory (default: ./logs)Then check logs:
tail -f logs/perplexity-mcp.logTo Disable All Logging:
export DISABLE_LOGGING="true"npm run buildnpm run watchnpm run devnpm run clean
npm run buildIf the MCP server appears to be "dead" or disconnects unexpectedly:
Check MCP client logs (logs are sent to stderr by default):
- macOS:
~/Library/Logs/Claude/mcp*.log - Windows:
%APPDATA%\Claude\Logs\mcp*.log
Server logs will appear in these files automatically.
If you encounter errors like ENOENT: no such file or directory, mkdir './logs':
This should not happen with default settings (console logging is default).
If you enabled file logging (LOG_TO_STDERR="false"):
Solution: Use a writable log directory:
{
"mcpServers": {
"perplexity": {
"command": "npx",
"args": ["-y", "github:mnthe/perplexity-mcp-server"],
"env": {
"PERPLEXITY_API_KEY": "pplx-your-api-key",
"LOG_TO_STDERR": "false",
"LOG_DIR": "/tmp/perplexity-logs"
}
}
}
}- Verify API key:
echo $PERPLEXITY_API_KEY - Check key validity at Perplexity Settings
- Ensure key has proper permissions
Session not found:
- Session may have expired (check
SESSION_TIMEOUT) - Server may have restarted (sessions are in-memory only)
- Solution: Server creates new session automatically or uses provided sessionId
Context not preserved:
- Verify
ENABLE_CONVERSATIONS="true" - Check
MAX_HISTORYsetting - Ensure using same
sessionIdacross queries
No citations found:
- Perplexity may not have found relevant sources
- Try broader search terms
- Remove or adjust domain filters
- Extend recency filter
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Perplexity AI SDK
- Uses Model Context Protocol
- Inspired by Claude Agent MCP Server