MCP HubMCP Hub
PoliTwit1984

second-opinion-mcp-server

by: PoliTwit1984

second opinion mcp server

12created 09/01/2025
Visit
second-opinion

📌Overview

Purpose: The Second Opinion MCP Server aims to provide AI-powered assistance for coding problems by leveraging insights from multiple AI sources.

Overview: This server integrates Google’s Gemini AI, Stack Overflow’s accepted answers, and Perplexity AI to deliver context-rich solutions for coding challenges, enhancing the way developers troubleshoot and improve their code.

Key Features:

  • Comprehensive Solutions: Offers detailed answers for coding issues by aggregating information from various authoritative sources, helping users quickly understand and resolve problems.

  • Automatic Language Detection: Identifies programming languages based on file extensions, streamlining the input process and ensuring accurate context for solutions.

  • Code Snippet Extraction: Extracts and formats relevant code snippets, making it easier for developers to see the context of their queries.

  • Markdown Report Generation: Creates markdown reports for solutions, providing users with well-organized documentation of the problem and the proposed fixes.

  • Git-Aware Context Gathering: Utilizes file context from Git repositories, allowing for better understanding of the situation based on historical code changes.


Second Opinion MCP Server

An MCP server that provides AI-powered assistance for coding problems by combining insights from:

  • Google's Gemini AI
  • Stack Overflow accepted answers
  • Perplexity AI analysis

Features

  • Detailed solutions for coding problems with context from multiple sources
  • Automatic language detection from file extensions
  • Code snippet extraction and formatting
  • Markdown report generation for solutions
  • Git-aware file context gathering

Setup

  1. Install dependencies:
    npm install
    
  2. Build the server:
    npm run build
    
  3. Configure environment variables in MCP settings:
    {
      "mcpServers": {
        "second-opinion": {
          "command": "node",
          "args": ["/path/to/second-opinion-server/build/index.js"],
          "env": {
            "GEMINI_API_KEY": "your-gemini-api-key",
            "PERPLEXITY_API_KEY": "your-perplexity-api-key",
            "STACK_EXCHANGE_KEY": "your-stack-exchange-key"
          }
        }
      }
    }
    

Required environment variables:

  • GEMINI_API_KEY: Google's Gemini AI API key
  • PERPLEXITY_API_KEY: Perplexity AI API key
  • STACK_EXCHANGE_KEY: Stack Exchange API key (optional, uses anonymous access if not provided)

Usage

The server provides a single tool:

get_second_opinion

Get AI-powered insights and solutions for coding problems.

Input Schema:

{
  "goal": "string (required) - What you're trying to accomplish",
  "error": "string (optional) - Any error messages you're seeing",
  "code": "string (optional) - Relevant code context",
  "solutionsTried": "string (optional) - What solutions you've already tried",
  "filePath": "string (optional) - Path to the file with the issue"
}

Example Input:

{
  "goal": "Fix React useEffect cleanup error",
  "error": "React Hook useEffect has a missing dependency: 'socket'. Either include it in the dependency array, or remove the dependency array  react-hooks/exhaustive-deps",
  "code": "useEffect(() => {\n  socket.on('message', handleMessage);\n  return () => socket.off('message');\n}, []);",
  "solutionsTried": "I tried adding socket to the dependency array but then it causes infinite re-renders"
}

Example Response

Understanding the Problem

The core issue lies in how React's useEffect hook manages side effects and their dependencies. When you provide an empty dependency array ([]), the effect runs once when the component mounts, and its cleanup runs when the component unmounts.

However, the socket object used within the effect creates a closure where the handleMessage function and cleanup function (socket.off('message')) depend on the current value of socket. If socket changes (e.g., due to reconnection), the effect may use an outdated socket instance.

Common Issues
  • Stale Closures: The event handler might be working with an outdated socket object
  • Incorrect Cleanup: socket.off('message') might not remove the intended listener
  • Memory Leaks: Failed cleanup can cause leaks and unexpected behavior

Solutions

1. Using useRef for Stable References
import React, { useEffect, useRef } from 'react';

function MyComponent({ socket }) {
  const socketRef = useRef(socket);

  useEffect(() => {
    const currentSocket = socketRef.current;
    currentSocket.on('message', handleMessage);
    return () => {
      currentSocket.off('message');
    };
  }, []); // Empty dependency array is now safe
}

Why This Works:

  • useRef creates a mutable object persistent across renders
  • The ref object doesn't change, preventing effect re-runs
  • Latest socket value is accessible via the ref
2. Using useCallback for Handlers
import React, { useEffect, useRef, useCallback } from 'react';

function MyComponent({ socket, someProp }) {
  const socketRef = useRef(socket);

  const handleMessage = useCallback((message) => {
    // Do something with the message and potentially someProp
    console.log(message, someProp);
  }, [someProp]); // Include dependencies of handleMessage

  useEffect(() => {
    const currentSocket = socketRef.current;
    currentSocket.on('message', handleMessage);
    return () => {
      currentSocket.off('message', handleMessage);
    };
  }, [handleMessage]); // Depend on the memoized handler
}
3. Managing Socket Lifecycle Inside useEffect
import React, { useEffect, useState } from 'react';

function MyComponent() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = new WebSocket('your_websocket_url');

    const handleMessage = (message) => {
      setMessages((prevMessages) => [...prevMessages, message]);
    };

    socket.on('message', handleMessage);

    return () => {
      socket.off('message', handleMessage);
      socket.close();
    };
  }, []); // Empty array is correct - socket lifecycle managed within
}
4. Custom Hook Solution
function useSocket(url) {
  const [messages, setMessages] = useState([]);
  const socketRef = useRef(null);

  useEffect(() => {
    const socket = new WebSocket(url);
    socketRef.current = socket;

    const handleMessage = (message) => {
      setMessages((prevMessages) => [...prevMessages, message]);
    };

    socket.on('message', handleMessage);

    return () => {
      socket.off('message', handleMessage);
      socket.close();
    };
  }, [url]);

  const sendMessage = useCallback((message) => {
    if (socketRef.current?.readyState === WebSocket.OPEN) {
      socketRef.current.send(message);
    }
  }, []);

  return { messages, sendMessage };
}

Best Practices

  1. Dependency Management

    • Use useRef for stable references
    • Memoize handlers with useCallback
    • Manage socket lifecycle properly
  2. Performance Optimization

    • Minimize unnecessary re-renders
    • Handle high-volume messages efficiently
    • Use appropriate cleanup patterns
  3. Error Handling

    • Handle connection errors gracefully
    • Implement reconnection logic if needed
    • Clean up resources properly
  4. Testing Considerations

    • Mock WebSocket connections in tests
    • Verify event listener cleanup
    • Test error scenarios

Project Structure

src/
├── config.ts        # Configuration and API settings
├── fileUtils.ts     # File operations and language detection
├── index.ts         # Entry point
├── perplexity.ts    # Perplexity AI integration
├── server.ts        # MCP server implementation
├── stackOverflow.ts # Stack Overflow API integration
└── types.ts         # TypeScript interfaces

Known Issues

See errors.md for current issues and workarounds.