# Agent700 MCP Backend Integration Guide
Table of Contents
- Architecture Overview
- Backend API Endpoints
- Authentication Flow
- WebSocket Communication
- Error Handling Patterns
- Code Examples
- Troubleshooting Guide
- 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
- Always check authentication token availability before establishing connections
- Handle WebSocket reconnection gracefully with user feedback
- Monitor MCP server status and provide restart functionality
- Parse tool results safely with error handling and fallbacks
- Update agent configurations atomically to prevent inconsistent state
- Provide clear user feedback for all MCP operations (enable, disable, restart)
- Implement proper cleanup for WebSocket connections and event listeners
Security Considerations
- JWT Token Handling: Never log tokens, use secure storage
- WebSocket Authentication: Include tokens in both headers and payload
- Server Validation: Validate MCP server responses before processing
- Error Information: Limit error details exposed to users
- 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.
Updated about 1 month ago
