# WebSocket Connection Debugging Guide - Agent700

Overview This guide provides detailed WebSocket connection implementation and debugging steps for the Agent700 MCP-enabled agents system.


Table of Contents

  1. Connection Establishment
  2. Authentication Flow
  3. MCP Event Handlers
  4. Error Handling
  5. Complete Working Example
  6. Debugging Steps
  7. Common Issues

Connection Establishment

Basic WebSocket Setup

import io from 'socket.io-client';
import secureStorage from 'src/utils/secureStorage';
import routes from 'src/config/routes';

const ChatContext = () => {
  const socketRef = useRef(null);
  const { user } = useAuth();
  const [isConnected, setIsConnected] = useState(false);
  const [reconnectAttempts, setReconnectAttempts] = useState(0);
  const maxReconnectAttempts = 20;
  
  const getAuthToken = () => {
    // Priority order: user context first, then secure storage
    const accessToken = user?.accessToken;
    const tokenFromStorage = !accessToken ? secureStorage.getItem('accessToken') : null;
    return accessToken || tokenFromStorage;
  };

  const connectSocket = () => {
    const token = getAuthToken();
    
    if (!token) {
      console.error('❌ No access token available for WebSocket connection');
      openSnackbar('Authentication error: Please log in again', 'error');
      return;
    }

    console.log('πŸ”Œ Establishing WebSocket connection...');
    
    // CRITICAL: Token must be in headers
    socketRef.current = io(routes.streamChat, {
      extraHeaders: {
        Authorization: `Bearer ${token}`
      },
      transports: ['websocket', 'polling'],
      reconnectionAttempts: maxReconnectAttempts,
      reconnectionDelay: 1000,
    });

    setupEventHandlers();
  };
};

Authentication Flow

Token Management

// Token retrieval with fallback
const getAuthToken = () => {
  const accessToken = user?.accessToken;
  const tokenFromStorage = !accessToken ? secureStorage.getItem('accessToken') : null;
  const finalToken = accessToken || tokenFromStorage;
  
  console.log('πŸ”‘ Token source:', accessToken ? 'user object' : 'secure storage');
  console.log('πŸ”‘ Token available:', !!finalToken);
  
  return finalToken;
};

// Validate token before connection
const validateToken = (token) => {
  if (!token) {
    console.error('❌ No authentication token available');
    return false;
  }
  
  // Basic JWT format check
  const parts = token.split('.');
  if (parts.length !== 3) {
    console.error('❌ Invalid token format');
    return false;
  }
  
  try {
    // Check if token is expired (basic check)
    const payload = JSON.parse(atob(parts[1]));
    const now = Math.floor(Date.now() / 1000);
    
    if (payload.exp && payload.exp < now) {
      console.error('❌ Token expired');
      return false;
    }
    
    console.log('βœ… Token validation passed');
    return true;
  } catch (error) {
    console.error('❌ Token parsing error:', error);
    return false;
  }
};

Connection Setup with Authentication

const setupEventHandlers = () => {
  // Connection success
  socketRef.current.on('connect', () => {
    console.log('βœ… WebSocket connected successfully');
    setIsConnected(true);
    setReconnectAttempts(0);
    removeLoadingComments();
  });

  // Disconnection
  socketRef.current.on('disconnect', (reason) => {
    console.log('πŸ”Œ WebSocket disconnected:', reason);
    setIsConnected(false);
    setLoading(false);
  });

  // Reconnection attempts
  socketRef.current.on('reconnect_attempt', () => {
    console.log('πŸ”„ Attempting to reconnect...');
    openSnackbar('Attempting to reconnect...', 'info');
    setReconnectAttempts(prev => prev + 1);
  });

  socketRef.current.on('reconnect_failed', () => {
    console.error('❌ Failed to reconnect after maximum attempts');
    openSnackbar('Failed to reconnect to the server. Please refresh the page.', 'error');
  });

  setupErrorHandlers();
  setupMCPHandlers();
};

MCP Event Handlers

Chat Message Response Handler

const setupMCPHandlers = () => {
  // Handle streaming AI responses
  socketRef.current.on('chat_message_response', (data) => {
    console.log('πŸ’¬ Received response chunk:', data.content?.substring(0, 50) + '...');
    
    if (data.finish_reason === 'tool_calls') {
      console.log('πŸ”§ MCP tool execution starting');
      toolExecutionInProgressRef.current = true;
    }
    else if (!data.finish_reason || (data.finish_reason && data.finish_reason.toLowerCase() !== 'stop')) {
      // Accumulate content in buffer
      if (data.content) {
        bufferedResponseRef.current += data.content;
      }
      // Add only the current chunk to chat history
      addAIResponseToChatHistory(data);
    } else {
      // Final chunk
      if (data.content) {
        bufferedResponseRef.current += data.content;
        addAIResponseToChatHistory({ content: data.content });
      }

      // Handle citations if present
      if (data.citations && data.citations.length > 0) {
        addCitationsToLastMessage(data.citations);
      }

      // Process SQL if needed
      if (bufferedResponseRef.current) {
        processFullResponseForSQL(bufferedResponseRef.current);
      }

      bufferedResponseRef.current = '';
      toolExecutionInProgressRef.current = false;
    }
  });

  // Handle MCP tool completion
  socketRef.current.on('mcp_tool_complete_in_content', (data) => {
    console.log('βœ… MCP tool execution completed:', data);
    
    if (data.result_block) {
      try {
        // Parse the tool result
        const resultMatch = data.result_block;
        if (resultMatch) {
          const toolResult = JSON.parse(resultMatch[1]);
          
          const toolResultMessage = {
            id: uuid(),
            role: 'tool',
            type: 'mcp_search_result',
            content: resultMatch
          };
          
          setChatHistory(existingChatHistory => [...existingChatHistory, toolResultMessage]);
          console.log('πŸ“‹ Tool result added to chat history');
        }
      } catch (error) {
        console.error('❌ Failed to parse MCP tool result:', error);
        // Fallback: add raw result
        const fallbackMessage = {
          id: uuid(),
          role: 'assistant',
          type: 'success',
          content: `Tool Result: ${JSON.stringify(data.result_block, null, 2)}`
        };
        setChatHistory(existingChatHistory => [...existingChatHistory, fallbackMessage]);
      }
    }
  });

  // Handle data tables from MCP tools
  socketRef.current.on('data_table', (dataTableResponse) => {
    console.log('πŸ“Š Received data table from MCP tool');
    addDataTableToChatHistory(dataTableResponse.content, 'user');
    setTimeout(() => {
      addAIResponseToChatHistory({ 
        content: 'Data successfully parsed. Feel free to explore or ask questions.' 
      });
    }, 0);
  });

  // Handle PII scrubbing messages
  socketRef.current.on('scrubbed_message', (data) => {
    console.log('πŸ›‘οΈ PII scrubbing applied');
    insertScrubbedMessage(data.content);
  });
};

Error Handling

Comprehensive Error Handler

const setupErrorHandlers = () => {
  // WebSocket-level errors
  socketRef.current.on('error', (data = {}) => {
    console.error(`❌ WebSocket error: ${JSON.stringify(data) || 'unknown'} (code: ${data.code})`);

    // 401 β†’ token invalid/expired β‡’ logout to prompt re-login
    if (data.code === 401) {
      console.error('πŸ” Authentication failed - token invalid/expired');
      logout();
      return;
    }
    
    // If it's the "not publicly shared" error (400), this is likely an auth issue
    if (data.code === 400 && data.error && data.error.includes('not publically shared')) {
      console.error('πŸ”‘ Authentication error: Token might not be in expected format or location');
      // Try to reconnect with fresh token
      if (socketRef.current) {
        socketRef.current.disconnect();
        setTimeout(() => {
          console.log('πŸ”„ Retrying connection with fresh token');
          connectSocket();
        }, 1000);
        return;
      }
    }

    // Otherwise just surface the error to the UI and keep the session alive
    openSnackbar(data.error || 'Realtime connection error', 'error');
  });

  // Handle connection-level errors
  socketRef.current.io.on('error', () => {}); // no-op to silence default uncaught
  socketRef.current.io.on('connect_error', (err) => {
    console.warn('⚠️ Socket connect_error:', err.message);
    openSnackbar('Unable to connect to realtime service', 'warning');
  });
};

Complete Working Example

Custom Hook Implementation

const useMCPWebSocket = () => {
  const socketRef = useRef(null);
  const [isConnected, setIsConnected] = useState(false);
  const [error, setError] = useState(null);
  const [reconnectAttempts, setReconnectAttempts] = useState(0);
  const { user, logout } = useAuth();
  const { openSnackbar } = useContext(SnackbarContext);

  const connect = useCallback(() => {
    // Step 1: Get and validate token
    const token = user?.accessToken || secureStorage.getItem('accessToken');
    
    if (!token) {
      const errorMsg = 'No authentication token available';
      console.error('❌', errorMsg);
      setError(errorMsg);
      return false;
    }

    if (!validateToken(token)) {
      const errorMsg = 'Invalid authentication token';
      console.error('❌', errorMsg);
      setError(errorMsg);
      return false;
    }

    console.log('πŸš€ Starting WebSocket connection...');
    
    // Step 2: Create socket connection
    socketRef.current = io('https://api.agent700.ai/api/stream-chat', {
      extraHeaders: {
        Authorization: `Bearer ${token}`
      },
      transports: ['websocket', 'polling'],
      reconnectionAttempts: 20,
      reconnectionDelay: 1000,
    });

    // Step 3: Setup event handlers
    socketRef.current.on('connect', () => {
      console.log('βœ… Connected to MCP WebSocket');
      setIsConnected(true);
      setError(null);
      setReconnectAttempts(0);
    });

    socketRef.current.on('disconnect', (reason) => {
      console.log('πŸ”Œ Disconnected from WebSocket:', reason);
      setIsConnected(false);
    });

    socketRef.current.on('error', (data) => {
      console.error('❌ WebSocket error:', data);
      
      if (data.code === 401) {
        console.error('πŸ” Token expired - logging out');
        logout();
      } else if (data.code === 400 && data.error?.includes('not publically shared')) {
        console.error('πŸ”‘ Token format issue - retrying...');
        setTimeout(() => connect(), 1000);
      } else {
        setError(data.error || 'Connection error');
      }
    });

    // MCP-specific handlers
    socketRef.current.on('mcp_tool_complete_in_content', (data) => {
      console.log('πŸ”§ MCP tool completed:', data);
      // Handle tool results here
    });

    socketRef.current.on('chat_message_response', (data) => {
      console.log('πŸ’¬ Chat response:', data.content?.substring(0, 50));
      if (data.finish_reason === 'tool_calls') {
        console.log('πŸ”§ Tool execution started');
      }
    });

    return true;
  }, [user, logout]);

  const sendMessage = useCallback((agentId, messages) => {
    const token = user?.accessToken || secureStorage.getItem('accessToken');
    
    if (!socketRef.current || !isConnected) {
      console.error('❌ Socket not connected');
      return false;
    }
    
    if (!token) {
      console.error('❌ No token available for message');
      return false;
    }

    const payload = {
      agentId,
      messages,
      Authorization: `Bearer ${token}` // Critical: token in payload too!
    };

    console.log('πŸ“€ Sending message to agent:', agentId);
    socketRef.current.emit('send_chat_message', payload);
    return true;
  }, [user, isConnected]);

  const disconnect = useCallback(() => {
    if (socketRef.current) {
      console.log('πŸ”Œ Disconnecting WebSocket');
      socketRef.current.disconnect();
      socketRef.current = null;
      setIsConnected(false);
    }
  }, []);

  return { 
    connect, 
    sendMessage, 
    disconnect, 
    isConnected, 
    error, 
    reconnectAttempts 
  };
};

Debugging Steps

Step 1: Token Verification

const debugToken = () => {
  const token = user?.accessToken || secureStorage.getItem('accessToken');
  
  console.log('πŸ” Token Debug:');
  console.log('- Token exists:', !!token);
  console.log('- Token source:', user?.accessToken ? 'user context' : 'secure storage');
  console.log('- Token length:', token?.length || 0);
  
  if (token) {
    try {
      const parts = token.split('.');
      console.log('- JWT parts:', parts.length);
      
      if (parts.length === 3) {
        const payload = JSON.parse(atob(parts[1]));
        const now = Math.floor(Date.now() / 1000);
        console.log('- Token expires:', new Date(payload.exp * 1000));
        console.log('- Token valid:', payload.exp > now);
      }
    } catch (e) {
      console.error('- Token parse error:', e);
    }
  }
};

Step 2: Connection Testing

const testConnection = () => {
  console.log('πŸ§ͺ Testing WebSocket connection...');
  
  const token = user?.accessToken || secureStorage.getItem('accessToken');
  
  if (!token) {
    console.error('❌ Test failed: No token');
    return;
  }

  const testSocket = io('https://api.agent700.ai/api/stream-chat', {
    extraHeaders: {
      Authorization: `Bearer ${token}`
    },
    timeout: 5000,
  });

  testSocket.on('connect', () => {
    console.log('βœ… Test connection successful');
    testSocket.disconnect();
  });

  testSocket.on('connect_error', (error) => {
    console.error('❌ Test connection failed:', error.message);
  });

  testSocket.on('error', (data) => {
    console.error('❌ Test error:', data);
  });

  // Auto-cleanup
  setTimeout(() => {
    if (testSocket.connected) {
      console.log('⏱️ Test timeout - disconnecting');
      testSocket.disconnect();
    }
  }, 10000);
};

Step 3: Network Analysis

const debugNetwork = () => {
  console.log('🌐 Network Debug:');
  console.log('- API Host:', process.env.REACT_APP_API_HOST);
  console.log('- Stream Chat URL:', routes.streamChat);
  console.log('- Current location:', window.location.origin);
  console.log('- CORS mode:', 'include');
  
  // Test basic connectivity
  fetch(routes.streamChat.replace('/stream-chat', '/health'), {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${user?.accessToken || secureStorage.getItem('accessToken')}`
    }
  })
  .then(response => {
    console.log('βœ… Basic API connectivity:', response.status);
  })
  .catch(error => {
    console.error('❌ Basic API connectivity failed:', error);
  });
};

Common Issues

Issue 1: "Connection Error" with No Details

Symptoms:

  • Generic "Connection error" message
  • No specific error code
  • Connection drops immediately

Debug Steps:

// Add detailed logging to connection
socketRef.current = io(routes.streamChat, {
  extraHeaders: { Authorization: `Bearer ${token}` },
  transports: ['websocket', 'polling'],
  forceNew: true, // Force new connection
  debug: true     // Enable debug mode
});

// Log all events
['connect', 'disconnect', 'error', 'connect_error', 'reconnect_error'].forEach(event => {
  socketRef.current.on(event, (data) => {
    console.log(`πŸ” Socket event '${event}':`, data);
  });
});

Common Causes:

  • Token not in correct format
  • Backend not accepting WebSocket connections
  • CORS issues
  • Network firewall blocking WebSocket

Issue 2: Authentication Failures

Symptoms:

  • 401 errors
  • "not publically shared" errors
  • Immediate disconnections

Solutions:

// Ensure token in both places
const requestData = {
  agentId: selectedAgent?.id,
  messages: chatHistory,
  Authorization: `Bearer ${token}`, // Must be here
  // other data...
};

// AND in headers
socketRef.current = io(routes.streamChat, {
  extraHeaders: {
    Authorization: `Bearer ${token}` // AND here
  }
});

Issue 3: MCP Function Name Length (68 vs 64 characters)

Problem: MCP protocol limits function names to 64 characters

Solutions:

  1. Rename the function (if you control the MCP server):
// Instead of: "get_detailed_user_information_with_preferences_and_settings"
// Use: "get_user_info_detailed"
  1. Create alias functions (if using third-party server):
// Map long names to short ones in your server config
const functionAliases = {
  'get_user_info': 'get_detailed_user_information_with_preferences_and_settings'
};
  1. Update MCP server configuration to use shorter, descriptive names

Issue 4: Tool Results Not Displaying

Debug Steps:

// Add logging to MCP tool handler
socketRef.current.on('mcp_tool_complete_in_content', (data) => {
  console.log('πŸ”§ Raw MCP tool data:', data);
  console.log('πŸ”§ Result block exists:', !!data.result_block);
  
  if (data.result_block) {
    console.log('πŸ”§ Result block content:', data.result_block);
    try {
      const parsed = JSON.parse(data.result_block[1]);
      console.log('πŸ”§ Parsed result:', parsed);
    } catch (e) {
      console.error('πŸ”§ Parse error:', e);
    }
  }
});

Testing Checklist

  • Token exists and is valid
  • WebSocket URL is correct
  • Authentication headers are set
  • Token is in both headers AND payload
  • Network connectivity to backend
  • MCP server function names are ≀ 64 characters
  • Event handlers are properly attached
  • Error handling is comprehensive
  • Tool results are being parsed correctly

Environment Variables Check

// Add to your debugging routine
const checkEnvironment = () => {
  console.log('πŸ”§ Environment Check:');
  console.log('- REACT_APP_API_HOST:', process.env.REACT_APP_API_HOST);
  console.log('- NODE_ENV:', process.env.NODE_ENV);
  console.log('- Stream Chat URL:', routes.streamChat);
  console.log('- Current origin:', window.location.origin);
};

Use this guide to systematically debug WebSocket connection issues in your Agent700 MCP integration.