Skip to main content
Glama

VirusTotal MCP Server

domain.ts7.13 kB
import { AxiosInstance } from 'axios'; import { queryVirusTotal } from '../utils/api.js'; import { formatDomainResults } from '../formatters/index.js'; import { GetDomainReportArgsSchema } from '../schemas/index.js'; import { logToFile } from '../utils/logging.js'; import { RelationshipItem, RelationshipData, DomainResponse } from '../types/virustotal.js'; // Default relationships to fetch if none specified const DEFAULT_RELATIONSHIPS = [ 'historical_whois', 'historical_ssl_certificates', 'resolutions', 'communicating_files', 'downloaded_files', 'referrer_files' ] as const; function formatDate(dateStr: string | number): string { try { if (typeof dateStr === 'number') { return new Date(dateStr * 1000).toLocaleDateString(); } return new Date(dateStr).toLocaleDateString(); } catch { return 'Unknown'; } } function formatRelationshipData(relType: string, item: RelationshipItem): string { const attrs = item.attributes || {}; switch (relType) { case 'resolutions': return ` • IP: ${attrs.ip_address} (${attrs.date ? new Date(Number(attrs.date) * 1000).toLocaleDateString() : 'Unknown'}) Host: ${attrs.host_name || 'Unknown'} Analysis Stats: - IP: 🔴 ${attrs.ip_address_last_analysis_stats?.malicious || 0} malicious, ✅ ${attrs.ip_address_last_analysis_stats?.harmless || 0} harmless - Host: 🔴 ${attrs.host_name_last_analysis_stats?.malicious || 0} malicious, ✅ ${attrs.host_name_last_analysis_stats?.harmless || 0} harmless`; case 'communicating_files': return ` • ${attrs.meaningful_name || item.id} Type: ${attrs.type_description || attrs.type || 'Unknown'} First Seen: ${attrs.first_submission_date ? new Date(attrs.first_submission_date * 1000).toLocaleDateString() : 'Unknown'}`; case 'downloaded_files': return ` • ${attrs.meaningful_name || item.id} Type: ${attrs.type_description || attrs.type || 'Unknown'} First Seen: ${attrs.first_submission_date ? new Date(attrs.first_submission_date * 1000).toLocaleDateString() : 'Unknown'}`; case 'urls': return ` • ${attrs.url || item.id} Last Analysis: ${attrs.last_analysis_date ? new Date(attrs.last_analysis_date * 1000).toLocaleDateString() : 'Unknown'} Reputation: ${attrs.reputation ?? 'Unknown'}`; case 'historical_whois': const whoisMap = attrs.whois_map || {}; const whoisInfo = []; if (whoisMap['Registrar']) whoisInfo.push(`Registrar: ${whoisMap['Registrar']}`); if (whoisMap['Creation Date']) whoisInfo.push(`Created: ${formatDate(whoisMap['Creation Date'])}`); if (whoisMap['Registry Expiry Date']) whoisInfo.push(`Expires: ${formatDate(whoisMap['Registry Expiry Date'])}`); if (whoisMap['Updated Date']) whoisInfo.push(`Updated: ${formatDate(whoisMap['Updated Date'])}`); if (whoisMap['Registrant Organization']) whoisInfo.push(`Organization: ${whoisMap['Registrant Organization']}`); if (attrs.registrar_name) whoisInfo.push(`Registrar: ${attrs.registrar_name}`); return ` • WHOIS Record from ${formatDate(attrs.last_updated || '')}${whoisInfo.length ? '\n ' + whoisInfo.join('\n ') : ''}`; case 'historical_ssl_certificates': const certInfo = []; if (attrs.subject?.CN) certInfo.push(`Subject: ${attrs.subject.CN}`); if (attrs.issuer?.CN) certInfo.push(`Issuer: ${attrs.issuer.CN}`); if (attrs.validity?.not_before) certInfo.push(`Valid From: ${formatDate(attrs.validity.not_before)}`); if (attrs.validity?.not_after) certInfo.push(`Valid Until: ${formatDate(attrs.validity.not_after)}`); if (attrs.serial_number) certInfo.push(`Serial: ${attrs.serial_number}`); const altNames = attrs.extensions?.subject_alternative_name; if (altNames && altNames.length) certInfo.push(`Alt Names: ${altNames.join(', ')}`); return ` • SSL Certificate${certInfo.length ? '\n ' + certInfo.join('\n ') : ''}`; case 'referrer_files': const stats = attrs.last_analysis_stats || {}; const totalDetections = (Object.values(stats) as number[]).reduce((a, b) => a + b, 0); return ` • ${attrs.meaningful_name || item.id} Type: ${attrs.type_description || attrs.type || 'Unknown'} Detection Ratio: ${attrs.last_analysis_stats ? `${attrs.last_analysis_stats.malicious}/${totalDetections}` : 'Unknown'}`; default: if (attrs.hostname) return ` • ${attrs.hostname}`; if (attrs.ip_address) return ` • ${attrs.ip_address}`; if (attrs.url) return ` • ${attrs.url}`; if (attrs.value) return ` • ${attrs.value}`; return ` • ${item.id}`; } } export async function handleGetDomainReport(axiosInstance: AxiosInstance, args: unknown) { const parsedArgs = GetDomainReportArgsSchema.safeParse(args); if (!parsedArgs.success) { throw new Error("Invalid domain format"); } const { domain, relationships = DEFAULT_RELATIONSHIPS } = parsedArgs.data; // First get the basic domain report logToFile('Getting domain report...'); const basicReport = await queryVirusTotal( axiosInstance, `/domains/${domain}`, 'get' ) as DomainResponse; // Then get full data for specified relationships const relationshipData: Record<string, RelationshipData> = {}; for (const relType of relationships) { logToFile(`Getting full data for ${relType}...`); try { const response = await queryVirusTotal( axiosInstance, `/domains/${domain}/${relType}`, 'get' ); // Format the relationship data if (Array.isArray(response.data)) { relationshipData[relType] = { data: response.data.map((item: RelationshipItem) => ({ ...item, formattedOutput: formatRelationshipData(relType, item) })), meta: response.meta }; } else if (response.data) { relationshipData[relType] = { data: { ...response.data, formattedOutput: formatRelationshipData(relType, response.data) }, meta: response.meta }; } } catch (error) { logToFile(`Error fetching ${relType} data: ${error}`); // Continue with other relationships even if one fails } } // Combine the basic report with detailed relationships const combinedData = { ...basicReport.data, relationships: relationshipData }; return { content: [ formatDomainResults(combinedData) ], }; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/BurtTheCoder/mcp-virustotal'

If you have feedback or need assistance with the MCP directory API, please join our Discord server