mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-23 18:29:21 -05:00
Add documentation for transport event handlers fix
Co-authored-by: samanhappy <2755122+samanhappy@users.noreply.github.com>
This commit is contained in:
179
docs/transport-event-handlers-fix.md
Normal file
179
docs/transport-event-handlers-fix.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Transport Event Handlers Fix
|
||||
|
||||
## Problem Statement
|
||||
|
||||
After adding SSE (Server-Sent Events) or Streamable HTTP protocol servers, the server status did not automatically update when connections failed or closed. The status remained "connected" even when the connection was lost.
|
||||
|
||||
## Root Cause
|
||||
|
||||
The MCP SDK provides `onclose` and `onerror` event handlers for all transport types (SSE, StreamableHTTP, and stdio), but the MCPHub implementation was not setting up these handlers. This meant that:
|
||||
|
||||
1. When a connection closed unexpectedly, the server status remained "connected"
|
||||
2. When transport errors occurred, the status was not updated
|
||||
3. Users could not see the actual connection state in the dashboard
|
||||
|
||||
## Solution
|
||||
|
||||
Added a `setupTransportEventHandlers()` helper function that:
|
||||
|
||||
1. Sets up `onclose` handler to update status to 'disconnected' when connections close
|
||||
2. Sets up `onerror` handler to update status and capture error messages
|
||||
3. Clears keep-alive ping intervals when connections fail
|
||||
4. Logs connection state changes for debugging
|
||||
|
||||
The handlers are set up in two places:
|
||||
|
||||
1. After successful initial connection in `initializeClientsFromSettings()`
|
||||
2. After reconnection in `callToolWithReconnect()`
|
||||
|
||||
## Changes Made
|
||||
|
||||
### File: `src/services/mcpService.ts`
|
||||
|
||||
#### New Function: `setupTransportEventHandlers()`
|
||||
|
||||
```typescript
|
||||
const setupTransportEventHandlers = (serverInfo: ServerInfo): void => {
|
||||
if (!serverInfo.transport) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up onclose handler to update status when connection closes
|
||||
serverInfo.transport.onclose = () => {
|
||||
console.log(`Transport closed for server: ${serverInfo.name}`);
|
||||
if (serverInfo.status === 'connected') {
|
||||
serverInfo.status = 'disconnected';
|
||||
serverInfo.error = 'Connection closed';
|
||||
}
|
||||
|
||||
// Clear keep-alive interval if it exists
|
||||
if (serverInfo.keepAliveIntervalId) {
|
||||
clearInterval(serverInfo.keepAliveIntervalId);
|
||||
serverInfo.keepAliveIntervalId = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// Set up onerror handler to update status on connection errors
|
||||
serverInfo.transport.onerror = (error: Error) => {
|
||||
console.error(`Transport error for server ${serverInfo.name}:`, error);
|
||||
if (serverInfo.status === 'connected') {
|
||||
serverInfo.status = 'disconnected';
|
||||
serverInfo.error = `Transport error: ${error.message}`;
|
||||
}
|
||||
|
||||
// Clear keep-alive interval if it exists
|
||||
if (serverInfo.keepAliveIntervalId) {
|
||||
clearInterval(serverInfo.keepAliveIntervalId);
|
||||
serverInfo.keepAliveIntervalId = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
console.log(`Transport event handlers set up for server: ${serverInfo.name}`);
|
||||
};
|
||||
```
|
||||
|
||||
#### Integration Points
|
||||
|
||||
1. **Initial Connection** - Added call after successful connection:
|
||||
```typescript
|
||||
if (!dataError) {
|
||||
serverInfo.status = 'connected';
|
||||
serverInfo.error = null;
|
||||
|
||||
// Set up transport event handlers for connection monitoring
|
||||
setupTransportEventHandlers(serverInfo);
|
||||
|
||||
// Set up keep-alive ping for SSE connections
|
||||
setupKeepAlive(serverInfo, expandedConf);
|
||||
}
|
||||
```
|
||||
|
||||
2. **Reconnection** - Added call after reconnection succeeds:
|
||||
```typescript
|
||||
// Update server info with new client and transport
|
||||
serverInfo.client = client;
|
||||
serverInfo.transport = newTransport;
|
||||
serverInfo.status = 'connected';
|
||||
|
||||
// Set up transport event handlers for the new connection
|
||||
setupTransportEventHandlers(serverInfo);
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Automated Tests
|
||||
|
||||
All 169 existing tests pass, including:
|
||||
- Integration tests for SSE transport (`tests/integration/sse-service-real-client.test.ts`)
|
||||
- Integration tests for StreamableHTTP transport
|
||||
- Unit tests for MCP service functionality
|
||||
|
||||
### Manual Testing
|
||||
|
||||
To manually test the fix:
|
||||
|
||||
1. **Add an SSE server** to `mcp_settings.json`:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"test-sse-server": {
|
||||
"type": "sse",
|
||||
"url": "http://localhost:9999/sse",
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Start MCPHub**: `pnpm dev`
|
||||
|
||||
3. **Observe the behavior**:
|
||||
- Server will initially show as "connecting"
|
||||
- When connection fails (port 9999 not available), status will update to "disconnected"
|
||||
- Error message will show: "Transport error: ..." or "Connection closed"
|
||||
|
||||
4. **Test connection recovery**:
|
||||
- Start an MCP server on the configured URL
|
||||
- The status should update to "connected" when available
|
||||
- Stop the MCP server
|
||||
- The status should update back to "disconnected"
|
||||
|
||||
### StreamableHTTP Testing
|
||||
|
||||
1. **Add a StreamableHTTP server** to `mcp_settings.json`:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"test-http-server": {
|
||||
"type": "streamable-http",
|
||||
"url": "http://localhost:9999/mcp",
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Follow the same testing steps as SSE
|
||||
|
||||
## Benefits
|
||||
|
||||
1. **Accurate Status**: Server status now reflects actual connection state
|
||||
2. **Better UX**: Users can see when connections fail in real-time
|
||||
3. **Debugging**: Error messages help diagnose connection issues
|
||||
4. **Resource Management**: Keep-alive intervals are properly cleaned up on connection failures
|
||||
5. **Consistent Behavior**: All transport types (SSE, StreamableHTTP, stdio) now have proper event handling
|
||||
|
||||
## Compatibility
|
||||
|
||||
- **Backwards Compatible**: No breaking changes to existing functionality
|
||||
- **SDK Version**: Works with `@modelcontextprotocol/sdk` v1.20.2+
|
||||
- **Node.js**: Compatible with all supported Node.js versions
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements for the future:
|
||||
|
||||
1. Add automatic reconnection logic for transient failures
|
||||
2. Add connection health metrics (uptime, error count)
|
||||
3. Emit events for UI notifications when status changes
|
||||
4. Add configurable retry strategies per server
|
||||
Reference in New Issue
Block a user