stellar-cyber-mcp-agents
Version:
Model Context Protocol (MCP) server for Stellar Cyber security operations with specialized multi-agent analysis capabilities
188 lines • 6.91 kB
JavaScript
import { EventEmitter } from 'eventemitter3';
export class InMemoryCommunicationChannel extends EventEmitter {
requestHandlers = new Map();
pendingRequests = new Map();
requestQueue = new Map(); // Queue per agent
defaultTimeout = 30000; // 30 seconds
constructor() {
super();
}
async sendRequest(request) {
const targetKey = this.getAgentKey(request.targetAgentId);
const handler = this.requestHandlers.get(targetKey);
if (!handler) {
throw new Error(`No handler registered for agent ${targetKey}`);
}
// Add to queue if agent is busy (simple queue implementation)
const queue = this.requestQueue.get(targetKey) || [];
if (queue.length > 0) {
queue.push(request);
this.requestQueue.set(targetKey, queue);
}
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
this.pendingRequests.delete(request.id);
reject(new Error(`Request ${request.id} timed out after ${request.timeout}ms`));
}, request.timeout || this.defaultTimeout);
this.pendingRequests.set(request.id, { resolve, reject, timeout });
// Process request immediately if no queue
if (queue.length === 0) {
this.processRequest(request, handler);
}
});
}
async processRequest(request, handler) {
try {
const response = await handler(request);
this.resolveRequest(request.id, response);
}
catch (error) {
const errorResponse = {
requestId: request.id,
sourceAgentId: request.targetAgentId,
success: false,
error: {
code: 'PROCESSING_ERROR',
message: error instanceof Error ? error.message : 'Unknown error',
details: error
},
timestamp: new Date(),
processingTime: Date.now() - request.timestamp.getTime()
};
this.resolveRequest(request.id, errorResponse);
}
// Process next request in queue
const targetKey = this.getAgentKey(request.targetAgentId);
const queue = this.requestQueue.get(targetKey) || [];
if (queue.length > 0) {
const nextRequest = queue.shift();
this.requestQueue.set(targetKey, queue);
this.processRequest(nextRequest, handler);
}
}
resolveRequest(requestId, response) {
const pending = this.pendingRequests.get(requestId);
if (pending) {
clearTimeout(pending.timeout);
this.pendingRequests.delete(requestId);
pending.resolve(response);
}
}
subscribeToRequests(agentId, callback) {
const agentKey = this.getAgentKey(agentId);
this.requestHandlers.set(agentKey, callback);
}
unsubscribeFromRequests(agentId) {
const agentKey = this.getAgentKey(agentId);
this.requestHandlers.delete(agentKey);
this.requestQueue.delete(agentKey);
}
async broadcastEvent(event) {
// Emit to all listeners
this.emit('agent_event', event);
// Could also implement specific event routing here
this.emit(`event_${event.type}`, event);
}
subscribeToEvents(callback) {
this.on('agent_event', callback);
}
unsubscribeFromEvents(callback) {
this.off('agent_event', callback);
}
async close() {
// Clear all pending requests
for (const [requestId, pending] of this.pendingRequests) {
clearTimeout(pending.timeout);
pending.reject(new Error('Communication channel closed'));
}
this.pendingRequests.clear();
// Clear handlers and queues
this.requestHandlers.clear();
this.requestQueue.clear();
// Remove all listeners
this.removeAllListeners();
}
/**
* Get channel statistics
*/
getChannelStats() {
const queuedRequests = Array.from(this.requestQueue.values())
.reduce((sum, queue) => sum + queue.length, 0);
return {
activeHandlers: this.requestHandlers.size,
pendingRequests: this.pendingRequests.size,
queuedRequests,
totalQueues: this.requestQueue.size
};
}
/**
* Get queue status for an agent
*/
getAgentQueueStatus(agentId) {
const agentKey = this.getAgentKey(agentId);
const queue = this.requestQueue.get(agentKey) || [];
return {
queueSize: queue.length,
hasHandler: this.requestHandlers.has(agentKey)
};
}
/**
* Priority-based request sending
*/
async sendPriorityRequest(request) {
const targetKey = this.getAgentKey(request.targetAgentId);
const queue = this.requestQueue.get(targetKey) || [];
// Insert request based on priority
if (queue.length > 0) {
let insertIndex = 0;
for (let i = 0; i < queue.length; i++) {
if (request.priority > queue[i].priority) {
insertIndex = i;
break;
}
insertIndex = i + 1;
}
queue.splice(insertIndex, 0, request);
this.requestQueue.set(targetKey, queue);
}
return this.sendRequest(request);
}
/**
* Batch request sending
*/
async sendBatchRequests(requests) {
const promises = requests.map(request => this.sendRequest(request));
return Promise.all(promises);
}
/**
* Request with retry logic
*/
async sendRequestWithRetry(request, maxRetries = 3, retryDelay = 1000) {
let lastError = null;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await this.sendRequest(request);
}
catch (error) {
lastError = error instanceof Error ? error : new Error('Unknown error');
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)));
}
}
}
throw lastError;
}
getAgentKey(agentId) {
return `${agentId.type}:${agentId.instance}:${agentId.uuid}`;
}
}
export class NetworkCommunicationChannel extends InMemoryCommunicationChannel {
// TODO: Implement network-based communication for distributed agents
// This would use HTTP/WebSocket/gRPC for inter-agent communication
// For now, we'll use the in-memory implementation
constructor() {
super();
// Future: Add network transport configuration
}
}
//# sourceMappingURL=communication-channel.js.map