claude-flow-tbowman01
Version:
Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)
206 lines • 6.52 kB
JavaScript
/**
* Standard I/O transport for MCP
*/
import { stdin, stdout } from 'node:process';
import { createInterface } from 'node:readline';
import { MCPTransportError } from '../../utils/errors.js';
/**
* Stdio transport implementation
*/
export class StdioTransport {
logger;
requestHandler;
notificationHandler;
readline;
messageCount = 0;
notificationCount = 0;
running = false;
constructor(logger) {
this.logger = logger;
}
async start() {
if (this.running) {
throw new MCPTransportError('Transport already running');
}
this.logger.info('Starting stdio transport');
try {
// Create readline interface for stdin
this.readline = createInterface({
input: stdin,
output: stdout,
terminal: false,
});
// Set up line handler
this.readline.on('line', (line) => {
this.processMessage(line.trim()).catch((error) => {
this.logger.error('Error processing message', { line, error });
});
});
this.readline.on('close', () => {
this.logger.info('Stdin closed');
this.running = false;
});
this.running = true;
this.logger.info('Stdio transport started');
}
catch (error) {
throw new MCPTransportError('Failed to start stdio transport', { error });
}
}
async stop() {
if (!this.running) {
return;
}
this.logger.info('Stopping stdio transport');
this.running = false;
if (this.readline) {
this.readline.close();
this.readline = undefined;
}
this.logger.info('Stdio transport stopped');
}
onRequest(handler) {
this.requestHandler = handler;
}
onNotification(handler) {
this.notificationHandler = handler;
}
async getHealthStatus() {
return {
healthy: this.running,
metrics: {
messagesReceived: this.messageCount,
notificationsSent: this.notificationCount,
stdinOpen: this.readline ? 1 : 0,
},
};
}
async processMessage(line) {
let message;
try {
message = JSON.parse(line);
if (!message.jsonrpc || message.jsonrpc !== '2.0') {
throw new Error('Invalid JSON-RPC version');
}
if (!message.method) {
throw new Error('Missing method');
}
}
catch (error) {
this.logger.error('Failed to parse message', { line, error });
// Send error response if we can extract an ID
let id = 'unknown';
try {
const parsed = JSON.parse(line);
if (parsed.id !== undefined) {
id = parsed.id;
}
}
catch {
// Ignore parse error for ID extraction
}
await this.sendResponse({
jsonrpc: '2.0',
id,
error: {
code: -32700,
message: 'Parse error',
},
});
return;
}
this.messageCount++;
// Check if this is a notification (no id field) or a request
if (message.id === undefined) {
// This is a notification
await this.handleNotification(message);
}
else {
// This is a request
await this.handleRequest(message);
}
}
async handleRequest(request) {
if (!this.requestHandler) {
await this.sendResponse({
jsonrpc: '2.0',
id: request.id,
error: {
code: -32603,
message: 'No request handler registered',
},
});
return;
}
try {
const response = await this.requestHandler(request);
await this.sendResponse(response);
}
catch (error) {
this.logger.error('Request handler error', { request, error });
await this.sendResponse({
jsonrpc: '2.0',
id: request.id,
error: {
code: -32603,
message: 'Internal error',
data: error instanceof Error ? error.message : String(error),
},
});
}
}
async handleNotification(notification) {
if (!this.notificationHandler) {
this.logger.warn('Received notification but no handler registered', {
method: notification.method,
});
return;
}
try {
await this.notificationHandler(notification);
}
catch (error) {
this.logger.error('Notification handler error', { notification, error });
// Notifications don't send error responses
}
}
async sendResponse(response) {
try {
const json = JSON.stringify(response);
stdout.write(json + '\n');
}
catch (error) {
this.logger.error('Failed to send response', { response, error });
}
}
async connect() {
// For STDIO transport, connect is handled by start()
if (!this.running) {
await this.start();
}
}
async disconnect() {
// For STDIO transport, disconnect is handled by stop()
await this.stop();
}
async sendRequest(request) {
// Send request to stdout
const json = JSON.stringify(request);
stdout.write(json + '\n');
// In STDIO transport, responses are handled asynchronously
// This would need a proper request/response correlation mechanism
throw new Error('STDIO transport sendRequest requires request/response correlation');
}
async sendNotification(notification) {
try {
const json = JSON.stringify(notification);
stdout.write(json + '\n');
this.notificationCount++;
}
catch (error) {
this.logger.error('Failed to send notification', { notification, error });
throw error;
}
}
}
//# sourceMappingURL=stdio.js.map