tool-handlers.js•14.6 kB
/**
* Web Proxy MCP Tool Handlers
* Implements the actual functionality for each MCP tool
*/
import { validateToolArgs } from './tool-definitions.js';
export class ToolHandlers {
constructor(proxyServer, targetManager, browserSetup, trafficAnalyzer, sslManager = null) {
this.proxyServer = proxyServer;
this.targetManager = targetManager;
this.browserSetup = browserSetup;
this.trafficAnalyzer = trafficAnalyzer;
this.sslManager = sslManager;
}
/**
* Handle tool call
* @param {string} name - Tool name
* @param {Object} args - Tool arguments
* @returns {Object} Tool result
*/
async handleTool(name, args = {}) {
// Validate arguments
const validation = validateToolArgs(name, args);
if (!validation.valid) {
return {
error: validation.error,
isError: true
};
}
try {
// Route to appropriate handler
if (name.startsWith('proxy_target_') || name.includes('_target')) {
return await this._handleTargetTool(name, args);
} else if (name.startsWith('proxy_server_') || name.includes('_server')) {
return await this._handleServerTool(name, args);
} else if (name.startsWith('ssl_')) {
return await this._handleSSLTool(name, args);
} else if (name.includes('setup') || name.includes('pac')) {
return await this._handleSetupTool(name, args);
} else if (name.includes('traffic') || name.includes('har')) {
return await this._handleTrafficTool(name, args);
} else if (name.includes('config')) {
return await this._handleConfigTool(name, args);
} else if (name.includes('analyze') || name.includes('metrics')) {
return await this._handleAnalysisTool(name, args);
}
return {
error: `Unknown tool: ${name}`,
isError: true
};
} catch (error) {
return {
error: error.message,
isError: true,
stack: error.stack
};
}
}
/**
* Handle SSL management tools
* @private
*/
async _handleSSLTool(name, args) {
if (!this.sslManager) {
return {
error: "SSL Manager not available",
isError: true
};
}
switch (name) {
case 'ssl_create_ca':
const caResult = await this.sslManager.createCA(
args.caName,
{
description: args.description,
overwrite: args.overwrite,
subject: args.subject
}
);
return {
content: [{
type: "text",
text: `✅ Certificate Authority created: ${caResult.caName}\n\n📁 CA Directory: ${caResult.caDir}\n🔑 CA Certificate: ${caResult.caCertPath}\n\n${caResult.installationInstructions}`
}]
};
case 'ssl_list_cas':
const cas = await this.sslManager.listCAs();
const caList = cas.map(ca =>
`• ${ca.name} ${ca.current ? '(current)' : ''} - ${ca.exists ? '✅ Available' : '❌ Missing'}\n Created: ${ca.created || 'Unknown'}\n Description: ${ca.description || 'No description'}`
).join('\n\n');
return {
content: [{
type: "text",
text: `🔒 Available Certificate Authorities\n\n${caList || 'No CAs found'}`
}]
};
case 'ssl_switch_ca':
const switchResult = await this.sslManager.switchCA(args.caName);
return {
content: [{
type: "text",
text: `🔄 Switched to CA: ${switchResult.caName}\n📁 CA Directory: ${switchResult.caDir}`
}]
};
case 'ssl_get_ca_certificate':
const caCert = await this.sslManager.getCACertificate();
return {
content: [{
type: "text",
text: `📜 CA Certificate\n\n📁 Certificate Path: ${caCert.certPath}\n\n${caCert.installationInstructions}`
}]
};
case 'ssl_generate_certificate':
const certResult = await this.sslManager.generateServerCertificate(
args.domain,
args.altNames
);
return {
content: [{
type: "text",
text: `📜 Generated certificate for: ${certResult.domain}\n\n🔑 Private Key: ${certResult.keyPath}\n📄 Certificate: ${certResult.certPath}\n🏷️ Alt Names: ${certResult.altNames.join(', ')}`
}]
};
case 'ssl_ca_status':
const status = this.sslManager.getCAStatus();
return {
content: [{
type: "text",
text: `🔒 SSL Manager Status\n\n${JSON.stringify(status, null, 2)}`
}]
};
default:
return { error: `Unknown SSL tool: ${name}`, isError: true };
}
}
/**
* Handle target management tools
* @private
*/
async _handleTargetTool(name, args) {
switch (name) {
case 'proxy_add_target':
const added = this.targetManager.addTarget(
args.domain,
args.description,
{
enabled: args.enabled,
captureHeaders: args.captureHeaders,
captureBody: args.captureBody
}
);
return {
content: [{
type: "text",
text: `Target added: ${args.domain}\nStatus: ${added ? 'success' : 'already exists'}\nMonitored domains: ${this.targetManager.getStats().enabled}`
}]
};
case 'proxy_remove_target':
const removed = this.targetManager.removeTarget(args.domain);
return {
content: [{
type: "text",
text: `Target removed: ${args.domain}\nStatus: ${removed ? 'success' : 'not found'}\nRemaining domains: ${this.targetManager.getStats().enabled}`
}]
};
case 'proxy_list_targets':
const targets = this.targetManager.listTargets(args.status);
const targetList = targets.map(t =>
`• ${t.domain} (${t.status}) - ${t.description || 'No description'}`
).join('\n');
return {
content: [{
type: "text",
text: `📋 Proxy Targets (${targets.length})\n\n${targetList || 'No targets configured'}\n\nStats: ${JSON.stringify(this.targetManager.getStats(), null, 2)}`
}]
};
case 'proxy_update_target':
const updates = {};
if ('enabled' in args) updates.enabled = args.enabled;
if ('description' in args) updates.description = args.description;
if ('captureHeaders' in args) updates.captureHeaders = args.captureHeaders;
if ('captureBody' in args) updates.captureBody = args.captureBody;
const updated = this.targetManager.updateTarget(args.domain, updates);
return {
content: [{
type: "text",
text: `Target updated: ${args.domain}\nStatus: ${updated ? 'success' : 'not found'}\nUpdates: ${JSON.stringify(updates, null, 2)}`
}]
};
default:
return { error: `Unknown target tool: ${name}`, isError: true };
}
}
/**
* Handle server control tools
* @private
*/
async _handleServerTool(name, args) {
switch (name) {
case 'proxy_start_server':
if (this.proxyServer.isRunning()) {
return {
content: [{
type: "text",
text: `Proxy server is already running on ${this.proxyServer.getAddress()}`
}]
};
}
await this.proxyServer.start(args.port, args.host, {
enableSSLBumping: args.enableSSLBumping
});
const serverStatus = this.proxyServer.getStatus();
return {
content: [{
type: "text",
text: `🚀 Proxy server started!\n\nAddress: ${this.proxyServer.getAddress()}\nPAC URL: ${this.proxyServer.getAddress()}/proxy.pac\nCA Certificate: ${this.proxyServer.getAddress()}/ca.crt\nSSL Bumping: ${serverStatus.sslBumpingEnabled ? 'ENABLED' : 'DISABLED'}\nMonitoring: ${this.targetManager.getStats().enabled} domains${serverStatus.sslBumpingEnabled ? '\n\n⚠️ SSL Bumping is active - install CA certificate for HTTPS interception!' : ''}`
}]
};
case 'proxy_stop_server':
if (!this.proxyServer.isRunning()) {
return {
content: [{
type: "text",
text: "Proxy server is not running"
}]
};
}
await this.proxyServer.stop();
return {
content: [{
type: "text",
text: "✅ Proxy server stopped"
}]
};
case 'proxy_server_status':
const status = this.proxyServer.getStatus();
const stats = this.targetManager.getStats();
return {
content: [{
type: "text",
text: `📊 Proxy Server Status\n\n${JSON.stringify({
...status,
targetStats: stats,
trafficEntries: this.trafficAnalyzer ? this.trafficAnalyzer.getEntryCount() : 0
}, null, 2)}`
}]
};
default:
return { error: `Unknown server tool: ${name}`, isError: true };
}
}
/**
* Handle setup and configuration tools
* @private
*/
async _handleSetupTool(name, args) {
switch (name) {
case 'proxy_generate_setup':
const setup = await this.browserSetup.generateAllSetups(
args.proxyHost,
args.proxyPort
);
const quickGuide = this.browserSetup.generateQuickSetup(
args.proxyHost,
args.proxyPort
);
return {
content: [{
type: "text",
text: `✅ Browser setup scripts generated!\n\n${quickGuide}\n\nGenerated files:\n${Object.entries(setup.files).map(([type, file]) => `• ${file.filename} - ${file.description}`).join('\n')}`
}]
};
case 'proxy_get_pac_file':
const pacContent = this.targetManager.generatePacFile(
args.proxyHost,
args.proxyPort
);
return {
content: [{
type: "text",
text: `📄 PAC File Content:\n\n\`\`\`javascript\n${pacContent}\n\`\`\``
}]
};
default:
return { error: `Unknown setup tool: ${name}`, isError: true };
}
}
/**
* Handle traffic analysis tools
* @private
*/
async _handleTrafficTool(name, args) {
if (!this.trafficAnalyzer) {
return {
error: "Traffic analyzer not available",
isError: true
};
}
switch (name) {
case 'proxy_get_traffic_log':
const entries = this.trafficAnalyzer.getEntries({
domain: args.domain,
method: args.method,
limit: args.limit,
since: args.since ? new Date(args.since) : undefined
});
const logText = entries.map(entry =>
`[${entry.timestamp}] ${entry.method} ${entry.url} -> ${entry.statusCode} (${entry.responseTime}ms)`
).join('\n');
return {
content: [{
type: "text",
text: `📈 Traffic Log (${entries.length} entries)\n\n${logText || 'No traffic captured'}`
}]
};
case 'proxy_export_har':
const harFile = await this.trafficAnalyzer.exportHAR({
domain: args.domain,
since: args.since ? new Date(args.since) : undefined,
filename: args.filename
});
return {
content: [{
type: "text",
text: `📁 HAR file exported: ${harFile.filename}\nEntries: ${harFile.entryCount}\nSize: ${harFile.fileSize} bytes`
}]
};
case 'proxy_clear_traffic_log':
if (!args.confirm) {
return {
error: "Confirmation required to clear traffic log",
isError: true
};
}
const cleared = this.trafficAnalyzer.clearEntries(args.domain);
return {
content: [{
type: "text",
text: `🗑️ Traffic log cleared\nEntries removed: ${cleared}\nRemaining: ${this.trafficAnalyzer.getEntryCount()}`
}]
};
default:
return { error: `Unknown traffic tool: ${name}`, isError: true };
}
}
/**
* Handle configuration management tools
* @private
*/
async _handleConfigTool(name, args) {
switch (name) {
case 'proxy_import_config':
const imported = await this.targetManager.importFromFile(
args.filepath,
args.merge
);
return {
content: [{
type: "text",
text: `📥 Configuration imported from ${args.filepath}\nTargets imported: ${imported.imported}\nTotal targets: ${this.targetManager.getStats().total}`
}]
};
case 'proxy_export_config':
const exported = await this.targetManager.exportToFile(
args.filepath,
args.includeTrafficLog && this.trafficAnalyzer ? {
trafficLog: this.trafficAnalyzer.getAllEntries()
} : undefined
);
return {
content: [{
type: "text",
text: `📤 Configuration exported to ${exported.filepath}\nTargets: ${exported.targetCount}\nFile size: ${exported.fileSize} bytes`
}]
};
default:
return { error: `Unknown config tool: ${name}`, isError: true };
}
}
/**
* Handle analysis tools
* @private
*/
async _handleAnalysisTool(name, args) {
switch (name) {
case 'proxy_analyze_traffic':
if (!this.trafficAnalyzer) {
return { error: "Traffic analyzer not available", isError: true };
}
const analysis = this.trafficAnalyzer.analyzeTraffic({
domain: args.domain,
timeframe: args.timeframe,
groupBy: args.groupBy
});
return {
content: [{
type: "text",
text: `📊 Traffic Analysis (${args.timeframe})\n\n${JSON.stringify(analysis, null, 2)}`
}]
};
case 'proxy_get_performance_metrics':
const metrics = this.proxyServer.getMetrics();
if (args.reset) {
this.proxyServer.resetMetrics();
}
return {
content: [{
type: "text",
text: `⚡ Performance Metrics\n\n${JSON.stringify(metrics, null, 2)}${args.reset ? '\n\n✅ Metrics reset' : ''}`
}]
};
default:
return { error: `Unknown analysis tool: ${name}`, isError: true };
}
}
}