vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
998 lines (997 loc) • 45.6 kB
JavaScript
import logger from '../../logger.js';
import { sseNotifier } from '../sse-notifier/index.js';
import { websocketServer } from '../websocket-server/index.js';
import { httpAgentAPI } from '../http-agent-api/index.js';
import { PortAllocator } from '../../utils/port-allocator.js';
const DEFAULT_CONFIG = {
sse: {
enabled: true
},
websocket: {
enabled: true,
port: 8080,
path: '/agent-ws'
},
http: {
enabled: true,
port: 3011,
cors: true
},
stdio: {
enabled: true
}
};
const DEFAULT_PORT_RANGES = {
websocket: { start: 8080, end: 8090, service: 'websocket' },
http: { start: 3011, end: 3030, service: 'http' },
sse: { start: 3000, end: 3010, service: 'sse' }
};
function getPortRangesFromEnvironment() {
logger.debug('Reading port ranges from environment variables with enhanced error handling');
const envVarErrors = [];
const envVarWarnings = [];
function safeParsePortRange(primaryVar, primaryValue, fallbackVar, fallbackValue, defaultRange, serviceName) {
if (primaryValue) {
try {
const range = PortAllocator.parsePortRange(primaryValue, defaultRange);
if (range.start === defaultRange.start && range.end === defaultRange.end &&
primaryValue !== `${defaultRange.start}-${defaultRange.end}` &&
primaryValue !== defaultRange.start.toString()) {
envVarErrors.push({
variable: primaryVar,
value: primaryValue,
error: 'Invalid format, using default range'
});
logger.warn({
variable: primaryVar,
value: primaryValue,
defaultUsed: `${defaultRange.start}-${defaultRange.end}`,
service: serviceName
}, `Invalid environment variable format for ${primaryVar}, using default`);
}
else {
logger.debug({
variable: primaryVar,
value: primaryValue,
parsed: `${range.start}-${range.end}`,
service: serviceName
}, `Successfully parsed ${primaryVar}`);
}
return { range, source: primaryVar };
}
catch (error) {
envVarErrors.push({
variable: primaryVar,
value: primaryValue,
error: error instanceof Error ? error.message : 'Parse error'
});
logger.error({
variable: primaryVar,
value: primaryValue,
error: error instanceof Error ? error.message : 'Unknown error',
service: serviceName
}, `Failed to parse ${primaryVar}, trying fallback`);
}
}
if (fallbackValue) {
try {
const range = PortAllocator.parsePortRange(fallbackValue, defaultRange);
if (range.start === defaultRange.start && range.end === defaultRange.end &&
fallbackValue !== `${defaultRange.start}-${defaultRange.end}` &&
fallbackValue !== defaultRange.start.toString()) {
envVarErrors.push({
variable: fallbackVar,
value: fallbackValue,
error: 'Invalid format, using default range'
});
logger.warn({
variable: fallbackVar,
value: fallbackValue,
defaultUsed: `${defaultRange.start}-${defaultRange.end}`,
service: serviceName
}, `Invalid environment variable format for ${fallbackVar}, using default`);
}
else {
logger.debug({
variable: fallbackVar,
value: fallbackValue,
parsed: `${range.start}-${range.end}`,
service: serviceName
}, `Successfully parsed ${fallbackVar}`);
}
return { range, source: fallbackVar };
}
catch (error) {
envVarErrors.push({
variable: fallbackVar,
value: fallbackValue,
error: error instanceof Error ? error.message : 'Parse error'
});
logger.error({
variable: fallbackVar,
value: fallbackValue,
error: error instanceof Error ? error.message : 'Unknown error',
service: serviceName
}, `Failed to parse ${fallbackVar}, using default`);
}
}
logger.info({
service: serviceName,
defaultRange: `${defaultRange.start}-${defaultRange.end}`,
reason: 'No valid environment variables found'
}, `Using default port range for ${serviceName} service`);
return { range: defaultRange, source: 'default' };
}
const websocketResult = safeParsePortRange('WEBSOCKET_PORT', process.env.WEBSOCKET_PORT, 'WEBSOCKET_PORT_RANGE', process.env.WEBSOCKET_PORT_RANGE, DEFAULT_PORT_RANGES.websocket, 'websocket');
const httpResult = safeParsePortRange('HTTP_AGENT_PORT', process.env.HTTP_AGENT_PORT, 'HTTP_AGENT_PORT_RANGE', process.env.HTTP_AGENT_PORT_RANGE, DEFAULT_PORT_RANGES.http, 'http');
const sseResult = safeParsePortRange('SSE_PORT', process.env.SSE_PORT, 'SSE_PORT_RANGE', process.env.SSE_PORT_RANGE, DEFAULT_PORT_RANGES.sse, 'sse');
logger.info({
websocket: {
source: websocketResult.source,
range: `${websocketResult.range.start}-${websocketResult.range.end}`,
envVars: {
WEBSOCKET_PORT: process.env.WEBSOCKET_PORT || 'not set',
WEBSOCKET_PORT_RANGE: process.env.WEBSOCKET_PORT_RANGE || 'not set'
}
},
http: {
source: httpResult.source,
range: `${httpResult.range.start}-${httpResult.range.end}`,
envVars: {
HTTP_AGENT_PORT: process.env.HTTP_AGENT_PORT || 'not set',
HTTP_AGENT_PORT_RANGE: process.env.HTTP_AGENT_PORT_RANGE || 'not set'
}
},
sse: {
source: sseResult.source,
range: `${sseResult.range.start}-${sseResult.range.end}`,
envVars: {
SSE_PORT: process.env.SSE_PORT || 'not set',
SSE_PORT_RANGE: process.env.SSE_PORT_RANGE || 'not set'
}
},
errors: envVarErrors,
warnings: envVarWarnings
}, 'Port ranges configured from environment with enhanced error handling');
if (envVarErrors.length > 0) {
logger.warn({
errorCount: envVarErrors.length,
errors: envVarErrors,
impact: 'Using default port ranges for affected services'
}, 'Environment variable parsing errors detected');
}
if (envVarWarnings.length > 0) {
logger.info({
warningCount: envVarWarnings.length,
warnings: envVarWarnings
}, 'Environment variable parsing warnings');
}
return {
websocket: websocketResult.range,
http: httpResult.range,
sse: sseResult.range
};
}
function validatePortRanges(ranges) {
const warnings = [];
const overlaps = [];
const services = Object.entries(ranges);
for (let i = 0; i < services.length; i++) {
for (let j = i + 1; j < services.length; j++) {
const [service1Name, range1] = services[i];
const [service2Name, range2] = services[j];
const overlapStart = Math.max(range1.start, range2.start);
const overlapEnd = Math.min(range1.end, range2.end);
if (overlapStart <= overlapEnd) {
const conflictRange = overlapStart === overlapEnd ?
`${overlapStart}` :
`${overlapStart}-${overlapEnd}`;
overlaps.push({
service1: service1Name,
service2: service2Name,
conflictRange
});
warnings.push(`Port range overlap detected: ${service1Name} (${range1.start}-${range1.end}) ` +
`and ${service2Name} (${range2.start}-${range2.end}) conflict on ports ${conflictRange}`);
}
}
}
if (overlaps.length > 0) {
logger.warn({ overlaps, warnings }, 'Port range validation found conflicts');
}
else {
logger.debug('Port range validation passed - no conflicts detected');
}
return {
valid: overlaps.length === 0,
warnings,
overlaps
};
}
class TransportManager {
static instance;
config;
isStarted = false;
startedServices = [];
startupTimestamp;
startupInProgress = false;
startupPromise;
static getInstance() {
if (!TransportManager.instance) {
TransportManager.instance = new TransportManager();
}
return TransportManager.instance;
}
constructor() {
this.config = { ...DEFAULT_CONFIG };
}
configure(config) {
this.config = {
...this.config,
...config,
sse: { ...this.config.sse, ...config.sse },
websocket: { ...this.config.websocket, ...config.websocket },
http: { ...this.config.http, ...config.http },
stdio: { ...this.config.stdio, ...config.stdio }
};
logger.info({ config: this.config }, 'Transport manager configured');
}
reset() {
this.config = { ...DEFAULT_CONFIG };
this.isStarted = false;
this.startedServices = [];
this.startupTimestamp = undefined;
logger.debug('Transport manager reset to initial state');
}
async startAll() {
if (this.isStarted) {
logger.warn('Transport manager already started');
return;
}
if (this.startupInProgress) {
logger.warn('Transport manager startup already in progress, waiting...');
await this.waitForStartupCompletion();
return;
}
this.startupInProgress = true;
this.startupPromise = (async () => {
try {
this.startupTimestamp = Date.now();
logger.info('Starting unified communication protocol transport services with dynamic port allocation...');
const portRanges = getPortRangesFromEnvironment();
const validation = validatePortRanges(portRanges);
if (!validation.valid) {
validation.warnings.forEach(warning => logger.warn(warning));
}
const servicesToAllocate = [];
if (this.config.websocket.enabled) {
servicesToAllocate.push(portRanges.websocket);
}
if (this.config.http.enabled) {
servicesToAllocate.push(portRanges.http);
}
if (this.config.sse.enabled && this.config.sse.portRange) {
servicesToAllocate.push(portRanges.sse);
}
const allocationSummary = await PortAllocator.allocatePortsForServices(servicesToAllocate);
for (const [serviceName, allocation] of allocationSummary.allocations) {
if (allocation.success) {
if (serviceName === 'websocket') {
this.config.websocket.allocatedPort = allocation.port;
}
else if (serviceName === 'http') {
this.config.http.allocatedPort = allocation.port;
}
else if (serviceName === 'sse') {
this.config.sse.allocatedPort = allocation.port;
}
}
}
await this.startServicesWithAllocatedPorts(allocationSummary);
this.isStarted = true;
this.logStartupSummary(allocationSummary);
}
catch (error) {
logger.error({ err: error }, 'Failed to start transport services');
await this.stopAll().catch(stopError => {
logger.error({ err: stopError }, 'Failed to cleanup after startup failure');
});
throw error;
}
finally {
this.startupInProgress = false;
this.startupPromise = undefined;
}
})();
await this.startupPromise;
}
async startServicesWithAllocatedPorts(allocationSummary) {
const serviceFailures = [];
const serviceSuccesses = [];
logger.info('Starting transport services with graceful degradation enabled');
if (this.config.stdio.enabled) {
try {
logger.info('stdio transport: Enabled (handled by MCP server)');
this.startedServices.push('stdio');
serviceSuccesses.push({ service: 'stdio', note: 'MCP server managed' });
}
catch (error) {
const failure = { service: 'stdio', reason: 'Startup failed', error };
serviceFailures.push(failure);
logger.error({ err: error }, 'stdio transport: Failed to start');
}
}
if (this.config.sse.enabled) {
try {
logger.info('SSE transport: Enabled (integrated with MCP server)');
this.startedServices.push('sse');
serviceSuccesses.push({ service: 'sse', note: 'MCP server integrated' });
}
catch (error) {
const failure = { service: 'sse', reason: 'Startup failed', error };
serviceFailures.push(failure);
logger.error({ err: error }, 'SSE transport: Failed to start');
}
}
if (this.config.websocket.enabled) {
const allocation = allocationSummary.allocations.get('websocket');
if (allocation && allocation.success) {
try {
await websocketServer.start(allocation.port);
logger.info({
port: allocation.port,
path: this.config.websocket.path,
attempted: allocation.attempted.length
}, 'WebSocket transport: Started with allocated port');
this.startedServices.push('websocket');
serviceSuccesses.push({ service: 'websocket', port: allocation.port });
}
catch (error) {
logger.warn({
err: error,
port: allocation.port,
retryEnabled: true
}, 'WebSocket transport: Initial startup failed, attempting retry with alternative ports');
const envPortRanges = getPortRangesFromEnvironment();
const retryRange = envPortRanges.websocket;
const retryResult = await this.retryServiceStartup('websocket', retryRange);
if (retryResult.success) {
logger.info({
port: retryResult.port,
attempts: retryResult.attempts,
path: this.config.websocket.path
}, 'WebSocket transport: Started successfully after retry');
this.startedServices.push('websocket');
serviceSuccesses.push({ service: 'websocket', port: retryResult.port });
}
else {
const failure = { service: 'websocket', reason: 'Service startup failed after retries', error };
serviceFailures.push(failure);
logger.error({
attempts: retryResult.attempts,
error: retryResult.error,
gracefulDegradation: true
}, 'WebSocket transport: Failed to start after retries, continuing with other transports');
}
}
}
else {
logger.warn({
allocation: allocation || 'none',
retryEnabled: true
}, 'WebSocket transport: Initial port allocation failed, attempting retry with alternative ports');
const envPortRanges = getPortRangesFromEnvironment();
const retryRange = envPortRanges.websocket;
const retryResult = await this.retryServiceStartup('websocket', retryRange);
if (retryResult.success) {
logger.info({
port: retryResult.port,
attempts: retryResult.attempts,
path: this.config.websocket.path
}, 'WebSocket transport: Started successfully after retry');
this.startedServices.push('websocket');
serviceSuccesses.push({ service: 'websocket', port: retryResult.port });
}
else {
const failure = { service: 'websocket', reason: 'Port allocation and retries failed' };
serviceFailures.push(failure);
logger.warn({
attempts: retryResult.attempts,
error: retryResult.error,
gracefulDegradation: true
}, 'WebSocket transport: Failed to allocate port after retries, continuing with other transports');
}
}
}
if (this.config.http.enabled) {
const allocation = allocationSummary.allocations.get('http');
if (allocation && allocation.success) {
try {
await httpAgentAPI.start(allocation.port);
logger.info({
port: allocation.port,
cors: this.config.http.cors,
attempted: allocation.attempted.length
}, 'HTTP transport: Started with allocated port');
this.startedServices.push('http');
serviceSuccesses.push({ service: 'http', port: allocation.port });
}
catch (error) {
logger.warn({
err: error,
port: allocation.port,
retryEnabled: true
}, 'HTTP transport: Initial startup failed, attempting retry with alternative ports');
const envPortRanges = getPortRangesFromEnvironment();
const retryRange = envPortRanges.http;
const retryResult = await this.retryServiceStartup('http', retryRange);
if (retryResult.success) {
logger.info({
port: retryResult.port,
attempts: retryResult.attempts,
cors: this.config.http.cors
}, 'HTTP transport: Started successfully after retry');
this.startedServices.push('http');
serviceSuccesses.push({ service: 'http', port: retryResult.port });
}
else {
const failure = { service: 'http', reason: 'Service startup failed after retries', error };
serviceFailures.push(failure);
logger.error({
attempts: retryResult.attempts,
error: retryResult.error,
gracefulDegradation: true
}, 'HTTP transport: Failed to start after retries, continuing with other transports');
}
}
}
else {
logger.warn({
allocation: allocation || 'none',
retryEnabled: true
}, 'HTTP transport: Initial port allocation failed, attempting retry with alternative ports');
const envPortRanges = getPortRangesFromEnvironment();
const retryRange = envPortRanges.http;
const retryResult = await this.retryServiceStartup('http', retryRange);
if (retryResult.success) {
logger.info({
port: retryResult.port,
attempts: retryResult.attempts,
cors: this.config.http.cors
}, 'HTTP transport: Started successfully after retry');
this.startedServices.push('http');
serviceSuccesses.push({ service: 'http', port: retryResult.port });
}
else {
const failure = { service: 'http', reason: 'Port allocation and retries failed' };
serviceFailures.push(failure);
logger.warn({
attempts: retryResult.attempts,
error: retryResult.error,
gracefulDegradation: true
}, 'HTTP transport: Failed to allocate port after retries, continuing with other transports');
}
}
}
this.logGracefulDegradationSummary(serviceSuccesses, serviceFailures);
}
logGracefulDegradationSummary(successes, failures) {
const totalServices = successes.length + failures.length;
const successRate = totalServices > 0 ? (successes.length / totalServices * 100).toFixed(1) : '0';
logger.info({
gracefulDegradation: {
totalServices,
successfulServices: successes.length,
failedServices: failures.length,
successRate: `${successRate}%`,
availableTransports: successes.map(s => s.service),
failedTransports: failures.map(f => f.service)
},
serviceDetails: {
successes: successes.map(s => ({
service: s.service,
port: s.port || 'N/A',
note: s.note || 'Network service'
})),
failures: failures.map(f => ({
service: f.service,
reason: f.reason,
hasError: !!f.error
}))
}
}, 'Graceful degradation summary: Transport services startup completed');
if (failures.length > 0) {
if (successes.length === 0) {
logger.error('Critical: All transport services failed to start');
}
else if (failures.some(f => f.service === 'websocket') && failures.some(f => f.service === 'http')) {
logger.warn('Network transports (WebSocket + HTTP) failed, continuing with SSE + stdio only');
}
else if (failures.some(f => f.service === 'websocket')) {
logger.warn('WebSocket transport failed, continuing with HTTP + SSE + stdio');
}
else if (failures.some(f => f.service === 'http')) {
logger.warn('HTTP transport failed, continuing with WebSocket + SSE + stdio');
}
}
else {
logger.info('All enabled transport services started successfully');
}
}
logStartupSummary(allocationSummary) {
const successful = Array.from(allocationSummary.allocations.values()).filter(r => r.success);
const attempted = allocationSummary.totalAttempted;
const conflicts = [];
const serviceDetails = {};
for (const [serviceName, allocation] of allocationSummary.allocations) {
attempted.push(...allocation.attempted);
serviceDetails[serviceName] = {
status: allocation.success ? 'success' : 'failed',
requested: allocation.attempted[0],
allocated: allocation.success ? allocation.port : null,
attempts: allocation.attempted.length,
attemptedPorts: allocation.attempted,
success: allocation.success,
conflicts: allocation.success ? [] : allocation.attempted,
reason: allocation.error
};
if (allocation.success) {
}
else {
conflicts.push(...allocation.attempted);
}
}
const allocationStats = {
totalServicesRequested: allocationSummary.allocations.size,
successfulAllocations: successful.length,
failedAllocations: allocationSummary.allocations.size - successful.length,
successRate: (successful.length / allocationSummary.allocations.size * 100).toFixed(1),
totalPortsAttempted: attempted.length,
uniquePortsAttempted: [...new Set(attempted)].length,
conflictedPorts: [...new Set(conflicts)],
conflictCount: [...new Set(conflicts)].length
};
const enhancedServiceStatus = {
total: this.startedServices.length,
started: this.startedServices,
websocket: this.config.websocket.allocatedPort ?
{
port: this.config.websocket.allocatedPort,
status: 'started',
endpoint: `ws://localhost:${this.config.websocket.allocatedPort}${this.config.websocket.path}`,
allocation: serviceDetails.websocket || null
} :
{
status: 'failed',
allocation: serviceDetails.websocket || null
},
http: this.config.http.allocatedPort ?
{
port: this.config.http.allocatedPort,
status: 'started',
endpoint: `http://localhost:${this.config.http.allocatedPort}`,
allocation: serviceDetails.http || null
} :
{
status: 'failed',
allocation: serviceDetails.http || null
},
sse: {
status: 'integrated',
note: 'MCP server',
port: this.config.sse.allocatedPort || 'N/A',
allocation: serviceDetails.sse || null
},
stdio: {
status: 'enabled',
note: 'MCP server',
allocation: 'N/A (no network port required)'
}
};
logger.info({
summary: 'Transport services startup completed with dynamic port allocation',
services: enhancedServiceStatus,
portAllocation: {
statistics: allocationStats,
attempted: [...new Set(attempted)],
successful,
conflicts: [...new Set(conflicts)],
serviceDetails
},
performance: {
startupTime: Date.now() - (this.startupTimestamp || Date.now()),
servicesStarted: this.startedServices.length,
portsAllocated: successful.length
}
}, 'Transport Manager: Startup Summary with Dynamic Port Allocation');
for (const [serviceName, details] of Object.entries(serviceDetails)) {
if (details.success) {
logger.info({
service: serviceName,
requestedPort: details.requested,
allocatedPort: details.allocated,
attempts: details.attempts,
status: 'success'
}, `Port allocation successful: ${serviceName} service`);
}
else {
logger.warn({
service: serviceName,
requestedPort: details.requested,
attemptedPorts: details.attemptedPorts,
attempts: details.attempts,
conflicts: details.conflicts,
status: 'failed'
}, `Port allocation failed: ${serviceName} service`);
}
}
logger.info({
successRate: `${allocationStats.successRate}%`,
successful: allocationStats.successfulAllocations,
failed: allocationStats.failedAllocations,
totalAttempts: allocationStats.totalPortsAttempted,
conflicts: allocationStats.conflictCount
}, 'Port Allocation Summary Statistics');
this.logDetailedServiceStatus();
}
logDetailedServiceStatus() {
logger.info('=== Transport Service Status Details ===');
if (this.config.websocket.enabled) {
const wsStatus = {
service: 'WebSocket',
enabled: true,
allocatedPort: this.config.websocket.allocatedPort,
configuredPort: this.config.websocket.port,
path: this.config.websocket.path,
endpoint: this.config.websocket.allocatedPort ?
`ws://localhost:${this.config.websocket.allocatedPort}${this.config.websocket.path}` :
'Not available',
status: this.startedServices.includes('websocket') ? 'running' : 'failed',
connections: this.startedServices.includes('websocket') ?
(typeof websocketServer.getConnectionCount === 'function' ? websocketServer.getConnectionCount() : 0) : 0
};
logger.info(wsStatus, 'WebSocket Service Status');
}
else {
logger.info({ service: 'WebSocket', enabled: false }, 'WebSocket Service Status: Disabled');
}
if (this.config.http.enabled) {
const httpStatus = {
service: 'HTTP Agent API',
enabled: true,
allocatedPort: this.config.http.allocatedPort,
configuredPort: this.config.http.port,
cors: this.config.http.cors,
endpoint: this.config.http.allocatedPort ?
`http://localhost:${this.config.http.allocatedPort}` :
'Not available',
status: this.startedServices.includes('http') ? 'running' : 'failed'
};
logger.info(httpStatus, 'HTTP Agent API Service Status');
}
else {
logger.info({ service: 'HTTP Agent API', enabled: false }, 'HTTP Agent API Service Status: Disabled');
}
if (this.config.sse.enabled) {
const sseStatus = {
service: 'SSE (Server-Sent Events)',
enabled: true,
allocatedPort: this.config.sse.allocatedPort || 'Integrated with MCP server',
endpoint: this.config.sse.allocatedPort ?
`http://localhost:${this.config.sse.allocatedPort}/events` :
'Integrated with MCP server',
status: this.startedServices.includes('sse') ? 'running' : 'integrated',
connections: this.startedServices.includes('sse') ?
(typeof sseNotifier.getConnectionCount === 'function' ? sseNotifier.getConnectionCount() : 'N/A') : 'N/A',
note: 'Integrated with MCP server lifecycle'
};
logger.info(sseStatus, 'SSE Service Status');
}
else {
logger.info({ service: 'SSE', enabled: false }, 'SSE Service Status: Disabled');
}
if (this.config.stdio.enabled) {
const stdioStatus = {
service: 'Stdio (Standard Input/Output)',
enabled: true,
port: 'N/A (no network port required)',
endpoint: 'stdio://mcp-server',
status: this.startedServices.includes('stdio') ? 'running' : 'enabled',
note: 'Handled by MCP server directly'
};
logger.info(stdioStatus, 'Stdio Service Status');
}
else {
logger.info({ service: 'Stdio', enabled: false }, 'Stdio Service Status: Disabled');
}
logger.info('=== End Transport Service Status Details ===');
}
async retryServiceStartup(serviceName, originalRange, maxRetries = 3) {
logger.info({
service: serviceName,
originalRange: `${originalRange.start}-${originalRange.end}`,
maxRetries,
operation: 'service_retry_start'
}, `Starting service retry for ${serviceName}`);
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
logger.debug({
service: serviceName,
attempt,
maxRetries,
operation: 'service_retry_attempt'
}, `Retry attempt ${attempt} for ${serviceName} service`);
const retryRange = originalRange;
const allocationResult = await PortAllocator.findAvailablePortInRange(retryRange);
if (allocationResult.success) {
if (serviceName === 'websocket') {
await websocketServer.start(allocationResult.port);
this.config.websocket.allocatedPort = allocationResult.port;
}
else if (serviceName === 'http') {
await httpAgentAPI.start(allocationResult.port);
this.config.http.allocatedPort = allocationResult.port;
}
logger.info({
service: serviceName,
port: allocationResult.port,
attempt,
retryRange: `${retryRange.start}-${retryRange.end}`,
operation: 'service_retry_success'
}, `Service retry successful for ${serviceName} on attempt ${attempt}`);
return {
success: true,
port: allocationResult.port,
attempts: attempt
};
}
else {
logger.warn({
service: serviceName,
attempt,
retryRange: `${retryRange.start}-${retryRange.end}`,
operation: 'service_retry_port_failed'
}, `Port allocation failed for ${serviceName} retry attempt ${attempt}`);
}
}
catch (error) {
logger.warn({
service: serviceName,
attempt,
error: error instanceof Error ? error.message : 'Unknown error',
operation: 'service_retry_error'
}, `Service startup failed for ${serviceName} retry attempt ${attempt}`);
if (attempt === maxRetries) {
return {
success: false,
attempts: attempt,
error: error instanceof Error ? error.message : 'Unknown error'
};
}
}
const backoffMs = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
logger.debug({
service: serviceName,
attempt,
backoffMs,
operation: 'service_retry_backoff'
}, `Waiting ${backoffMs}ms before next retry attempt`);
await new Promise(resolve => setTimeout(resolve, backoffMs));
}
return {
success: false,
attempts: maxRetries,
error: `All ${maxRetries} retry attempts failed`
};
}
async stopAll() {
if (!this.isStarted) {
logger.warn('Transport manager not started');
return;
}
try {
logger.info('Stopping unified communication protocol transport services...');
if (this.startedServices.includes('websocket')) {
await websocketServer.stop();
logger.info('WebSocket transport: Stopped');
}
if (this.startedServices.includes('http')) {
await httpAgentAPI.stop();
logger.info('HTTP transport: Stopped');
}
if (this.startedServices.includes('sse')) {
logger.info('SSE transport: Stopped (handled by MCP server)');
}
if (this.startedServices.includes('stdio')) {
logger.info('stdio transport: Stopped (handled by MCP server)');
}
this.isStarted = false;
this.startedServices = [];
logger.info('All transport services stopped successfully');
}
catch (error) {
logger.error({ err: error }, 'Failed to stop transport services');
throw error;
}
}
async restart() {
logger.info('Restarting transport services...');
await this.stopAll();
await this.startAll();
logger.info('Transport services restarted successfully');
}
getStatus() {
const serviceDetails = {};
const websocketRunning = this.startedServices.includes('websocket');
if (this.config.websocket.enabled) {
serviceDetails.websocket = {
port: this.config.websocket.allocatedPort || this.config.websocket.port,
path: this.config.websocket.path,
connections: websocketRunning && typeof websocketServer.getConnectionCount === 'function' ? websocketServer.getConnectionCount() : 0,
connectedAgents: websocketRunning && typeof websocketServer.getConnectedAgents === 'function' ? websocketServer.getConnectedAgents() : [],
running: websocketRunning
};
}
const httpRunning = this.startedServices.includes('http');
if (this.config.http.enabled) {
serviceDetails.http = {
port: this.config.http.allocatedPort || this.config.http.port,
cors: this.config.http.cors,
running: httpRunning
};
}
const sseRunning = this.startedServices.includes('sse');
if (this.config.sse.enabled) {
serviceDetails.sse = {
connections: sseRunning && typeof sseNotifier.getConnectionCount === 'function' ? sseNotifier.getConnectionCount() : 0,
enabled: true,
running: sseRunning
};
}
const stdioRunning = this.startedServices.includes('stdio');
if (this.config.stdio.enabled) {
serviceDetails.stdio = {
enabled: true,
note: 'Handled by MCP server',
running: stdioRunning
};
}
return {
isStarted: this.isStarted,
isConfigured: this.config.websocket.enabled || this.config.http.enabled || this.config.sse.enabled || this.config.stdio.enabled,
startupInProgress: this.startupInProgress,
startedServices: this.startedServices,
config: this.config,
serviceDetails,
websocket: this.config.websocket.enabled ? {
running: websocketRunning,
port: this.config.websocket.allocatedPort || this.config.websocket.port,
path: this.config.websocket.path,
connections: websocketRunning && typeof websocketServer.getConnectionCount === 'function' ? websocketServer.getConnectionCount() : 0
} : undefined,
http: this.config.http.enabled ? {
running: httpRunning,
port: this.config.http.allocatedPort || this.config.http.port,
cors: this.config.http.cors
} : undefined,
sse: this.config.sse.enabled ? {
running: sseRunning,
connections: sseRunning && typeof sseNotifier.getConnectionCount === 'function' ? sseNotifier.getConnectionCount() : 0
} : undefined,
stdio: this.config.stdio.enabled ? {
running: stdioRunning
} : undefined
};
}
isTransportRunning(transport) {
return this.isStarted && this.startedServices.includes(transport);
}
getTransportConfig(transport) {
return this.config[transport];
}
setTransportEnabled(transport, enabled) {
this.config[transport].enabled = enabled;
logger.info({ transport, enabled }, 'Transport enabled status updated');
}
getAllocatedPorts() {
return {
websocket: this.startedServices.includes('websocket') ? this.config.websocket.allocatedPort : undefined,
http: this.startedServices.includes('http') ? this.config.http.allocatedPort : undefined,
sse: this.startedServices.includes('sse') ? this.config.sse.allocatedPort : undefined,
stdio: undefined
};
}
async waitForStartupCompletion() {
if (this.startupPromise) {
await this.startupPromise;
}
}
getServicePort(serviceName) {
switch (serviceName) {
case 'websocket':
return this.startedServices.includes('websocket') ? this.config.websocket.allocatedPort : undefined;
case 'http':
return this.startedServices.includes('http') ? this.config.http.allocatedPort : undefined;
case 'sse':
return this.startedServices.includes('sse') ? this.config.sse.allocatedPort : undefined;
case 'stdio':
return undefined;
default:
logger.warn({ serviceName }, 'Unknown service name for port query');
return undefined;
}
}
getServiceEndpoints() {
const endpoints = {};
if (this.startedServices.includes('websocket') && this.config.websocket.allocatedPort) {
endpoints.websocket = `ws://localhost:${this.config.websocket.allocatedPort}${this.config.websocket.path}`;
}
if (this.startedServices.includes('http') && this.config.http.allocatedPort) {
endpoints.http = `http://localhost:${this.config.http.allocatedPort}`;
}
if (this.startedServices.includes('sse') && this.config.sse.allocatedPort) {
endpoints.sse = `http://localhost:${this.config.sse.allocatedPort}/events`;
}
endpoints.stdio = 'stdio://mcp-server';
return endpoints;
}
async getHealthStatus() {
const health = {};
health.stdio = {
status: this.config.stdio.enabled ? 'healthy' : 'disabled',
details: { note: 'Handled by MCP server' }
};
health.sse = {
status: this.config.sse.enabled ? 'healthy' : 'disabled',
details: {
connections: this.isTransportRunning('sse') ?
(typeof sseNotifier.getConnectionCount === 'function' ? sseNotifier.getConnectionCount() : 0) : 0
}
};
if (this.config.websocket.enabled) {
try {
const connectionCount = typeof websocketServer.getConnectionCount === 'function' ? websocketServer.getConnectionCount() : 0;
health.websocket = {
status: this.isTransportRunning('websocket') ? 'healthy' : 'unhealthy',
details: {
port: this.config.websocket.port,
connections: connectionCount,
connectedAgents: websocketServer.getConnectedAgents().length
}
};
}
catch (error) {
health.websocket = {
status: 'unhealthy',
details: { error: error instanceof Error ? error.message : 'Unknown error' }
};
}
}
else {
health.websocket = { status: 'disabled' };
}
if (this.config.http.enabled) {
try {
health.http = {
status: this.isTransportRunning('http') ? 'healthy' : 'unhealthy',
details: {
port: this.config.http.port,
cors: this.config.http.cors
}
};
}
catch (error) {
health.http = {
status: 'unhealthy',
details: { error: error instanceof Error ? error.message : 'Unknown error' }
};
}
}
else {
health.http = { status: 'disabled' };
}
return health;
}
}
export const transportManager = TransportManager.getInstance();
export { TransportManager };