final-mcp-client.js•7.26 kB
// Final MCP Client with proper SSE and JSON handling
import http from 'http';
class FinalMCPClient {
constructor() {
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': 'application/json, text/event-stream',
'Content-Length': Buffer.byteLength(postData),
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Accept-Encoding': 'identity' // Disable compression for SSE
}
};
// Add session ID if available (except for initialize)
if (this.sessionId && method !== 'initialize') {
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:', JSON.stringify(res.headers, null, 2));
// 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:', this.sessionId);
}
let buffer = '';
const results = [];
let isSSE = res.headers['content-type']?.includes('text/event-stream');
res.on('data', (chunk) => {
const chunkStr = chunk.toString();
buffer += chunkStr;
if (isSSE) {
// Process SSE format: event: message\n // data: {json_data_here}
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('data: ')) {
const dataStr = line.substring(5).trim();
if (dataStr) {
try {
const data = JSON.parse(dataStr);
results.push(data);
console.log('SSE Data:', JSON.stringify(data, null, 2));
} catch (e) {
console.error('Error parsing SSE data:', e);
console.error('Problematic data:', dataStr);
}
}
}
}
}
});
res.on('end', () => {
if (buffer.trim()) {
try {
if (isSSE) {
// Try to parse any remaining data as SSE
const lines = buffer.split('\n');
for (const line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('data: ')) {
const dataStr = trimmed.substring(5).trim();
if (dataStr) {
const data = JSON.parse(dataStr);
results.push(data);
console.log('SSE Data (end):', JSON.stringify(data, null, 2));
}
}
}
} else {
// Try to parse as JSON
const data = JSON.parse(buffer);
results.push(data);
console.log('JSON Data:', JSON.stringify(data, null, 2));
}
} catch (e) {
console.error('Error parsing final buffer:', e);
console.error('Buffer content:', 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: 'final-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', {});
}
async startScan(target, moduleList = 'all', scanName = 'mcp-scan') {
return this.request('spiderfoot_start_scan', {
scanname: scanName,
scantarget: target,
modulelist: moduleList,
usecase: 'all'
});
}
}
// Test the client
async function testClient() {
const client = new FinalMCPClient();
try {
console.log('=== Testing Final MCP Client ===');
// 1. Initialize the client
console.log('\n1. Initializing...');
await client.initialize();
if (!client.sessionId) {
throw new Error('Failed to get session ID');
}
// 2. List tools
console.log('\n2. Listing tools...');
const toolsResponse = await client.listTools();
console.log('Tools response:', JSON.stringify(toolsResponse.data, null, 2));
// 3. Test ping
console.log('\n3. Testing ping...');
const pingResponse = await client.ping();
console.log('Ping response:', JSON.stringify(pingResponse.data, null, 2));
// 4. List scans
console.log('\n4. 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();