search
Analyze coding errors and questions by querying Perplexity. Submit a code snippet for targeted solutions.
Instructions
Search Perplexity for coding help
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | The error or coding question to analyze | |
| code | No | Code snippet to analyze (optional) | |
| language | No | Programming language of the code snippet (optional) | auto |
Implementation Reference
- src/index.ts:144-561 (handler)The CallToolRequestSchema handler for the 'search' tool. Extracts query/code/language arguments, optionally runs local code analysis, calls Perplexity API with a structured prompt, and returns formatted analysis.
this.server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name !== 'search') { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}` ); } const { query, code, language = 'auto' } = request.params.arguments as { query: string; code?: string; language?: string; }; // Format code block if provided const codeBlock = code ? ` Code to analyze: \`\`\`${language} ${code} \`\`\` ` : ''; try { // Check for custom analysis first let codeAnalysis: CodeAnalysis | null = null; if (code) { codeAnalysis = analyzeCode(code); if (codeAnalysis) { const customAnalysis: CustomAnalysis = { text: `1. Root Cause Analysis ---------------- • Technical Cause: Python is strongly typed and does not allow operations between incompatible types (string and integer) • Common Scenarios: Dictionary values from external sources (like JSON, CSV, or user input) often store numbers as strings • Technical Background: Python dictionary values maintain their original types, requiring explicit conversion for numeric operations 2. Step-by-Step Solution ---------------- • Step 1: Identify the data type issue The 'price' values in the dictionary are strings ('10' and '2') but used in numeric addition • Step 2: Add type conversion Use int() to convert item['price'] to integer before adding to total • Step 3: Add error handling Wrap the conversion in try-except to handle invalid price values • Step 4: Test the solution Verify the total is calculated correctly (12 = 10 + 2) 3. Best Practices for Prevention ---------------- • Design Pattern: Data validation and type conversion at input boundaries • Code Organization: Convert data types when reading from external sources, maintain consistent types in data structures • Common Pitfalls: Assuming dictionary values have the correct type, missing error handling for invalid values • Error Handling: Use try-except blocks to handle type conversion errors, validate data before operations 4. Code Examples ---------------- Before: \`\`\`${language} ${code} \`\`\` After: \`\`\`${language} ${codeAnalysis.fixed} \`\`\` Alternative Approaches: \`\`\`${language} ${codeAnalysis.alternatives} \`\`\`` }; return { content: [ { type: 'text', text: customAnalysis.text, }, ], }; } } const prompt = `As an expert software developer, analyze this coding question and provide a comprehensive solution. CRITICAL FORMATTING INSTRUCTIONS: 1. Use the EXACT section headers and bullet points provided 2. Keep all bullet points and section markers exactly as shown 3. Replace only the text in [brackets] with your analysis 4. Do not add any additional sections or bullet points 5. Do not modify the formatting or structure in any way 6. Start each section with the exact numbered header and dashed line shown QUERY TO ANALYZE: ${query} ${codeBlock} 1. Root Cause Analysis ---------------- • Technical Cause: [Explain the fundamental technical reason for the error] • Common Scenarios: [List typical situations where this error occurs] • Technical Background: [Provide relevant language/framework context] 2. Step-by-Step Solution ---------------- • Step 1: [First step with clear explanation] [Code snippet if applicable] • Step 2: [Second step with clear explanation] [Code snippet if applicable] • Step 3: [Third step with clear explanation] [Code snippet if applicable] • Step 4: [Final verification step] [Working code demonstration] 3. Best Practices for Prevention ---------------- • Design Pattern: [Recommended pattern to prevent this issue] • Code Organization: [How to structure code to avoid this] • Common Pitfalls: [Specific mistakes to watch for] • Error Handling: [How to properly handle edge cases] 4. Code Examples ---------------- Before: \`\`\`python [Code that causes the error] \`\`\` After: \`\`\`python [Fixed version of the code] \`\`\` Alternative Approaches: \`\`\`python [Other valid solutions] \`\`\``; const response = await this.axiosInstance.post<PerplexityResponse>('/chat/completions', { model: 'llama-3.1-sonar-huge-128k-online', messages: [ { role: 'system', content: 'You are an expert software developer focused on debugging and solving coding problems. Always structure your responses exactly as requested.', }, { role: 'user', content: prompt, }, ], }); let analysis = response.data.choices[0]?.message?.content; if (!analysis) { throw new Error('No analysis received from Perplexity'); } // Helper functions for code analysis function analyzeCode(sourceCode: string | undefined): CodeAnalysis | null { if (!sourceCode) return null; // Dictionary string value case const isDictionaryPricePattern = sourceCode.includes("item['price']") || sourceCode.includes('item["price"]') || ( sourceCode.includes('price') && sourceCode.includes('item[') && sourceCode.includes('for') && sourceCode.includes('in') && sourceCode.includes('total') && sourceCode.includes('def calculate_total') && sourceCode.includes('TypeError') ); if (isDictionaryPricePattern) { const totalVar = 'total'; return { fixed: `def calculate_total(items): ${totalVar} = 0 for item in items: try: ${totalVar} = ${totalVar} + int(item['price']) # Convert string to integer before adding except ValueError: raise ValueError(f"Invalid price value: {item['price']}") return ${totalVar} data = [ {'name': 'Book', 'price': '10'}, {'name': 'Pen', 'price': '2'} ] try: total = calculate_total(data) # Result will be 12 print(f"Total: {totalVar}") except ValueError as e: print(f"Error: {e}")`, alternatives: `# Solution 1: Convert during dictionary creation data = [ {'name': 'Book', 'price': int('10')}, {'name': 'Pen', 'price': int('2')} ] def calculate_total(items): total = 0 for item in items: total = total + item['price'] # No conversion needed return total # Solution 2: Use list comprehension with type conversion def calculate_total(items): return sum(int(item['price']) for item in items) # Solution 3: Use map and sum for functional approach def calculate_total(items): return sum(map(lambda x: int(x['price']), items)) # Solution 4: Use list comprehension with validation def calculate_total(items): try: total = sum(int(item['price']) for item in items) return total except ValueError as e: raise ValueError(f"Invalid price value found in items: {e}") except KeyError: raise KeyError("Missing 'price' key in one or more items") except Exception as e: raise Exception(f"Unexpected error calculating total: {e}") # Solution 5: Using dataclasses for better type safety from dataclasses import dataclass from typing import List @dataclass class Item: name: str price: int # Store price as integer to prevent type issues @classmethod def from_string_price(cls, name: str, price_str: str) -> 'Item': try: return cls(name=name, price=int(price_str)) except ValueError: raise ValueError(f"Invalid price value: {price_str}") def calculate_total(items: List[Item]) -> int: return sum(item.price for item in items) # Usage: items = [ Item.from_string_price('Book', '10'), Item.from_string_price('Pen', '2') ] total = calculate_total(items)` }; } // Simple string + int case if (sourceCode.includes('+') && /["'].*?\+.*?\d/.test(sourceCode)) { return { fixed: sourceCode.replace(/["'](\d+)["']\s*\+\s*(\d+)/, 'int("$1") + $2'), alternatives: `# Solution 1: Convert string to int num_str = "123" result = int(num_str) + 456 # Solution 2: Use string formatting num_str = "123" result = f"{num_str}456" # For string concatenation # Solution 3: With error handling def safe_add(str_num, int_num): try: return int(str_num) + int_num except ValueError: raise ValueError("String must be a valid number")` }; } return null; } // Use provided code as the "before" example if available const beforeCode = code || extractCodeExample(analysis, 'incorrect', 'problematic', 'error'); // Generate solutions based on code analysis const afterCode = (codeAnalysis as CodeAnalysis | null)?.fixed || extractCodeExample(analysis, 'correct', 'fixed', 'solution'); const alternativeCode = (codeAnalysis as CodeAnalysis | null)?.alternatives || extractCodeExample(analysis, 'alternative', 'another', 'other'); // Generate response sections const rootCauseSection = formatSection('Root Cause Analysis', { 'Technical Cause': extractTechnicalCause(analysis, query), 'Common Scenarios': extractCommonScenarios(analysis), 'Technical Background': extractTechnicalBackground(analysis) }); const solutionSection = formatSection('Step-by-Step Solution', { 'Steps': extractSteps(analysis) }); const preventionSection = formatSection('Best Practices for Prevention', { 'Design Pattern': extractDesignPattern(analysis), 'Code Organization': extractCodeOrganization(analysis), 'Common Pitfalls': extractCommonPitfalls(analysis), 'Error Handling': extractErrorHandling(analysis) }); const examplesSection = formatCodeExamples(language, beforeCode, afterCode, alternativeCode); // Combine sections const structuredResponse = [ rootCauseSection, solutionSection, preventionSection, examplesSection ].join('\n\n'); // Helper function to format sections function formatSection(title: string, items: Record<string, string>): string { const header = `${title}\n----------------`; const content = Object.entries(items) .map(([key, value]) => { if (key === 'Steps') return value; return `• ${key}: ${value}`; }) .join('\n'); return `${header}\n${content}`; } // Helper function to format code examples function formatCodeExamples(lang: string, before: string, after: string, alternatives: string): string { return `Code Examples ---------------- Before: \`\`\`${lang} ${before} \`\`\` After: \`\`\`${lang} ${after} \`\`\` Alternative Approaches: \`\`\`${lang} ${alternatives} \`\`\``; } // Helper functions to extract information from analysis function extractCodeExample(text: string, ...keywords: string[]): string { // Use string literal for code block markers to avoid escaping issues const codeBlockStart = '```'; const pattern = new RegExp(`(?:${keywords.join('|')}).*?${codeBlockStart}.*?\\n([\\s\\S]*?)${codeBlockStart}`, 'i'); const match = text.match(pattern); return match ? match[1].trim() : '[No code example provided]'; } function extractTechnicalCause(text: string, query: string): string { if (query.includes('TypeError')) { return 'Python is strongly typed and does not allow operations between incompatible types'; } const cause = text.match(/technical(?:\s+cause)?:?\s*([^•\n]+)/i); return cause ? cause[1].trim() : 'Unable to determine cause'; } function extractCommonScenarios(text: string): string { const scenarios = text.match(/common(?:\s+scenarios)?:?\s*([^•\n]+)/i); return scenarios ? scenarios[1].trim() : 'Various scenarios where type mismatches occur'; } function extractTechnicalBackground(text: string): string { const background = text.match(/(?:technical\s+)?background:?\s*([^•\n]+)/i); return background ? background[1].trim() : 'Language-specific type system requirements'; } function extractSteps(text: string): string { const steps = text.match(/step(?:\s+\d+)?:?\s*([^•\n]+)/gi); if (!steps) return '• Step 1: Identify the issue\n• Step 2: Apply the fix\n• Step 3: Test the solution'; return steps.map((step, i) => `• Step ${i + 1}: ${step.replace(/step\s+\d+:?\s*/i, '')}`).join('\n'); } function extractDesignPattern(text: string): string { const pattern = text.match(/(?:design\s+pattern|pattern):?\s*([^•\n]+)/i); return pattern ? pattern[1].trim() : 'Type validation and conversion patterns'; } function extractCodeOrganization(text: string): string { const org = text.match(/(?:code\s+organization|organize):?\s*([^•\n]+)/i); return org ? org[1].trim() : 'Separate data processing from business logic'; } function extractCommonPitfalls(text: string): string { const pitfalls = text.match(/(?:common\s+pitfalls|pitfalls):?\s*([^•\n]+)/i); return pitfalls ? pitfalls[1].trim() : 'Mixing types without proper validation'; } function extractErrorHandling(text: string): string { const handling = text.match(/(?:error\s+handling|handle):?\s*([^•\n]+)/i); return handling ? handling[1].trim() : 'Use try-catch blocks for type conversions'; } return { content: [ { type: 'text', text: structuredResponse, }, ], }; } catch (error) { if (axios.isAxiosError(error)) { return { content: [ { type: 'text', text: `Perplexity API error: ${error.response?.data?.error?.message || error.message}`, }, ], isError: true, }; } throw error; } }); - src/index.ts:117-140 (schema)Tool registration with inputSchema defining 'query' (required string), 'code' (optional string), and 'language' (optional string with default 'auto').
tools: [ { name: 'search', description: 'Search Perplexity for coding help', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'The error or coding question to analyze', }, code: { type: 'string', description: 'Code snippet to analyze (optional)', }, language: { type: 'string', description: 'Programming language of the code snippet (optional)', default: 'auto' } }, required: ['query'], }, }, - src/index.ts:116-142 (registration)ListToolsRequestSchema handler that lists the 'search' tool, registering it for use via MCP.
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'search', description: 'Search Perplexity for coding help', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'The error or coding question to analyze', }, code: { type: 'string', description: 'Code snippet to analyze (optional)', }, language: { type: 'string', description: 'Programming language of the code snippet (optional)', default: 'auto' } }, required: ['query'], }, }, ], })); - src/index.ts:300-417 (helper)Helper function 'analyzeCode' that performs local pattern matching on code snippets (e.g., dictionary price string-to-int conversion) to generate fixed code and alternative solutions.
function analyzeCode(sourceCode: string | undefined): CodeAnalysis | null { if (!sourceCode) return null; // Dictionary string value case const isDictionaryPricePattern = sourceCode.includes("item['price']") || sourceCode.includes('item["price"]') || ( sourceCode.includes('price') && sourceCode.includes('item[') && sourceCode.includes('for') && sourceCode.includes('in') && sourceCode.includes('total') && sourceCode.includes('def calculate_total') && sourceCode.includes('TypeError') ); if (isDictionaryPricePattern) { const totalVar = 'total'; return { fixed: `def calculate_total(items): ${totalVar} = 0 for item in items: try: ${totalVar} = ${totalVar} + int(item['price']) # Convert string to integer before adding except ValueError: raise ValueError(f"Invalid price value: {item['price']}") return ${totalVar} data = [ {'name': 'Book', 'price': '10'}, {'name': 'Pen', 'price': '2'} ] try: total = calculate_total(data) # Result will be 12 print(f"Total: {totalVar}") except ValueError as e: print(f"Error: {e}")`, alternatives: `# Solution 1: Convert during dictionary creation data = [ {'name': 'Book', 'price': int('10')}, {'name': 'Pen', 'price': int('2')} ] def calculate_total(items): total = 0 for item in items: total = total + item['price'] # No conversion needed return total # Solution 2: Use list comprehension with type conversion def calculate_total(items): return sum(int(item['price']) for item in items) # Solution 3: Use map and sum for functional approach def calculate_total(items): return sum(map(lambda x: int(x['price']), items)) # Solution 4: Use list comprehension with validation def calculate_total(items): try: total = sum(int(item['price']) for item in items) return total except ValueError as e: raise ValueError(f"Invalid price value found in items: {e}") except KeyError: raise KeyError("Missing 'price' key in one or more items") except Exception as e: raise Exception(f"Unexpected error calculating total: {e}") # Solution 5: Using dataclasses for better type safety from dataclasses import dataclass from typing import List @dataclass class Item: name: str price: int # Store price as integer to prevent type issues @classmethod def from_string_price(cls, name: str, price_str: str) -> 'Item': try: return cls(name=name, price=int(price_str)) except ValueError: raise ValueError(f"Invalid price value: {price_str}") def calculate_total(items: List[Item]) -> int: return sum(item.price for item in items) # Usage: items = [ Item.from_string_price('Book', '10'), Item.from_string_price('Pen', '2') ] total = calculate_total(items)` }; } // Simple string + int case if (sourceCode.includes('+') && /["'].*?\+.*?\d/.test(sourceCode)) { return { fixed: sourceCode.replace(/["'](\d+)["']\s*\+\s*(\d+)/, 'int("$1") + $2'), alternatives: `# Solution 1: Convert string to int num_str = "123" result = int(num_str) + 456 # Solution 2: Use string formatting num_str = "123" result = f"{num_str}456" # For string concatenation # Solution 3: With error handling def safe_add(str_num, int_num): try: return int(str_num) + int_num except ValueError: raise ValueError("String must be a valid number")` }; } return null; } - src/index.ts:454-537 (helper)Helper functions (formatSection, formatCodeExamples, extractCodeExample, extractTechnicalCause, extractCommonScenarios, extractTechnicalBackground, extractSteps, extractDesignPattern, extractCodeOrganization, extractCommonPitfalls, extractErrorHandling) used to parse and format the Perplexity API response into structured sections.
// Helper function to format sections function formatSection(title: string, items: Record<string, string>): string { const header = `${title}\n----------------`; const content = Object.entries(items) .map(([key, value]) => { if (key === 'Steps') return value; return `• ${key}: ${value}`; }) .join('\n'); return `${header}\n${content}`; } // Helper function to format code examples function formatCodeExamples(lang: string, before: string, after: string, alternatives: string): string { return `Code Examples ---------------- Before: \`\`\`${lang} ${before} \`\`\` After: \`\`\`${lang} ${after} \`\`\` Alternative Approaches: \`\`\`${lang} ${alternatives} \`\`\``; } // Helper functions to extract information from analysis function extractCodeExample(text: string, ...keywords: string[]): string { // Use string literal for code block markers to avoid escaping issues const codeBlockStart = '```'; const pattern = new RegExp(`(?:${keywords.join('|')}).*?${codeBlockStart}.*?\\n([\\s\\S]*?)${codeBlockStart}`, 'i'); const match = text.match(pattern); return match ? match[1].trim() : '[No code example provided]'; } function extractTechnicalCause(text: string, query: string): string { if (query.includes('TypeError')) { return 'Python is strongly typed and does not allow operations between incompatible types'; } const cause = text.match(/technical(?:\s+cause)?:?\s*([^•\n]+)/i); return cause ? cause[1].trim() : 'Unable to determine cause'; } function extractCommonScenarios(text: string): string { const scenarios = text.match(/common(?:\s+scenarios)?:?\s*([^•\n]+)/i); return scenarios ? scenarios[1].trim() : 'Various scenarios where type mismatches occur'; } function extractTechnicalBackground(text: string): string { const background = text.match(/(?:technical\s+)?background:?\s*([^•\n]+)/i); return background ? background[1].trim() : 'Language-specific type system requirements'; } function extractSteps(text: string): string { const steps = text.match(/step(?:\s+\d+)?:?\s*([^•\n]+)/gi); if (!steps) return '• Step 1: Identify the issue\n• Step 2: Apply the fix\n• Step 3: Test the solution'; return steps.map((step, i) => `• Step ${i + 1}: ${step.replace(/step\s+\d+:?\s*/i, '')}`).join('\n'); } function extractDesignPattern(text: string): string { const pattern = text.match(/(?:design\s+pattern|pattern):?\s*([^•\n]+)/i); return pattern ? pattern[1].trim() : 'Type validation and conversion patterns'; } function extractCodeOrganization(text: string): string { const org = text.match(/(?:code\s+organization|organize):?\s*([^•\n]+)/i); return org ? org[1].trim() : 'Separate data processing from business logic'; } function extractCommonPitfalls(text: string): string { const pitfalls = text.match(/(?:common\s+pitfalls|pitfalls):?\s*([^•\n]+)/i); return pitfalls ? pitfalls[1].trim() : 'Mixing types without proper validation'; } function extractErrorHandling(text: string): string { const handling = text.match(/(?:error\s+handling|handle):?\s*([^•\n]+)/i); return handling ? handling[1].trim() : 'Use try-catch blocks for type conversions'; }