UNPKG

auto-publishing-mcp-server

Version:

Enterprise-grade MCP Server for Auto-Publishing with pre-publish validation, multi-cloud deployment, and monitoring

1,142 lines (1,053 loc) 37.8 kB
import { exec } from 'child_process'; import { promisify } from 'util'; import { promises as fs } from 'fs'; import axios from 'axios'; const execAsync = promisify(exec); class DatadogMonitor { constructor() { this.apiKey = process.env.DD_API_KEY; this.appKey = process.env.DD_APP_KEY; this.baseUrl = 'https://api.datadoghq.com/api/v1'; this.metricsUrl = 'https://api.datadoghq.com/api/v2'; this.site = process.env.DD_SITE || 'datadoghq.com'; if (!this.apiKey || !this.appKey) { console.warn('Datadog API keys not configured. Some features may not work.'); } this.defaultHeaders = { 'DD-API-KEY': this.apiKey, 'DD-APPLICATION-KEY': this.appKey, 'Content-Type': 'application/json' }; } async submitMetrics(args) { const { metrics = [], host = 'auto-publishing-server', timestamp = Math.floor(Date.now() / 1000) } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const formattedMetrics = metrics.map(metric => ({ metric: metric.name, points: [[timestamp, metric.value]], tags: metric.tags || [], host: metric.host || host, type: metric.type || 'gauge' })); const response = await axios.post( `${this.baseUrl}/series`, { series: formattedMetrics }, { headers: this.defaultHeaders } ); return { success: true, message: `Successfully submitted ${metrics.length} metrics to Datadog`, data: { metricsCount: metrics.length, status: response.data.status } }; } catch (error) { return { success: false, message: `Failed to submit metrics: ${error.message}`, error: error.response?.data || error.message }; } } async queryMetrics(args) { const { query, from = Math.floor((Date.now() - 3600000) / 1000), // 1 hour ago to = Math.floor(Date.now() / 1000) } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const response = await axios.get(`${this.baseUrl}/query`, { headers: this.defaultHeaders, params: { query: query, from: from, to: to } }); return { success: true, data: { query: query, series: response.data.series || [], from: from, to: to, status: response.data.status } }; } catch (error) { return { success: false, message: `Failed to query metrics: ${error.message}`, error: error.response?.data || error.message }; } } async createDashboard(args) { const { title, description = '', widgets = [], layout_type = 'ordered' } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const defaultWidgets = widgets.length > 0 ? widgets : [ { definition: { type: 'timeseries', requests: [ { q: 'avg:docker.cpu.usage{*}', display_type: 'line' } ], title: 'Docker CPU Usage' } }, { definition: { type: 'timeseries', requests: [ { q: 'avg:docker.mem.usage{*}', display_type: 'line' } ], title: 'Docker Memory Usage' } } ]; const dashboardData = { title: title, description: description, layout_type: layout_type, widgets: defaultWidgets }; const response = await axios.post( `${this.baseUrl}/dashboard`, dashboardData, { headers: this.defaultHeaders } ); return { success: true, message: `Dashboard '${title}' created successfully`, data: { dashboardId: response.data.id, url: response.data.url, title: response.data.title } }; } catch (error) { return { success: false, message: `Failed to create dashboard: ${error.message}`, error: error.response?.data || error.message }; } } async createMonitor(args) { const { name, type = 'metric alert', query, message = '', thresholds = { critical: 80, warning: 60 }, tags = [], notifyNoData = true, noDataTimeframe = 20 } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const monitorData = { name: name, type: type, query: query, message: message, tags: tags, options: { thresholds: thresholds, notify_no_data: notifyNoData, no_data_timeframe: noDataTimeframe, notify_audit: false, require_full_window: false, new_host_delay: 300, include_tags: true } }; const response = await axios.post( `${this.baseUrl}/monitor`, monitorData, { headers: this.defaultHeaders } ); return { success: true, message: `Monitor '${name}' created successfully`, data: { monitorId: response.data.id, name: response.data.name, query: response.data.query, state: response.data.overall_state } }; } catch (error) { return { success: false, message: `Failed to create monitor: ${error.message}`, error: error.response?.data || error.message }; } } async listMonitors(args) { const { group_states = ['all'], name = '', tags = [] } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const params = { group_states: group_states.join(',') }; if (name) params.name = name; if (tags.length > 0) params.tags = tags.join(','); const response = await axios.get(`${this.baseUrl}/monitor`, { headers: this.defaultHeaders, params: params }); const monitors = response.data.map(monitor => ({ id: monitor.id, name: monitor.name, type: monitor.type, query: monitor.query, state: monitor.overall_state, tags: monitor.tags, created: monitor.created, modified: monitor.modified })); return { success: true, data: { monitors: monitors, totalCount: monitors.length } }; } catch (error) { return { success: false, message: `Failed to list monitors: ${error.message}`, error: error.response?.data || error.message }; } } async sendEvent(args) { const { title, text, dateHappened = Math.floor(Date.now() / 1000), priority = 'normal', tags = [], alertType = 'info', aggregationKey = '', sourceTypeName = 'auto-publishing' } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const eventData = { title: title, text: text, date_happened: dateHappened, priority: priority, tags: tags, alert_type: alertType, aggregation_key: aggregationKey, source_type_name: sourceTypeName }; const response = await axios.post( `${this.baseUrl}/events`, eventData, { headers: this.defaultHeaders } ); return { success: true, message: `Event '${title}' sent successfully`, data: { eventId: response.data.event.id, url: response.data.event.url, status: response.data.status } }; } catch (error) { return { success: false, message: `Failed to send event: ${error.message}`, error: error.response?.data || error.message }; } } async sendLogs(args) { const { logs = [], hostname = 'auto-publishing-server', service = 'auto-publishing', source = 'nodejs' } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const logEntries = logs.map(log => ({ timestamp: log.timestamp || new Date().toISOString(), level: log.level || 'INFO', message: log.message, hostname: log.hostname || hostname, service: log.service || service, source: log.source || source, tags: log.tags || [] })); // Use Datadog's log intake API const response = await axios.post( `https://http-intake.logs.${this.site}/v1/input/${this.apiKey}`, logEntries, { headers: { 'Content-Type': 'application/json', 'DD-API-KEY': this.apiKey } } ); return { success: true, message: `Successfully sent ${logs.length} log entries to Datadog`, data: { logCount: logs.length, status: response.status } }; } catch (error) { return { success: false, message: `Failed to send logs: ${error.message}`, error: error.response?.data || error.message }; } } async createSLO(args) { const { name, description = '', type = 'metric', sliSpecification, thresholds = [ { target: 99.9, timeframe: '7d' }, { target: 99.5, timeframe: '30d' } ], tags = [] } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const sloData = { name: name, description: description, type: type, sli_specification: sliSpecification || { time_slice: { query: { numerator: 'sum:requests.success{*}', denominator: 'sum:requests.total{*}' }, comparator: '>', threshold: 0.95 } }, thresholds: thresholds, tags: tags }; const response = await axios.post( `${this.baseUrl}/slo`, sloData, { headers: this.defaultHeaders } ); return { success: true, message: `SLO '${name}' created successfully`, data: { sloId: response.data.data[0].id, name: response.data.data[0].name, thresholds: response.data.data[0].thresholds } }; } catch (error) { return { success: false, message: `Failed to create SLO: ${error.message}`, error: error.response?.data || error.message }; } } async getServiceMap(args) { const { env = 'prod', service = '', start = Math.floor((Date.now() - 3600000) / 1000), end = Math.floor(Date.now() / 1000) } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const params = { env: env, start: start, end: end }; if (service) params.service = service; const response = await axios.get(`${this.baseUrl}/apm/service-map`, { headers: this.defaultHeaders, params: params }); return { success: true, data: { services: response.data.services || [], dependencies: response.data.dependencies || [], env: env, timeRange: { start, end } } }; } catch (error) { return { success: false, message: `Failed to get service map: ${error.message}`, error: error.response?.data || error.message }; } } async getInfrastructureMetrics(args) { const { hostnames = [], timeframe = '1h', metrics = ['system.cpu.user', 'system.mem.used', 'system.disk.used'] } = args; if (!this.apiKey) { return { success: false, message: 'Datadog API key not configured' }; } try { const from = Math.floor((Date.now() - this.parseTimeframe(timeframe)) / 1000); const to = Math.floor(Date.now() / 1000); const queries = metrics.map(metric => { let query = `avg:${metric}{*}`; if (hostnames.length > 0) { query = `avg:${metric}{host:${hostnames.join(',')}}`; } return query; }); const results = {}; for (const query of queries) { try { const response = await axios.get(`${this.baseUrl}/query`, { headers: this.defaultHeaders, params: { query, from, to } }); const metricName = query.split(':')[1].split('{')[0]; results[metricName] = response.data.series || []; } catch (queryError) { console.warn(`Failed to query ${query}:`, queryError.message); results[query] = []; } } return { success: true, data: { metrics: results, timeframe: { from, to }, hostnames: hostnames } }; } catch (error) { return { success: false, message: `Failed to get infrastructure metrics: ${error.message}`, error: error.response?.data || error.message }; } } parseTimeframe(timeframe) { const timeframeMs = { '5m': 5 * 60 * 1000, '15m': 15 * 60 * 1000, '30m': 30 * 60 * 1000, '1h': 60 * 60 * 1000, '4h': 4 * 60 * 60 * 1000, '1d': 24 * 60 * 60 * 1000, '1w': 7 * 24 * 60 * 60 * 1000 }; return timeframeMs[timeframe] || timeframeMs['1h']; } async installDatadogAgent(args) { const { apiKey = this.apiKey, site = this.site, hostname = '', tags = [] } = args; if (!apiKey) { return { success: false, message: 'Datadog API key required for agent installation' }; } try { // Create installation script const installScript = `#!/bin/bash set -e echo "Installing Datadog Agent..." # Download and install the agent DD_API_KEY=${apiKey} DD_SITE="${site}" bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script.sh)" # Configure agent cat > /etc/datadog-agent/datadog.yaml << EOF api_key: ${apiKey} site: ${site} ${hostname ? `hostname: ${hostname}` : ''} ${tags.length > 0 ? `tags:\n${tags.map(tag => ` - ${tag}`).join('\n')}` : ''} # Enable logs collection logs_enabled: true # Enable APM apm_config: enabled: true # Enable process monitoring process_config: enabled: "true" # Enable network monitoring network_config: enabled: true EOF # Start the agent systemctl enable datadog-agent systemctl start datadog-agent echo "Datadog Agent installed and started successfully" `; await fs.writeFile('/tmp/install-datadog.sh', installScript); await execAsync('chmod +x /tmp/install-datadog.sh'); // Execute installation (requires sudo) const { stdout, stderr } = await execAsync('sudo /tmp/install-datadog.sh'); return { success: true, message: 'Datadog Agent installed successfully', data: { stdout: stdout, stderr: stderr, configPath: '/etc/datadog-agent/datadog.yaml' } }; } catch (error) { return { success: false, message: `Failed to install Datadog Agent: ${error.message}`, error: error.stderr || error.message }; } } async configureIntegrations(args) { const { integrations = ['docker', 'nginx', 'postgres'], configDir = '/etc/datadog-agent/conf.d' } = args; try { const configurations = {}; for (const integration of integrations) { switch (integration) { case 'docker': configurations[integration] = { configFile: `${configDir}/docker.d/conf.yaml`, config: ` init_config: instances: - url: "unix://var/run/docker.sock" collect_container_size: true collect_volume_count: true collect_images_stats: true collect_image_size: true collect_disk_stats: true collect_exit_codes: true ` }; break; case 'nginx': configurations[integration] = { configFile: `${configDir}/nginx.d/conf.yaml`, config: ` init_config: instances: - nginx_status_url: http://localhost/nginx_status/ tags: - instance:nginx-main ` }; break; case 'postgres': configurations[integration] = { configFile: `${configDir}/postgres.d/conf.yaml`, config: ` init_config: instances: - host: localhost port: 5432 username: datadog password: <PASSWORD> dbname: postgres tags: - db:postgresql ` }; break; } } // Write configuration files for (const [integration, config] of Object.entries(configurations)) { try { await execAsync(`sudo mkdir -p $(dirname ${config.configFile})`); await fs.writeFile('/tmp/dd-config.yaml', config.config.trim()); await execAsync(`sudo mv /tmp/dd-config.yaml ${config.configFile}`); await execAsync(`sudo chown dd-agent:dd-agent ${config.configFile}`); } catch (configError) { console.warn(`Failed to configure ${integration}:`, configError.message); } } // Restart agent to pick up new configurations await execAsync('sudo systemctl restart datadog-agent'); return { success: true, message: `Configured ${integrations.length} Datadog integrations`, data: { integrations: integrations, configurations: Object.keys(configurations) } }; } catch (error) { return { success: false, message: `Failed to configure integrations: ${error.message}`, error: error.message }; } } getToolDefinitions() { return [ { name: 'datadog/submit-metrics', description: 'Submit custom metrics to Datadog', inputSchema: { type: 'object', properties: { metrics: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, value: { type: 'number' }, tags: { type: 'array', items: { type: 'string' } }, type: { type: 'string', enum: ['gauge', 'count', 'rate'], default: 'gauge' }, host: { type: 'string' } }, required: ['name', 'value'] } }, host: { type: 'string', default: 'auto-publishing-server' }, timestamp: { type: 'number' } }, required: ['metrics'] } }, { name: 'datadog/query-metrics', description: 'Query metrics from Datadog', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Datadog query string' }, from: { type: 'number', description: 'Start timestamp (Unix epoch)' }, to: { type: 'number', description: 'End timestamp (Unix epoch)' } }, required: ['query'] } }, { name: 'datadog/create-dashboard', description: 'Create a new Datadog dashboard', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Dashboard title' }, description: { type: 'string', description: 'Dashboard description' }, widgets: { type: 'array', items: { type: 'object' }, description: 'Dashboard widgets configuration' }, layout_type: { type: 'string', enum: ['ordered', 'free'], default: 'ordered' } }, required: ['title'] } }, { name: 'datadog/create-monitor', description: 'Create a new Datadog monitor/alert', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Monitor name' }, type: { type: 'string', enum: ['metric alert', 'service check', 'event alert'], default: 'metric alert' }, query: { type: 'string', description: 'Monitor query' }, message: { type: 'string', description: 'Alert message' }, thresholds: { type: 'object', description: 'Alert thresholds', properties: { critical: { type: 'number' }, warning: { type: 'number' }, ok: { type: 'number' } } }, tags: { type: 'array', items: { type: 'string' }, description: 'Monitor tags' } }, required: ['name', 'query'] } }, { name: 'datadog/list-monitors', description: 'List Datadog monitors', inputSchema: { type: 'object', properties: { group_states: { type: 'array', items: { type: 'string' }, description: 'Filter by monitor states', default: ['all'] }, name: { type: 'string', description: 'Filter by monitor name' }, tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' } } } }, { name: 'datadog/send-event', description: 'Send event to Datadog', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'Event title' }, text: { type: 'string', description: 'Event description' }, priority: { type: 'string', enum: ['normal', 'low'], default: 'normal' }, tags: { type: 'array', items: { type: 'string' }, description: 'Event tags' }, alertType: { type: 'string', enum: ['error', 'warning', 'info', 'success'], default: 'info' } }, required: ['title', 'text'] } }, { name: 'datadog/send-logs', description: 'Send log entries to Datadog', inputSchema: { type: 'object', properties: { logs: { type: 'array', items: { type: 'object', properties: { message: { type: 'string' }, level: { type: 'string' }, timestamp: { type: 'string' }, tags: { type: 'array', items: { type: 'string' } }, service: { type: 'string' }, source: { type: 'string' } }, required: ['message'] } }, hostname: { type: 'string', default: 'auto-publishing-server' }, service: { type: 'string', default: 'auto-publishing' }, source: { type: 'string', default: 'nodejs' } }, required: ['logs'] } }, { name: 'datadog/create-slo', description: 'Create a Service Level Objective (SLO)', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'SLO name' }, description: { type: 'string', description: 'SLO description' }, type: { type: 'string', enum: ['metric', 'monitor'], default: 'metric' }, sliSpecification: { type: 'object', description: 'SLI specification' }, thresholds: { type: 'array', items: { type: 'object', properties: { target: { type: 'number' }, timeframe: { type: 'string' } } } }, tags: { type: 'array', items: { type: 'string' } } }, required: ['name'] } }, { name: 'datadog/get-service-map', description: 'Get APM service map', inputSchema: { type: 'object', properties: { env: { type: 'string', description: 'Environment', default: 'prod' }, service: { type: 'string', description: 'Specific service name' }, start: { type: 'number', description: 'Start timestamp' }, end: { type: 'number', description: 'End timestamp' } } } }, { name: 'datadog/get-infrastructure-metrics', description: 'Get infrastructure metrics', inputSchema: { type: 'object', properties: { hostnames: { type: 'array', items: { type: 'string' }, description: 'Specific hostnames to query' }, timeframe: { type: 'string', enum: ['5m', '15m', '30m', '1h', '4h', '1d', '1w'], default: '1h' }, metrics: { type: 'array', items: { type: 'string' }, description: 'Specific metrics to retrieve', default: ['system.cpu.user', 'system.mem.used', 'system.disk.used'] } } } }, { name: 'datadog/install-agent', description: 'Install Datadog Agent on the server', inputSchema: { type: 'object', properties: { apiKey: { type: 'string', description: 'Datadog API key' }, site: { type: 'string', description: 'Datadog site', default: 'datadoghq.com' }, hostname: { type: 'string', description: 'Custom hostname' }, tags: { type: 'array', items: { type: 'string' }, description: 'Host tags' } } } }, { name: 'datadog/configure-integrations', description: 'Configure Datadog integrations', inputSchema: { type: 'object', properties: { integrations: { type: 'array', items: { type: 'string' }, description: 'Integrations to configure', default: ['docker', 'nginx', 'postgres'] }, configDir: { type: 'string', description: 'Configuration directory', default: '/etc/datadog-agent/conf.d' } } } } ]; } } export default DatadogMonitor;