The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "OpenShift Performance Metrics - MCP",
"nodes": [
{
"parameters": {},
"id": "trigger",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
180,
300
]
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "ns",
"name": "namespace",
"value": "openshift-lightspeed",
"type": "string"
},
{
"id": "email",
"name": "destinationEmail",
"value": "maximiliano.pizarro.5@gmail.com",
"type": "string"
},
{
"id": "mcpHost",
"name": "mcpHost",
"value": "field-content-ols.openshift-lightspeed.svc.cluster.local",
"type": "string"
},
{
"id": "mcpPort",
"name": "mcpPort",
"value": "8080",
"type": "string"
},
{
"id": "tool",
"name": "toolName",
"value": "getPerformanceMetrics",
"type": "string"
},
{
"id": "litellmHost",
"name": "litellmHost",
"value": "field-content-ols-litellm.openshift-lightspeed.svc.cluster.local",
"type": "string"
},
{
"id": "litellmPort",
"name": "litellmPort",
"value": "4000",
"type": "string"
},
{
"id": "litellmApiKey",
"name": "litellmApiKey",
"value": "sk-openshift-mcp-1234",
"type": "string"
}
]
},
"options": {}
},
"id": "set-params",
"name": "Set Parameters",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
400,
300
]
},
{
"parameters": {
"jsCode": "const http = require('http');\n\nfunction mcpRequest(host, port, body, sessionId) {\n return new Promise((resolve, reject) => {\n const data = JSON.stringify(body);\n const headers = { 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream', 'Content-Length': Buffer.byteLength(data) };\n if (sessionId) headers['Mcp-Session-Id'] = sessionId;\n const req = http.request({ hostname: host, port, path: '/mcp', method: 'POST', headers }, (res) => {\n let body = '';\n res.on('data', (chunk) => body += chunk);\n res.on('end', () => {\n const lines = body.split('\\n');\n let jsonData = '';\n for (const line of lines) { if (line.startsWith('data: ')) jsonData = line.substring(6); }\n resolve({ status: res.statusCode, headers: res.headers, body: jsonData || body });\n });\n });\n req.on('error', reject);\n req.write(data);\n req.end();\n });\n}\n\nasync function callMcpTool(host, port, toolName, toolArgs) {\n const init = await mcpRequest(host, port, { jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'n8n-workflow', version: '1.0' } } });\n const sid = init.headers['mcp-session-id'];\n await mcpRequest(host, port, { jsonrpc: '2.0', method: 'notifications/initialized' }, sid);\n const result = await mcpRequest(host, port, { jsonrpc: '2.0', id: 3, method: 'tools/call', params: { name: toolName, arguments: toolArgs } }, sid);\n const parsed = JSON.parse(result.body);\n return parsed.result?.content?.[0]?.text || 'Error: ' + JSON.stringify(parsed.error);\n}\n\nconst j = $input.first().json;\nconst host = j.mcpHost;\nconst port = parseInt(j.mcpPort, 10);\nconst mcpOutput = await callMcpTool(host, port, j.toolName, { namespace: j.namespace });\nreturn [{ json: { ...j, mcpOutput } }];\n"
},
"id": "mcp-call",
"name": "MCP Tool Call",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
620,
300
]
},
{
"parameters": {
"jsCode": "const http = require('http');\nconst input = $input.first().json;\nconst litellmHost = input.litellmHost;\nconst litellmPort = parseInt(input.litellmPort, 10);\nconst litellmApiKey = input.litellmApiKey || '';\nconst toolName = input.toolName;\nconst namespace = input.namespace;\nconst mcpOutputFull = input.mcpOutput || '';\nconst mcpOutput = mcpOutputFull.length > 800 ? mcpOutputFull.substring(0, 800) + '\\n[... truncated for AI analysis, see Raw MCP Output below for full data]' : mcpOutputFull;\nconst systemPrompt = 'Analyze this OpenShift MCP output for tool \"' + toolName + '\" in namespace \"' + namespace + '\". Reply with: 1) HTML table of key data, 2) Health status (Healthy/Warning/Critical). Be concise, use inline CSS.';\nconst requestBody = JSON.stringify({\n model: 'granite',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: mcpOutput }\n ],\n temperature: 0.3,\n max_tokens: 1024\n});\nreturn new Promise((resolve, reject) => {\n const req = http.request({\n hostname: litellmHost,\n port: litellmPort,\n path: '/v1/chat/completions',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer ' + litellmApiKey,\n 'Content-Length': Buffer.byteLength(requestBody)\n }\n }, (res) => {\n let data = '';\n res.on('data', (chunk) => { data += chunk; });\n res.on('end', () => {\n try {\n const parsed = JSON.parse(data);\n let rawAI = parsed.choices?.[0]?.message?.content || 'AI analysis unavailable';\n rawAI = rawAI.replace(/<think>[\\s\\S]*?<\\/think>/g, '').trim();\n const aiAnalysis = rawAI.length > 1800 ? rawAI.substring(0, 1800) + '\n[... truncated ...]' : rawAI;\n const model = parsed.model || 'deepseek-r1-distill-qwen-14b';\n resolve([{ json: { ...input, aiAnalysis, aiModel: model } }]);\n } catch (e) {\n resolve([{ json: { ...input, aiAnalysis: 'AI analysis failed: ' + e.message, aiModel: 'error' } }]);\n }\n });\n });\n req.on('error', (e) => {\n resolve([{ json: { ...input, aiAnalysis: 'AI service unavailable: ' + e.message, aiModel: 'unreachable' } }]);\n });\n req.write(requestBody);\n req.end();\n});\n"
},
"id": "ai-format",
"name": "AI Format & Explain",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
840,
300
]
},
{
"parameters": {
"jsCode": "const http = require('http');\nconst input = $input.first().json;\nconst namespace = input.namespace;\nconst email = input.destinationEmail;\nconst toolName = input.toolName;\nconst mcpOutput = input.mcpOutput || '';\nconst aiAnalysis = input.aiAnalysis || '';\nconst aiModel = input.aiModel || 'granite';\nconst ts = new Date().toISOString();\nconst title = \"Performance Metrics Report\";\nconst subtitle = \"Resource usage and performance monitoring via MCP\";\nconst escapedOutput = mcpOutput.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');\nconst html = `<html><body style=\"margin:0;padding:0;background:#f0f0f0;font-family:'Red Hat Display',Arial,sans-serif;\">\n<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#f0f0f0;\"><tr><td align=\"center\" style=\"padding:24px;\">\n<table width=\"640\" style=\"max-width:640px;width:100%;\">\n<tr><td style=\"background:#151515;padding:16px 32px;border-radius:8px 8px 0 0;\">\n<span style=\"color:#EE0000;font-size:20px;font-weight:700;\">Red Hat</span>\n<span style=\"float:right;color:#C6C6C6;font-size:12px;line-height:28px;\">OpenShift Developer Sandbox</span></td></tr>\n<tr><td style=\"background:#DC382D;padding:28px 32px;\">\n<h1 style=\"margin:0;font-size:24px;color:#FFF;\">${title}</h1>\n<p style=\"margin:6px 0 0;font-size:14px;color:rgba(255,255,255,.85);\">${subtitle}</p></td></tr>\n<tr><td style=\"background:#292929;padding:0;\">\n<table width=\"100%\"><tr>\n<td style=\"padding:12px 16px;text-align:center;color:#A3A3A3;font-size:11px;border-right:1px solid #3C3C3C;width:25%;\">\n<b style=\"display:block;color:#FFF;font-size:13px;\">${namespace}</b>Namespace</td>\n<td style=\"padding:12px 16px;text-align:center;color:#A3A3A3;font-size:11px;border-right:1px solid #3C3C3C;width:25%;\">\n<b style=\"display:block;color:#FFF;font-size:13px;\">${ts}</b>Generated</td>\n<td style=\"padding:12px 16px;text-align:center;color:#A3A3A3;font-size:11px;border-right:1px solid #3C3C3C;width:25%;\">\n<b style=\"display:block;color:#FFF;font-size:13px;\">${toolName}</b>MCP Tool</td>\n<td style=\"padding:12px 16px;text-align:center;color:#A3A3A3;font-size:11px;width:25%;\">\n<b style=\"display:block;color:#FFF;font-size:13px;\">${aiModel}</b>AI Model</td>\n</tr></table></td></tr>\n<tr><td style=\"background:#FFF;padding:28px 32px;\">\n<h2 style=\"margin:0 0 16px;font-size:17px;border-bottom:2px solid #DC382D;padding-bottom:10px;\">AI Analysis</h2>\n<div style=\"padding:8px 0;font-size:14px;line-height:1.6;\">${aiAnalysis}</div>\n</td></tr>\n<tr><td style=\"background:#FFF;padding:0 32px 28px 32px;\">\n<h2 style=\"margin:0 0 16px;font-size:17px;border-bottom:2px solid #292929;padding-bottom:10px;color:#6A6E73;\">Raw MCP Output</h2>\n<pre style=\"background:#1E1E1E;color:#D4D4D4;padding:16px;border-radius:6px;overflow-x:auto;font-size:12px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word;\">${escapedOutput}</pre>\n</td></tr>\n<tr><td style=\"background:#F5F5F5;padding:16px 32px;border-top:1px solid #E0E0E0;text-align:center;\">\n<span style=\"display:inline-block;background:#EE0000;color:white;font-size:10px;font-weight:700;padding:4px 10px;border-radius:3px;\">RED HAT</span>\n<span style=\"font-size:11px;color:#6A6E73;margin:0 8px;\">OpenShift MCP Server</span>\n<span style=\"font-size:11px;color:#DC382D;font-weight:600;margin:0 8px;\">n8n</span>\n</td></tr>\n<tr><td style=\"background:#151515;padding:20px 32px;border-radius:0 0 8px 8px;text-align:center;\">\n<p style=\"margin:0;font-size:12px;color:#A3A3A3;\">Auto-generated by n8n on <b style=\"color:#FFF;\">Red Hat OpenShift Developer Sandbox</b></p>\n</td></tr></table></td></tr></table></body></html>`;\nconst subject = `[Red Hat OpenShift] ${title} | ${namespace} | ${ts}`;\nconst mailBody = JSON.stringify({\n From: { Name: 'n8n Workflow', Email: 'workflow@n8n.local' },\n To: [{ Name: 'Admin', Email: email }],\n Subject: subject,\n HTML: html\n});\nreturn new Promise((resolve, reject) => {\n const req = http.request({\n hostname: 'n8n-mailpit',\n port: 8025,\n path: '/api/v1/send',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(mailBody)\n }\n }, (res) => {\n let data = '';\n res.on('data', (chunk) => { data += chunk; });\n res.on('end', () => {\n resolve([{ json: { mailpitStatus: res.statusCode, mailpitResponse: data, subject } }]);\n });\n });\n req.on('error', reject);\n req.write(mailBody);\n req.end();\n});\n"
},
"id": "report-email",
"name": "Build Report & Send Email",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1060,
300
]
}
],
"connections": {
"Manual Trigger": {
"main": [
[
{
"node": "Set Parameters",
"type": "main",
"index": 0
}
]
]
},
"Set Parameters": {
"main": [
[
{
"node": "MCP Tool Call",
"type": "main",
"index": 0
}
]
]
},
"MCP Tool Call": {
"main": [
[
{
"node": "AI Format & Explain",
"type": "main",
"index": 0
}
]
]
},
"AI Format & Explain": {
"main": [
[
{
"node": "Build Report & Send Email",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"active": false
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
OpenShift Performance Metrics - MCP. Event-driven trigger; 5 nodes.
Source: https://github.com/maximilianoPizarro/test-drive-pe-oscg/blob/09e735c5d24529c3d70207baaa5cac1b34d55967/examples/helm/components/connectivity-link-openshift-lightspeed/workflows/workflow-resource-quota-monitor.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Blotato. Uses googleSheets, @blotato/n8n-nodes-blotato. Event-driven trigger; 65 nodes.
This template is a hands-on, practical exam designed to help you master n8n Expressions—the key to accessing and manipulating data in your workflows.
This template is a hands-on, practical exam designed to test your understanding of the fundamental JSON data types. It's the perfect way to solidify your knowledge after learning the basics.
Agendamiento. Uses n8n-nodes-evolution-api, redis, dataTable, executeWorkflowTrigger. Event-driven trigger; 60 nodes.
Kv Cloudflare Key Value Database Full Api Integration Workflow. Uses stickyNote, httpRequest, manualTrigger. Event-driven trigger; 47 nodes.