Skip to main content
Glama

search

Analyze coding errors and questions by querying Perplexity. Submit a code snippet for targeted solutions.

Instructions

Search Perplexity for coding help

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesThe error or coding question to analyze
codeNoCode snippet to analyze (optional)
languageNoProgramming language of the code snippet (optional)auto

Implementation Reference

  • 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;
          }
        });
  • 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'],
          },
        },
      ],
    }));
  • 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;
            }
  • 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';
            }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries full responsibility. It merely states the tool's purpose without disclosing behavioral traits like external dependencies, rate limits, or result format.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence that efficiently conveys the tool's purpose with no unnecessary words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite a simple schema, the description fails to explain output format or side effects. For a search tool, this is incomplete; agents need to know what the results look like.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The tool description adds no additional meaning beyond the schema, which is minimally adequate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Search Perplexity for coding help' clearly states the verb (search), resource (Perplexity), and domain (coding help). With no sibling tools, differentiation is not required.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives, nor any exclusions or context. The description only implies it is for coding help but does not elaborate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

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/PoliTwit1984/mcp-perplexity-server'

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