# Directory Structure
```
├── .env.example
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
├── src
│ ├── index.ts
│ └── tools.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
```
PERPLEXITY_API_KEY=<YOUR_PERPLEXITY_API_KEY>
```
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# Node.js
node_modules
dist
# Environment variables
.env
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# TypeScript
*.tsbuildinfo
# IDEs and editors
.vscode/
.idea/
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.history/
# MacOS
.DS_Store
# Other
coverage
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# Perplexity MCP Server
## Overview
The Perplexity MCP Server is a Node.js implementation of Anthropic's Model Context Protocol (MCP) that enables Claude to interact with Perplexity's language models. This server provides a secure bridge between Claude and Perplexity AI's capabilities, allowing for enhanced AI interactions through tool use.
## Available Tools
The server currently implements two main tools:
### 1. perplexity_chat
Advanced chat completion tool with full message history support.
```javascript
{
"name": "perplexity_chat",
"description": "Generate a chat completion using Perplexity AI",
"parameters": {
"model": "string (optional) - One of: llama-3.1-sonar-small-128k-online, llama-3.1-sonar-large-128k-online, llama-3.1-sonar-huge-128k-online",
"messages": "array of {role, content} objects - The conversation history",
"temperature": "number (optional) - Sampling temperature between 0-2"
}
}
```
### 2. perplexity_ask
Simplified single-query interface for quick questions.
```javascript
{
"name": "perplexity_ask",
"description": "Send a simple query to Perplexity AI",
"parameters": {
"query": "string - The question or prompt to send",
"model": "string (optional) - One of: llama-3.1-sonar-small-128k-online, llama-3.1-sonar-large-128k-online, llama-3.1-sonar-huge-128k-online"
}
}
```
## Installation
1. Clone the repository:
```bash
git clone https://github.com/yourusername/perplexity-mcp-server.git
cd perplexity-mcp-server
```
2. Install dependencies:
```bash
npm install
```
3. Create `.env` file:
```env
PERPLEXITY_API_KEY=your-api-key-here
```
4. Build the project:
```bash
npm run build
```
## Claude Desktop Configuration
To add this server to Claude Desktop, update your `claude_desktop_config.json`:
```json
{
"mcpServers": {
//more servers...
"perplexity": {
"command": "node",
"args": ["path\\to\\perplexity-mcp-server\\dist\\index.js"],
"env": {
"PERPLEXITY_API_KEY": "YOUR_PERPLEXITY_API_KEY"
}
}
//more servers...
}
}
```
The configuration file is typically located at:
- Windows: `%APPDATA%/Claude/config/claude_desktop_config.json`
- macOS: `~/Library/Application Support/Claude/config/claude_desktop_config.json`
- Linux: `~/.config/Claude/config/claude_desktop_config.json`
## Development
Start the development server with automatic recompilation:
```bash
npm run dev
```
The server uses TypeScript and implements the MCP protocol using the `@modelcontextprotocol/sdk` package.
## Architecture
### Core Components
1. **PerplexityServer Class**
- Implements MCP server protocol
- Handles tool registration and execution
- Manages error handling and server lifecycle
2. **Tools System**
- Modular tool definitions
- Type-safe tool handlers
- Structured input validation
### Technical Details
- Built with TypeScript for type safety
- Uses `@modelcontextprotocol/sdk` for MCP implementation
- Communicates via stdio transport
- Environment-based configuration
## Error Handling
The server implements comprehensive error handling:
- API error reporting
- Invalid tool requests handling
- Connection error management
- Process signal handling
## Dependencies
- `@modelcontextprotocol/sdk`: ^1.0.3
- `dotenv`: ^16.4.7
- `isomorphic-fetch`: ^3.0.0
## Contributing
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
## Security
- API keys are managed through environment variables
- Input validation for all tool parameters
- Error messages are sanitized before output
- Process isolation through MCP protocol
## License
This project is licensed under the ISC License.
## Troubleshooting
Common issues and solutions:
1. **Server Not Found**
- Verify the path in `claude_desktop_config.json` is correct
- Ensure the server is built (`npm run build`)
- Check if Node.js is in your PATH
2. **Authentication Errors**
- Verify your Perplexity API key in .env
- Check if the API key has the required permissions
3. **Tool Execution Errors**
- Verify the tool parameters match the schema
- Check network connectivity
- Review server logs for detailed error messages
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "perplexity-mcp-server",
"version": "1.0.0",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsc -w"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.3",
"dotenv": "^16.4.7",
"isomorphic-fetch": "^3.0.0"
},
"devDependencies": {
"@types/isomorphic-fetch": "^0.0.39",
"@types/node": "^22.10.2",
"typescript": "^5.7.2"
}
}
```
--------------------------------------------------------------------------------
/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 {
ListToolsRequestSchema,
CallToolRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
import { tools, Tool } from "./tools.js";
class PerplexityServer {
private server: Server;
constructor() {
this.server = new Server({
name: "perplexity-mcp-server",
version: "0.1.0"
}, {
capabilities: {
tools: {}
}
});
this.setupHandlers();
this.setupErrorHandling();
}
private setupErrorHandling(): void {
this.server.onerror = (error) => {
console.error("[MCP Error]", error);
};
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
private setupHandlers(): void {
this.server.setRequestHandler(
ListToolsRequestSchema,
async () => ({
tools: tools.map(({ name, description, inputSchema }) => ({
name,
description,
inputSchema
}))
})
);
this.server.setRequestHandler(
CallToolRequestSchema,
async (request) => {
const tool = tools.find(t => t.name === request.params.name) as Tool;
if (!tool) {
return {
content: [{
type: "text",
text: `Unknown tool: ${request.params.name}`
}],
isError: true
};
}
try {
const result = await tool.handler(request.params.arguments || {});
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Perplexity API error: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
}
async run(): Promise<void> {
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);
```
--------------------------------------------------------------------------------
/src/tools.ts:
--------------------------------------------------------------------------------
```typescript
import dotenv from 'dotenv';
import fetch from 'isomorphic-fetch';
dotenv.config();
if (!process.env.PERPLEXITY_API_KEY) {
throw new Error('PERPLEXITY_API_KEY environment variable is required');
}
type ToolHandler<T> = (args: T) => Promise<unknown>;
export interface Tool<T = any> {
name: string;
description: string;
inputSchema: {
type: string;
properties: Record<string, unknown>;
required?: string[];
};
handler: ToolHandler<T>;
}
const PERPLEXITY_API_URL = 'https://api.perplexity.ai/chat/completions';
export const tools: Tool[] = [
{
name: 'perplexity_chat',
description: 'Generate a chat completion using Perplexity AI',
inputSchema: {
type: 'object',
properties: {
model: {
type: 'string',
enum: ['mixtral-8x7b-instruct', 'codellama-34b-instruct', 'sonar-small-chat', 'sonar-small-online'],
description: 'The model to use for completion'
},
messages: {
type: 'array',
items: {
type: 'object',
properties: {
role: {
type: 'string',
enum: ['system', 'user', 'assistant']
},
content: {
type: 'string'
}
},
required: ['role', 'content']
},
description: 'Array of messages in the conversation'
},
temperature: {
type: 'number',
description: 'Sampling temperature (0-2)',
minimum: 0,
maximum: 2
}
},
required: ['messages']
},
handler: async (args: {
model?: string;
messages: Array<{ role: string; content: string }>;
temperature?: number;
}) => {
const response = await fetch(PERPLEXITY_API_URL, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.PERPLEXITY_API_KEY}`
},
body: JSON.stringify({
model: args.model || 'mixtral-8x7b-instruct',
messages: args.messages,
temperature: args.temperature || 0.7
})
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Perplexity API error: ${error}`);
}
const data = await response.json();
return data;
}
},
{
name: 'perplexity_ask',
description: 'Send a simple query to Perplexity AI',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'The question or prompt to send'
},
model: {
type: 'string',
enum: ['llama-3.1-sonar-small-128k-online', 'llama-3.1-sonar-large-128k-online', 'llama-3.1-sonar-huge-128k-online'],
description: 'The model to use for completion'
}
},
required: ['query']
},
handler: async (args: { query: string; model?: string }) => {
const messages = [{ role: 'user', content: args.query }];
const response = await fetch(PERPLEXITY_API_URL, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.PERPLEXITY_API_KEY}`
},
body: JSON.stringify({
model: args.model || 'llama-3.1-sonar-small-128k-online',
messages
})
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Perplexity API error: ${error}`);
}
const data = await response.json();
return data.choices[0].message.content;
}
}
];
```