UNPKG

@superadnim/osint-mcp-server

Version:

Professional OSINT MCP Server for intelligence gathering with privacy protection

295 lines 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MindMapManager = void 0; const uuid_1 = require("uuid"); class MindMapManager { cache; logger; investigationCache = new Map(); constructor(cache, logger) { this.cache = cache; this.logger = logger; } async handleAction(investigationId, action, args) { switch (action) { case 'create': return this.createInvestigation(investigationId, args); case 'add_node': return this.addNode(investigationId, args); case 'add_connection': return this.addConnection(investigationId, args); case 'view': return this.viewInvestigation(investigationId); case 'export': return this.exportInvestigation(investigationId, args); case 'search': return this.searchInvestigation(investigationId, args); default: throw new Error(`Unknown mind map action: ${action}`); } } async createInvestigation(investigationId, args) { const investigation = { id: investigationId, name: args.name || `Investigation ${investigationId}`, description: args.description, nodes: [], connections: [], created_at: new Date().toISOString(), updated_at: new Date().toISOString(), metadata: { creator: 'osint-mcp-server', version: '1.0.0', }, }; this.investigationCache.set(investigationId, investigation); this.cache.set(`investigation:${investigationId}`, investigation, 7 * 24 * 3600); // 7 days this.logger.info('Investigation created', { investigation_id: investigationId }); return investigation; } async addNode(investigationId, args) { const investigation = await this.getInvestigation(investigationId); if (!args.node_data || typeof args.node_data !== 'object') { throw new Error('node_data is required for add_node action'); } const nodeData = args.node_data; const node = { id: (0, uuid_1.v4)(), type: nodeData.type, content: nodeData.content, metadata: nodeData.metadata || {}, confidence: nodeData.confidence || 0.5, source: nodeData.source, timestamp: new Date().toISOString(), }; investigation.nodes.push(node); investigation.updated_at = new Date().toISOString(); await this.saveInvestigation(investigation); this.logger.info('Node added to investigation', { investigation_id: investigationId, node_id: node.id, node_type: node.type, }); return node; } async addConnection(investigationId, args) { const investigation = await this.getInvestigation(investigationId); if (!args.connection_data || typeof args.connection_data !== 'object') { throw new Error('connection_data is required for add_connection action'); } const connectionData = args.connection_data; // Validate that the nodes exist const fromNodeExists = investigation.nodes.some(node => node.id === connectionData.from_node); const toNodeExists = investigation.nodes.some(node => node.id === connectionData.to_node); if (!fromNodeExists || !toNodeExists) { throw new Error('Both from_node and to_node must exist in the investigation'); } const connection = { id: (0, uuid_1.v4)(), from_node: connectionData.from_node, to_node: connectionData.to_node, relationship: connectionData.relationship, strength: connectionData.strength || 0.5, evidence: connectionData.evidence || [], timestamp: new Date().toISOString(), }; investigation.connections.push(connection); investigation.updated_at = new Date().toISOString(); await this.saveInvestigation(investigation); this.logger.info('Connection added to investigation', { investigation_id: investigationId, connection_id: connection.id, relationship: connection.relationship, }); return connection; } async viewInvestigation(investigationId) { return this.getInvestigation(investigationId); } async exportInvestigation(investigationId, args) { const investigation = await this.getInvestigation(investigationId); const format = args.export_format || 'json'; switch (format) { case 'json': return this.exportAsJSON(investigation); case 'graphml': return this.exportAsGraphML(investigation); case 'csv': return this.exportAsCSV(investigation); case 'markdown': return this.exportAsMarkdown(investigation); default: throw new Error(`Unsupported export format: ${format}`); } } async searchInvestigation(investigationId, args) { const investigation = await this.getInvestigation(investigationId); const searchTerm = args.search_term || ''; if (!searchTerm) { throw new Error('search_term is required for search action'); } const matchingNodes = investigation.nodes.filter(node => node.content.toLowerCase().includes(searchTerm.toLowerCase()) || node.type.toLowerCase().includes(searchTerm.toLowerCase())); const matchingConnections = investigation.connections.filter(connection => connection.relationship.toLowerCase().includes(searchTerm.toLowerCase()) || connection.evidence.some(evidence => evidence.toLowerCase().includes(searchTerm.toLowerCase()))); return { search_term: searchTerm, matching_nodes: matchingNodes, matching_connections: matchingConnections, total_matches: matchingNodes.length + matchingConnections.length, }; } async getInvestigation(investigationId) { // Check memory cache first const cached = this.investigationCache.get(investigationId); if (cached) { return cached; } // Check persistent cache const investigation = this.cache.get(`investigation:${investigationId}`); if (investigation) { this.investigationCache.set(investigationId, investigation); return investigation; } throw new Error(`Investigation not found: ${investigationId}`); } async saveInvestigation(investigation) { this.investigationCache.set(investigation.id, investigation); this.cache.set(`investigation:${investigation.id}`, investigation, 7 * 24 * 3600); // 7 days } exportAsJSON(investigation) { return JSON.stringify(investigation, null, 2); } exportAsGraphML(investigation) { let graphml = `<?xml version="1.0" encoding="UTF-8"?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"> <key id="type" for="node" attr.name="type" attr.type="string"/> <key id="content" for="node" attr.name="content" attr.type="string"/> <key id="confidence" for="node" attr.name="confidence" attr.type="double"/> <key id="relationship" for="edge" attr.name="relationship" attr.type="string"/> <key id="strength" for="edge" attr.name="strength" attr.type="double"/> <graph id="${investigation.id}" edgedefault="directed"> `; // Add nodes for (const node of investigation.nodes) { graphml += ` <node id="${node.id}"> <data key="type">${this.escapeXML(node.type)}</data> <data key="content">${this.escapeXML(node.content)}</data> <data key="confidence">${node.confidence}</data> </node> `; } // Add edges for (const connection of investigation.connections) { graphml += ` <edge source="${connection.from_node}" target="${connection.to_node}"> <data key="relationship">${this.escapeXML(connection.relationship)}</data> <data key="strength">${connection.strength}</data> </edge> `; } graphml += ` </graph> </graphml>`; return graphml; } exportAsCSV(investigation) { let csv = 'Type,ID,Content,Details\n'; // Add nodes for (const node of investigation.nodes) { csv += `Node,${node.id},"${this.escapeCSV(node.content)}","Type: ${node.type}, Confidence: ${node.confidence}"\n`; } // Add connections for (const connection of investigation.connections) { csv += `Connection,${connection.id},"${this.escapeCSV(connection.relationship)}","From: ${connection.from_node}, To: ${connection.to_node}, Strength: ${connection.strength}"\n`; } return csv; } exportAsMarkdown(investigation) { let markdown = `# ${investigation.name}\n\n`; if (investigation.description) { markdown += `${investigation.description}\n\n`; } markdown += `**Created:** ${investigation.created_at}\n`; markdown += `**Last Updated:** ${investigation.updated_at}\n\n`; markdown += `## Nodes (${investigation.nodes.length})\n\n`; for (const node of investigation.nodes) { markdown += `### ${node.type}: ${node.content}\n`; markdown += `- **ID:** ${node.id}\n`; markdown += `- **Confidence:** ${node.confidence}\n`; markdown += `- **Source:** ${node.source}\n`; markdown += `- **Timestamp:** ${node.timestamp}\n\n`; } markdown += `## Connections (${investigation.connections.length})\n\n`; for (const connection of investigation.connections) { const fromNode = investigation.nodes.find(n => n.id === connection.from_node); const toNode = investigation.nodes.find(n => n.id === connection.to_node); markdown += `### ${fromNode?.content || connection.from_node}${toNode?.content || connection.to_node}\n`; markdown += `- **Relationship:** ${connection.relationship}\n`; markdown += `- **Strength:** ${connection.strength}\n`; if (connection.evidence.length > 0) { markdown += `- **Evidence:**\n`; for (const evidence of connection.evidence) { markdown += ` - ${evidence}\n`; } } markdown += `- **Timestamp:** ${connection.timestamp}\n\n`; } return markdown; } escapeXML(text) { return text .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&apos;'); } escapeCSV(text) { return text.replace(/"/g, '""'); } async autoCorrelate(investigationId) { const investigation = await this.getInvestigation(investigationId); const newConnections = []; // Simple auto-correlation logic for (let i = 0; i < investigation.nodes.length; i++) { for (let j = i + 1; j < investigation.nodes.length; j++) { const node1 = investigation.nodes[i]; const node2 = investigation.nodes[j]; // Check if nodes should be connected based on content similarity const similarity = this.calculateSimilarity(node1.content, node2.content); if (similarity > 0.7) { const connection = { id: (0, uuid_1.v4)(), from_node: node1.id, to_node: node2.id, relationship: 'similar_content', strength: similarity, evidence: ['Auto-detected content similarity'], timestamp: new Date().toISOString(), }; newConnections.push(connection); } } } // Add new connections to investigation investigation.connections.push(...newConnections); investigation.updated_at = new Date().toISOString(); await this.saveInvestigation(investigation); this.logger.info('Auto-correlation completed', { investigation_id: investigationId, new_connections: newConnections.length, }); return newConnections; } calculateSimilarity(text1, text2) { const words1 = text1.toLowerCase().split(/\s+/); const words2 = text2.toLowerCase().split(/\s+/); const intersection = words1.filter(word => words2.includes(word)); const union = [...new Set([...words1, ...words2])]; return union.length > 0 ? intersection.length / union.length : 0; } } exports.MindMapManager = MindMapManager; //# sourceMappingURL=mind-map.js.map