Node-RED MCP fixes: N2 input validation, N6 auto api_help

This commit is contained in:
root
2026-07-04 20:21:16 +00:00
parent 51c134051d
commit a1026df830
2 changed files with 49 additions and 19 deletions

View File

@@ -75,7 +75,15 @@ async def update_flows(flows_json: str, deployment_type: str = "full") -> str:
Returns:
Confirmation message.
Raises:
ValueError: If deployment_type is not one of: full, nodes, flows, reload.
"""
_VALID_DEPLOYMENT_TYPES = ("full", "nodes", "flows", "reload")
if deployment_type not in _VALID_DEPLOYMENT_TYPES:
raise ValueError(
f"Invalid deployment_type '{deployment_type}'. Must be one of: {', '.join(_VALID_DEPLOYMENT_TYPES)}"
)
client = get_client()
flows = json.loads(flows_json)
await client.update_flows(flows, deployment_type=deployment_type)
@@ -144,9 +152,15 @@ async def create_flow(flow_json: str) -> FlowCreateResult:
Returns:
FlowCreateResult with the new flow ID.
Raises:
ValueError: If flow_json is not valid JSON.
"""
try:
flow = json.loads(flow_json)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in flow_json: {e}") from e
client = get_client()
flow = json.loads(flow_json)
result = await client.create_flow(flow)
return FlowCreateResult.from_api(result)
@@ -187,7 +201,12 @@ async def set_flows_state(state: str) -> FlowState:
Returns:
FlowState with new state.
Raises:
ValueError: If state is not "start" or "stop".
"""
if state not in ("start", "stop"):
raise ValueError(f"Invalid state '{state}'. Must be 'start' or 'stop'.")
client = get_client()
raw_state = await client.set_flows_state(state)
return FlowState.from_api(raw_state)

View File

@@ -2,6 +2,32 @@
from nodered_mcp.server import mcp
# Centralized endpoint registry: maps path to (method, description, implemented)
_ENDPOINT_REGISTRY = [
("GET", "/flows", "Get all flows", True),
("GET", "/flows/formatted", "Get flows with formatted summary", True),
("GET", "/flows/visualize", "Visualize flow structure", True),
("POST", "/flows", "Update all flows", True),
("GET", "/flow/:id", "Get a specific flow", True),
("PUT", "/flow/:id", "Update a specific flow", True),
("PATCH", "/flow/:id", "Patch a flow incrementally", True),
("DELETE","/flow/:id", "Delete a specific flow", True),
("POST", "/flow", "Create a new flow", True),
("GET", "/flows/state", "Get the state of flows", True),
("POST", "/flows/state", "Set the state of flows", True),
("GET", "/tabs", "List all flow tabs", True),
("GET", "/nodes", "Get list of installed nodes", True),
("GET", "/nodes/:module", "Get node module info", True),
("GET", "/nodes/search", "Search for nodes", True),
("GET", "/nodes/find", "Find nodes by type", True),
("POST", "/nodes/:module/toggle", "Toggle node module", True),
("POST", "/nodes", "Install a new node module", False),
("GET", "/settings", "Get runtime settings", True),
("GET", "/diagnostics", "Get diagnostics information", True),
("POST", "/inject/:id", "Trigger an inject node", True),
("GET", "/lint/:id", "Lint flow layout", True),
]
@mcp.tool()
async def api_help() -> str:
@@ -9,23 +35,8 @@ async def api_help() -> str:
Returns:
Markdown table of API endpoints and their MCP implementation status.
Endpoints are defined in the centralized _ENDPOINT_REGISTRY.
"""
endpoints = [
("GET", "/flows", "Get all flows", True),
("POST", "/flows", "Update all flows", True),
("GET", "/flow/:id", "Get a specific flow", True),
("PUT", "/flow/:id", "Update a specific flow", True),
("DELETE", "/flow/:id", "Delete a specific flow", True),
("POST", "/flow", "Create a new flow", True),
("GET", "/flows/state", "Get the state of flows", True),
("POST", "/flows/state", "Set the state of flows", True),
("GET", "/nodes", "Get list of installed nodes", True),
("POST", "/nodes", "Install a new node module", False),
("GET", "/settings", "Get runtime settings", True),
("GET", "/diagnostics", "Get diagnostics information", True),
("POST", "/inject/:id", "Trigger an inject node", True),
]
output = [
"# Node-RED API Help",
"",
@@ -33,8 +44,8 @@ async def api_help() -> str:
"|--------|------|-------------|---------------------|",
]
for method, path, description, implemented in endpoints:
status = "" if implemented else ""
for method, path, description, implemented in _ENDPOINT_REGISTRY:
status = "\u2705" if implemented else "\u274c"
output.append(f"| {method} | {path} | {description} | {status} |")
return "\n".join(output)