# Agent700 MCP Backend Integration Guide


Table of Contents

  1. Architecture Overview
  2. Backend API Endpoints
  3. Authentication Flow
  4. WebSocket Communication
  5. Error Handling Patterns
  6. Code Examples
  7. Troubleshooting Guide
  8. Testing Strategies

Architecture Overview

High-Level MCP Integration Architecture

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   React Frontend │    │   Backend API    │    │   MCP Servers   │
│                 │◄──►│                  │◄──►│                 │
│  - AgentContext │    │  - REST/WebSocket│    │  - Public       │
│  - MCP UI       │    │  - Authentication│    │  - Custom       │
│  - ChatContext  │    │  - Server Mgmt   │    │  - Tools/Resources│
└─────────────────┘    └──────────────────┘    └─────────────────┘
         │                        │
         ▼                        ▼
┌─────────────────┐    ┌──────────────────┐
│  Agent Storage  │    │  Tool Execution  │
│                 │    │                  │
│  - Agent Config │    │  - Real-time     │
│  - MCP Settings │    │  - Result Stream │
│  - Server Lists │    │  - Error Handling│
└─────────────────┘    └──────────────────┘

Key Components

1. Agent-Centric MCP Management

  • Each agent maintains its own MCP server configuration
  • Servers can be enabled/disabled per agent
  • Public and custom server support

2. Multi-Layer Communication

  • REST API: Server CRUD operations, configuration management
  • WebSocket: Real-time tool execution and streaming results
  • Authentication: JWT-based with secure token storage

3. Server Types

  • Public Servers: Pre-configured servers (Brave Search, Fetch, etc.)
  • Custom Servers: User-defined MCP servers per agent

Backend API Endpoints

Core MCP Routes

const API_HOST = process.env.REACT_APP_API_HOST || 'https://api.agent700.ai/api';

// MCP Management Routes
const routes = {
  // Get all MCP servers for an agent
  agentsmcp: `${API_HOST}/agents`,
  
  // Specific endpoints constructed dynamically:
  // GET /agents/{agentId}/mcp/servers - List all servers
  // POST /agents/{agentId}/mcp/servers/{serverName}/restart - Restart server
  // DELETE /agents/{agentId}/mcp/servers/{serverId} - Delete custom server
  
  // WebSocket endpoint for real-time communication
  streamChat: `${API_HOST}/stream-chat`,
};

MCP Server Operations

1. Fetch Agent MCP Servers

GET /agents/{agentId}/mcp/servers
Authorization: Bearer {jwt_token}

Response:

{
  "success": true,
  "servers": [
    {
      "id": "server_id",
      "name": "brave-search",
      "status": "connected",
      "tools": [{"name": "search", "description": "Web search"}],
      "resources": [],
      "disabled": false,
      "public": true,
      "agent": null
    }
  ]
}

2. Restart MCP Server

POST /agents/{agentId}/mcp/servers/{serverName}/restart
Authorization: Bearer {jwt_token}

3. Delete Custom MCP Server

DELETE /agents/{agentId}/mcp/servers/{serverId}
Authorization: Bearer {jwt_token}

Authentication Flow

JWT Token Management

1. Token Storage Pattern

// Priority order for token retrieval:
// 1. User context object
// 2. Secure storage fallback
const getAuthToken = () => {
  const accessToken = user?.accessToken;
  const tokenFromStorage = !accessToken ? secureStorage.getItem('accessToken') : null;
  return accessToken || tokenFromStorage;
};

2. WebSocket Authentication

// Token must be included in WebSocket headers AND payload
const connectSocket = () => {
  const token = getAuthToken();
  
  socketRef.current = io(routes.streamChat, {
    extraHeaders: {
      Authorization: `Bearer ${token}`
    },
    transports: ['websocket', 'polling'],
    reconnectionAttempts: maxReconnectAttempts,
  });
  
  // CRITICAL: Token also required in message payload
  requestData.Authorization = `Bearer ${token}`;
  socketRef.current.emit('send_chat_message', requestData);
};

3. Authentication Error Handling

socketRef.current.on('error', (data = {}) => {
  // Handle authentication failures
  if (data.code === 401) {
    logout(); // Force re-authentication
    return;
  }
  
  // Handle specific MCP auth errors
  if (data.code === 400 && data.error.includes('not publically shared')) {
    // Token format issue - retry connection
    setTimeout(() => connectSocket(), 1000);
    return;
  }
});

WebSocket Communication

MCP Tool Execution Flow

1. Connection Establishment

// WebSocket setup with proper authentication
const socketRef = useRef(null);

const connectSocket = () => {
  socketRef.current = io(routes.streamChat, {
    extraHeaders: { Authorization: `Bearer ${token}` },
    transports: ['websocket', 'polling'],
    reconnectionAttempts: 20,
    reconnectionDelay: 1000,
  });
};

2. Event Handlers for MCP Operations

// Handle streaming chat responses
socketRef.current.on('chat_message_response', (data) => {
  if (data.finish_reason === 'tool_calls') {
    toolExecutionInProgressRef.current = true;
  }
  // Process streaming content
  addAIResponseToChatHistory(data);
});

// Handle MCP tool completion
socketRef.current.on('mcp_tool_complete_in_content', (data) => {
  if (data.result_block) {
    try {
      const toolResult = JSON.parse(data.result_block[1]);
      const toolResultMessage = {
        id: uuid(),
        role: 'tool',
        type: 'mcp_search_result',
        content: data.result_block
      };
      setChatHistory(existingChatHistory => [...existingChatHistory, toolResultMessage]);
    } catch (error) {
      logger.error('Failed to parse MCP tool result:', error);
    }
  }
});

// Handle data tables from tools
socketRef.current.on('data_table', (dataTableResponse) => {
  addDataTableToChatHistory(dataTableResponse.content, 'user');
});

3. Message Payload Structure

const requestData = {
  agentId: selectedAgent?.id,
  agentRevisionId: selectedAgentRevision?.id,
  messages: chatHistory,
  Authorization: `Bearer ${token}`, // Critical for auth
  // Agent configuration
  model: selectedAgentRevision?.model,
  temperature: selectedAgentRevision?.temperature,
  maxTokens: selectedAgentRevision?.maxTokens,
  // MCP configuration is handled server-side based on agent settings
};

Error Handling Patterns

Connection Recovery Strategies

1. Automatic Reconnection

const [reconnectAttempts, setReconnectAttempts] = useState(0);
const maxReconnectAttempts = 20;

socketRef.current.on('reconnect_attempt', () => {
  openSnackbar('Attempting to reconnect...', 'info');
  setReconnectAttempts(prev => prev + 1);
});

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

2. MCP Server Status Monitoring

const fetchMcpServers = useCallback(async () => {
  try {
    const { data } = await axios.get(`${routes.agentsmcp}/${selectedAgent.id}/mcp/servers`);
    if (data.success) {
      const servers = data.servers.map(server => ({
        label: getServerDisplayName(server.name),
        status: server.status, // connected, disconnected, error
        toolsCount: server.tools.length,
        disabled: server.disabled,
      }));
      setSelectedAgentAvailableMcpSevers(servers);
    }
  } catch (error) {
    console.error('Error fetching agent MCP servers:', error);
    openSnackbar('Failed to fetch agent MCP servers', 'error');
  }
}, [selectedAgent?.id, openSnackbar]);

3. Server Restart Functionality

const refreshMcpServerConnection = async (serverName) => {
  try {
    const { data } = await axios.post(
      `${routes.agentsmcp}/${selectedAgent.id}/mcp/servers/${encodeURIComponent(serverName)}/restart`
    );
    
    if (data.success) {
      openSnackbar(data.message, 'success');
      await fetchMcpServers(); // Refresh server list
    } else {
      openSnackbar(data.error || 'Failed to refresh MCP server', 'error');
    }
  } catch (error) {
    openSnackbar(error.response?.data?.error || 'Failed to refresh MCP server', 'error');
  }
};

Code Examples

Complete MCP Server Management Component

const McpIntegrationsList = ({ title, list }) => {
  const { 
    selectedAgent, 
    selectedAgentRevision, 
    tempAgentRevision, 
    setTempAgentRevision,
    fetchMcpServers, 
    saveAgentRevision 
  } = useAgentContext();
  const { openSnackbar } = useContext(SnackbarContext);
  const { mcpServerNames } = tempAgentRevision;

  // Enable/disable MCP server for agent
  const handleEnableMcpServer = async (event, serverName) => {
    try {
      const checked = event?.target?.checked;
      const updatedMcpServerNames = checked 
        ? [...mcpServerNames, serverName] 
        : mcpServerNames.filter(name => name !== serverName);
      
      const updatedTempAgentRevision = { 
        ...tempAgentRevision, 
        mcpServerNames: updatedMcpServerNames 
      };
      
      setTempAgentRevision(updatedTempAgentRevision);
      await saveAgentRevision(selectedAgent, updatedTempAgentRevision);
    } catch (e) {
      console.error('Error saving MCP server selection:', e);
      openSnackbar('Failed to update MCP servers', 'error');
    }
  };

  // Delete custom MCP server
  const handleDeleteMcpServer = async (server) => {
    try {
      const { id: serverId, value: serverName } = server;
      const { data } = await axios.delete(
        `${routes.agentsmcp}/${selectedAgent.id}/mcp/servers/${encodeURIComponent(serverId)}`
      );

      if (!data.success) {
        openSnackbar(data.error || 'Failed to delete MCP server', 'error');
        return;
      }

      openSnackbar(data.message, 'success');
      await fetchMcpServers();
      
      // Update agent revision
      const updatedServerNames = (selectedAgentRevision.mcpServerNames || [])
        .filter(name => name !== serverName);
      setSelectedAgentRevision({ 
        ...selectedAgentRevision, 
        mcpServerNames: updatedServerNames 
      });
    } catch (error) {
      openSnackbar(error?.data?.error || 'Failed to delete MCP server', 'error');
    }
  };

  return (
    <Grid container spacing={1}>
      {list.map(server => {
        const isSelected = mcpServerNames.includes(server?.value);
        return (
          <Grid item xs={12} lg={3} key={server?.id ?? server?.value}>
            <McpIntegrationsItem 
              server={server} 
              checked={isSelected} 
              disabled={selectedAgent?.sharedWithUser} 
              onChange={(e) => handleEnableMcpServer(e, server.value)}
              onDelete={() => handleDeleteMcpServer(server)}
            />
          </Grid>
        );
      })}
    </Grid>
  );
};

AgentContext MCP Integration

const AgentProvider = ({ children }) => {
  const [selectedAgentAvailableMcpSevers, setSelectedAgentAvailableMcpSevers] = useState([]);
  const [selectedAgentAvailablePublicMcpServers, setSelectedAgentAvailablePublicMcpServers] = useState([]);
  const [selectedAgentAvailableCustomMcpServers, setSelectedAgentAvailableCustomMcpServers] = useState([]);

  const fetchMcpServers = useCallback(async () => {
    if (!selectedAgent?.id) return;
    
    try {
      const { data } = await axios.get(`${routes.agentsmcp}/${selectedAgent.id}/mcp/servers`);
      if (data.success) {
        const servers = data.servers.map(server => ({
          label: getServerDisplayName(server.name),
          id: server.id,
          value: server.name,
          status: server.status,
          toolsCount: server.tools.length,
          resourcesCount: server.resources.length,
          disabled: server.disabled,
          isCustom: server.agent,
          isPublic: server.public,
          tools: server.tools,
          resources: server.resources,
        }));
        
        const publicServers = servers.filter(s => s.isPublic && !s.isCustom);
        const customServers = servers.filter(s => s.isCustom);

        setSelectedAgentAvailableMcpSevers(servers);
        setSelectedAgentAvailablePublicMcpServers(publicServers);
        setSelectedAgentAvailableCustomMcpServers(customServers);
      }
    } catch (error) {
      console.error('Error fetching agent MCP servers:', error);
      openSnackbar('Failed to fetch agent MCP servers', 'error');
    }
  }, [selectedAgent?.id, openSnackbar]);

  // Auto-fetch MCP servers when agent changes
  useEffect(() => {
    if (!selectedAgent?.id || selectedAgent?.sharedWithUser) return;
    fetchMcpServers();
  }, [selectedAgent?.id, selectedAgent?.sharedWithUser, fetchMcpServers]);

  const getServerDisplayName = (serverName) => {
    const serverDisplayNames = {
      'brave-search': 'Brave Search',
      'fetch': 'Fetch (Web Content)',
      'sequential-thinking': 'Sequential Thinking',
      'filesystem': 'File System',
      'sqlite': 'SQLite Database',
      'postgres': 'PostgreSQL Database'
    };
    return serverDisplayNames[serverName] || serverName;
  };

  return (
    <AgentContext.Provider value={{
      selectedAgentAvailableMcpSevers,
      selectedAgentAvailablePublicMcpServers,
      selectedAgentAvailableCustomMcpServers,
      fetchMcpServers,
      refreshMcpServerConnection,
      // ... other context values
    }}>
      {children}
    </AgentContext.Provider>
  );
};

Troubleshooting Guide

Common Issues and Solutions

1. WebSocket Authentication Failures

Symptoms:

  • 401 errors in WebSocket connection
  • "not publically shared" error messages
  • Immediate disconnections

Solutions:

// Ensure token is available in both locations
const token = user?.accessToken || secureStorage.getItem('accessToken');

// Check token format and expiration
if (!token) {
  console.error('No access token available for WebSocket connection');
  logout(); // Force re-authentication
  return;
}

// Include token in both headers and payload
socketRef.current = io(routes.streamChat, {
  extraHeaders: { Authorization: `Bearer ${token}` }
});

// Critical: Also include in message payload
requestData.Authorization = `Bearer ${token}`;

2. MCP Server Connection Issues

Symptoms:

  • Servers showing as "disconnected"
  • Tool execution failures
  • Empty server lists

Solutions:

// Check server status and restart if needed
const checkAndRestartServers = async () => {
  const servers = await fetchMcpServers();
  const disconnectedServers = servers.filter(s => s.status !== 'connected');
  
  for (const server of disconnectedServers) {
    try {
      await refreshMcpServerConnection(server.name);
      console.log(`Restarted server: ${server.name}`);
    } catch (error) {
      console.error(`Failed to restart ${server.name}:`, error);
    }
  }
};

3. Agent MCP Configuration Not Persisting

Symptoms:

  • MCP settings not saving
  • Server selections reverting
  • Revision update failures

Solutions:

// Ensure proper revision update flow
const handleEnableMcpServer = async (event, serverName) => {
  const checked = event?.target?.checked;
  
  // 1. Update temporary revision
  const updatedTempAgentRevision = { 
    ...tempAgentRevision, 
    mcpServerNames: checked 
      ? [...tempAgentRevision.mcpServerNames, serverName] 
      : tempAgentRevision.mcpServerNames.filter(name => name !== serverName)
  };
  
  // 2. Update state
  setTempAgentRevision(updatedTempAgentRevision);
  
  // 3. Persist to backend
  await saveAgentRevision(selectedAgent, updatedTempAgentRevision);
  
  // 4. Refresh MCP servers list
  await fetchMcpServers();
};

4. Tool Execution Results Not Displaying

Symptoms:

  • Tools execute but no results shown
  • Missing tool result messages in chat
  • JSON parsing errors

Solutions:

// Proper MCP tool result handling
socketRef.current.on('mcp_tool_complete_in_content', (data) => {
  console.log('MCP tool completed:', data);
  
  if (data.result_block) {
    try {
      // Parse the tool result
      const resultMatch = data.result_block;
      const toolResult = JSON.parse(resultMatch[1]);
      
      // Add formatted result to chat
      const toolResultMessage = {
        id: uuid(),
        role: 'tool',
        type: 'mcp_search_result',
        content: resultMatch
      };
      
      setChatHistory(existingChatHistory => [...existingChatHistory, toolResultMessage]);
    } catch (error) {
      console.error('Failed to parse MCP tool result:', error);
      
      // Fallback: show raw result
      const fallbackMessage = {
        id: uuid(),
        role: 'assistant',
        type: 'success',
        content: `Tool Result: ${JSON.stringify(data.result_block, null, 2)}`
      };
      setChatHistory(existingChatHistory => [...existingChatHistory, fallbackMessage]);
    }
  }
});

Testing Strategies

1. MCP Server Connectivity Testing

// Test MCP server connection
const testMcpServerConnection = async (agentId, serverName) => {
  try {
    // 1. Check server status
    const { data } = await axios.get(`${routes.agentsmcp}/${agentId}/mcp/servers`);
    const server = data.servers.find(s => s.name === serverName);
    
    console.log(`Server ${serverName} status:`, server?.status);
    console.log(`Available tools:`, server?.tools?.length || 0);
    
    // 2. Test restart functionality
    if (server?.status !== 'connected') {
      await axios.post(`${routes.agentsmcp}/${agentId}/mcp/servers/${serverName}/restart`);
      console.log(`Restarted ${serverName}`);
    }
    
    return true;
  } catch (error) {
    console.error(`MCP server test failed:`, error);
    return false;
  }
};

2. WebSocket Connection Testing

// Test WebSocket authentication and connection
const testWebSocketConnection = () => {
  const token = user?.accessToken || secureStorage.getItem('accessToken');
  
  if (!token) {
    console.error('❌ No authentication token available');
    return false;
  }
  
  const socket = io(routes.streamChat, {
    extraHeaders: { Authorization: `Bearer ${token}` },
    timeout: 5000,
  });
  
  socket.on('connect', () => {
    console.log('✅ WebSocket connected successfully');
    socket.disconnect();
  });
  
  socket.on('connect_error', (error) => {
    console.error('❌ WebSocket connection failed:', error);
  });
  
  socket.on('error', (data) => {
    console.error('❌ WebSocket error:', data);
  });
};

3. End-to-End MCP Tool Testing

// Test complete MCP tool execution flow
const testMcpToolExecution = async (agentId, prompt) => {
  const token = getAuthToken();
  
  const socket = io(routes.streamChat, {
    extraHeaders: { Authorization: `Bearer ${token}` }
  });
  
  socket.on('connect', () => {
    console.log('🔌 Connected for MCP tool test');
    
    const requestData = {
      agentId,
      messages: [{ role: 'user', content: prompt }],
      Authorization: `Bearer ${token}`
    };
    
    socket.emit('send_chat_message', requestData);
  });
  
  socket.on('chat_message_response', (data) => {
    console.log('💬 Response chunk:', data.content?.substring(0, 100) + '...');
    
    if (data.finish_reason === 'tool_calls') {
      console.log('🔧 Tool execution started');
    }
  });
  
  socket.on('mcp_tool_complete_in_content', (data) => {
    console.log('✅ MCP tool completed:', data.result_block ? 'Success' : 'Failed');
    socket.disconnect();
  });
  
  setTimeout(() => {
    console.log('⏱️ Test timeout - disconnecting');
    socket.disconnect();
  }, 30000);
};

Best Practices

  1. Always check authentication token availability before establishing connections
  2. Handle WebSocket reconnection gracefully with user feedback
  3. Monitor MCP server status and provide restart functionality
  4. Parse tool results safely with error handling and fallbacks
  5. Update agent configurations atomically to prevent inconsistent state
  6. Provide clear user feedback for all MCP operations (enable, disable, restart)
  7. Implement proper cleanup for WebSocket connections and event listeners

Security Considerations

  1. JWT Token Handling: Never log tokens, use secure storage
  2. WebSocket Authentication: Include tokens in both headers and payload
  3. Server Validation: Validate MCP server responses before processing
  4. Error Information: Limit error details exposed to users
  5. Connection Limits: Implement reasonable timeout and retry limits

This guide is based on the Agent700 Web UI implementation. For specific backend API details, consult the backend team or API documentation.