mcp-client-v2.js•4.42 kB
// MCP Client v2 - Properly handles MCP protocol with session management
import axios from 'axios';
class MCPClientV2 {
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',
},
// Don't throw on HTTP error status codes
validateStatus: () => true
});
}
/**
* Send a raw JSON-RPC request to the MCP server
*/
async _sendRequest(method, params = {}) {
const requestId = this.requestId++;
const requestData = {
jsonrpc: '2.0',
method,
params,
id: requestId
};
const headers = {};
// Only include session ID for non-initialize requests
if (method !== 'mcp.initialize' && this.sessionId) {
headers['mcp-session-id'] = this.sessionId;
}
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,
// Get raw response to access headers
transformResponse: (res) => res
});
console.log('\n--- Received Response ---');
console.log('Status:', response.status, response.statusText);
console.log('Headers:', JSON.stringify(response.headers, null, 2));
console.log('Data:', response.data);
// Handle HTTP errors
if (response.status < 200 || response.status >= 300) {
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
}
// Handle JSON-RPC errors
if (response.data && response.data.error) {
const error = response.data.error;
throw new Error(`MCP Error (${error.code}): ${error.message}`);
}
// Store session ID from response headers if this was an initialize request
if (method === 'mcp.initialize' && response.headers['mcp-session-id']) {
this.sessionId = response.headers['mcp-session-id'];
console.log('New session ID:', this.sessionId);
}
return response.data;
} catch (error) {
console.error('Request failed:', error.message);
if (error.response) {
console.error('Response data:', error.response.data);
}
throw error;
}
}
/**
* Initialize a new MCP session
*/
async initialize() {
const result = await this._sendRequest('mcp.initialize', {
capabilities: {}
});
return result;
}
/**
* List available tools
*/
async listTools() {
const result = await this._sendRequest('mcp.list_tools');
return result.result || [];
}
/**
* Call a specific tool
*/
async callTool(toolName, params = {}) {
const result = await this._sendRequest('mcp.call_tool', {
name: toolName,
parameters: params
});
return result.result;
}
/**
* Get tool schema
*/
async getToolSchema(toolName) {
const result = await this._sendRequest('mcp.get_tool_schema', {
name: toolName
});
return result.result;
}
}
// Example usage
async function main() {
const client = new MCPClientV2();
try {
// 1. Initialize session
console.log('Initializing MCP session...');
await client.initialize();
if (!client.sessionId) {
throw new Error('Failed to establish MCP session');
}
// 2. List available tools
console.log('\nListing available tools...');
const tools = await client.listTools();
console.log('Available tools:', JSON.stringify(tools, null, 2));
// 3. Call ping tool
console.log('\nPinging SpiderFoot...');
const pingResult = await client.callTool('spiderfoot_ping');
console.log('Ping result:', JSON.stringify(pingResult, null, 2));
// 4. List scans
console.log('\nListing scans...');
const scans = await client.callTool('spiderfoot_scans');
console.log('Scans:', JSON.stringify(scans, 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 MCPClientV2;