Skip to main content
Glama
mnthe

perplexity-mcp-server

by mnthe
README.md
# perplexity-mcp-server

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.

## Purpose

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 `search` and `fetch` tools for integration

## Key Features

### 🌐 Web Search Specialization
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!)

### šŸ” Advanced Search Filtering
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

### šŸ“š Rich Metadata Extraction
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

### šŸ’¬ Session Management
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

### šŸ›”ļø Security for Local Deployment
**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

### šŸ“ Observability
- File-based logging (`logs/perplexity-mcp.log`)
- Configurable log directory or disable logging for npx/containerized environments
- Detailed query traces for debugging

## Prerequisites

- Node.js 18 or higher
- Perplexity API key ([Get one here](https://www.perplexity.ai/settings/api))

## Quick Start

### Installation

#### Option 1: npx (Recommended)
```bash
npx -y github:mnthe/perplexity-mcp-server
```

#### Option 2: From Source
```bash
git clone https://github.com/mnthe/perplexity-mcp-server.git
cd perplexity-mcp-server
npm install
npm run build
```

### Authentication

Set your Perplexity API key:
```bash
export PERPLEXITY_API_KEY="pplx-your-api-key-here"
```

### Configuration

**Required Environment Variable:**
```bash
export PERPLEXITY_API_KEY="pplx-your-api-key-here"
```

**Optional Model Settings:**
```bash
export PERPLEXITY_MODEL="sonar"           # Default: sonar, also: sonar-pro, sonar-reasoning
```

**Optional Conversation Settings:**
```bash
export ENABLE_CONVERSATIONS="true"        # Default: true
export SESSION_TIMEOUT="1800"             # Seconds, default: 30 minutes
export MAX_HISTORY="20"                   # Messages per session
```

**Optional Logging Configuration:**
```bash
# 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"
```

### MCP Client Integration

Add to your MCP client configuration:

**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
  "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):
```json
{
  "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):
```bash
# Command to run
npx -y github:mnthe/perplexity-mcp-server

# Or direct execution
node /path/to/perplexity-mcp-server/build/index.js
```

## Available Tools

This MCP server provides **3 core tools** for web-grounded information retrieval:

### query

Main interface for web-grounded question answering with rich metadata.

**Parameters:**
- `prompt` (string, required): The question to ask Perplexity
- `sessionId` (string, optional): Conversation session ID for multi-turn conversations
- `searchOptions` (object, optional): Search configuration
  - `recencyFilter` (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 URLs
  - `returnRelatedQuestions` (boolean): Include follow-up suggestions (default: true)
  - `mode` (string): 'default' or 'academic' for scholarly sources

**How It Works:**
1. Searches the web for current information
2. Analyzes results and generates answer
3. Extracts citations and sources
4. Suggests related questions
5. Returns structured response with metadata

**Examples:**
```bash
# 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 TypeScript
```

**Response Includes:**
- Answer content with inline citations
- Metadata: citations, sources, related questions, search stats
- Session ID (if conversations enabled)

### search

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:**
```bash
# 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 content
```

### fetch

Fetch 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:**
```bash
# 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: "..." }
  }
```

## Response Format

```json
{
  "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
  }
}
```

## Architecture

### Pattern: Claude Agent MCP Style

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)

### Component Flow

```
MCP Protocol (Server)
    ↓
Business Logic (Handler)
    ↓
SDK Wrapper (Service)
    ↓
Perplexity API
```

### Project Structure

```
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
```

### Component Details

#### Configuration (`config/`)
- Loads environment variables
- Validates required API key
- Provides defaults for optional settings

#### Services (`services/`)
- **PerplexityService**: Wraps Perplexity SDK
  - Handles message formatting
  - Maps searchOptions to API parameters
  - Manages API calls with retry logic (2 retries, 60s timeout)

#### Managers (`managers/`)
- **ConversationManager**: Session management
  - Creates and tracks sessions
  - Stores conversation history
  - Automatic cleanup (30-minute timeout)
  - Limits history size (20 messages default)

#### Handlers (`handlers/`)
- **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

#### Utilities (`utils/`)
- **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

#### Server (`server/`)
- **PerplexityMCPServer**: MCP protocol implementation
  - Registers tools with MCP
  - Routes tool calls to handlers
  - Manages search cache (30-minute TTL)
  - Handles errors gracefully

## Advanced Usage

### Search Options

#### Recency Filtering
```bash
# 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" }
```

#### Domain Filtering
```bash
# 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"
}
```

#### Image Inclusion
```bash
# Get visual results
query: "Modern UI design trends"
searchOptions: { returnImages: true }
→ Response includes images array with URLs
```

### Session Management

Conversations are automatically managed when enabled:

```bash
export ENABLE_CONVERSATIONS="true"
export SESSION_TIMEOUT="1800"  # 30 minutes
export MAX_HISTORY="20"         # Keep last 20 messages
```

**Session Lifecycle:**
1. **Creation**: New session created on first query (or if sessionId not provided)
2. **Usage**: Pass sessionId to subsequent queries to maintain context
3. **Expiration**: Sessions expire after timeout period of inactivity
4. **Cleanup**: Expired sessions automatically removed every minute

**Example Multi-Turn Conversation:**
```typescript
// 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 drawbacks
```

### Metadata Utilization

The rich metadata enables advanced workflows:

```typescript
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}`);
});
```

### Logging Configuration

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:**
```bash
export LOG_TO_STDERR="false"              # Disable console, use files
export LOG_DIR="./logs"                   # Log directory (default: ./logs)
```

Then check logs:
```bash
tail -f logs/perplexity-mcp.log
```

**To Disable All Logging:**
```bash
export DISABLE_LOGGING="true"
```

## Development

### Build
```bash
npm run build
```

### Watch Mode
```bash
npm run watch
```

### Development Mode
```bash
npm run dev
```

### Clean Build
```bash
npm run clean
npm run build
```

## Troubleshooting

### MCP Server Connection Issues

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

### Log Directory Errors

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:
```json
{
  "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"
      }
    }
  }
}
```

### Authentication Errors

1. Verify API key: `echo $PERPLEXITY_API_KEY`
2. Check key validity at [Perplexity Settings](https://www.perplexity.ai/settings/api)
3. Ensure key has proper permissions

### Session Issues

**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_HISTORY` setting
- Ensure using same `sessionId` across queries

### Empty Search Results

**No citations found:**
- Perplexity may not have found relevant sources
- Try broader search terms
- Remove or adjust domain filters
- Extend recency filter

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Acknowledgments

- Built with [Perplexity AI SDK](https://docs.perplexity.ai/)
- Uses [Model Context Protocol](https://github.com/modelcontextprotocol/typescript-sdk)
- Inspired by [Claude Agent MCP Server](https://github.com/mnthe/claude-agent-mcp-server)

## Support

- [Report Issues](https://github.com/mnthe/perplexity-mcp-server/issues)
- [Perplexity Documentation](https://docs.perplexity.ai/)
- [MCP Documentation](https://modelcontextprotocol.io/)

TDQS

A3.7/5.0

Scored across 3 tools

Disambiguation3/5

search and query both perform searches, which could cause confusion, but query is clearly described as more advanced with citations, sources, and multi-turn support, while search is more basic and spec-compliant. fetch is distinct as it retrieves full document contents by ID. The descriptions help differentiate, though some overlap remains.

Naming Consistency4/5

All tool names are single lowercase verbs (search, query, fetch), which is consistent and predictable. There is no verb_noun pattern, but the naming convention is uniform and easy to follow.

Tool Count4/5

Three tools is a minimal but appropriate size for a search-focused server. Each tool serves a clear purpose, and the count is not excessive or overly thin for the apparent scope.

Completeness4/5

The tools cover the core search workflow: basic search, advanced querying with rich metadata and multi-turn support, and fetching full contents. Minor gaps might include a dedicated tool for managing conversation history, but the surface is largely complete for common use cases.

Maintenance

ActivityInactive
ResponsivenessNo issues