sse-mcp-client.js•6.09 kB
// MCP Client with SSE (Server-Sent Events) support
import { EventEmitter } from 'events';
import http from 'http';
class SSEMCPClient extends EventEmitter {
constructor() {
super();
this.sessionId = null;
this.requestId = 1;
this.hostname = 'localhost';
this.port = 5002;
this.path = '/mcp';
}
async request(method, params = {}) {
return new Promise((resolve, reject) => {
const postData = JSON.stringify({
jsonrpc: '2.0',
method: method,
params: params,
id: this.requestId++
});
const options = {
hostname: this.hostname,
port: this.port,
path: this.path,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
'Content-Length': Buffer.byteLength(postData),
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
}
};
// Add session ID if available
if (this.sessionId) {
options.headers['mcp-session-id'] = this.sessionId;
}
console.log(`\n=== Request: ${method} ===`);
console.log('Headers:', options.headers);
console.log('Body:', postData);
const req = http.request(options, (res) => {
console.log(`\n=== Response: ${res.statusCode} ===`);
console.log('Headers:', res.headers);
// Save session ID from response headers (case-insensitive)
const sessionHeader = Object.entries(res.headers).find(
([key]) => key.toLowerCase() === 'mcp-session-id'
);
if (sessionHeader && sessionHeader[1]) {
this.sessionId = sessionHeader[1];
console.log('Session ID updated:', this.sessionId);
}
let buffer = '';
const results = [];
res.on('data', (chunk) => {
buffer += chunk;
// Process each line in the buffer
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep the last incomplete line
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = line.substring(6).trim();
if (data) {
const json = JSON.parse(data);
results.push(json);
this.emit('data', json);
}
} catch (error) {
console.error('Error parsing SSE data:', error);
console.error('Problematic line:', line);
}
}
}
});
res.on('end', () => {
if (buffer.trim()) {
try {
const json = JSON.parse(buffer);
results.push(json);
this.emit('data', json);
} catch (error) {
console.error('Error parsing final buffer:', error);
console.error('Problematic buffer:', buffer);
}
}
if (res.statusCode >= 400) {
const error = new Error(`Request failed with status code ${res.statusCode}`);
error.statusCode = res.statusCode;
error.headers = res.headers;
error.data = results[0] || null;
reject(error);
} else {
// For initialization, we expect a single result
// For other requests, return all results
const result = method === 'initialize' ? results[0] : results;
resolve({
statusCode: res.statusCode,
headers: res.headers,
data: result
});
}
});
});
req.on('error', (error) => {
console.error('Request error:', error);
reject(error);
});
// Write data to request body
req.write(postData);
req.end();
});
}
async initialize() {
console.log('Initializing MCP client...');
const response = await this.request('initialize', {
protocolVersion: '1.0',
clientInfo: {
name: 'sse-mcp-client',
version: '1.0.0'
},
capabilities: {}
});
console.log('Initialization successful');
return response;
}
async listTools() {
return this.request('mcp.list_tools', {});
}
async ping() {
return this.request('spiderfoot_ping', {});
}
async listScans() {
return this.request('spiderfoot_scans', {});
}
}
// Test the client
async function testClient() {
const client = new SSEMCPClient();
// Listen for SSE data events
client.on('data', (data) => {
console.log('SSE Data:', JSON.stringify(data, null, 2));
});
try {
console.log('=== Testing SSE MCP Client ===');
// 1. Initialize the client
const initResponse = await client.initialize();
console.log('Init response:', JSON.stringify(initResponse.data, null, 2));
if (!client.sessionId) {
throw new Error('Failed to get session ID');
}
// 2. List tools
console.log('\n=== Listing Tools ===');
const toolsResponse = await client.listTools();
console.log('Tools response:', JSON.stringify(toolsResponse.data, null, 2));
// 3. Test ping
console.log('\n=== Testing Ping ===');
const pingResponse = await client.ping();
console.log('Ping response:', JSON.stringify(pingResponse.data, null, 2));
// 4. List scans
console.log('\n=== Listing Scans ===');
try {
const scansResponse = await client.listScans();
console.log('Scans response:', JSON.stringify(scansResponse.data, null, 2));
} catch (error) {
console.warn('Failed to list scans:', error.message);
}
console.log('\n✅ Test completed successfully!');
} catch (error) {
console.error('\n❌ Test failed:', error.message);
if (error.statusCode) {
console.error('Status code:', error.statusCode);
}
if (error.data) {
console.error('Error data:', JSON.stringify(error.data, null, 2));
}
process.exit(1);
}
}
// Run the test
testClient();