Skip to main content
Glama

MCP Perplexity Pro

stdio-server.tsโ€ข14.1 kB
#!/usr/bin/env node /** * Stdio-only entry point for Claude Desktop and other stdio-based MCP clients * This bypasses the HTTP launcher and starts directly with stdio transport */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import { configSchema } from './types.js'; import { handleAskPerplexity, handleResearchPerplexity } from './tools/query.js'; import { handleChatPerplexity, handleListChats, handleReadChat, handleStorageStats, } from './tools/chat.js'; import { handleAsyncPerplexity, handleCheckAsync, handleListAsyncJobs } from './tools/async.js'; import { handleListProjects, handleDeleteProject } from './tools/projects.js'; import { getModelSummary } from './models.js'; // Smart project root detection for MCP servers function detectProjectRoot(): string { const cwd = process.cwd(); // If we're running from filesystem root or system directories, // use the user's home directory instead if ( cwd === '/' || cwd.startsWith('/usr/') || cwd.startsWith('/opt/') || cwd.startsWith('/var/') ) { return process.env.HOME || process.env.USERPROFILE || cwd; } // For other cases, use the current working directory return cwd; } // Load configuration - try multiple sources for API key const apiKey = process.env.PERPLEXITY_API_KEY || process.env.API_KEY || process.env.PERPLEXITY_API_TOKEN || ''; const config = { api_key: apiKey, default_model: (process.env.DEFAULT_MODEL as any) || ('sonar-reasoning-pro' as const), project_root: process.env.PROJECT_ROOT || detectProjectRoot(), storage_path: process.env.STORAGE_PATH || '.perplexity', session_id: process.env.SESSION_ID, }; // Validate configuration const validatedConfig = configSchema.parse(config); // Create MCP server const server = new Server( { name: 'mcp-perplexity-pro', version: '1.0.0', }, { capabilities: { tools: {}, progress: true, }, } ); // Register tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'ask_perplexity', description: 'Query Perplexity AI with intelligent model selection for your questions.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Your question or prompt' }, project_name: { type: 'string', description: 'Project name for organizing data (auto-detected if not provided)', }, model: { type: 'string', enum: [ 'sonar', 'sonar-pro', 'sonar-reasoning', 'sonar-reasoning-pro', 'sonar-deep-research', ], description: 'Override default model selection', }, temperature: { type: 'number', minimum: 0, maximum: 1, description: 'Sampling temperature (0.0-1.0, default: 0.2)', }, max_tokens: { type: 'number', minimum: 1, description: 'Maximum response length' }, save_report: { type: 'boolean', description: 'Save response to project directory' }, }, required: ['query'], }, }, { name: 'research_perplexity', description: 'Conduct comprehensive research with Perplexity and save detailed reports.', inputSchema: { type: 'object', properties: { topic: { type: 'string', description: 'Research topic or question' }, project_name: { type: 'string', description: 'Project name for organizing reports (auto-detected if not provided)', }, model: { type: 'string', enum: [ 'sonar', 'sonar-pro', 'sonar-reasoning', 'sonar-reasoning-pro', 'sonar-deep-research', ], description: 'Defaults to sonar-deep-research for comprehensive research', }, save_report: { type: 'boolean', description: 'Save research report (default: true)' }, }, required: ['topic'], }, }, { name: 'chat_perplexity', description: 'Maintain conversations with Perplexity stored in project directory.', inputSchema: { type: 'object', properties: { message: { type: 'string', description: 'Your message' }, project_name: { type: 'string', description: 'Project name for organizing conversations (auto-detected if not provided)', }, chat_id: { type: 'string', description: 'Continue existing conversation' }, title: { type: 'string', description: 'Required for new chat - conversation title' }, model: { type: 'string', enum: [ 'sonar', 'sonar-pro', 'sonar-reasoning', 'sonar-reasoning-pro', 'sonar-deep-research', ], description: 'Override default model', }, temperature: { type: 'number', minimum: 0, maximum: 1, description: '0.0-1.0, default 0.2', }, max_tokens: { type: 'number', minimum: 1, description: 'Maximum response length' }, save_report: { type: 'boolean', description: 'Save conversation to project directory' }, }, required: ['message'], }, }, { name: 'async_perplexity', description: 'Create async jobs for complex queries that may take longer to process.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Your question or prompt' }, model: { type: 'string', enum: [ 'sonar', 'sonar-pro', 'sonar-reasoning', 'sonar-reasoning-pro', 'sonar-deep-research', ], description: 'Override default model', }, project_name: { type: 'string', description: 'Project name for organizing data (auto-detected if not provided)', }, }, required: ['query'], }, }, { name: 'check_async_perplexity', description: 'Check the status of an async job and retrieve results if complete.', inputSchema: { type: 'object', properties: { job_id: { type: 'string', description: 'Job ID returned from async_perplexity' }, }, required: ['job_id'], }, }, { name: 'list_async_jobs', description: 'List all async jobs and their current status.', inputSchema: { type: 'object', properties: { limit: { type: 'number', minimum: 1, maximum: 50, description: 'Number of jobs to return', }, project_name: { type: 'string', description: 'Filter by project (auto-detected if not provided)', }, }, }, }, { name: 'list_chats_perplexity', description: 'List all stored conversations in your project.', inputSchema: { type: 'object', properties: { project_name: { type: 'string', description: 'Project name to list chats from (auto-detected if not provided)', }, }, }, }, { name: 'read_chat_perplexity', description: 'Read the full conversation history from a stored chat.', inputSchema: { type: 'object', properties: { chat_id: { type: 'string', description: 'Chat ID to retrieve' }, project_name: { type: 'string', description: 'Project name (auto-detected if not provided)', }, }, required: ['chat_id'], }, }, { name: 'storage_stats_perplexity', description: 'Get storage usage statistics for your projects.', inputSchema: { type: 'object', properties: { project_name: { type: 'string', description: 'Specific project to analyze (all projects if not provided)', }, }, }, }, { name: 'list_projects_perplexity', description: 'List all projects with their conversation and report counts.', inputSchema: { type: 'object', properties: {}, }, }, { name: 'delete_project_perplexity', description: 'Safely delete a project and all its data after confirmation.', inputSchema: { type: 'object', properties: { project_name: { type: 'string', description: 'Project name to delete' }, confirm: { type: 'boolean', description: 'Must be true to confirm deletion - this action cannot be undone', }, }, required: ['project_name', 'confirm'], }, }, { name: 'model_info_perplexity', description: 'Get information about available Perplexity models and selection guidance.', inputSchema: { type: 'object', properties: {}, }, }, ], }; }); // Register tool call handler server.setRequestHandler(CallToolRequestSchema, async request => { const { name, arguments: args } = request.params; try { switch (name) { case 'ask_perplexity': { const result = await handleAskPerplexity(args as any, validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'research_perplexity': { const result = await handleResearchPerplexity(args as any, validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'chat_perplexity': { const result = await handleChatPerplexity(args as any, validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'list_chats_perplexity': { const result = await handleListChats(validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'read_chat_perplexity': { const result = await handleReadChat(args as any, validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'async_perplexity': { const result = await handleAsyncPerplexity(args as any, validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'check_async_perplexity': { const result = await handleCheckAsync(args as any, validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'list_async_jobs': { const jobArgs = args as any; const result = await handleListAsyncJobs( validatedConfig, jobArgs?.limit, jobArgs?.next_token ); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'storage_stats_perplexity': { const result = await handleStorageStats(validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'list_projects_perplexity': { const result = await handleListProjects(args as any, validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'delete_project_perplexity': { const result = await handleDeleteProject(args as any, validatedConfig); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], }; } case 'model_info_perplexity': { const modelInfo = { available_models: getModelSummary(), default_model: validatedConfig.default_model, automatic_selection: 'Enabled - models selected based on query complexity and requirements', override_capability: 'All tools accept optional "model" parameter to override automatic selection', selection_factors: [ 'Query complexity and length', 'Keywords indicating specific needs (research, analysis, etc.)', 'Task type (facts vs reasoning vs research)', 'Performance vs cost trade-offs', ], }; return { content: [{ type: 'text', text: JSON.stringify(modelInfo, null, 2) }], }; } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); } main().catch(error => { console.error('Failed to start stdio MCP server:', error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cfdude/mcp-perplexity-pro'

If you have feedback or need assistance with the MCP directory API, please join our Discord server