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
JavaScript
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;