{ "name": "Server Health Monitor - Claude Code", "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "minutes", "minutesInterval": 5 } ] } }, "id": "schedule-trigger", "name": "Every 5 Minutes", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.2, "position": [0, 0] }, { "parameters": { "operation": "executeCommand", "command": "/root/.local/bin/claude -p \"Run python3 ~/.claude/skills/server-diagnostics/client.py health paper-dynasty and analyze the results. If any containers are not running or there are critical issues, summarize them. Otherwise just say 'All systems healthy'.\" --output-format json --json-schema '{\"type\":\"object\",\"properties\":{\"status\":{\"type\":\"string\",\"enum\":[\"healthy\",\"issues_found\"]},\"summary\":{\"type\":\"string\"},\"root_cause\":{\"type\":\"string\"},\"severity\":{\"type\":\"string\",\"enum\":[\"low\",\"medium\",\"high\",\"critical\"]},\"affected_services\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"actions_taken\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"status\",\"severity\",\"summary\"]}' --allowedTools \"Read,Grep,Glob,Bash(python3 ~/.claude/skills/server-diagnostics/client.py *)\"", "options": {} }, "id": "ssh-claude-code", "name": "Run Claude Diagnostics", "type": "n8n-nodes-base.ssh", "typeVersion": 1, "position": [220, 0], "credentials": { "sshPassword": { "id": "REPLACE_WITH_CREDENTIAL_ID", "name": "Claude Code LXC" } } }, { "parameters": { "jsCode": "// Parse Claude Code JSON output (uses --json-schema for structured_output)\nconst stdout = $input.first().json.stdout || '';\n\ntry {\n const response = JSON.parse(stdout);\n const data = response.structured_output || JSON.parse(response.result || '{}');\n \n return [{\n json: {\n success: !response.is_error,\n status: data.status || 'unknown',\n summary: data.summary || response.result || 'No result',\n severity: data.severity || 'low',\n root_cause: data.root_cause || null,\n affected_services: data.affected_services || [],\n actions_taken: data.actions_taken || [],\n duration_ms: response.duration_ms,\n cost_usd: response.total_cost_usd,\n session_id: response.session_id,\n has_issues: data.status === 'issues_found',\n raw: response\n }\n }];\n} catch (e) {\n return [{\n json: {\n success: false,\n status: 'error',\n summary: 'Failed to parse Claude response',\n severity: 'high',\n error: e.message,\n raw_stdout: stdout,\n has_issues: true\n }\n }];\n}" }, "id": "parse-response", "name": "Parse Claude Response", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [440, 0] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict" }, "conditions": [ { "id": "check-issues", "leftValue": "={{ $json.has_issues }}", "rightValue": true, "operator": { "type": "boolean", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "check-issues", "name": "Has Issues?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [660, 0] }, { "parameters": { "method": "POST", "url": "https://discord.com/api/webhooks/1451783909409816763/O9PMDiNt6ZIWRf8HKocIZ_E4vMGV_lEwq50aAiZ9HVFR2UGwO6J1N9_wOm82p0MetIqT", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"embeds\": [{\n \"title\": \"{{ $json.severity === 'critical' ? '🔴' : $json.severity === 'high' ? '🟠' : '🟡' }} Server Alert\",\n \"description\": {{ JSON.stringify($json.summary) }},\n \"color\": {{ $json.severity === 'critical' ? 15158332 : $json.severity === 'high' ? 15105570 : 16776960 }},\n \"fields\": [\n {\n \"name\": \"Severity\",\n \"value\": \"{{ $json.severity.toUpperCase() }}\",\n \"inline\": true\n },\n {\n \"name\": \"Server\",\n \"value\": \"paper-dynasty (10.10.0.88)\",\n \"inline\": true\n },\n {\n \"name\": \"Cost\",\n \"value\": \"${{ $json.cost_usd ? $json.cost_usd.toFixed(4) : '0.0000' }}\",\n \"inline\": true\n },\n {\n \"name\": \"Root Cause\",\n \"value\": \"{{ $json.root_cause || 'N/A' }}\",\n \"inline\": false\n },\n {\n \"name\": \"Affected Services\",\n \"value\": \"{{ $json.affected_services.length ? $json.affected_services.join(', ') : 'None' }}\",\n \"inline\": false\n },\n {\n \"name\": \"Actions Taken\",\n \"value\": \"{{ $json.actions_taken.length ? $json.actions_taken.join('\\n') : 'None' }}\",\n \"inline\": false\n }\n ],\n \"timestamp\": \"{{ new Date().toISOString() }}\"\n }]\n}", "options": {} }, "id": "discord-alert", "name": "Discord Alert", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [880, -100] }, { "parameters": { "method": "POST", "url": "https://discord.com/api/webhooks/1451783909409816763/O9PMDiNt6ZIWRf8HKocIZ_E4vMGV_lEwq50aAiZ9HVFR2UGwO6J1N9_wOm82p0MetIqT", "sendBody": true, "specifyBody": "json", "jsonBody": "={\n \"embeds\": [{\n \"title\": \"Health Check OK\",\n \"description\": {{ JSON.stringify($json.result) }},\n \"color\": 3066993,\n \"fields\": [\n {\n \"name\": \"Server\",\n \"value\": \"paper-dynasty (10.10.0.88)\",\n \"inline\": true\n },\n {\n \"name\": \"Duration\",\n \"value\": \"{{ $json.duration_ms }}ms\",\n \"inline\": true\n }\n ],\n \"timestamp\": \"{{ new Date().toISOString() }}\"\n }]\n}", "options": {} }, "id": "discord-ok", "name": "Discord OK (Optional)", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "position": [880, 100], "disabled": true } ], "connections": { "Every 5 Minutes": { "main": [ [ { "node": "Run Claude Diagnostics", "type": "main", "index": 0 } ] ] }, "Run Claude Diagnostics": { "main": [ [ { "node": "Parse Claude Response", "type": "main", "index": 0 } ] ] }, "Parse Claude Response": { "main": [ [ { "node": "Has Issues?", "type": "main", "index": 0 } ] ] }, "Has Issues?": { "main": [ [ { "node": "Discord Alert", "type": "main", "index": 0 } ], [ { "node": "Discord OK (Optional)", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1" }, "staticData": null, "tags": [], "triggerCount": 0, "pinData": {} }