node-red-contrib-mcp-server
Version:
A comprehensive Node-RED wrapper for Model Context Protocol (MCP) servers providing standardized AI agent tool interfaces, server lifecycle management, real-time communication capabilities, and visual MCP server creation
321 lines (270 loc) • 14.5 kB
HTML
<!-- MCP Server Node -->
<script type="text/javascript">
RED.nodes.registerType('mcp-server', {
category: 'mcp',
color: '#4A90E2',
defaults: {
name: {value: ""},
serverName: {value: "mcp-server", required: true},
serverType: {value: "python", required: true},
serverPath: {value: "", required: true},
serverArgs: {value: ""},
serverPort: {value: 8000, required: true, validate: function(v) { return v > 0 && v < 65536; }},
autoStart: {value: false},
healthCheck: {value: true},
healthCheckInterval: {value: 30000, validate: function(v) { return v >= 5000; }},
maxRestarts: {value: 3, validate: function(v) { return v >= 0; }},
restartDelay: {value: 5000, validate: function(v) { return v >= 1000; }}
},
inputs: 1,
outputs: 1,
icon: "font-awesome/fa-server",
label: function() {
return this.name || `${this.serverName}:${this.serverPort}`;
},
labelStyle: function() {
return this.name ? "node_label_italic" : "";
},
oneditprepare: function() {
var node = this;
// Update form fields based on server type
function updateServerTypeFields() {
var serverType = $("#node-input-serverType").val();
if (serverType === "python") {
$("#server-path-hint").text("Path to Python MCP server script (e.g., /path/to/server.py)");
$("#server-args-hint").text("Additional Python arguments (e.g., --host 0.0.0.0 --debug)");
} else if (serverType === "node") {
$("#server-path-hint").text("Path to Node.js MCP server script (e.g., /path/to/server.js)");
$("#server-args-hint").text("Additional Node.js arguments (e.g., --port 8000)");
} else {
$("#server-path-hint").text("Full command to run MCP server (e.g., /usr/bin/my-mcp-server)");
$("#server-args-hint").text("Additional command arguments");
}
}
// Initialize server type change handler
$("#node-input-serverType").change(updateServerTypeFields);
// Initialize form
updateServerTypeFields();
// Auto-generate server name from path
$("#node-input-serverPath").on('input', function() {
var path = $(this).val();
if (path && !$("#node-input-serverName").val()) {
var fileName = path.split('/').pop().split('\\').pop();
var baseName = fileName.replace(/\.(py|js|exe)$/, '');
$("#node-input-serverName").val(baseName);
}
});
// Test server path button
$("#test-server-path-btn").click(function() {
var button = $(this);
var originalText = button.text();
button.text("Testing...").prop('disabled', true);
var serverType = $("#node-input-serverType").val();
var serverPath = $("#node-input-serverPath").val();
if (!serverPath) {
button.text(originalText).prop('disabled', false);
RED.notify("Server path is required", "error");
return;
}
// Simple validation based on server type
var command;
if (serverType === "python") {
command = `python3 -c "import os; print('OK' if os.path.exists('${serverPath}') else 'NOT_FOUND')"`;
} else if (serverType === "node") {
command = `node -e "const fs=require('fs'); console.log(fs.existsSync('${serverPath}') ? 'OK' : 'NOT_FOUND')"`;
} else {
command = `test -f "${serverPath}" && echo OK || echo NOT_FOUND`;
}
setTimeout(function() {
button.text(originalText).prop('disabled', false);
RED.notify("Path validation feature coming soon", "warning");
}, 1000);
});
// Load Omnispindle preset
$("#load-omnispindle-preset-btn").click(function() {
$("#node-input-serverName").val("omnispindle");
$("#node-input-serverType").val("python");
$("#node-input-serverPath").val("src/Omnispindle/__init__.py");
$("#node-input-serverArgs").val("--host 0.0.0.0 --port 8000");
$("#node-input-serverPort").val(8000);
$("#node-input-autoStart").prop('checked', true);
updateServerTypeFields();
RED.notify("Omnispindle preset loaded", "success");
});
// Advanced settings toggle
$("#advanced-toggle").click(function() {
$("#advanced-settings").slideToggle();
var icon = $(this).find("i");
icon.toggleClass("fa-chevron-down fa-chevron-up");
});
}
});
</script>
<script type="text/html" data-template-name="mcp-server">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Optional display name">
</div>
<div class="form-row">
<label for="node-input-serverName"><i class="fa fa-server"></i> Server Name</label>
<input type="text" id="node-input-serverName" placeholder="mcp-server">
<div style="margin-top: 5px; font-size: 12px; color: #666;">
Unique identifier for this MCP server instance
</div>
</div>
<hr>
<h4>Server Configuration</h4>
<div class="form-row">
<label for="node-input-serverType"><i class="fa fa-cogs"></i> Server Type</label>
<select id="node-input-serverType">
<option value="python">Python</option>
<option value="node">Node.js</option>
<option value="custom">Custom Command</option>
</select>
</div>
<div class="form-row">
<label for="node-input-serverPath"><i class="fa fa-file-code-o"></i> Server Path</label>
<input type="text" id="node-input-serverPath" placeholder="/path/to/mcp/server">
<div id="server-path-hint" style="margin-top: 5px; font-size: 12px; color: #666;">
Path to MCP server script or executable
</div>
<button type="button" id="test-server-path-btn" class="btn btn-secondary" style="margin-top: 5px;">
<i class="fa fa-check"></i> Test Path
</button>
</div>
<div class="form-row">
<label for="node-input-serverArgs"><i class="fa fa-terminal"></i> Server Arguments</label>
<input type="text" id="node-input-serverArgs" placeholder="--host 0.0.0.0 --debug">
<div id="server-args-hint" style="margin-top: 5px; font-size: 12px; color: #666;">
Additional command line arguments for the server
</div>
</div>
<div class="form-row">
<label for="node-input-serverPort"><i class="fa fa-plug"></i> Server Port</label>
<input type="number" id="node-input-serverPort" min="1" max="65535" placeholder="8000">
<div style="margin-top: 5px; font-size: 12px; color: #666;">
Port number for the MCP server (1-65535)
</div>
</div>
<div class="form-row">
<label for="node-input-autoStart"><i class="fa fa-play"></i> Auto Start</label>
<input type="checkbox" id="node-input-autoStart" style="width: auto;">
<span style="margin-left: 10px; font-size: 12px; color: #666;">Automatically start server when Node-RED starts</span>
</div>
<!-- Preset Buttons -->
<div class="form-row">
<label></label>
<button type="button" id="load-omnispindle-preset-btn" class="btn btn-primary">
<i class="fa fa-download"></i> Load Omnispindle Preset
</button>
</div>
<!-- Advanced Settings -->
<hr>
<div class="form-row">
<label></label>
<button type="button" id="advanced-toggle" class="btn btn-link" style="padding: 0; text-align: left;">
<i class="fa fa-chevron-down"></i> Advanced Settings
</button>
</div>
<div id="advanced-settings" style="display: none;">
<div class="form-row">
<label for="node-input-healthCheck"><i class="fa fa-heartbeat"></i> Health Check</label>
<input type="checkbox" id="node-input-healthCheck" style="width: auto;">
<span style="margin-left: 10px; font-size: 12px; color: #666;">Enable periodic health checks</span>
</div>
<div class="form-row">
<label for="node-input-healthCheckInterval"><i class="fa fa-clock-o"></i> Health Check Interval</label>
<input type="number" id="node-input-healthCheckInterval" min="5000" step="1000" placeholder="30000">
<span style="margin-left: 10px; font-size: 12px; color: #666;">milliseconds</span>
</div>
<div class="form-row">
<label for="node-input-maxRestarts"><i class="fa fa-refresh"></i> Max Restarts</label>
<input type="number" id="node-input-maxRestarts" min="0" max="10" placeholder="3">
<div style="margin-top: 5px; font-size: 12px; color: #666;">
Maximum number of automatic restart attempts (0 = no restarts)
</div>
</div>
<div class="form-row">
<label for="node-input-restartDelay"><i class="fa fa-pause"></i> Restart Delay</label>
<input type="number" id="node-input-restartDelay" min="1000" step="1000" placeholder="5000">
<span style="margin-left: 10px; font-size: 12px; color: #666;">milliseconds</span>
</div>
</div>
</script>
<script type="text/html" data-help-name="mcp-server">
<p>A Node-RED node for running and managing Model Context Protocol (MCP) servers. Provides lifecycle management, health monitoring, and real-time communication with MCP server processes.</p>
<h3>Configuration</h3>
<dl class="message-properties">
<dt>Server Name <span class="property-type">string</span></dt>
<dd>Unique identifier for this MCP server instance</dd>
<dt>Server Type <span class="property-type">string</span></dt>
<dd>Type of MCP server: Python, Node.js, or Custom Command</dd>
<dt>Server Path <span class="property-type">string</span></dt>
<dd>Path to the MCP server script or executable file</dd>
<dt>Server Arguments <span class="property-type">string</span></dt>
<dd>Additional command line arguments to pass to the server</dd>
<dt>Server Port <span class="property-type">number</span></dt>
<dd>Port number for the MCP server (1-65535)</dd>
<dt>Auto Start <span class="property-type">boolean</span></dt>
<dd>Whether to automatically start the server when Node-RED starts</dd>
</dl>
<h3>Advanced Settings</h3>
<dl class="message-properties">
<dt>Health Check <span class="property-type">boolean</span></dt>
<dd>Enable periodic health checks via HTTP endpoint</dd>
<dt>Health Check Interval <span class="property-type">number</span></dt>
<dd>Interval between health checks in milliseconds (minimum 5000)</dd>
<dt>Max Restarts <span class="property-type">number</span></dt>
<dd>Maximum number of automatic restart attempts (0 disables restarts)</dd>
<dt>Restart Delay <span class="property-type">number</span></dt>
<dd>Delay between restart attempts in milliseconds (minimum 1000)</dd>
</dl>
<h3>Input Commands</h3>
<p>Send messages with the following topics to control the server:</p>
<dl class="message-properties">
<dt>start <span class="property-type">string</span></dt>
<dd>Start the MCP server</dd>
<dt>stop <span class="property-type">string</span></dt>
<dd>Stop the MCP server</dd>
<dt>restart <span class="property-type">string</span></dt>
<dd>Restart the MCP server</dd>
<dt>status <span class="property-type">string</span></dt>
<dd>Get current server status</dd>
</dl>
<h3>Output Messages</h3>
<p>The node outputs messages with different topics based on server events:</p>
<dl class="message-properties">
<dt>stdout <span class="property-type">string</span></dt>
<dd>Standard output from the MCP server process</dd>
<dt>stderr <span class="property-type">string</span></dt>
<dd>Standard error output from the MCP server process</dd>
<dt>started <span class="property-type">object</span></dt>
<dd>Server started successfully with details</dd>
<dt>exit <span class="property-type">object</span></dt>
<dd>Server process exited with exit code and signal</dd>
</dl>
<h3>Omnispindle Integration</h3>
<p>This node includes a preset for the Omnispindle MCP server from this project. Use the "Load Omnispindle Preset" button to automatically configure the node for running the local Omnispindle server.</p>
<h3>Examples</h3>
<p><strong>Python FastMCP Server:</strong></p>
<ul>
<li>Server Type: Python</li>
<li>Server Path: <code>src/Omnispindle/__init__.py</code></li>
<li>Server Args: <code>--host 0.0.0.0 --port 8000</code></li>
</ul>
<p><strong>Node.js MCP Server:</strong></p>
<ul>
<li>Server Type: Node.js</li>
<li>Server Path: <code>/path/to/mcp-server.js</code></li>
<li>Server Args: <code>--port 3000</code></li>
</ul>
<h3>Requirements</h3>
<p>The appropriate runtime must be installed:</p>
<ul>
<li><strong>Python servers:</strong> Python 3.8+ with required packages</li>
<li><strong>Node.js servers:</strong> Node.js 16+ with required packages</li>
<li><strong>Custom commands:</strong> Executable must be in PATH or use full path</li>
</ul>
<h3>Admin Endpoint</h3>
<p>View running MCP servers: <code>GET /mcp-servers</code></p>
</script>