Initial commit: MCP Hub Community Edition v3.0.0

Community edition generated from private repo via sync pipeline.
Includes 9 plugins (WordPress, WooCommerce, WP Advanced, Gitea, n8n,
Supabase, OpenPanel, Appwrite, Directus) with ~587 tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-17 08:34:44 +03:30
commit cf62e65c55
237 changed files with 87596 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
"""n8n Plugin Handlers"""
from plugins.n8n.handlers import (
credentials,
executions,
projects,
system,
tags,
users,
variables,
workflows,
)
__all__ = [
"workflows",
"executions",
"credentials",
"tags",
"users",
"projects",
"variables",
"system",
]

View File

@@ -0,0 +1,161 @@
"""Credentials Handler - manages n8n credentials"""
import json
from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
{
"name": "get_credential",
"method_name": "get_credential",
"description": "Get credential metadata by ID. Note: Listing all credentials is not supported in n8n Public API.",
"schema": {
"type": "object",
"properties": {"credential_id": {"type": "string", "minLength": 1}},
"required": ["credential_id"],
},
"scope": "read",
},
{
"name": "create_credential",
"method_name": "create_credential",
"description": "Create a new credential. Use get_credential_schema to see required fields.",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1, "description": "Credential name"},
"type": {
"type": "string",
"minLength": 1,
"description": "Credential type (e.g., 'githubApi')",
},
"data": {
"type": "object",
"description": "Credential data matching the type schema",
},
},
"required": ["name", "type", "data"],
},
"scope": "admin",
},
{
"name": "delete_credential",
"method_name": "delete_credential",
"description": "Delete a credential.",
"schema": {
"type": "object",
"properties": {"credential_id": {"type": "string", "minLength": 1}},
"required": ["credential_id"],
},
"scope": "admin",
},
{
"name": "get_credential_schema",
"method_name": "get_credential_schema",
"description": "Get the schema for a credential type. Shows required fields.",
"schema": {
"type": "object",
"properties": {
"credential_type": {
"type": "string",
"minLength": 1,
"description": "Credential type name",
}
},
"required": ["credential_type"],
},
"scope": "read",
},
{
"name": "transfer_credential",
"method_name": "transfer_credential",
"description": "[Enterprise] Transfer a credential to another project. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"credential_id": {"type": "string", "minLength": 1},
"destination_project_id": {"type": "string", "minLength": 1},
},
"required": ["credential_id", "destination_project_id"],
},
"scope": "admin",
},
]
# === HANDLER FUNCTIONS ===
# Note: list_credentials is not supported in n8n Public API (GET /credentials returns 405)
async def get_credential(client: N8nClient, credential_id: str) -> str:
"""Get credential metadata"""
try:
cred = await client.get_credential(credential_id)
result = {
"success": True,
"credential": {
"id": cred.get("id"),
"name": cred.get("name"),
"type": cred.get("type"),
"created_at": cred.get("createdAt"),
"updated_at": cred.get("updatedAt"),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_credential(client: N8nClient, name: str, type: str, data: dict[str, Any]) -> str:
"""Create a new credential"""
try:
cred_data = {"name": name, "type": type, "data": data}
cred = await client.create_credential(cred_data)
result = {
"success": True,
"message": f"Credential '{name}' created successfully",
"credential": {
"id": cred.get("id"),
"name": cred.get("name"),
"type": cred.get("type"),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_credential(client: N8nClient, credential_id: str) -> str:
"""Delete a credential"""
try:
await client.delete_credential(credential_id)
return json.dumps(
{"success": True, "message": f"Credential {credential_id} deleted"}, indent=2
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_credential_schema(client: N8nClient, credential_type: str) -> str:
"""Get credential type schema"""
try:
schema = await client.get_credential_schema(credential_type)
return json.dumps(
{"success": True, "credential_type": credential_type, "schema": schema}, indent=2
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def transfer_credential(
client: N8nClient, credential_id: str, destination_project_id: str
) -> str:
"""Transfer credential to another project"""
try:
await client.transfer_credential(credential_id, destination_project_id)
return json.dumps(
{
"success": True,
"message": f"Credential {credential_id} transferred to project {destination_project_id}",
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)

View File

@@ -0,0 +1,437 @@
"""Execution Handler - manages n8n workflow executions"""
import asyncio
import json
from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
# === LIST EXECUTIONS ===
{
"name": "list_executions",
"method_name": "list_executions",
"description": "List workflow executions with filters by status, workflow, and date. Returns execution history. All filter parameters are OPTIONAL.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "OPTIONAL: Filter by workflow ID. Omit for all executions.",
},
"status": {
"type": "string",
"enum": ["success", "error", "waiting", "running", "new"],
"description": "OPTIONAL: Filter by execution status. Omit for all statuses.",
},
"include_data": {
"type": "boolean",
"description": "Include full execution data",
"default": False,
},
"limit": {
"type": "integer",
"description": "Maximum results",
"default": 20,
"minimum": 1,
"maximum": 250,
},
"cursor": {"type": "string", "description": "OPTIONAL: Pagination cursor."},
},
},
"scope": "read",
},
# === GET EXECUTION ===
{
"name": "get_execution",
"method_name": "get_execution",
"description": "Get detailed information about a specific execution including status, timing, and node outputs.",
"schema": {
"type": "object",
"properties": {
"execution_id": {
"type": "string",
"description": "Execution ID",
"minLength": 1,
},
"include_data": {
"type": "boolean",
"description": "Include full node execution data",
"default": True,
},
},
"required": ["execution_id"],
},
"scope": "read",
},
# === DELETE EXECUTION ===
{
"name": "delete_execution",
"method_name": "delete_execution",
"description": "Delete a single execution record. This removes the execution from history.",
"schema": {
"type": "object",
"properties": {
"execution_id": {
"type": "string",
"description": "Execution ID to delete",
"minLength": 1,
}
},
"required": ["execution_id"],
},
"scope": "write",
},
# === DELETE EXECUTIONS (BULK) ===
{
"name": "delete_executions",
"method_name": "delete_executions",
"description": "Bulk delete multiple execution records.",
"schema": {
"type": "object",
"properties": {
"execution_ids": {
"type": "array",
"description": "List of execution IDs to delete",
"items": {"type": "string"},
"minItems": 1,
}
},
"required": ["execution_ids"],
},
"scope": "write",
},
# === STOP EXECUTION ===
{
"name": "stop_execution",
"method_name": "stop_execution",
"description": "Stop a currently running execution.",
"schema": {
"type": "object",
"properties": {
"execution_id": {
"type": "string",
"description": "Execution ID to stop",
"minLength": 1,
}
},
"required": ["execution_id"],
},
"scope": "write",
},
# === RETRY EXECUTION ===
{
"name": "retry_execution",
"method_name": "retry_execution",
"description": "Retry a failed execution. Creates a new execution with the same input data.",
"schema": {
"type": "object",
"properties": {
"execution_id": {
"type": "string",
"description": "Execution ID to retry",
"minLength": 1,
}
},
"required": ["execution_id"],
},
"scope": "write",
},
# === GET EXECUTION DATA ===
{
"name": "get_execution_data",
"method_name": "get_execution_data",
"description": "Get full execution data including all node inputs and outputs. Useful for debugging.",
"schema": {
"type": "object",
"properties": {
"execution_id": {
"type": "string",
"description": "Execution ID",
"minLength": 1,
}
},
"required": ["execution_id"],
},
"scope": "read",
},
# === WAIT FOR EXECUTION ===
{
"name": "wait_for_execution",
"method_name": "wait_for_execution",
"description": "Poll an execution until it completes. Returns final status and output.",
"schema": {
"type": "object",
"properties": {
"execution_id": {
"type": "string",
"description": "Execution ID to wait for",
"minLength": 1,
},
"timeout_seconds": {
"type": "integer",
"description": "Maximum seconds to wait",
"default": 60,
"minimum": 5,
"maximum": 300,
},
"poll_interval": {
"type": "integer",
"description": "Seconds between status checks",
"default": 2,
"minimum": 1,
"maximum": 30,
},
},
"required": ["execution_id"],
},
"scope": "read",
},
]
# === HANDLER FUNCTIONS ===
async def list_executions(
client: N8nClient,
workflow_id: str | None = None,
status: str | None = None,
include_data: bool = False,
limit: int = 20,
cursor: str | None = None,
) -> str:
"""List workflow executions"""
try:
response = await client.list_executions(
workflow_id=workflow_id,
status=status,
include_data=include_data,
limit=limit,
cursor=cursor,
)
executions = response.get("data", [])
next_cursor = response.get("nextCursor")
result = {
"success": True,
"count": len(executions),
"executions": [
{
"id": e.get("id"),
"workflow_id": e.get("workflowId"),
"workflow_name": e.get("workflowData", {}).get("name"),
"status": e.get("status"),
"finished": e.get("finished"),
"started_at": e.get("startedAt"),
"stopped_at": e.get("stoppedAt"),
"mode": e.get("mode"),
}
for e in executions
],
"next_cursor": next_cursor,
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_execution(client: N8nClient, execution_id: str, include_data: bool = True) -> str:
"""Get execution details"""
try:
execution = await client.get_execution(execution_id, include_data)
result = {
"success": True,
"execution": {
"id": execution.get("id"),
"workflow_id": execution.get("workflowId"),
"workflow_name": execution.get("workflowData", {}).get("name"),
"status": execution.get("status"),
"finished": execution.get("finished"),
"started_at": execution.get("startedAt"),
"stopped_at": execution.get("stoppedAt"),
"mode": execution.get("mode"),
"retry_of": execution.get("retryOf"),
"retry_success_id": execution.get("retrySuccessId"),
},
}
# Include node execution data if requested
if include_data and "data" in execution:
result["execution"]["node_data"] = execution.get("data")
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_execution(client: N8nClient, execution_id: str) -> str:
"""Delete a single execution"""
try:
await client.delete_execution(execution_id)
result = {"success": True, "message": f"Execution {execution_id} deleted successfully"}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_executions(client: N8nClient, execution_ids: list[str]) -> str:
"""Bulk delete executions"""
try:
deleted = []
failed = []
for exec_id in execution_ids:
try:
await client.delete_execution(exec_id)
deleted.append(exec_id)
except Exception as e:
failed.append({"id": exec_id, "error": str(e)})
result = {
"success": len(failed) == 0,
"deleted_count": len(deleted),
"deleted": deleted,
"failed": failed if failed else None,
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def stop_execution(client: N8nClient, execution_id: str) -> str:
"""Stop a running execution"""
try:
await client.stop_execution(execution_id)
result = {"success": True, "message": f"Execution {execution_id} stopped successfully"}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def retry_execution(client: N8nClient, execution_id: str) -> str:
"""Retry a failed execution"""
try:
# Get the original execution to find workflow ID
original = await client.get_execution(execution_id, include_data=True)
workflow_id = original.get("workflowId")
if not workflow_id:
raise Exception("Cannot find workflow ID from execution")
# Get input data from original execution
input_data = None
exec_data = original.get("data", {})
if exec_data:
# Try to get input from first node
result_data = exec_data.get("resultData", {})
run_data = result_data.get("runData", {})
if run_data:
first_node = list(run_data.keys())[0] if run_data else None
if first_node:
node_data = run_data[first_node]
if node_data and len(node_data) > 0:
input_data = node_data[0].get("data", {}).get("main", [[]])[0]
# Execute the workflow again
new_execution = await client.execute_workflow(workflow_id, data=input_data)
result = {
"success": True,
"message": f"Retrying execution {execution_id}",
"original_execution_id": execution_id,
"new_execution": {
"id": new_execution.get("id"),
"workflow_id": workflow_id,
"status": new_execution.get("status", "running"),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_execution_data(client: N8nClient, execution_id: str) -> str:
"""Get full execution data"""
try:
execution = await client.get_execution(execution_id, include_data=True)
exec_data = execution.get("data", {})
result_data = exec_data.get("resultData", {})
result = {
"success": True,
"execution_id": execution_id,
"status": execution.get("status"),
"finished": execution.get("finished"),
"run_data": result_data.get("runData", {}),
"last_node_executed": result_data.get("lastNodeExecuted"),
"error": result_data.get("error"),
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def wait_for_execution(
client: N8nClient, execution_id: str, timeout_seconds: int = 60, poll_interval: int = 2
) -> str:
"""Wait for execution to complete"""
try:
elapsed = 0
final_status = None
execution = None
while elapsed < timeout_seconds:
execution = await client.get_execution(execution_id, include_data=True)
status = execution.get("status")
finished = execution.get("finished")
if finished or status in ["success", "error", "crashed"]:
final_status = status
break
await asyncio.sleep(poll_interval)
elapsed += poll_interval
if not final_status:
return json.dumps(
{
"success": False,
"error": f"Timeout after {timeout_seconds} seconds",
"execution_id": execution_id,
"last_status": execution.get("status") if execution else "unknown",
},
indent=2,
)
result = {
"success": True,
"execution_id": execution_id,
"final_status": final_status,
"finished": execution.get("finished"),
"started_at": execution.get("startedAt"),
"stopped_at": execution.get("stoppedAt"),
"duration_seconds": elapsed,
}
# Include error if failed
if final_status == "error":
exec_data = execution.get("data", {})
result_data = exec_data.get("resultData", {})
result["error"] = result_data.get("error")
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)

View File

@@ -0,0 +1,228 @@
"""Projects Handler - manages n8n projects (Enterprise/Pro)"""
import json
from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
{
"name": "list_projects",
"method_name": "list_projects",
"description": "[Enterprise] List all projects. Requires n8n Enterprise/Pro license. All parameters are OPTIONAL.",
"schema": {
"type": "object",
"properties": {
"limit": {"type": "integer", "default": 100, "minimum": 1, "maximum": 250},
"cursor": {"type": "string", "description": "OPTIONAL: Pagination cursor."},
},
},
"scope": "read",
},
{
"name": "get_project",
"method_name": "get_project",
"description": "[Enterprise] Get project details by ID. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {"project_id": {"type": "string", "minLength": 1}},
"required": ["project_id"],
},
"scope": "read",
},
{
"name": "create_project",
"method_name": "create_project",
"description": "[Enterprise] Create a new project. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1, "description": "Project name"}
},
"required": ["name"],
},
"scope": "admin",
},
{
"name": "update_project",
"method_name": "update_project",
"description": "[Enterprise] Update project metadata. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"project_id": {"type": "string", "minLength": 1},
"name": {"type": "string", "minLength": 1, "description": "New project name"},
},
"required": ["project_id", "name"],
},
"scope": "admin",
},
{
"name": "delete_project",
"method_name": "delete_project",
"description": "[Enterprise] Delete a project. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {"project_id": {"type": "string", "minLength": 1}},
"required": ["project_id"],
},
"scope": "admin",
},
{
"name": "add_project_users",
"method_name": "add_project_users",
"description": "[Enterprise] Add users to a project with roles. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"project_id": {"type": "string", "minLength": 1},
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"user_id": {"type": "string"},
"role": {
"type": "string",
"enum": ["project:admin", "project:editor", "project:viewer"],
},
},
"required": ["user_id", "role"],
},
"minItems": 1,
},
},
"required": ["project_id", "users"],
},
"scope": "admin",
},
{
"name": "change_project_user_role",
"method_name": "change_project_user_role",
"description": "[Enterprise] Change a user's role in a project. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"project_id": {"type": "string", "minLength": 1},
"user_id": {"type": "string", "minLength": 1},
"role": {
"type": "string",
"enum": ["project:admin", "project:editor", "project:viewer"],
},
},
"required": ["project_id", "user_id", "role"],
},
"scope": "admin",
},
{
"name": "remove_project_user",
"method_name": "remove_project_user",
"description": "[Enterprise] Remove a user from a project. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"project_id": {"type": "string", "minLength": 1},
"user_id": {"type": "string", "minLength": 1},
},
"required": ["project_id", "user_id"],
},
"scope": "admin",
},
]
async def list_projects(client: N8nClient, limit: int = 100, cursor: str | None = None) -> str:
try:
response = await client.list_projects(limit=limit, cursor=cursor)
projects = response.get("data", [])
result = {
"success": True,
"count": len(projects),
"projects": [{"id": p.get("id"), "name": p.get("name")} for p in projects],
"next_cursor": response.get("nextCursor"),
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_project(client: N8nClient, project_id: str) -> str:
try:
project = await client.get_project(project_id)
return json.dumps(
{"success": True, "project": {"id": project.get("id"), "name": project.get("name")}},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_project(client: N8nClient, name: str) -> str:
try:
project = await client.create_project(name)
return json.dumps(
{
"success": True,
"message": f"Project '{name}' created",
"project": {"id": project.get("id"), "name": project.get("name")},
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_project(client: N8nClient, project_id: str, name: str) -> str:
try:
project = await client.update_project(project_id, name)
return json.dumps(
{
"success": True,
"message": f"Project updated to '{name}'",
"project": {"id": project.get("id"), "name": project.get("name")},
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_project(client: N8nClient, project_id: str) -> str:
try:
await client.delete_project(project_id)
return json.dumps({"success": True, "message": f"Project {project_id} deleted"}, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def add_project_users(client: N8nClient, project_id: str, users: list[dict[str, str]]) -> str:
try:
relations = [{"userId": u["user_id"], "role": u["role"]} for u in users]
await client.add_project_users(project_id, relations)
return json.dumps(
{"success": True, "message": f"Added {len(users)} user(s) to project {project_id}"},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def change_project_user_role(
client: N8nClient, project_id: str, user_id: str, role: str
) -> str:
try:
await client.change_project_user_role(project_id, user_id, role)
return json.dumps(
{
"success": True,
"message": f"User {user_id} role changed to {role} in project {project_id}",
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def remove_project_user(client: N8nClient, project_id: str, user_id: str) -> str:
try:
await client.remove_project_user(project_id, user_id)
return json.dumps(
{"success": True, "message": f"User {user_id} removed from project {project_id}"},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)

View File

@@ -0,0 +1,150 @@
"""System Handler - manages n8n system operations (audit, source control, health)"""
import json
from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
{
"name": "run_security_audit",
"method_name": "run_security_audit",
"description": "Run a security audit on the n8n instance. Returns security diagnostics grouped by category. All parameters are OPTIONAL.",
"schema": {
"type": "object",
"properties": {
"categories": {
"type": "array",
"items": {"type": "string"},
"description": "OPTIONAL: Specific categories to audit. Omit for all categories.",
}
},
},
"scope": "admin",
},
{
"name": "source_control_pull",
"method_name": "source_control_pull",
"description": "[Enterprise] Pull workflows from source control (Git). Syncs workflows from connected repository. Requires n8n Enterprise/Pro license. All parameters are OPTIONAL.",
"schema": {
"type": "object",
"properties": {
"variables": {
"type": "object",
"description": "OPTIONAL: Variables to set during pull. Omit if not needed.",
},
"force": {
"type": "boolean",
"description": "Force pull even if conflicts exist",
"default": False,
},
},
},
"scope": "admin",
},
{
"name": "get_instance_info",
"method_name": "get_instance_info",
"description": "Get n8n instance information including version and configuration.",
"schema": {"type": "object", "properties": {}},
"scope": "read",
},
{
"name": "health_check",
"method_name": "health_check",
"description": "Check if the n8n instance is healthy and accessible.",
"schema": {"type": "object", "properties": {}},
"scope": "read",
},
]
async def run_security_audit(client: N8nClient, categories: list[str] | None = None) -> str:
"""Run security audit"""
try:
result = await client.run_audit(categories)
# Parse audit results
audit_data = {"success": True, "audit_results": result}
# Extract summary if available
if isinstance(result, dict):
risk_report = result.get("risk", {})
if risk_report:
audit_data["summary"] = {
"risk_categories": list(risk_report.keys()),
"total_issues": sum(
len(issues) if isinstance(issues, list) else 0
for issues in risk_report.values()
),
}
return json.dumps(audit_data, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def source_control_pull(
client: N8nClient, variables: dict[str, str] | None = None, force: bool = False
) -> str:
"""Pull from source control"""
try:
result = await client.source_control_pull(variables=variables, force=force)
return json.dumps(
{"success": True, "message": "Source control pull completed", "result": result},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_instance_info(client: N8nClient) -> str:
"""Get instance information"""
try:
# Try multiple endpoints to gather instance info
info = {}
# Get version info from settings or health
try:
health = await client.health_check()
info["health"] = health
except:
pass
# Get current user to verify connectivity
try:
user = await client.request("GET", "user")
info["current_user"] = {
"id": user.get("id"),
"email": user.get("email"),
"role": user.get("role"),
}
except:
pass
info["instance_url"] = client.site_url
info["api_base"] = client.api_base
return json.dumps({"success": True, "instance_info": info}, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def health_check(client: N8nClient) -> str:
"""Check instance health"""
try:
result = await client.health_check()
return json.dumps(
{
"success": True,
"healthy": result.get("healthy", False),
"status": result.get("status", "unknown"),
"instance_url": client.site_url,
},
indent=2,
)
except Exception as e:
return json.dumps(
{"success": False, "healthy": False, "error": str(e), "instance_url": client.site_url},
indent=2,
)

View File

@@ -0,0 +1,160 @@
"""Tags Handler - manages n8n tags"""
import json
from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
{
"name": "list_tags",
"method_name": "list_tags",
"description": "List all tags used for workflow organization. All parameters are OPTIONAL.",
"schema": {
"type": "object",
"properties": {
"limit": {"type": "integer", "default": 100, "minimum": 1, "maximum": 250},
"cursor": {"type": "string", "description": "OPTIONAL: Pagination cursor."},
},
},
"scope": "read",
},
{
"name": "get_tag",
"method_name": "get_tag",
"description": "Get tag details by ID.",
"schema": {
"type": "object",
"properties": {"tag_id": {"type": "string", "minLength": 1}},
"required": ["tag_id"],
},
"scope": "read",
},
{
"name": "create_tag",
"method_name": "create_tag",
"description": "Create a new tag for workflow organization.",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1, "description": "Tag name"}
},
"required": ["name"],
},
"scope": "write",
},
{
"name": "update_tag",
"method_name": "update_tag",
"description": "Update tag name.",
"schema": {
"type": "object",
"properties": {
"tag_id": {"type": "string", "minLength": 1},
"name": {"type": "string", "minLength": 1, "description": "New tag name"},
},
"required": ["tag_id", "name"],
},
"scope": "write",
},
{
"name": "delete_tag",
"method_name": "delete_tag",
"description": "Delete a tag.",
"schema": {
"type": "object",
"properties": {"tag_id": {"type": "string", "minLength": 1}},
"required": ["tag_id"],
},
"scope": "write",
},
{
"name": "delete_tags",
"method_name": "delete_tags",
"description": "Bulk delete multiple tags.",
"schema": {
"type": "object",
"properties": {
"tag_ids": {"type": "array", "items": {"type": "string"}, "minItems": 1}
},
"required": ["tag_ids"],
},
"scope": "write",
},
]
async def list_tags(client: N8nClient, limit: int = 100, cursor: str | None = None) -> str:
try:
response = await client.list_tags(limit=limit, cursor=cursor)
tags = response.get("data", [])
result = {
"success": True,
"count": len(tags),
"tags": [{"id": t.get("id"), "name": t.get("name")} for t in tags],
"next_cursor": response.get("nextCursor"),
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_tag(client: N8nClient, tag_id: str) -> str:
try:
tag = await client.get_tag(tag_id)
return json.dumps(
{"success": True, "tag": {"id": tag.get("id"), "name": tag.get("name")}}, indent=2
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_tag(client: N8nClient, name: str) -> str:
try:
tag = await client.create_tag(name)
return json.dumps(
{
"success": True,
"message": f"Tag '{name}' created",
"tag": {"id": tag.get("id"), "name": tag.get("name")},
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_tag(client: N8nClient, tag_id: str, name: str) -> str:
try:
tag = await client.update_tag(tag_id, name)
return json.dumps(
{
"success": True,
"message": f"Tag updated to '{name}'",
"tag": {"id": tag.get("id"), "name": tag.get("name")},
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_tag(client: N8nClient, tag_id: str) -> str:
try:
await client.delete_tag(tag_id)
return json.dumps({"success": True, "message": f"Tag {tag_id} deleted"}, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_tags(client: N8nClient, tag_ids: list[str]) -> str:
try:
deleted, failed = [], []
for tid in tag_ids:
try:
await client.delete_tag(tid)
deleted.append(tid)
except Exception as e:
failed.append({"id": tid, "error": str(e)})
return json.dumps(
{"success": len(failed) == 0, "deleted": deleted, "failed": failed if failed else None},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)

View File

@@ -0,0 +1,167 @@
"""Users Handler - manages n8n users"""
import json
from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
{
"name": "list_users",
"method_name": "list_users",
"description": "List all users in the n8n instance. All parameters are OPTIONAL.",
"schema": {
"type": "object",
"properties": {
"limit": {"type": "integer", "default": 100, "minimum": 1, "maximum": 250},
"cursor": {"type": "string", "description": "OPTIONAL: Pagination cursor."},
"include_role": {"type": "boolean", "default": True},
},
},
"scope": "admin",
},
{
"name": "get_user",
"method_name": "get_user",
"description": "Get user details by ID or email.",
"schema": {
"type": "object",
"properties": {
"user_id": {
"type": "string",
"minLength": 1,
"description": "User ID or email",
},
"include_role": {"type": "boolean", "default": True},
},
"required": ["user_id"],
},
"scope": "admin",
},
{
"name": "create_user",
"method_name": "create_user",
"description": "Invite/create a new user.",
"schema": {
"type": "object",
"properties": {
"email": {"type": "string", "format": "email", "description": "User email"},
"role": {
"type": "string",
"enum": ["global:owner", "global:admin", "global:member"],
"default": "global:member",
},
},
"required": ["email"],
},
"scope": "admin",
},
{
"name": "delete_user",
"method_name": "delete_user",
"description": "Delete a user from the instance.",
"schema": {
"type": "object",
"properties": {"user_id": {"type": "string", "minLength": 1}},
"required": ["user_id"],
},
"scope": "admin",
},
{
"name": "change_user_role",
"method_name": "change_user_role",
"description": "Change a user's global role.",
"schema": {
"type": "object",
"properties": {
"user_id": {"type": "string", "minLength": 1},
"new_role": {
"type": "string",
"enum": ["global:owner", "global:admin", "global:member"],
"description": "New role for the user",
},
},
"required": ["user_id", "new_role"],
},
"scope": "admin",
},
]
async def list_users(
client: N8nClient, limit: int = 100, cursor: str | None = None, include_role: bool = True
) -> str:
try:
response = await client.list_users(limit=limit, cursor=cursor, include_role=include_role)
users = response.get("data", [])
result = {
"success": True,
"count": len(users),
"users": [
{
"id": u.get("id"),
"email": u.get("email"),
"first_name": u.get("firstName"),
"last_name": u.get("lastName"),
"role": u.get("role"),
"is_pending": u.get("isPending"),
}
for u in users
],
"next_cursor": response.get("nextCursor"),
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_user(client: N8nClient, user_id: str, include_role: bool = True) -> str:
try:
user = await client.get_user(user_id, include_role)
return json.dumps(
{
"success": True,
"user": {
"id": user.get("id"),
"email": user.get("email"),
"first_name": user.get("firstName"),
"last_name": user.get("lastName"),
"role": user.get("role"),
"is_pending": user.get("isPending"),
},
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_user(client: N8nClient, email: str, role: str = "global:member") -> str:
try:
users = [{"email": email, "role": role}]
response = await client.create_user(users)
return json.dumps(
{
"success": True,
"message": f"User {email} invited successfully",
"users": response.get("data", []),
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_user(client: N8nClient, user_id: str) -> str:
try:
await client.delete_user(user_id)
return json.dumps({"success": True, "message": f"User {user_id} deleted"}, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def change_user_role(client: N8nClient, user_id: str, new_role: str) -> str:
try:
await client.change_user_role(user_id, new_role)
return json.dumps(
{"success": True, "message": f"User {user_id} role changed to {new_role}"}, indent=2
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)

View File

@@ -0,0 +1,186 @@
"""Variables Handler - manages n8n environment variables"""
import json
from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
{
"name": "list_variables",
"method_name": "list_variables",
"description": "[Enterprise] List all environment variables. Requires n8n Enterprise/Pro license. All parameters are OPTIONAL.",
"schema": {
"type": "object",
"properties": {
"limit": {"type": "integer", "default": 100, "minimum": 1, "maximum": 250},
"cursor": {"type": "string", "description": "OPTIONAL: Pagination cursor."},
},
},
"scope": "read",
},
{
"name": "get_variable",
"method_name": "get_variable",
"description": "[Enterprise] Get variable value by key. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"key": {"type": "string", "minLength": 1, "description": "Variable key"}
},
"required": ["key"],
},
"scope": "read",
},
{
"name": "create_variable",
"method_name": "create_variable",
"description": "[Enterprise] Create a new environment variable. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"key": {"type": "string", "minLength": 1, "description": "Variable key"},
"value": {"type": "string", "description": "Variable value"},
},
"required": ["key", "value"],
},
"scope": "admin",
},
{
"name": "update_variable",
"method_name": "update_variable",
"description": "[Enterprise] Update an existing variable's value. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"key": {"type": "string", "minLength": 1},
"value": {"type": "string", "description": "New value"},
},
"required": ["key", "value"],
},
"scope": "admin",
},
{
"name": "delete_variable",
"method_name": "delete_variable",
"description": "[Enterprise] Delete an environment variable. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {"key": {"type": "string", "minLength": 1}},
"required": ["key"],
},
"scope": "admin",
},
{
"name": "set_variables",
"method_name": "set_variables",
"description": "[Enterprise] Bulk set multiple variables at once. Requires n8n Enterprise/Pro license.",
"schema": {
"type": "object",
"properties": {
"variables": {
"type": "object",
"description": "Key-value pairs of variables to set",
"additionalProperties": {"type": "string"},
}
},
"required": ["variables"],
},
"scope": "admin",
},
]
async def list_variables(client: N8nClient, limit: int = 100, cursor: str | None = None) -> str:
try:
response = await client.list_variables(limit=limit, cursor=cursor)
variables = response.get("data", [])
result = {
"success": True,
"count": len(variables),
"variables": [{"key": v.get("key"), "value": v.get("value")} for v in variables],
"next_cursor": response.get("nextCursor"),
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_variable(client: N8nClient, key: str) -> str:
try:
variable = await client.get_variable(key)
return json.dumps(
{
"success": True,
"variable": {"key": variable.get("key"), "value": variable.get("value")},
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_variable(client: N8nClient, key: str, value: str) -> str:
try:
await client.create_variable(key, value)
return json.dumps(
{
"success": True,
"message": f"Variable '{key}' created",
"variable": {"key": key, "value": value},
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_variable(client: N8nClient, key: str, value: str) -> str:
try:
await client.update_variable(key, value)
return json.dumps(
{
"success": True,
"message": f"Variable '{key}' updated",
"variable": {"key": key, "value": value},
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_variable(client: N8nClient, key: str) -> str:
try:
await client.delete_variable(key)
return json.dumps({"success": True, "message": f"Variable '{key}' deleted"}, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def set_variables(client: N8nClient, variables: dict[str, str]) -> str:
try:
created, updated, failed = [], [], []
for key, value in variables.items():
try:
# Try to get existing variable
try:
await client.get_variable(key)
# Variable exists, update it
await client.update_variable(key, value)
updated.append(key)
except:
# Variable doesn't exist, create it
await client.create_variable(key, value)
created.append(key)
except Exception as e:
failed.append({"key": key, "error": str(e)})
return json.dumps(
{
"success": len(failed) == 0,
"created": created,
"updated": updated,
"failed": failed if failed else None,
},
indent=2,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)

View File

@@ -0,0 +1,680 @@
"""Workflow Handler - manages n8n workflows"""
import json
from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
# === LIST WORKFLOWS ===
{
"name": "list_workflows",
"method_name": "list_workflows",
"description": "List all n8n workflows with optional filters. Returns workflow ID, name, active status, tags, and metadata. All filter parameters are OPTIONAL.",
"schema": {
"type": "object",
"properties": {
"active": {
"type": "boolean",
"description": "OPTIONAL: Filter by active/inactive status. Omit for all workflows.",
},
"tags": {
"type": "string",
"description": "OPTIONAL: Filter by tag name(s), comma-separated. Omit for all workflows.",
},
"name": {
"type": "string",
"description": "OPTIONAL: Filter by workflow name (partial match). Omit for all workflows.",
},
"limit": {
"type": "integer",
"description": "Maximum workflows to return",
"default": 50,
"minimum": 1,
"maximum": 250,
},
"cursor": {
"type": "string",
"description": "OPTIONAL: Pagination cursor for next page.",
},
},
},
"scope": "read",
},
# === GET WORKFLOW ===
{
"name": "get_workflow",
"method_name": "get_workflow",
"description": "Get detailed information about a specific workflow including nodes, connections, and settings.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {"type": "string", "description": "Workflow ID", "minLength": 1}
},
"required": ["workflow_id"],
},
"scope": "read",
},
# === CREATE WORKFLOW ===
{
"name": "create_workflow",
"method_name": "create_workflow",
"description": "Create a new workflow from JSON definition. Workflow will be inactive by default. Note: settings and static_data are OPTIONAL - omit them entirely if not needed.",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Workflow name", "minLength": 1},
"nodes": {
"type": "array",
"description": "Array of node definitions",
"items": {"type": "object"},
"default": [],
},
"connections": {
"type": "object",
"description": "Node connections definition",
"default": {},
},
"settings": {
"type": "object",
"description": "OPTIONAL: Workflow settings (timezone, error workflow, etc.). Omit this parameter if not needed.",
"default": {},
},
"static_data": {
"type": "object",
"description": "OPTIONAL: Static data for the workflow. Omit this parameter if not needed.",
"default": {},
},
},
"required": ["name"],
},
"scope": "write",
},
# === UPDATE WORKFLOW ===
{
"name": "update_workflow",
"method_name": "update_workflow",
"description": "Update an existing workflow. Can modify nodes, connections, settings, or name. All parameters except workflow_id are OPTIONAL - omit them to keep current values.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Workflow ID to update",
"minLength": 1,
},
"name": {
"type": "string",
"description": "OPTIONAL: New workflow name. Omit to keep current.",
},
"nodes": {
"type": "array",
"items": {"type": "object"},
"description": "OPTIONAL: Updated node definitions. Omit to keep current.",
},
"connections": {
"type": "object",
"description": "OPTIONAL: Updated connections. Omit to keep current.",
},
"settings": {
"type": "object",
"description": "OPTIONAL: Updated settings. Omit to keep current.",
},
},
"required": ["workflow_id"],
},
"scope": "write",
},
# === DELETE WORKFLOW ===
{
"name": "delete_workflow",
"method_name": "delete_workflow",
"description": "Permanently delete a workflow. This action cannot be undone.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Workflow ID to delete",
"minLength": 1,
}
},
"required": ["workflow_id"],
},
"scope": "admin",
},
# === ACTIVATE WORKFLOW ===
{
"name": "activate_workflow",
"method_name": "activate_workflow",
"description": "Activate a workflow so it runs automatically when triggered. Workflow must have at least one trigger node.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Workflow ID to activate",
"minLength": 1,
}
},
"required": ["workflow_id"],
},
"scope": "write",
},
# === DEACTIVATE WORKFLOW ===
{
"name": "deactivate_workflow",
"method_name": "deactivate_workflow",
"description": "Deactivate a workflow. It will no longer run automatically but can still be executed manually.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Workflow ID to deactivate",
"minLength": 1,
}
},
"required": ["workflow_id"],
},
"scope": "write",
},
# === EXECUTE WORKFLOW ===
{
"name": "execute_workflow",
"method_name": "execute_workflow",
"description": "Manually execute a workflow and return execution ID. Use get_execution to check status and results.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Workflow ID to execute",
"minLength": 1,
}
},
"required": ["workflow_id"],
},
"scope": "write",
},
# === EXECUTE WORKFLOW WITH DATA ===
{
"name": "execute_workflow_with_data",
"method_name": "execute_workflow_with_data",
"description": "Execute workflow with custom input data. Useful for workflows with webhook or manual triggers.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Workflow ID to execute",
"minLength": 1,
},
"data": {
"type": "object",
"description": "Input data to pass to workflow trigger node",
},
},
"required": ["workflow_id", "data"],
},
"scope": "write",
},
# === DUPLICATE WORKFLOW ===
{
"name": "duplicate_workflow",
"method_name": "duplicate_workflow",
"description": "Create a copy of an existing workflow with a new name.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Workflow ID to duplicate",
"minLength": 1,
},
"new_name": {
"type": "string",
"description": "Name for the duplicated workflow",
"minLength": 1,
},
},
"required": ["workflow_id", "new_name"],
},
"scope": "write",
},
# === EXPORT WORKFLOW ===
{
"name": "export_workflow",
"method_name": "export_workflow",
"description": "Export a workflow as JSON. Can be used for backup or sharing.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {
"type": "string",
"description": "Workflow ID to export",
"minLength": 1,
}
},
"required": ["workflow_id"],
},
"scope": "read",
},
# === IMPORT WORKFLOW ===
{
"name": "import_workflow",
"method_name": "import_workflow",
"description": "Import a workflow from JSON definition. Similar to create but accepts full workflow JSON.",
"schema": {
"type": "object",
"properties": {
"workflow_json": {
"type": "object",
"description": "Full workflow JSON to import",
},
"name_override": {
"type": "string",
"description": "OPTIONAL: Override the workflow name from JSON. Omit to use the name from JSON.",
},
},
"required": ["workflow_json"],
},
"scope": "write",
},
# === GET WORKFLOW TAGS ===
{
"name": "get_workflow_tags",
"method_name": "get_workflow_tags",
"description": "Get list of tags assigned to a workflow.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {"type": "string", "description": "Workflow ID", "minLength": 1}
},
"required": ["workflow_id"],
},
"scope": "read",
},
# === SET WORKFLOW TAGS ===
{
"name": "set_workflow_tags",
"method_name": "set_workflow_tags",
"description": "Assign tags to a workflow. Replaces existing tags. Pass empty array [] to remove all tags.",
"schema": {
"type": "object",
"properties": {
"workflow_id": {"type": "string", "description": "Workflow ID", "minLength": 1},
"tag_ids": {
"type": "array",
"description": "List of tag IDs to assign. Use empty array [] to remove all tags. Get tag IDs using n8n_list_tags first.",
"items": {"type": "string"},
"default": [],
},
},
"required": ["workflow_id"],
},
"scope": "write",
},
]
# === HANDLER FUNCTIONS ===
async def list_workflows(
client: N8nClient,
active: bool | None = None,
tags: str | None = None,
name: str | None = None,
limit: int = 50,
cursor: str | None = None,
) -> str:
"""List all workflows with filters"""
try:
response = await client.list_workflows(
active=active, tags=tags, name=name, limit=limit, cursor=cursor
)
workflows = response.get("data", [])
next_cursor = response.get("nextCursor")
result = {
"success": True,
"count": len(workflows),
"workflows": [
{
"id": w.get("id"),
"name": w.get("name"),
"active": w.get("active"),
"tags": [t.get("name") for t in w.get("tags", [])],
"created_at": w.get("createdAt"),
"updated_at": w.get("updatedAt"),
}
for w in workflows
],
"next_cursor": next_cursor,
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_workflow(client: N8nClient, workflow_id: str) -> str:
"""Get workflow details"""
try:
workflow = await client.get_workflow(workflow_id)
result = {
"success": True,
"workflow": {
"id": workflow.get("id"),
"name": workflow.get("name"),
"active": workflow.get("active"),
"nodes": workflow.get("nodes", []),
"connections": workflow.get("connections", {}),
"settings": workflow.get("settings", {}),
"static_data": workflow.get("staticData"),
"tags": [t.get("name") for t in workflow.get("tags", [])],
"created_at": workflow.get("createdAt"),
"updated_at": workflow.get("updatedAt"),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_workflow(
client: N8nClient,
name: str,
nodes: list[dict[str, Any]] | None = None,
connections: dict[str, Any] | None = None,
settings: dict[str, Any] | None = None,
static_data: dict[str, Any] | None = None,
) -> str:
"""Create a new workflow"""
try:
# Use defaults for optional parameters
data = {
"name": name,
"nodes": nodes if nodes is not None else [],
"connections": connections if connections is not None else {},
}
# Only add settings/static_data if provided and non-empty
if settings and isinstance(settings, dict) and len(settings) > 0:
data["settings"] = settings
if static_data and isinstance(static_data, dict) and len(static_data) > 0:
data["staticData"] = static_data
workflow = await client.create_workflow(data)
result = {
"success": True,
"message": f"Workflow '{name}' created successfully",
"workflow": {
"id": workflow.get("id"),
"name": workflow.get("name"),
"active": workflow.get("active", False),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_workflow(
client: N8nClient,
workflow_id: str,
name: str | None = None,
nodes: list[dict[str, Any]] | None = None,
connections: dict[str, Any] | None = None,
settings: dict[str, Any] | None = None,
) -> str:
"""Update an existing workflow"""
try:
# Get current workflow first
current = await client.get_workflow(workflow_id)
# Build update data
data = {
"name": name or current.get("name"),
"nodes": nodes or current.get("nodes", []),
"connections": connections or current.get("connections", {}),
}
if settings:
data["settings"] = settings
workflow = await client.update_workflow(workflow_id, data)
result = {
"success": True,
"message": f"Workflow '{workflow.get('name')}' updated successfully",
"workflow": {
"id": workflow.get("id"),
"name": workflow.get("name"),
"active": workflow.get("active"),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_workflow(client: N8nClient, workflow_id: str) -> str:
"""Delete a workflow"""
try:
await client.delete_workflow(workflow_id)
result = {"success": True, "message": f"Workflow {workflow_id} deleted successfully"}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def activate_workflow(client: N8nClient, workflow_id: str) -> str:
"""Activate a workflow"""
try:
workflow = await client.activate_workflow(workflow_id)
result = {
"success": True,
"message": f"Workflow '{workflow.get('name')}' activated successfully",
"workflow": {
"id": workflow.get("id"),
"name": workflow.get("name"),
"active": workflow.get("active"),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def deactivate_workflow(client: N8nClient, workflow_id: str) -> str:
"""Deactivate a workflow"""
try:
workflow = await client.deactivate_workflow(workflow_id)
result = {
"success": True,
"message": f"Workflow '{workflow.get('name')}' deactivated successfully",
"workflow": {
"id": workflow.get("id"),
"name": workflow.get("name"),
"active": workflow.get("active"),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def execute_workflow(client: N8nClient, workflow_id: str) -> str:
"""Execute a workflow manually"""
try:
execution = await client.execute_workflow(workflow_id)
result = {
"success": True,
"message": "Workflow execution started",
"execution": {
"id": execution.get("id"),
"workflow_id": workflow_id,
"status": execution.get("status", "running"),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def execute_workflow_with_data(
client: N8nClient, workflow_id: str, data: dict[str, Any]
) -> str:
"""Execute workflow with custom input data"""
try:
execution = await client.execute_workflow(workflow_id, data=data)
result = {
"success": True,
"message": "Workflow execution started with custom data",
"execution": {
"id": execution.get("id"),
"workflow_id": workflow_id,
"status": execution.get("status", "running"),
"input_data": data,
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def duplicate_workflow(client: N8nClient, workflow_id: str, new_name: str) -> str:
"""Duplicate a workflow"""
try:
# Get the original workflow
original = await client.get_workflow(workflow_id)
# Create new workflow with same structure
data = {
"name": new_name,
"nodes": original.get("nodes", []),
"connections": original.get("connections", {}),
"settings": original.get("settings", {}),
}
new_workflow = await client.create_workflow(data)
result = {
"success": True,
"message": f"Workflow duplicated as '{new_name}'",
"original": {"id": original.get("id"), "name": original.get("name")},
"duplicate": {"id": new_workflow.get("id"), "name": new_workflow.get("name")},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def export_workflow(client: N8nClient, workflow_id: str) -> str:
"""Export workflow as JSON"""
try:
workflow = await client.get_workflow(workflow_id)
# Return full workflow JSON for export
result = {"success": True, "workflow_json": workflow}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def import_workflow(
client: N8nClient, workflow_json: dict[str, Any], name_override: str | None = None
) -> str:
"""Import workflow from JSON"""
try:
# Use name override if provided
if name_override:
workflow_json["name"] = name_override
# Ensure required fields
if "nodes" not in workflow_json:
workflow_json["nodes"] = []
if "connections" not in workflow_json:
workflow_json["connections"] = {}
workflow = await client.create_workflow(workflow_json)
result = {
"success": True,
"message": f"Workflow '{workflow.get('name')}' imported successfully",
"workflow": {
"id": workflow.get("id"),
"name": workflow.get("name"),
"active": workflow.get("active", False),
},
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_workflow_tags(client: N8nClient, workflow_id: str) -> str:
"""Get tags assigned to a workflow"""
try:
tags = await client.get_workflow_tags(workflow_id)
result = {"success": True, "workflow_id": workflow_id, "tags": tags}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def set_workflow_tags(
client: N8nClient, workflow_id: str, tag_ids: list[str] | None = None
) -> str:
"""Set tags for a workflow"""
try:
# Use empty list if tag_ids is None
tags_to_set = tag_ids if tag_ids is not None else []
# Get current workflow
current = await client.get_workflow(workflow_id)
# Update with new tags
data = {
"name": current.get("name"),
"nodes": current.get("nodes", []),
"connections": current.get("connections", {}),
"tags": [{"id": tid} for tid in tags_to_set],
}
workflow = await client.update_workflow(workflow_id, data)
result = {
"success": True,
"message": f"Tags updated for workflow {workflow_id}",
"workflow_id": workflow_id,
"tags": [t.get("name") for t in workflow.get("tags", [])],
}
return json.dumps(result, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)