Skip to main content
Glama

Bybit MCP Server

by sammcj
multiStepAgent.ts13.9 kB
/** * Multi-step agent service for enhanced agentic capabilities * Implements multi-step tool calling and workflow orchestration */ import type { AgentConfig, AgentState } from '@/types/agent'; import type { WorkflowEvent } from '@/types/workflow'; import { WorkflowEventEmitter, createWorkflowEvent } from '@/types/workflow'; import { agentConfigService } from './agentConfig'; import { aiClient } from './aiClient'; import { mcpClient } from './mcpClient'; import { agentMemory } from './agentMemory'; import { performanceOptimiser } from './performanceOptimiser'; import { systemPromptService } from './systemPrompt'; import type { ChatMessage } from '@/types/ai'; export class MultiStepAgentService { private availableTools: any[] = []; private isInitialized = false; private eventEmitter: WorkflowEventEmitter; private currentConfig: AgentConfig; private conversationHistory: ChatMessage[] = []; private currentConversationId?: string; constructor() { this.eventEmitter = new WorkflowEventEmitter(); this.currentConfig = agentConfigService.getConfig(); // Subscribe to config changes agentConfigService.subscribe((config) => { this.currentConfig = config; this.reinitializeAgents(); }); } /** * Initialize the agent service */ async initialize(): Promise<void> { if (this.isInitialized) return; try { console.log('🤖 Initializing Multi-Step Agent Service...'); // Load MCP tools await this.loadMCPTools(); // Initialize agents based on configuration await this.initializeAgents(); // Update state agentConfigService.updateState({ isProcessing: false }); this.isInitialized = true; console.log('✅ Multi-Step Agent Service initialized successfully'); } catch (error) { console.error('❌ Failed to initialize Multi-Step Agent Service:', error); agentConfigService.updateState({ isProcessing: false }); throw error; } } /** * Load MCP tools from the server */ private async loadMCPTools(): Promise<void> { try { console.log('🔧 Loading MCP tools...'); // Get available tools from MCP client const tools = await mcpClient.listTools(); this.availableTools = tools; console.log(`🔧 Loaded ${this.availableTools.length} MCP tools:`, this.availableTools.map(t => t.name)); } catch (error) { console.error('❌ Failed to load MCP tools:', error); // Continue with empty tools array for now this.availableTools = []; } } /** * Initialize agent system */ private async initializeAgents(): Promise<void> { console.log('🤖 Initializing multi-step agent system...'); // Agent system is ready - we'll use the existing AI client with multi-step logic console.log('✅ Multi-step agent system initialized'); } /** * Build system prompt based on configuration and memory context */ private async buildSystemPrompt(symbol?: string): Promise<string> { // Get base system prompt from centralized service const basePrompt = await systemPromptService.generateSystemPrompt({ includeTimestamp: true, includeTools: true, includeMemoryContext: false }); // Add memory context if available const memoryContext = agentMemory.buildContextSummary(symbol); const finalPrompt = basePrompt + memoryContext; return finalPrompt; } /** * Process a chat message with the agent using multi-step reasoning */ async chat(message: string): Promise<string> { if (!this.isInitialized) { await this.initialize(); } const startTime = Date.now(); const toolCallsCount = 0; try { agentConfigService.updateState({ isProcessing: true }); console.log('💬 Processing chat message with multi-step agent...'); // Start new conversation if needed if (!this.currentConversationId) { this.currentConversationId = agentMemory.startConversation(); } // Add user message to conversation history const userMessage: ChatMessage = { role: 'user', content: message }; this.conversationHistory.push(userMessage); agentMemory.addMessage(this.currentConversationId, userMessage); // Extract symbol from message for context const symbolMatch = message.match(/\b([A-Z]{2,5})(?:USD|USDT)?\b/); const symbol = symbolMatch ? symbolMatch[1] : undefined; // Run multi-step agent loop const result = await this.runAgentLoop(symbol); // Add assistant response to memory if (this.currentConversationId) { const assistantMessage: ChatMessage = { role: 'assistant', content: result }; agentMemory.addMessage(this.currentConversationId, assistantMessage); } // Record analysis in memory if (symbol) { const duration = Date.now() - startTime; agentMemory.recordAnalysis({ symbol, analysisType: this.determineAnalysisType(), query: message, response: result, toolsUsed: [], // Will be populated by runAgentLoop duration }); } // Record successful query const duration = Date.now() - startTime; agentConfigService.recordQuery(duration, toolCallsCount); return result; } catch (error) { console.error('❌ Multi-step agent chat failed:', error); agentConfigService.recordFailure(); agentConfigService.updateState({ isProcessing: false }); throw error; } finally { agentConfigService.updateState({ isProcessing: false }); } } /** * Run the multi-step agent reasoning loop */ private async runAgentLoop(symbol?: string): Promise<string> { const maxIterations = this.currentConfig.maxIterations; let iteration = 0; // Build system prompt once and cache it for this conversation const systemPrompt = await this.buildSystemPrompt(symbol); console.log('🎯 System prompt generated once for conversation'); const messages: ChatMessage[] = [ { role: 'system', content: systemPrompt }, ...this.conversationHistory ]; while (iteration < maxIterations) { iteration++; console.log(`🔄 Agent iteration ${iteration}/${maxIterations}`); // Emit workflow step event this.emitEvent(createWorkflowEvent('workflow_step', { stepName: `Iteration ${iteration}`, stepDescription: 'Agent reasoning and tool execution', progress: iteration, totalSteps: maxIterations })); // Get AI response with tool calling const response = await aiClient.chatWithTools(messages); // Find the latest assistant message const assistantMessages = response.filter(msg => msg.role === 'assistant'); const latestAssistant = assistantMessages[assistantMessages.length - 1]; if (!latestAssistant) { throw new Error('No assistant response received'); } // Check if there are tool calls if (latestAssistant.tool_calls && latestAssistant.tool_calls.length > 0) { console.log(`🔧 Processing ${latestAssistant.tool_calls.length} tool calls`); // Update conversation history with the complete response this.conversationHistory = response.slice(1); // Remove system message // Continue the loop for next iteration - rebuild messages with cached system prompt messages.length = 1; // Keep only system message (already cached) messages.push(...this.conversationHistory); continue; } // No more tool calls - we have the final response if (latestAssistant.content) { // Check if content is meaningful (not just placeholder text) const trimmedContent = latestAssistant.content.trim(); const isPlaceholder = trimmedContent === '...' || trimmedContent === '' || trimmedContent.length < 3; if (!isPlaceholder) { // Add final response to conversation history this.conversationHistory.push({ role: 'assistant', content: latestAssistant.content }); console.log(`✅ Multi-step agent completed in ${iteration} iterations`); return latestAssistant.content; } else { console.log(`⚠️ Received placeholder content: "${trimmedContent}", continuing iteration...`); // Continue to next iteration - treat as if no meaningful response } } // If we get here and it's not the last iteration, continue if (iteration < maxIterations) { console.log(`🔄 No meaningful response in iteration ${iteration}, continuing...`); continue; } // If we get here on the last iteration, something went wrong throw new Error('Assistant response has no meaningful content and no tool calls'); } // Max iterations reached const fallbackResponse = 'I apologise, but I reached the maximum number of reasoning steps. Let me provide what I can based on the analysis so far.'; this.conversationHistory.push({ role: 'assistant', content: fallbackResponse }); return fallbackResponse; } /** * Stream chat with real-time events */ async streamChat( message: string, onChunk: (chunk: string) => void, onEvent?: (event: WorkflowEvent) => void ): Promise<void> { if (!this.isInitialized) { await this.initialize(); } const startTime = Date.now(); const toolCallsCount = 0; try { agentConfigService.updateState({ isProcessing: true }); console.log('💬 Streaming chat with multi-step agent...'); // Subscribe to events if callback provided let unsubscribe: (() => void) | undefined; if (onEvent) { unsubscribe = this.onEvent(onEvent); } // Add user message to conversation history this.conversationHistory.push({ role: 'user', content: message }); // Run multi-step agent loop and stream the final response const result = await this.runAgentLoop(); // Stream the final result const words = result.split(' '); for (let i = 0; i < words.length; i++) { const chunk = (i === 0 ? '' : ' ') + words[i]; onChunk(chunk); // Small delay for streaming effect await new Promise(resolve => setTimeout(resolve, 30)); } // Clean up event subscription if (unsubscribe) { unsubscribe(); } // Record successful query const duration = Date.now() - startTime; agentConfigService.recordQuery(duration, toolCallsCount); } catch (error) { console.error('❌ Multi-step agent stream chat failed:', error); agentConfigService.recordFailure(); agentConfigService.updateState({ isProcessing: false }); throw error; } finally { agentConfigService.updateState({ isProcessing: false }); } } /** * Emit a workflow event */ private emitEvent(event: WorkflowEvent): void { this.eventEmitter.emit(event); } /** * Check if the service is connected and ready */ async isConnected(): Promise<boolean> { return this.isInitialized && this.availableTools.length > 0; } /** * Get current agent state */ getState(): AgentState { return agentConfigService.getState(); } /** * Subscribe to workflow events */ onEvent(listener: (event: WorkflowEvent) => void): () => void { this.eventEmitter.on('all', listener); return () => this.eventEmitter.off('all', listener); } /** * Determine analysis type based on current configuration */ private determineAnalysisType(): 'quick' | 'standard' | 'comprehensive' { const maxIterations = this.currentConfig.maxIterations; if (maxIterations <= 2) return 'quick'; if (maxIterations <= 5) return 'standard'; return 'comprehensive'; } // Note: Market context updating will be implemented in future iterations // when tool response interception is added to the agent loop /** * Reinitialise agents when configuration changes */ private async reinitializeAgents(): Promise<void> { if (!this.isInitialized) return; console.log('🔄 Reinitialising multi-step agents due to configuration change...'); try { await this.initializeAgents(); console.log('✅ Multi-step agents reinitialised successfully'); } catch (error) { console.error('❌ Failed to reinitialise multi-step agents:', error); } } /** * Get memory statistics */ getMemoryStats() { return agentMemory.getMemoryStats(); } /** * Get performance statistics */ getPerformanceStats() { return performanceOptimiser.getPerformanceStats(); } /** * Get conversation history for a symbol */ getSymbolHistory(symbol: string, limit: number = 5) { return agentMemory.getSymbolContext(symbol, limit); } /** * Get recent analysis history */ getAnalysisHistory(symbol?: string, limit: number = 10) { if (symbol) { return agentMemory.getSymbolAnalysisHistory(symbol, limit); } return agentMemory.getRecentAnalysisHistory(limit); } /** * Clear all memory data */ clearMemory(): void { agentMemory.clearAllMemory(); this.conversationHistory = []; this.currentConversationId = undefined; console.log('🧹 Multi-step agent memory cleared'); } /** * Start a new conversation session */ startNewConversation(): void { this.conversationHistory = []; this.currentConversationId = undefined; console.log('🆕 New conversation session started'); } } // Singleton instance export const multiStepAgent = new MultiStepAgentService();

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sammcj/bybit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server