- Fixed MCP transport initialization (unblocked 111 tests) - Fixed database isolation and FTS5 search syntax (9 tests) - Fixed MSW mock server setup and handlers (6 tests) - Fixed MCP error handling response structures (16 tests) - Fixed performance test thresholds for CI environment (15 tests) - Fixed session management timeouts and cleanup (5 tests) - Fixed database connection management (3 tests) Improvements: - Added NODE_DB_PATH support for in-memory test databases - Added test mode logger suppression - Enhanced template sanitizer for security - Implemented environment-aware performance thresholds Results: 229/246 tests passing (93.5% success rate) Remaining: 16 tests need additional work (protocol compliance, timeouts) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
8.9 KiB
8.9 KiB
Agent 6: Session Management Fix Brief
Assignment
Fix 5 failing tests related to MCP session management and state persistence.
Files to Fix
tests/integration/mcp-protocol/session-management.test.ts(5 tests)
Specific Failures to Address
Based on the timeout issue observed, the session management tests are likely failing due to:
-
Session Creation Timeout
- Session initialization taking too long
- Missing or slow handshake process
-
Session State Persistence
- State not properly saved between requests
- Session data corruption or loss
-
Concurrent Session Handling
- Race conditions with multiple sessions
- Session ID conflicts
-
Session Cleanup
- Sessions not properly terminated
- Resource leaks causing subsequent timeouts
-
Session Recovery
- Failed session recovery after disconnect
- Invalid session state after errors
Root Causes
- Timeout Configuration: Default timeout too short for session operations
- State Management: Session state not properly isolated
- Resource Cleanup: Sessions leaving connections open
- Synchronization: Async operations not properly awaited
Recommended Fixes
1. Fix Session Creation and Timeout
describe('Session Management', () => {
let mcpClient: MCPClient;
let sessionManager: SessionManager;
// Increase timeout for session tests
jest.setTimeout(30000);
beforeEach(async () => {
sessionManager = new SessionManager();
mcpClient = new MCPClient({
sessionManager,
timeout: 10000 // Explicit timeout
});
// Ensure clean session state
await sessionManager.clearAllSessions();
});
afterEach(async () => {
// Proper cleanup
await mcpClient.close();
await sessionManager.clearAllSessions();
});
it('should create new session successfully', async () => {
const sessionId = await mcpClient.createSession({
clientId: 'test-client',
capabilities: ['tools', 'resources']
});
expect(sessionId).toBeDefined();
expect(typeof sessionId).toBe('string');
// Verify session is active
const session = await sessionManager.getSession(sessionId);
expect(session).toBeDefined();
expect(session.status).toBe('active');
});
});
2. Implement Proper Session State Management
class SessionManager {
private sessions: Map<string, Session> = new Map();
private locks: Map<string, Promise<void>> = new Map();
async createSession(config: SessionConfig): Promise<string> {
const sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
const session: Session = {
id: sessionId,
clientId: config.clientId,
capabilities: config.capabilities,
state: {},
status: 'active',
createdAt: new Date(),
lastActivity: new Date()
};
this.sessions.set(sessionId, session);
// Initialize session state
await this.initializeSessionState(sessionId);
return sessionId;
}
async getSession(sessionId: string): Promise<Session | null> {
const session = this.sessions.get(sessionId);
if (session) {
session.lastActivity = new Date();
}
return session || null;
}
async updateSessionState(sessionId: string, updates: Partial<SessionState>): Promise<void> {
// Use lock to prevent concurrent updates
const lockKey = `update-${sessionId}`;
while (this.locks.has(lockKey)) {
await this.locks.get(lockKey);
}
const lockPromise = this._updateSessionState(sessionId, updates);
this.locks.set(lockKey, lockPromise);
try {
await lockPromise;
} finally {
this.locks.delete(lockKey);
}
}
private async _updateSessionState(sessionId: string, updates: Partial<SessionState>): Promise<void> {
const session = this.sessions.get(sessionId);
if (!session) {
throw new Error(`Session ${sessionId} not found`);
}
session.state = { ...session.state, ...updates };
session.lastActivity = new Date();
}
async clearAllSessions(): Promise<void> {
// Wait for all locks to clear
await Promise.all(Array.from(this.locks.values()));
// Close all sessions
for (const session of this.sessions.values()) {
await this.closeSession(session.id);
}
this.sessions.clear();
}
private async closeSession(sessionId: string): Promise<void> {
const session = this.sessions.get(sessionId);
if (session) {
session.status = 'closed';
// Cleanup any resources
if (session.resources) {
await this.cleanupSessionResources(session);
}
}
}
}
3. Fix Concurrent Session Tests
it('should handle concurrent sessions', async () => {
const numSessions = 5;
const sessionPromises = [];
// Create multiple sessions concurrently
for (let i = 0; i < numSessions; i++) {
sessionPromises.push(
mcpClient.createSession({
clientId: `client-${i}`,
capabilities: ['tools']
})
);
}
const sessionIds = await Promise.all(sessionPromises);
// All sessions should be unique
const uniqueIds = new Set(sessionIds);
expect(uniqueIds.size).toBe(numSessions);
// Each session should be independently accessible
const verifyPromises = sessionIds.map(async (id) => {
const session = await sessionManager.getSession(id);
expect(session).toBeDefined();
expect(session.status).toBe('active');
});
await Promise.all(verifyPromises);
});
4. Implement Session Recovery
it('should recover session after disconnect', async () => {
// Create session
const sessionId = await mcpClient.createSession({
clientId: 'test-client',
capabilities: ['tools']
});
// Store some state
await mcpClient.request('session/update', {
sessionId,
state: { counter: 5, lastTool: 'list_nodes' }
});
// Simulate disconnect
await mcpClient.disconnect();
// Reconnect with same session ID
const newClient = new MCPClient({ sessionManager });
await newClient.resumeSession(sessionId);
// Verify state is preserved
const session = await sessionManager.getSession(sessionId);
expect(session.state.counter).toBe(5);
expect(session.state.lastTool).toBe('list_nodes');
});
5. Add Session Timeout Handling
it('should handle session timeouts gracefully', async () => {
// Create session with short timeout
const sessionId = await mcpClient.createSession({
clientId: 'test-client',
capabilities: ['tools'],
timeout: 1000 // 1 second
});
// Wait for timeout
await new Promise(resolve => setTimeout(resolve, 1500));
// Session should be expired
const session = await sessionManager.getSession(sessionId);
expect(session.status).toBe('expired');
// Attempting to use expired session should create new one
const response = await mcpClient.request('tools/list', { sessionId });
expect(response.newSessionId).toBeDefined();
expect(response.newSessionId).not.toBe(sessionId);
});
6. Session Cleanup Helper
class SessionCleanupService {
private cleanupInterval: NodeJS.Timeout | null = null;
start(sessionManager: SessionManager, intervalMs: number = 60000): void {
this.cleanupInterval = setInterval(async () => {
await this.cleanupExpiredSessions(sessionManager);
}, intervalMs);
}
stop(): void {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
}
async cleanupExpiredSessions(sessionManager: SessionManager): Promise<void> {
const now = new Date();
const sessions = await sessionManager.getAllSessions();
for (const session of sessions) {
const inactiveTime = now.getTime() - session.lastActivity.getTime();
// Expire after 30 minutes of inactivity
if (inactiveTime > 30 * 60 * 1000) {
await sessionManager.expireSession(session.id);
}
}
}
}
Testing Strategy
- Increase timeouts for session tests
- Ensure proper cleanup between tests
- Test both success and failure scenarios
- Verify resource cleanup
- Test concurrent session scenarios
Dependencies
- Depends on Agent 3 (MCP Error) for proper error handling
- May need MSW handlers from Agent 2 for session API mocking
Success Metrics
- All 5 session management tests pass
- No timeout errors
- Sessions properly isolated
- Resources cleaned up after tests
- Concurrent sessions handled correctly
Progress Tracking
Create /tests/integration/fixes/agent-6-progress.md and update after each fix:
# Agent 6 Progress
## Fixed Tests
- [ ] should create new session successfully
- [ ] should persist session state
- [ ] should handle concurrent sessions
- [ ] should recover session after disconnect
- [ ] should handle session timeouts gracefully
## Blockers
- None yet
## Notes
- [Document session management improvements]
- [Note any timeout adjustments made]