feat: support multiple concurrent Claude Code instances#5
Conversation
…e scanning Previously, each MCP server hardcoded port 9222 for the WebSocket bridge, so a second Claude Code instance would crash with EADDRINUSE. The extension also only connected to that single port. Now the server auto-discovers a free port in the range 9222–9231, and the extension connects to all ports simultaneously via a new ConnectionManager. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR Review: Support Multiple Concurrent Claude Code InstancesSummaryThis PR adds support for running multiple Claude Code instances concurrently by implementing port range discovery (9222-9231) on the server side and multi-port connection management on the extension side. Overall, this is a well-implemented feature with good test coverage. ✅ Strengths1. Code Quality
2. Architecture
3. Test Coverage
4. Documentation
🔍 Areas for Consideration1. Resource Management - ConnectionManager (Medium Priority)Issue: The Location: Impact:
Suggestion:
Alternative: If you want to keep the current approach, document this behavior and consider:
2. Request Broadcasting Logic Missing (High Priority)Issue: The extension connects to multiple servers, but I don't see logic for which server should handle a request. Questions:
Current state: Suggestion: Clarify the request routing strategy: // Option 1: Broadcast to all
async requestAll(action, params): Promise<unknown[]>
// Option 2: Route to first available
async request(action, params): Promise<unknown>
// Option 3: Route by criteria
async requestToServer(serverId, action, params): Promise<unknown>3. Error Handling in Port Discovery (Low Priority)Issue: In Scenario: If port 9222 has a transient permission error, the entire startup fails instead of trying 9223-9231. Suggestion: } catch (err) {
if ((err as NodeJS.ErrnoException).code === 'EADDRINUSE') {
console.error(`[Paparazzi] Port ${port} is already in use, trying next...`);
continue;
}
// Log but continue trying other ports for other errors
console.error(`[Paparazzi] Port ${port} failed with error, trying next...`, err);
continue; // Instead of: throw err;
}Only throw if all ports fail. 4. Port Range Size Configuration (Low Priority)Issue: Consideration:
Suggestion: export const WS_PORT_RANGE_SIZE = Number(process.env.PAPARAZZI_PORT_RANGE_SIZE) || 10;5. Logging Improvements (Low Priority)Current: Each client logs with its port: Enhancement: Consider adding timestamps and log levels for production debugging: console.error(`${new Date().toISOString()} ${this.logPrefix} Connected to MCP server`);6. Race Condition in tryPort (Very Low Priority)Location: Issue: The Impact: Minimal - port will be released shortly, but technically not immediately. Suggestion: wss.on('error', (err) => {
wss.close(() => reject(err));
});🔒 Security Review✅ No security concerns identified
🚀 Performance Considerations
Verdict: Performance impact is acceptable for the use case. 📝 Minor Suggestions
🎯 RecommendationsMust Address Before Merge:
Should Address:
Nice to Have:
✨ Overall AssessmentRating: 8/10 - Strong implementation with excellent test coverage. The architecture is sound, and the code quality is high. The main gap is clarity around request routing between multiple servers. Address the "must fix" item and this will be ready to merge! Great work on this feature! 🎉 |
Summary
EADDRINUSEwhen port 9222 is takenConnectionManagerconnects to all ports in the range simultaneously, so it talks to every running MCP server instanceDEFAULT_WS_PORT,WS_PORT_RANGE_SIZE) in@paparazzi/sharedso both sides agree on the rangeTest plan
pnpm build— all packages compilepnpm test:run— 59/59 tests pass (3 new port discovery tests)pnpm lint— cleanpnpm typecheck— cleanpnpm knip— no new unused exports🤖 Generated with Claude Code