mcp-client-fixed.js•4.71 kB
// MCP Client with proper session handling for SpiderFoot MCP integration
import axios from 'axios';
import { v4 as uuidv4 } from 'uuid';
class MCPClient {
constructor(baseUrl = 'http://localhost:5002') {
this.baseUrl = baseUrl;
this.sessionId = null;
this.requestId = 1;
this.client = axios.create({
baseURL: baseUrl,
headers: {
'Content-Type': 'application/json',
}
});
}
/**
* Initialize a new MCP session
*/
async initialize() {
try {
const response = await this._sendRequest({
jsonrpc: '2.0',
method: 'mcp.initialize',
params: { capabilities: {} },
id: this.requestId++
});
// Extract session ID from response headers
if (response.headers && response.headers['mcp-session-id']) {
this.sessionId = response.headers['mcp-session-id'];
}
return response.data;
} catch (error) {
console.error('Failed to initialize MCP session:', error.message);
throw error;
}
}
/**
* List available MCP tools
*/
async listTools() {
return this._sendRequest({
jsonrpc: '2.0',
method: 'mcp.list_tools',
params: {},
id: this.requestId++
});
}
/**
* Call a specific MCP tool
*/
async callTool(toolName, params = {}) {
return this._sendRequest({
jsonrpc: '2.0',
method: 'mcp.call_tool',
params: {
name: toolName,
parameters: params
},
id: this.requestId++
});
}
/**
* Send a request to the MCP server with proper session handling
*/
async _sendRequest(requestData) {
const headers = { ...this.client.defaults.headers.common };
// Add session ID if available
if (this.sessionId) {
headers['mcp-session-id'] = this.sessionId;
}
// Log the outgoing request
console.log('\n--- Sending Request ---');
console.log('URL:', `${this.baseUrl}/mcp`);
console.log('Method: POST');
console.log('Headers:', JSON.stringify(headers, null, 2));
console.log('Body:', JSON.stringify(requestData, null, 2));
try {
const response = await this.client.post('/mcp', requestData, {
headers,
// Enable response headers in the response object
transformResponse: (res) => res
});
// Log the response
console.log('\n--- Received Response ---');
console.log('Status:', response.status, response.statusText);
console.log('Headers:', JSON.stringify(response.headers, null, 2));
console.log('Data:', JSON.stringify(response.data, null, 2));
// Handle JSON-RPC errors
if (response.data && response.data.error) {
throw new Error(`MCP Error (${response.data.error.code}): ${response.data.error.message}`);
}
return response;
} catch (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.error('MCP Server Error:', error.response.status, error.response.statusText);
if (error.response.data) {
console.error('Error details:', JSON.stringify(error.response.data, null, 2));
}
} else if (error.request) {
// The request was made but no response was received
console.error('No response from MCP server:', error.request);
} else {
// Something happened in setting up the request
console.error('Request setup error:', error.message);
}
throw error;
}
}
}
// Example usage
async function main() {
const client = new MCPClient();
try {
// Initialize session
console.log('Initializing MCP session...');
const initResult = await client.initialize();
console.log('Session initialized:', initResult.data);
// List available tools
console.log('\nListing available tools...');
const tools = await client.listTools();
console.log('Available tools:', JSON.stringify(tools.data, null, 2));
// Example: Call ping tool
console.log('\nPinging SpiderFoot...');
const pingResult = await client.callTool('spiderfoot_ping');
console.log('Ping result:', JSON.stringify(pingResult.data, null, 2));
// Example: List scans
console.log('\nListing scans...');
const scans = await client.callTool('spiderfoot_scans');
console.log('Scans:', JSON.stringify(scans.data, null, 2));
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
}
// Run the example if this file is executed directly
if (import.meta.url === `file://${process.argv[1]}`) {
main();
}
export default MCPClient;